add ecs sync consul
This commit is contained in:
parent
64e919f265
commit
1c4aa683df
6
Makefile
6
Makefile
|
@ -1,11 +1,9 @@
|
|||
help:
|
||||
echo "Read Makefile"
|
||||
|
||||
echo "Read Makefile" && echo "make build" && echo "make push vf=x.x.x vb=x.x.x"
|
||||
build:
|
||||
cd flask-consul && docker build -t flask-consul:latest .
|
||||
cd vue-consul && docker build -t nginx-consul:latest .
|
||||
echo -e "\n\n自行编译的版本,注意修改docker-compose.yml中的镜像地址为本地仓库后再启动。"
|
||||
echo -e "\n\nBlackbox-Manager:\nhttp://{ip}:1026"
|
||||
echo -e "\n\n自行编译的版本,注意修改docker-compose.yml中的镜像地址为本地仓库后再启动。\nBlackbox-Manager:\nhttp://{ip}:1026\n"
|
||||
|
||||
push:
|
||||
docker login --username=starsliao@163.com registry.cn-shenzhen.aliyuncs.com
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
FROM python:3-alpine
|
||||
ADD . /flask
|
||||
WORKDIR /flask
|
||||
RUN pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
#&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
||||
&& apk add --no-cache gcc libc-dev libffi-dev \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& pip3 install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple \
|
||||
&& pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
EXPOSE 2026
|
||||
CMD ["python3","./manager.py"]
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
import os
|
||||
from itsdangerous import TimedJSONWebSignatureSerializer
|
||||
|
||||
consul_token = os.environ.get('consul_token','635abc53-c18c-f780-58a9-f04feb28fef1')
|
||||
consul_url = os.environ.get('consul_url','http://10.0.0.26:8500/v1')
|
||||
consul_token = os.environ.get('consul_token','0a79caed-8a45-49b9-97a6-86e50e12b234')
|
||||
consul_url = os.environ.get('consul_url','http://10.5.148.67:8500/v1')
|
||||
admin_passwd = os.environ.get('admin_passwd','123456')
|
||||
secret_key = os.environ.get('secret_key',consul_token)
|
||||
s = TimedJSONWebSignatureSerializer(secret_key)
|
||||
|
||||
vendors = {'alicloud': '阿里云','tencent_cloud': '腾讯云','huaweicloud': '华为云'}
|
||||
regions = {'huaweicloud':{'none': '无','cn-east-3': '华东-上海一','cn-east-2': '华东-上海二',
|
||||
'cn-south-1': '华南-广州','cn-north-1': '华北-北京一','cn-north-4': '华北-北京四',
|
||||
'cn-southwest-2': '西南-贵阳一','ap-southeast-1': '中国-香港' },
|
||||
'alicloud':{'none': '无','cn-qingdao':'华北1(青岛)', 'cn-beijing':'华北2(北京)',
|
||||
'cn-zhangjiakou':'华北3(张家口)','cn-huhehaote':'华北5(呼和浩特)',
|
||||
'cn-wulanchabu':'华北6(乌兰察布)', 'cn-hangzhou':'华东1(杭州)',
|
||||
'cn-shanghai':'华东2(上海)', 'cn-shenzhen':'华南1(深圳)', 'cn-heyuan':'华南2(河源)',
|
||||
'cn-guangzhou':'华南3(广州)', 'cn-chengdu':'西南1(成都)', 'cn-hongkong':'中国(香港)',
|
||||
'cn-nanjing':'华东5(南京-本地地域)'},
|
||||
'tencent_cloud':{'none': '无',"ap-nanjing":"华东地区(南京)","ap-shanghai":"华东地区(上海)",
|
||||
"ap-guangzhou":"华南地区(广州)","ap-beijing":"华北地区(北京)","ap-tianjin":"华北地区(天津)",
|
||||
"ap-chengdu":"西南地区(成都)","ap-chongqing":"西南地区(重庆)",
|
||||
"ap-hongkong":"港澳台地区(中国香港)"}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
from flask import Flask
|
||||
from views import login, blackbox, consul
|
||||
from units import consul_kv
|
||||
import uuid
|
||||
skey_path = 'ConsulManager/assets/secret/skey'
|
||||
if consul_kv.get_kv_dict(skey_path) == {}:
|
||||
consul_kv.put_kv(skey_path,{'sk':''.join(str(uuid.uuid4()).split('-'))})
|
||||
|
||||
from views import login, blackbox, consul, jobs, nodes
|
||||
from units.cloud import huaweicloud,alicloud,tencent_cloud
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(login.blueprint)
|
||||
app.register_blueprint(blackbox.blueprint)
|
||||
app.register_blueprint(consul.blueprint)
|
||||
app.register_blueprint(jobs.blueprint)
|
||||
app.register_blueprint(nodes.blueprint)
|
||||
|
||||
class Config(object):
|
||||
JOBS = []
|
||||
SCHEDULER_API_ENABLED = True
|
||||
init_jobs = consul_kv.get_kv_dict('ConsulManager/jobs')
|
||||
if init_jobs is not None:
|
||||
for k,v in init_jobs.items():
|
||||
print(f'初始化任务:{k}:\n {v}', flush=True)
|
||||
Config.JOBS = init_jobs.values()
|
||||
app.config.from_object(Config())
|
||||
|
||||
if __name__ == "__main__":
|
||||
scheduler = jobs.init()
|
||||
scheduler.init_app(app)
|
||||
scheduler.start()
|
||||
app.run(host="0.0.0.0", port=2026)
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
Werkzeug==2.0.3
|
||||
itsdangerous==2.0.1
|
||||
flask==2.0.2
|
||||
flask-restful==0.3.9
|
||||
flask-cors==3.0.10
|
||||
itsdangerous==2.0.1
|
||||
Flask-HTTPAuth==4.5.0
|
||||
requests==2.27.1
|
||||
Flask-APScheduler==1.12.3
|
||||
#pyDes==2.0.1
|
||||
pycryptodome==3.14.1
|
||||
huaweicloudsdkcore==3.0.78
|
||||
huaweicloudsdkecs==3.0.78
|
||||
huaweicloudsdkeps==3.0.78
|
||||
alibabacloud_resourcemanager20200331==2.1.0
|
||||
alibabacloud_ecs20140526==2.1.0
|
||||
tencentcloud-sdk-python-common==3.0.607
|
||||
tencentcloud-sdk-python-cvm==3.0.607
|
||||
tencentcloud-sdk-python-dcdb==3.0.607
|
||||
|
|
|
@ -61,3 +61,149 @@ def del_service(module,company,project,env,name):
|
|||
return {"code": 20000, "data": f"【{sid}】删除成功!"}
|
||||
else:
|
||||
return {"code": 50000, "data": f"{reg.status_code}【{sid}】{reg.text}"}
|
||||
|
||||
def get_rules():
|
||||
rules = """
|
||||
- name: Domain
|
||||
rules:
|
||||
- alert: 站点可用性
|
||||
expr: probe_success == 0
|
||||
for: 1m
|
||||
labels:
|
||||
alertype: domain
|
||||
severity: critical
|
||||
annotations:
|
||||
description: "{{ $labels.env }}_{{ $labels.name }}({{ $labels.project }}):站点无法访问\\n> {{ $labels.instance }}"
|
||||
|
||||
- alert: 站点1h可用性低于80%
|
||||
expr: sum_over_time(probe_success[1h])/count_over_time(probe_success[1h]) * 100 < 80
|
||||
for: 3m
|
||||
labels:
|
||||
alertype: domain
|
||||
severity: warning
|
||||
annotations:
|
||||
description: "{{ $labels.env }}_{{ $labels.name }}({{ $labels.project }}):站点1h可用性:{{ $value | humanize }}%\\n> {{ $labels.instance }}"
|
||||
|
||||
- alert: 站点状态异常
|
||||
expr: (probe_success == 0 and probe_http_status_code > 499) or probe_http_status_code == 0
|
||||
for: 1m
|
||||
labels:
|
||||
alertype: domain
|
||||
severity: warning
|
||||
annotations:
|
||||
description: "{{ $labels.env }}_{{ $labels.name }}({{ $labels.project }}):站点状态异常:{{ $value }}\\n> {{ $labels.instance }}"
|
||||
|
||||
- alert: 站点耗时过高
|
||||
expr: probe_duration_seconds > 0.5
|
||||
for: 2m
|
||||
labels:
|
||||
alertype: domain
|
||||
severity: warning
|
||||
annotations:
|
||||
description: "{{ $labels.env }}_{{ $labels.name }}({{ $labels.project }}):当前站点耗时:{{ $value | humanize }}s\\n> {{ $labels.instance }}"
|
||||
|
||||
- alert: SSL证书有效期
|
||||
expr: (probe_ssl_earliest_cert_expiry-time()) / 3600 / 24 < 15
|
||||
for: 2m
|
||||
labels:
|
||||
alertype: domain
|
||||
severity: warning
|
||||
annotations:
|
||||
description: "{{ $labels.env }}_{{ $labels.name }}({{ $labels.project }}):证书有效期剩余{{ $value | humanize }}天\\n> {{ $labels.instance }}"
|
||||
"""
|
||||
return {"code": 20000, "rules": rules}
|
||||
|
||||
def get_bconfig():
|
||||
bconfig = """
|
||||
modules:
|
||||
http_2xx:
|
||||
prober: http
|
||||
http:
|
||||
valid_status_codes: [200,204]
|
||||
no_follow_redirects: false
|
||||
preferred_ip_protocol: ip4
|
||||
ip_protocol_fallback: false
|
||||
|
||||
# 用于需要检查SSL证书有效性,但是该域名访问后又会重定向到其它域名的情况,这样检查的证书有效期就是重定向后域名的。
|
||||
# 如果需要检查源域名信息,需要在blackbox中增加禁止重定向参数。
|
||||
httpNoRedirect4ssl:
|
||||
prober: http
|
||||
http:
|
||||
valid_status_codes: [200,204,301,302,303]
|
||||
no_follow_redirects: true
|
||||
preferred_ip_protocol: ip4
|
||||
ip_protocol_fallback: false
|
||||
|
||||
# 用于忽略SSL证书检查的站点监控。
|
||||
http200igssl:
|
||||
prober: http
|
||||
http:
|
||||
valid_status_codes:
|
||||
- 200
|
||||
tls_config:
|
||||
insecure_skip_verify: true
|
||||
|
||||
http_4xx:
|
||||
prober: http
|
||||
http:
|
||||
valid_status_codes: [401,403,404]
|
||||
preferred_ip_protocol: ip4
|
||||
ip_protocol_fallback: false
|
||||
|
||||
http_5xx:
|
||||
prober: http
|
||||
http:
|
||||
valid_status_codes: [500,502]
|
||||
preferred_ip_protocol: ip4
|
||||
ip_protocol_fallback: false
|
||||
|
||||
http_post_2xx:
|
||||
prober: http
|
||||
http:
|
||||
method: POST
|
||||
|
||||
icmp:
|
||||
prober: icmp
|
||||
|
||||
tcp_connect:
|
||||
prober: tcp
|
||||
|
||||
ssh_banner:
|
||||
prober: tcp
|
||||
tcp:
|
||||
query_response:
|
||||
- expect: "^SSH-2.0-"
|
||||
- send: "SSH-2.0-blackbox-ssh-check"
|
||||
"""
|
||||
return {"code": 20000, "bconfig": bconfig}
|
||||
|
||||
def get_pconfig():
|
||||
consul_server = consul_url.split("/")[2]
|
||||
pconfig = f"""
|
||||
- job_name: 'blackbox_exporter'
|
||||
metrics_path: /probe
|
||||
consul_sd_configs:
|
||||
- server: '{consul_server}'
|
||||
token: '{consul_token}'
|
||||
services: ['blackbox_exporter']
|
||||
relabel_configs:
|
||||
- source_labels: ["__meta_consul_service_metadata_instance"]
|
||||
target_label: __param_target
|
||||
- source_labels: [__meta_consul_service_metadata_module]
|
||||
target_label: __param_module
|
||||
- source_labels: [__meta_consul_service_metadata_module]
|
||||
target_label: module
|
||||
- source_labels: ["__meta_consul_service_metadata_company"]
|
||||
target_label: company
|
||||
- source_labels: ["__meta_consul_service_metadata_env"]
|
||||
target_label: env
|
||||
- source_labels: ["__meta_consul_service_metadata_name"]
|
||||
target_label: name
|
||||
- source_labels: ["__meta_consul_service_metadata_project"]
|
||||
target_label: project
|
||||
- source_labels: [__param_target]
|
||||
target_label: instance
|
||||
- target_label: __address__
|
||||
replacement: 127.0.0.1:9115
|
||||
"""
|
||||
return {"code": 20000, "pconfig": pconfig}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
from alibabacloud_resourcemanager20200331.client import Client as ResourceManager20200331Client
|
||||
from alibabacloud_tea_openapi import models as open_api_models
|
||||
from alibabacloud_resourcemanager20200331 import models as resource_manager_20200331_models
|
||||
from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
|
||||
from alibabacloud_ecs20140526 import models as ecs_20140526_models
|
||||
from Tea.exceptions import TeaException
|
||||
|
||||
import sys,datetime
|
||||
from units import consul_kv
|
||||
from units.cloud import sync_ecs
|
||||
|
||||
def group(account):
|
||||
ak,sk = consul_kv.get_aksk('alicloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
config = open_api_models.Config(access_key_id=ak,access_key_secret=sk)
|
||||
config.endpoint = f'resourcemanager.aliyuncs.com'
|
||||
client = ResourceManager20200331Client(config)
|
||||
list_resource_groups_request = resource_manager_20200331_models.ListResourceGroupsRequest(page_size=100)
|
||||
try:
|
||||
proj = client.list_resource_groups(list_resource_groups_request)
|
||||
proj_list = proj.body.resource_groups.to_map()['ResourceGroup']
|
||||
group_dict = {i['Id']:i['DisplayName'] for i in proj_list}
|
||||
consul_kv.put_kv(f'ConsulManager/assets/alicloud/group/{account}',group_dict)
|
||||
count = len(group_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'msg':f'同步资源组成功!总数:{count}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/group', data)
|
||||
print('【JOB】===>', 'alicloud_group', account, data, flush=True)
|
||||
except TeaException as e:
|
||||
emsg = e.message.split('. ',1)[0]
|
||||
print("【code:】",e.code,"\n【message:】",emsg, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/alicloud/{account}/group')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败{e.code}','status':50000,'msg':emsg}
|
||||
else:
|
||||
data['update'] = f'失败{e.code}'
|
||||
data['msg'] = emsg
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/group', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/group', data)
|
||||
|
||||
def ecs(account,region):
|
||||
ak,sk = consul_kv.get_aksk('alicloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
group_dict = consul_kv.get_value(f'ConsulManager/assets/alicloud/group/{account}')
|
||||
|
||||
config = open_api_models.Config(access_key_id=ak,access_key_secret=sk)
|
||||
config.endpoint = f'ecs.{region}.aliyuncs.com'
|
||||
client = Ecs20140526Client(config)
|
||||
|
||||
next_token = '0'
|
||||
ecs_dict = {}
|
||||
try:
|
||||
while next_token != '':
|
||||
describe_instances_request = ecs_20140526_models.DescribeInstancesRequest(
|
||||
max_results=100,
|
||||
region_id=region,
|
||||
next_token=next_token
|
||||
)
|
||||
ecs = client.describe_instances(describe_instances_request)
|
||||
ecs_list = ecs.body.instances.to_map()['Instance']
|
||||
ecs_dict_temp = {i['InstanceId']:{
|
||||
'name':i['InstanceName'],'group':group_dict.get(i['ResourceGroupId'],'无'),'ostype':i['OSType'].lower(),
|
||||
'status':i['Status'],'region':region,
|
||||
'ip':i["InnerIpAddress"]["IpAddress"] if len(i["InnerIpAddress"]["IpAddress"]) != 0 else i['NetworkInterfaces']['NetworkInterface'][0]['PrimaryIpAddress'],
|
||||
'cpu':f"{i['Cpu']}核",'mem':f"{str(round(i['Memory']/1024,1)).rstrip('.0')}GB",'exp':i['ExpiredTime'].split('T')[0]
|
||||
}for i in ecs_list}
|
||||
ecs_dict.update(ecs_dict_temp)
|
||||
next_token = ecs.body.next_token
|
||||
|
||||
count = len(ecs_dict)
|
||||
off,on = sync_ecs.w2consul('alicloud',account,region,ecs_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'on':on,'off':off,'msg':f'ECS同步成功!总数:{count},开机:{on},关机:{off}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/ecs/{region}', data)
|
||||
print('【JOB】===>', 'alicloud_ecs', account,region, data, flush=True)
|
||||
except TeaException as e:
|
||||
emsg = e.message.split('. ',1)[0]
|
||||
print("【code:】",e.code,"\n【message:】",emsg, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/alicloud/{account}/ecs/{region}')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败{e.code}','status':50000,'msg':emsg}
|
||||
else:
|
||||
data['update'] = f'失败{e.code}'
|
||||
data['msg'] = emsg
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/ecs/{region}', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/alicloud/{account}/ecs/{region}', data)
|
|
@ -0,0 +1,87 @@
|
|||
from huaweicloudsdkcore.auth.credentials import GlobalCredentials,BasicCredentials
|
||||
from huaweicloudsdkeps.v1.region.eps_region import EpsRegion
|
||||
from huaweicloudsdkcore.exceptions import exceptions
|
||||
from huaweicloudsdkeps.v1 import *
|
||||
from huaweicloudsdkecs.v2.region.ecs_region import EcsRegion
|
||||
from huaweicloudsdkecs.v2 import *
|
||||
import sys,datetime
|
||||
from units import consul_kv
|
||||
from units.cloud import sync_ecs
|
||||
def group(account):
|
||||
ak,sk = consul_kv.get_aksk('huaweicloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
credentials = GlobalCredentials(ak, sk)
|
||||
try:
|
||||
client = EpsClient.new_builder() \
|
||||
.with_credentials(credentials) \
|
||||
.with_region(EpsRegion.value_of("cn-north-4")) \
|
||||
.build()
|
||||
request = ListEnterpriseProjectRequest()
|
||||
request.status = 1
|
||||
request.offset = 0
|
||||
info = client.list_enterprise_project(request).to_dict()['enterprise_projects']
|
||||
group_dict = {i['id']:i['name'] for i in info}
|
||||
consul_kv.put_kv(f'ConsulManager/assets/huaweicloud/group/{account}',group_dict)
|
||||
count = len(group_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'msg':f'同步企业项目成功!总数:{count}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/group', data)
|
||||
print('【JOB】===>', 'huaweicloud_group', account, data, flush=True)
|
||||
except exceptions.ClientRequestException as e:
|
||||
print(e.status_code, flush=True)
|
||||
print(e.request_id, flush=True)
|
||||
print(e.error_code, flush=True)
|
||||
print(e.error_msg, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/huaweicloud/{account}/group')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败{e.status_code}','status':50000,'msg':e.error_msg}
|
||||
else:
|
||||
data['update'] = f'失败{e.status_code}'
|
||||
data['msg'] = e.error_msg
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/group', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/group', data)
|
||||
|
||||
def ecs(account,region):
|
||||
ak,sk = consul_kv.get_aksk('huaweicloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
group_dict = consul_kv.get_value(f'ConsulManager/assets/huaweicloud/group/{account}')
|
||||
credentials = BasicCredentials(ak, sk)
|
||||
try:
|
||||
client = EcsClient.new_builder() \
|
||||
.with_credentials(credentials) \
|
||||
.with_region(EcsRegion.value_of(region)) \
|
||||
.build()
|
||||
request = ListServersDetailsRequest()
|
||||
request.limit = 1000
|
||||
info = client.list_servers_details(request).to_dict()['servers']
|
||||
ecs_dict = {i['id']:{'name':i['name'],
|
||||
'ip':i['addresses'][i['metadata']['vpc_id']][0].addr,
|
||||
'region':region,
|
||||
'group':group_dict[i['enterprise_project_id']],
|
||||
'status':i['status'],
|
||||
'ostype':i['metadata']['os_type'].lower(),
|
||||
'cpu':i['flavor']['vcpus'] + '核',
|
||||
'mem':f"{str(round(int(i['flavor']['ram'])/1024,1)).rstrip('.0')}GB",
|
||||
'exp': '-'
|
||||
} for i in info}
|
||||
count = len(ecs_dict)
|
||||
off,on = sync_ecs.w2consul('huaweicloud',account,region,ecs_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'on':on,'off':off,'msg':f'ECS同步成功!总数:{count},开机:{on},关机:{off}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/ecs/{region}', data)
|
||||
print('【JOB】===>', 'huaweicloud_ecs', account,region, data, flush=True)
|
||||
except exceptions.ClientRequestException as e:
|
||||
print(e.status_code, flush=True)
|
||||
print(e.request_id, flush=True)
|
||||
print(e.error_code, flush=True)
|
||||
print(e.error_msg, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/huaweicloud/{account}/ecs/{region}')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败{e.status_code}','status':50000,'on':0,'off':0,'msg':e.error_msg}
|
||||
else:
|
||||
data['update'] = f'失败{e.status_code}'
|
||||
data['msg'] = e.error_msg
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/ecs/{region}', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/huaweicloud/{account}/ecs/{region}', data)
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/python3
|
||||
import requests,json
|
||||
from config import consul_token,consul_url,vendors,regions
|
||||
headers = {'X-Consul-Token': consul_token}
|
||||
geturl = f'{consul_url}/agent/services'
|
||||
delurl = f'{consul_url}/agent/service/deregister'
|
||||
puturl = f'{consul_url}/agent/service/register'
|
||||
def w2consul(vendor,account,region,ecs_dict):
|
||||
service_name = f'{vendor}_{account}_ecs'
|
||||
params = {'filter': f'Service == "{service_name}" and "{region}" in Tags and Meta.account == "{account}"'}
|
||||
try:
|
||||
consul_ecs_iid_list = requests.get(geturl, headers=headers, params=params).json().keys()
|
||||
except:
|
||||
consul_ecs_iid_list = []
|
||||
|
||||
#在consul中删除云厂商不存在的ecs
|
||||
for del_ecs in [x for x in consul_ecs_iid_list if x not in ecs_dict.keys()]:
|
||||
dereg = requests.put(f'{delurl}/{del_ecs}', headers=headers)
|
||||
if dereg.status_code == 200:
|
||||
print({"code": 20000,"data": f"{account}-删除成功!"}, flush=True)
|
||||
else:
|
||||
print({"code": 50000,"data": f'{dereg.status_code}:{dereg.text}'}, flush=True)
|
||||
off,on = 0,0
|
||||
for k,v in ecs_dict.items():
|
||||
iid = k
|
||||
#去除consul中关机的ecs
|
||||
if v['status'] in ['SHUTOFF','Stopped','STOPPED']:
|
||||
off = off + 1
|
||||
if k in consul_ecs_iid_list:
|
||||
dereg = requests.put(f'{delurl}/{iid}', headers=headers)
|
||||
if dereg.status_code == 200:
|
||||
print({"code": 20000,"data": f"{account}-删除成功!"}, flush=True)
|
||||
else:
|
||||
print({"code": 50000,"data": f'{dereg.status_code}:{dereg.text}'}, flush=True)
|
||||
else:
|
||||
on = on + 1
|
||||
port = 9100 if v['ostype'] == 'linux' else 9182
|
||||
ip = v['ip'] if isinstance(v['ip'],list) is False else v['ip'][0]
|
||||
instance = f'{ip}:{port}'
|
||||
data = {
|
||||
'id': iid,
|
||||
'name': service_name,
|
||||
'Address': ip,
|
||||
'port': port,
|
||||
'tags': [v['ostype'],region],
|
||||
'Meta': {
|
||||
'iid': iid,
|
||||
'name': v['name'],
|
||||
'region': regions[vendor].get(region,'未找到'),
|
||||
'group': v['group'],
|
||||
'instance': instance,
|
||||
'account': account,
|
||||
'vendor': vendors.get(vendor,'未找到'),
|
||||
'os': v['ostype'],
|
||||
'cpu': v['cpu'],
|
||||
'mem': v['mem'],
|
||||
'exp': v['exp']
|
||||
},
|
||||
"check": {
|
||||
"tcp": f"{ip}:{port}",
|
||||
"interval": "60s"
|
||||
}
|
||||
}
|
||||
reg = requests.put(puturl, headers=headers, data=json.dumps(data))
|
||||
if reg.status_code == 200:
|
||||
pass
|
||||
#print({f"{account}:code": 20000,"data": "增加成功!"}, flush=True)
|
||||
else:
|
||||
print({f"{account}:code": 50000,"data": f'{reg.status_code}:{reg.text}'}, flush=True)
|
||||
return off,on
|
|
@ -0,0 +1,95 @@
|
|||
import json
|
||||
from tencentcloud.common import credential
|
||||
from tencentcloud.common.profile.client_profile import ClientProfile
|
||||
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||||
|
||||
import sys,datetime
|
||||
#sys.path.append("..")
|
||||
#import consul_kv,sync_ecs
|
||||
from units import consul_kv
|
||||
from units.cloud import sync_ecs
|
||||
|
||||
def group(account):
|
||||
from tencentcloud.dcdb.v20180411 import dcdb_client, models
|
||||
ak,sk = consul_kv.get_aksk('tencent_cloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
try:
|
||||
cred = credential.Credential(ak, sk)
|
||||
httpProfile = HttpProfile()
|
||||
httpProfile.endpoint = "dcdb.tencentcloudapi.com"
|
||||
clientProfile = ClientProfile()
|
||||
clientProfile.httpProfile = httpProfile
|
||||
client = dcdb_client.DcdbClient(cred, "ap-guangzhou", clientProfile)
|
||||
req = models.DescribeProjectsRequest()
|
||||
params = {}
|
||||
req.from_json_string(json.dumps(params))
|
||||
proj_list = client.DescribeProjects(req).Projects
|
||||
|
||||
group_dict = {i.ProjectId:i.Name for i in proj_list}
|
||||
consul_kv.put_kv(f'ConsulManager/assets/tencent_cloud/group/{account}',group_dict)
|
||||
count = len(group_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'msg':f'同步资源组成功!总数:{count}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/group', data)
|
||||
print('【JOB】===>', 'tencent_cloud_group', account, data, flush=True)
|
||||
except TencentCloudSDKException as err:
|
||||
print(err, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/tencent_cloud/{account}/group')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(err)}
|
||||
else:
|
||||
data['update'] = f'失败'
|
||||
data['msg'] = str(err)
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/group', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/group', data)
|
||||
|
||||
def ecs(account,region):
|
||||
from tencentcloud.cvm.v20170312 import cvm_client, models
|
||||
ak,sk = consul_kv.get_aksk('tencent_cloud',account)
|
||||
now = datetime.datetime.now().strftime('%m%d/%H:%M')
|
||||
group_dict = consul_kv.get_value(f'ConsulManager/assets/tencent_cloud/group/{account}')
|
||||
try:
|
||||
cred = credential.Credential(ak, sk)
|
||||
httpProfile = HttpProfile()
|
||||
httpProfile.endpoint = "cvm.tencentcloudapi.com"
|
||||
|
||||
clientProfile = ClientProfile()
|
||||
clientProfile.httpProfile = httpProfile
|
||||
client = cvm_client.CvmClient(cred, region, clientProfile)
|
||||
req = models.DescribeInstancesRequest()
|
||||
offset = 0
|
||||
total = 0
|
||||
ecs_dict = {}
|
||||
while offset <= total:
|
||||
params = {"Offset": offset, "Limit": 100}
|
||||
req.from_json_string(json.dumps(params))
|
||||
resp = client.DescribeInstances(req)
|
||||
ecs_list = resp.InstanceSet
|
||||
total = resp.TotalCount
|
||||
ecs_dict_temp = {i.InstanceId:{'name':i.InstanceName,'group':group_dict.get(str(i.Placement.ProjectId),'无'),
|
||||
'ostype': 'windows' if 'win' in i.OsName.lower() else 'linux',
|
||||
'status': i.InstanceState, 'region': region, 'ip':i.PrivateIpAddresses[0],
|
||||
'cpu': f'{i.CPU}核','mem': f'{i.Memory}GB','exp': i.ExpiredTime.split('T')[0]
|
||||
} for i in ecs_list}
|
||||
offset = offset + 100
|
||||
ecs_dict.update(ecs_dict_temp)
|
||||
|
||||
count = len(ecs_dict)
|
||||
off,on = sync_ecs.w2consul('tencent_cloud',account,region,ecs_dict)
|
||||
data = {'count':count,'update':now,'status':20000,'on':on,'off':off,'msg':f'ECS同步成功!总数:{count},开机:{on},关机:{off}'}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/ecs/{region}', data)
|
||||
print('【JOB】===>', 'tencent_cloud_ecs', account,region, data, flush=True)
|
||||
except TencentCloudSDKException as err:
|
||||
print(err, flush=True)
|
||||
data = consul_kv.get_value(f'ConsulManager/record/jobs/tencent_cloud/{account}/ecs/{region}')
|
||||
if data == {}:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(err)}
|
||||
else:
|
||||
data['update'] = f'失败'
|
||||
data['msg'] = str(err)
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/ecs/{region}', data)
|
||||
except Exception as e:
|
||||
data = {'count':'无','update':f'失败','status':50000,'msg':str(e)}
|
||||
consul_kv.put_kv(f'ConsulManager/record/jobs/tencent_cloud/{account}/ecs/{region}', data)
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
import requests,json,sys,os
|
||||
from base64 import b64decode
|
||||
from config import consul_token,consul_url
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
#import myaes
|
||||
|
||||
headers = {'X-Consul-Token': consul_token}
|
||||
|
||||
def get_value(path):
|
||||
url = f'{consul_url}/kv/{path}?raw'
|
||||
response = requests.get(url, headers=headers)
|
||||
response.encoding='utf-8'
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return {}
|
||||
def get_kv_dict(path):
|
||||
url = f'{consul_url}/kv/{path}?recurse'
|
||||
response = requests.get(url, headers=headers)
|
||||
response.encoding='utf-8'
|
||||
if response.status_code == 200 and response.text != '':
|
||||
info = response.json()
|
||||
kv_dict = {i['Key']:json.loads(b64decode(i['Value']).decode('utf-8')) for i in info if i['Value'] != None}
|
||||
if kv_dict != {}:
|
||||
return kv_dict
|
||||
else:
|
||||
return {}
|
||||
else:
|
||||
return {}
|
||||
def get_keys_list(path):
|
||||
url = f'{consul_url}/kv/{path}?keys'
|
||||
response = requests.get(url, headers=headers)
|
||||
response.encoding='utf-8'
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return []
|
||||
def put_kv(path,value):
|
||||
url = f'{consul_url}/kv/{path}'
|
||||
payload = json.dumps(value,ensure_ascii=False).encode("utf-8")
|
||||
response = requests.put(url, headers=headers, data=payload)
|
||||
return response.json()
|
||||
|
||||
def del_key(path):
|
||||
url = f'{consul_url}/kv/{path}'
|
||||
response = requests.delete(url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_ecs_services(job_id):
|
||||
cloud,account,itype,region = job_id.split('/')
|
||||
service = f'{cloud}_{account}_{itype}'
|
||||
region = f'and "{region}" in Tags'
|
||||
url = f'{consul_url}/agent/services?filter=Service == "{service}" {region}'
|
||||
response = requests.get(url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
info = response.json()
|
||||
ecs_list = [i['Meta'] for i in info.values()]
|
||||
return {'code': 20000,'ecs_list': ecs_list}
|
||||
else:
|
||||
return {'code': 50000, 'data': f'{response.status_code}:{response.text}'}
|
||||
|
||||
def get_aksk(cloud,account):
|
||||
import myaes
|
||||
aksk_dict = get_value(f'ConsulManager/assets/{cloud}/aksk/{account}')
|
||||
ak = myaes.decrypt(aksk_dict['ak'])
|
||||
sk = myaes.decrypt(aksk_dict['sk'])
|
||||
return ak, sk
|
||||
|
||||
def put_aksk(cloud,account,ak,sk):
|
||||
import myaes
|
||||
encrypt_aksk = {'ak': myaes.encrypt(ak), 'sk': myaes.encrypt(sk)}
|
||||
return put_kv(f'ConsulManager/assets/{cloud}/aksk/{account}', encrypt_aksk)
|
|
@ -112,7 +112,7 @@ def add_instance(instance_dict):
|
|||
|
||||
del instance_dict['metaInfo']
|
||||
del instance_dict['checkInfo']
|
||||
print(instance_dict)
|
||||
print(instance_dict, flush=True)
|
||||
|
||||
reg = requests.put(f'{consul_url}/agent/service/register', headers=headers, data=json.dumps(instance_dict))
|
||||
if reg.status_code == 200:
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
from config import consul_token,consul_url
|
||||
def ecs_config(services_list,ostype_list):
|
||||
consul_server = consul_url.split("/")[2]
|
||||
job_dict = {'linux':'node_exporter','windows':'windows_exporter'}
|
||||
configs = ''
|
||||
for ostype in ostype_list:
|
||||
job_name = job_dict[ostype]
|
||||
config_str = f"""
|
||||
- job_name: {job_name}
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 5s
|
||||
consul_sd_configs:
|
||||
- server: '{consul_server}'
|
||||
token: '{consul_token}'
|
||||
refresh_interval: 30s
|
||||
services: {services_list}
|
||||
tags: ['{ostype}']
|
||||
relabel_configs:
|
||||
- source_labels: ['__meta_consul_service_metadata_vendor']
|
||||
target_label: vendor
|
||||
- source_labels: ['__meta_consul_service_metadata_region']
|
||||
target_label: region
|
||||
- source_labels: ['__meta_consul_service_metadata_group']
|
||||
target_label: group
|
||||
- source_labels: ['__meta_consul_service_metadata_account']
|
||||
target_label: account
|
||||
- source_labels: ['__meta_consul_service_metadata_name']
|
||||
target_label: name
|
||||
- source_labels: ['__meta_consul_service_metadata_iid']
|
||||
target_label: iid
|
||||
- source_labels: ['__meta_consul_service_metadata_exp']
|
||||
target_label: exp
|
||||
- source_labels: ['__meta_consul_service_metadata_instance']
|
||||
target_label: instance
|
||||
- source_labels: [instance]
|
||||
target_label: __address__
|
||||
"""
|
||||
configs = configs + config_str
|
||||
return {'code': 20000,'configs': configs }
|
|
@ -0,0 +1,20 @@
|
|||
# encoding:utf-8
|
||||
from base64 import b64encode,b64decode
|
||||
from Crypto.Util.Padding import pad,unpad
|
||||
from Crypto.Cipher import AES
|
||||
import consul_kv
|
||||
secret_key = consul_kv.get_value('ConsulManager/assets/secret/skey')['sk'].encode('utf8')
|
||||
|
||||
def encrypt(data):
|
||||
data = data.encode('utf8')
|
||||
cipher = AES.new(secret_key, AES.MODE_CBC)
|
||||
encrypted_data = cipher.encrypt(pad(data, 16))
|
||||
data = cipher.iv + encrypted_data
|
||||
return b64encode(data).decode('utf8')
|
||||
|
||||
def decrypt(data):
|
||||
data = b64decode(data)
|
||||
iv = data[:16]
|
||||
cipher = AES.new(secret_key, AES.MODE_CBC, iv)
|
||||
data = unpad(cipher.decrypt(data[16:]), 16)
|
||||
return data.decode('utf8')
|
|
@ -0,0 +1,14 @@
|
|||
from pyDes import des, ECB, PAD_PKCS5
|
||||
import binascii, consul_kv
|
||||
secret_key = consul_kv.get_value('ConsulManager/assets/secret/skey')['sk']
|
||||
key = secret_key[:8]
|
||||
iv = key
|
||||
k = des(key, ECB, iv, pad=None, padmode=PAD_PKCS5)
|
||||
|
||||
def encrypt(s):
|
||||
en = k.encrypt(s, padmode=PAD_PKCS5)
|
||||
return binascii.b2a_hex(en).decode()
|
||||
|
||||
def decrypt(s):
|
||||
de = k.decrypt(binascii.a2b_hex(s), padmode=PAD_PKCS5)
|
||||
return de.decode()
|
|
@ -1,8 +1,8 @@
|
|||
from flask_httpauth import HTTPTokenAuth
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
from config import s
|
||||
|
||||
from itsdangerous import TimedJSONWebSignatureSerializer
|
||||
from units import consul_kv
|
||||
secret_key = consul_kv.get_value('ConsulManager/assets/secret/skey')['sk']
|
||||
s = TimedJSONWebSignatureSerializer(secret_key)
|
||||
auth = HTTPTokenAuth()
|
||||
|
||||
@auth.verify_token
|
||||
|
|
|
@ -23,6 +23,16 @@ class GetAllList(Resource):
|
|||
args = parser.parse_args()
|
||||
return blackbox_manager.get_all_list(args['module'],args['company'],args['project'],args['env'])
|
||||
|
||||
class GetConfig(Resource):
|
||||
@token_auth.auth.login_required
|
||||
def get(self, stype):
|
||||
if stype == 'rules':
|
||||
return blackbox_manager.get_rules()
|
||||
elif stype == 'bconfig':
|
||||
return blackbox_manager.get_bconfig()
|
||||
elif stype == 'pconfig':
|
||||
return blackbox_manager.get_pconfig()
|
||||
|
||||
class BlackboxApi(Resource):
|
||||
decorators = [token_auth.auth.login_required]
|
||||
def get(self):
|
||||
|
@ -49,3 +59,4 @@ class BlackboxApi(Resource):
|
|||
|
||||
api.add_resource(GetAllList,'/api/blackbox/alllist')
|
||||
api.add_resource(BlackboxApi, '/api/blackbox/service')
|
||||
api.add_resource(GetConfig,'/api/blackboxcfg/<stype>')
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
from flask import Blueprint
|
||||
from flask_restful import reqparse, Resource, Api
|
||||
from flask_apscheduler import APScheduler
|
||||
from config import vendors,regions
|
||||
from units import token_auth,consul_kv
|
||||
import json
|
||||
blueprint = Blueprint('jobs',__name__)
|
||||
api = Api(blueprint)
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('job_id',type=str)
|
||||
parser.add_argument('job_dict',type=dict)
|
||||
parser.add_argument('query_dict',type=str)
|
||||
|
||||
def init():
|
||||
global Scheduler
|
||||
Scheduler = APScheduler()
|
||||
return Scheduler
|
||||
|
||||
class Jobs(Resource):
|
||||
decorators = [token_auth.auth.login_required]
|
||||
def get(self):
|
||||
args = parser.parse_args()
|
||||
query_dict = json.loads(args['query_dict'])
|
||||
if query_dict['vendor'] != '':
|
||||
query_dict['vendor'] = {v : k for k, v in vendors.items()}[query_dict['vendor']]
|
||||
query_set = set({k:v for k,v in query_dict.items() if v != ''}.items())
|
||||
job_list = list(consul_kv.get_kv_dict(f'ConsulManager/jobs').values())
|
||||
job_run_dict = {job.id:job.next_run_time.strftime("%m%d/%H:%M") for job in Scheduler.get_jobs()}
|
||||
job_count_dict = consul_kv.get_kv_dict('ConsulManager/record/jobs')
|
||||
jobs = []
|
||||
for i in job_list:
|
||||
vendor,account,itype = i['id'].split('/')[0:3]
|
||||
job_info_dict = {'vendor':vendor,'account':account,'itype':itype}
|
||||
if query_set.issubset(job_info_dict.items()):
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
region = i['args'][-1] if len(i['args']) == 2 else 'none'
|
||||
interval = i['minutes']
|
||||
if f'ConsulManager/record/jobs/{i["id"]}' in job_count_dict:
|
||||
count = job_count_dict[f'ConsulManager/record/jobs/{i["id"]}']['count']
|
||||
runtime = job_count_dict[f'ConsulManager/record/jobs/{i["id"]}']['update']
|
||||
on = job_count_dict[f'ConsulManager/record/jobs/{i["id"]}'].get('on',0)
|
||||
off = job_count_dict[f'ConsulManager/record/jobs/{i["id"]}'].get('off',0)
|
||||
else:
|
||||
count = '无'
|
||||
runtime = '无'
|
||||
on,off = 0,0
|
||||
jobs.append({'region':regions[vendor][region],'vendor':vendors[vendor],'account':account,'itype':itype,
|
||||
'interval':interval,'jobid':i['id'],'nextime':job_run_dict[i['id']],'on':on,'off':off,
|
||||
'count':count, 'runtime':runtime})
|
||||
vendor_list = sorted(list(set([i['vendor'] for i in jobs])))
|
||||
account_list = sorted(list(set([i['account'] for i in jobs])))
|
||||
itype_list = sorted(list(set([i['itype'] for i in jobs])))
|
||||
return {'code': 20000,'all_jobs':jobs,'vendor_list':vendor_list,'account_list':account_list,'itype_list':itype_list}
|
||||
|
||||
def post(self):
|
||||
args = parser.parse_args()
|
||||
job_dict = args['job_dict']
|
||||
job_status = job_dict['dialogStatus']
|
||||
if job_status == 'create':
|
||||
ak = job_dict['ak']
|
||||
sk = job_dict['sk']
|
||||
consul_kv.put_aksk(job_dict['vendor'],job_dict['account'],ak,sk)
|
||||
|
||||
proj_job_id = f"{job_dict['vendor']}/{job_dict['account']}/group"
|
||||
proj_job_func = f"__main__:{job_dict['vendor']}.group"
|
||||
proj_job_args = [job_dict['account']]
|
||||
proj_job_interval = int(job_dict['proj_interval'])
|
||||
|
||||
ecs_job_id = f"{job_dict['vendor']}/{job_dict['account']}/ecs/{job_dict['region']}"
|
||||
ecs_job_func = f"__main__:{job_dict['vendor']}.ecs"
|
||||
ecs_job_args = [job_dict['account'],job_dict['region']]
|
||||
ecs_job_interval = int(job_dict['ecs_interval'])
|
||||
|
||||
Scheduler.add_job(id=proj_job_id, func=proj_job_func, args=proj_job_args, trigger='interval',
|
||||
minutes=proj_job_interval, replace_existing=True)
|
||||
Scheduler.add_job(id=ecs_job_id, func=ecs_job_func, args=ecs_job_args, trigger='interval',
|
||||
minutes=ecs_job_interval, replace_existing=True)
|
||||
|
||||
proj_job_dict = {'id':proj_job_id,'func':proj_job_func,'args':proj_job_args,'minutes':proj_job_interval,
|
||||
"trigger": "interval","replace_existing": True}
|
||||
Scheduler.run_job(proj_job_id)
|
||||
ecs_job_dict = {'id':ecs_job_id,'func':ecs_job_func,'args':ecs_job_args,'minutes':ecs_job_interval,
|
||||
"trigger": "interval","replace_existing": True}
|
||||
record_dict = consul_kv.get_value(f"ConsulManager/record/jobs/{proj_job_id}")
|
||||
if record_dict['status'] == 20000:
|
||||
consul_kv.put_kv(f'ConsulManager/jobs/{proj_job_id}',proj_job_dict)
|
||||
consul_kv.put_kv(f'ConsulManager/jobs/{ecs_job_id}',ecs_job_dict)
|
||||
else:
|
||||
Scheduler.remove_job(proj_job_id)
|
||||
Scheduler.remove_job(ecs_job_id)
|
||||
return {'code': record_dict['status'], 'data': f"{record_dict['update']}:{record_dict['msg']}"}
|
||||
elif job_status == 'update':
|
||||
jobid = job_dict['jobid']
|
||||
interval = int(job_dict['interval'])
|
||||
Scheduler.modify_job(jobid,trigger='interval',minutes=interval)
|
||||
upjob_dict = consul_kv.get_value(f'ConsulManager/jobs/{jobid}')
|
||||
upjob_dict['minutes'] = interval
|
||||
consul_kv.put_kv(f'ConsulManager/jobs/{jobid}',upjob_dict)
|
||||
return {'code': 20000, 'data': '更新成功!'}
|
||||
elif job_status == 'run':
|
||||
Scheduler.run_job(job_dict['jobid'])
|
||||
record_dict = consul_kv.get_value(f"ConsulManager/record/jobs/{job_dict['jobid']}")
|
||||
return {'code': record_dict['status'], 'data': f"{record_dict['update']}:{record_dict['msg']}"}
|
||||
|
||||
def delete(self):
|
||||
args = parser.parse_args()
|
||||
job_id = args['job_id']
|
||||
Scheduler.remove_job(job_id)
|
||||
del_job = consul_kv.del_key(f'ConsulManager/jobs/{job_id}')
|
||||
return {'code': 20000, 'data': '删除成功!'}
|
||||
|
||||
api.add_resource(Jobs, '/api/jobs')
|
|
@ -1,9 +1,12 @@
|
|||
from flask import Blueprint
|
||||
from flask_restful import reqparse, Resource, Api
|
||||
from itsdangerous import TimedJSONWebSignatureSerializer
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
from config import admin_passwd,s
|
||||
from units import token_auth
|
||||
from config import admin_passwd
|
||||
from units import token_auth, consul_kv
|
||||
secret_key = consul_kv.get_value('ConsulManager/assets/secret/skey')['sk']
|
||||
s = TimedJSONWebSignatureSerializer(secret_key)
|
||||
|
||||
blueprint = Blueprint('login',__name__)
|
||||
api = Api(blueprint)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
from flask import Blueprint
|
||||
from flask_restful import reqparse, Resource, Api
|
||||
from flask_apscheduler import APScheduler
|
||||
#import sys
|
||||
#sys.path.append("..")
|
||||
from units import token_auth,consul_kv,gen_config
|
||||
|
||||
blueprint = Blueprint('nodes',__name__)
|
||||
api = Api(blueprint)
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('job_id',type=str)
|
||||
parser.add_argument('services_dict',type=dict)
|
||||
|
||||
class Nodes(Resource):
|
||||
decorators = [token_auth.auth.login_required]
|
||||
def get(self, stype):
|
||||
job_id = parser.parse_args()['job_id']
|
||||
if stype == 'group':
|
||||
cloud,account,itype = job_id.split('/')
|
||||
group_dict = consul_kv.get_value(f'ConsulManager/assets/{cloud}/group/{account}')
|
||||
group_list = [{'gid':k,'gname':v}for k,v in group_dict.items()]
|
||||
return {'code': 20000,'group':group_list}
|
||||
elif stype == 'ecs':
|
||||
if job_id == '':
|
||||
return {'code': 20000,'ecs_list': [] }
|
||||
else:
|
||||
return consul_kv.get_ecs_services(job_id)
|
||||
elif stype == 'jobecs':
|
||||
jobecs = consul_kv.get_keys_list('ConsulManager/jobs')
|
||||
jobecs_list = [i.split('/jobs/')[1] for i in jobecs if '/ecs/' in i]
|
||||
return {'code': 20000,'jobecs':jobecs_list}
|
||||
elif stype == 'ecs_services':
|
||||
jobecs = consul_kv.get_keys_list('ConsulManager/jobs')
|
||||
jobecs_list = [i.split('/jobs/')[1] for i in jobecs if '/ecs/' in i]
|
||||
services_list = []
|
||||
for i in jobecs_list:
|
||||
serivces = i.split("/")
|
||||
services_list.append(f'{serivces[0]}_{serivces[1]}_{serivces[2]}')
|
||||
return {'code': 20000,'services_list': sorted(set(services_list))}
|
||||
|
||||
def post(self, stype):
|
||||
if stype == 'config':
|
||||
args = parser.parse_args()
|
||||
services_dict = args['services_dict']
|
||||
return gen_config.ecs_config(services_dict['services_list'],services_dict['ostype_list'])
|
||||
api.add_resource(Nodes, '/api/nodes/<stype>')
|
|
@ -18,14 +18,16 @@
|
|||
"core-js": "3.6.5",
|
||||
"element-ui": "2.15.7",
|
||||
"file-saver": "2.0.1",
|
||||
"xlsx": "0.17.0",
|
||||
"js-cookie": "2.2.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"vue": "2.6.10",
|
||||
"vue-clipboard2": "^0.3.3",
|
||||
"vue-highlightjs": "^1.3.3",
|
||||
"vue-router": "3.0.6",
|
||||
"vuex": "3.1.0"
|
||||
"vuex": "3.1.0",
|
||||
"xlsx": "0.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.4",
|
||||
|
|
|
@ -35,3 +35,21 @@ export function delService(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
export function getRules() {
|
||||
return request({
|
||||
url: '/api/blackboxcfg/rules',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getPconfig() {
|
||||
return request({
|
||||
url: '/api/blackboxcfg/pconfig',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getBconfig() {
|
||||
return request({
|
||||
url: '/api/blackboxcfg/bconfig',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import request from '@/utils/request-ops'
|
||||
|
||||
export function getAllJobs(query_dict) {
|
||||
return request({
|
||||
url: '/api/jobs',
|
||||
method: 'get',
|
||||
params: { query_dict }
|
||||
})
|
||||
}
|
||||
|
||||
export function PostJob(job_dict) {
|
||||
return request({
|
||||
url: '/api/jobs',
|
||||
method: 'post',
|
||||
data: { job_dict }
|
||||
})
|
||||
}
|
||||
|
||||
export function DelJob(job_id) {
|
||||
return request({
|
||||
url: '/api/jobs',
|
||||
method: 'delete',
|
||||
params: { job_id }
|
||||
})
|
||||
}
|
||||
|
||||
export function getGroup(job_id) {
|
||||
return request({
|
||||
url: '/api/nodes/group',
|
||||
method: 'get',
|
||||
params: { job_id }
|
||||
})
|
||||
}
|
||||
|
||||
export function getEcsList(job_id) {
|
||||
return request({
|
||||
url: '/api/nodes/ecs',
|
||||
method: 'get',
|
||||
params: { job_id }
|
||||
})
|
||||
}
|
||||
|
||||
export function getJobEcs() {
|
||||
return request({
|
||||
url: '/api/nodes/jobecs',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getServicesList() {
|
||||
return request({
|
||||
url: '/api/nodes/ecs_services',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getConfig(services_dict) {
|
||||
return request({
|
||||
url: '/api/nodes/config',
|
||||
method: 'post',
|
||||
data: { services_dict }
|
||||
})
|
||||
}
|
|
@ -15,6 +15,13 @@ import router from './router'
|
|||
import '@/icons' // icon
|
||||
import '@/permission' // permission control
|
||||
import * as filters from './filters' // global filters
|
||||
|
||||
import VueHighlightJS from 'vue-highlightjs'
|
||||
import 'highlight.js/styles/xt256.css'
|
||||
Vue.use(VueHighlightJS)
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
Vue.use(VueClipboard)
|
||||
|
||||
/**
|
||||
* If you don't want to use mock-server
|
||||
* you want to use MockJs for mock api
|
||||
|
|
|
@ -70,32 +70,82 @@ export const constantRoutes = [
|
|||
},
|
||||
{
|
||||
path: 'services',
|
||||
name: 'Services',
|
||||
name: '服务组',
|
||||
component: () => import('@/views/consul/services'),
|
||||
meta: { title: 'Services', icon: 'el-icon-news' }
|
||||
meta: { title: '服务组', icon: 'el-icon-news' }
|
||||
},
|
||||
{
|
||||
path: 'instances',
|
||||
name: 'Instances',
|
||||
name: '实例管理',
|
||||
component: () => import('@/views/consul/instances'),
|
||||
meta: { title: 'Instances', icon: 'el-icon-connection' }
|
||||
meta: { title: '实例管理', icon: 'el-icon-connection' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/nodes',
|
||||
component: Layout,
|
||||
redirect: '/nodes/jobs',
|
||||
name: 'ECS 云主机监控',
|
||||
meta: { title: 'ECS 云主机监控', icon: 'example' },
|
||||
children: [
|
||||
{
|
||||
path: 'jobs',
|
||||
name: '接入数据源',
|
||||
component: () => import('@/views/node-exporter/jobs'),
|
||||
meta: { title: '接入数据源', icon: 'el-icon-school' }
|
||||
},
|
||||
{
|
||||
path: 'lists',
|
||||
name: '云主机列表',
|
||||
component: () => import('@/views/node-exporter/lists'),
|
||||
meta: { title: '云主机列表', icon: 'el-icon-s-platform' }
|
||||
},
|
||||
{
|
||||
path: 'pconfig',
|
||||
name: 'Prometheus 配置',
|
||||
component: () => import('@/views/node-exporter/pconfig'),
|
||||
meta: { title: 'Prometheus 配置', icon: 'el-icon-set-up' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/blackbox',
|
||||
component: Layout,
|
||||
children: [{
|
||||
path: 'index',
|
||||
redirect: '/blackbox/index',
|
||||
name: 'Blackbox 站点监控',
|
||||
meta: { title: 'Blackbox 站点监控', icon: 'tree' },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: '站点管理',
|
||||
component: () => import('@/views/blackbox/index'),
|
||||
meta: { title: 'Blackbox 站点监控', icon: 'tree' }
|
||||
}]
|
||||
meta: { title: '站点管理', icon: 'el-icon-s-order' }
|
||||
},
|
||||
{
|
||||
path: '友情链接',
|
||||
path: 'bconfig',
|
||||
name: 'Blackbox 配置',
|
||||
component: () => import('@/views/blackbox/bconfig'),
|
||||
meta: { title: 'Blackbox 配置', icon: 'el-icon-c-scale-to-original' }
|
||||
},
|
||||
{
|
||||
path: 'pconfig',
|
||||
name: 'Prometheus 配置',
|
||||
component: () => import('@/views/blackbox/pconfig'),
|
||||
meta: { title: 'Prometheus 配置', icon: 'el-icon-set-up' }
|
||||
},
|
||||
{
|
||||
path: 'rules',
|
||||
name: '告警规则',
|
||||
component: () => import('@/views/blackbox/rules'),
|
||||
meta: { title: '告警规则', icon: 'el-icon-bell' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '快速链接',
|
||||
component: Layout,
|
||||
meta: { title: '友情链接', icon: 'link' },
|
||||
meta: { title: '快速链接', icon: 'link' },
|
||||
children: [
|
||||
{
|
||||
path: 'https://starsl.cn',
|
||||
|
@ -103,18 +153,22 @@ export const constantRoutes = [
|
|||
},
|
||||
{
|
||||
path: 'https://github.com/starsliao?tab=repositories',
|
||||
meta: { title: '我的Github', icon: 'el-icon-star-off' }
|
||||
meta: { title: '我的 Github', icon: 'el-icon-star-off' }
|
||||
},
|
||||
{
|
||||
path: 'https://grafana.com/orgs/starsliao/dashboards',
|
||||
meta: { title: '我的Grafana', icon: 'el-icon-odometer' }
|
||||
meta: { title: '我的 Grafana', icon: 'el-icon-odometer' }
|
||||
},
|
||||
{
|
||||
path: 'https://starsl.cn/static/img/qr.png',
|
||||
path: 'https://starsl.cn/static/img/thanks.png',
|
||||
meta: { title: '我的公众号', icon: 'el-icon-chat-dot-round' }
|
||||
},
|
||||
{
|
||||
path: 'https://element.eleme.cn',
|
||||
path: 'https://github.com/starsliao/ConsulManager/blob/main/Thanks.md',
|
||||
meta: { title: '特别鸣谢', icon: 'el-icon-cold-drink' }
|
||||
},
|
||||
{
|
||||
path: 'https://element.eleme.cn/#/zh-CN/component/icon',
|
||||
meta: { title: 'Element', icon: 'el-icon-eleme' }
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ const service = axios.create({
|
|||
// baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
baseURL: '',
|
||||
timeout: 5000 // request timeout
|
||||
timeout: 10000 // request timeout
|
||||
})
|
||||
|
||||
// request interceptor
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-button v-clipboard:copy="bconfig" v-clipboard:success="onCopy" v-clipboard:error="onError" class="filter-item" type="warning" icon="el-icon-document-copy">
|
||||
复制配置
|
||||
</el-button>
|
||||
<pre v-highlightjs="bconfig" style="line-height:120%"><code class="yaml yamlcode" /></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getBconfig } from '@/api/blackbox'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
bconfig: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchBconfig()
|
||||
},
|
||||
methods: {
|
||||
onCopy() {
|
||||
this.$message({
|
||||
message: '复制成功!',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
onError() {
|
||||
this.$message.error('复制失败!')
|
||||
},
|
||||
fetchBconfig() {
|
||||
this.listLoading = true
|
||||
getBconfig().then(response => {
|
||||
this.bconfig = response.bconfig
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.yamlcode {
|
||||
font-family:'Consolas';
|
||||
}
|
||||
pre {
|
||||
max-height: 640px;
|
||||
white-space: pre-wrap;
|
||||
overflow:auto;
|
||||
}
|
||||
</style>
|
|
@ -92,22 +92,22 @@
|
|||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="handleFilter" />
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="40%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="80px" style="width: 400px; margin-left:50px;">
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="37%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="right" label-width="auto" style="width: 90%; margin-left: 20px;">
|
||||
<el-form-item label="监控类型" prop="module">
|
||||
<el-autocomplete v-model="temp.module" :fetch-suggestions="Sugg_module" placeholder="优先选择" clearable style="width: 360px" class="filter-item" />
|
||||
<el-autocomplete v-model="temp.module" :fetch-suggestions="Sugg_module" placeholder="优先选择" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="公司部门" prop="company">
|
||||
<el-autocomplete v-model="temp.company" :fetch-suggestions="Sugg_company" placeholder="优先选择" clearable style="width: 360px" class="filter-item" />
|
||||
<el-autocomplete v-model="temp.company" :fetch-suggestions="Sugg_company" placeholder="优先选择" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目" prop="project">
|
||||
<el-autocomplete v-model="temp.project" :fetch-suggestions="Sugg_project" placeholder="优先选择" clearable style="width: 360px" class="filter-item" />
|
||||
<el-autocomplete v-model="temp.project" :fetch-suggestions="Sugg_project" placeholder="优先选择" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="环境" prop="env">
|
||||
<el-autocomplete v-model="temp.env" :fetch-suggestions="Sugg_env" placeholder="优先选择" clearable style="width: 360px" class="filter-item" />
|
||||
<el-autocomplete v-model="temp.env" :fetch-suggestions="Sugg_env" placeholder="优先选择" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="temp.name" placeholder="请输入" clearable style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="temp.name" placeholder="请输入" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="instance">
|
||||
<span slot="label">
|
||||
|
@ -118,7 +118,7 @@
|
|||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<el-input v-model="temp.instance" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入" style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="temp.instance" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="请输入" class="filter-item" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-button v-clipboard:copy="pconfig" v-clipboard:success="onCopy" v-clipboard:error="onError" class="filter-item" type="warning" icon="el-icon-document-copy">
|
||||
复制配置
|
||||
</el-button>
|
||||
<pre v-highlightjs="pconfig" style="line-height:120%"><code class="yaml yamlcode" /></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPconfig } from '@/api/blackbox'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
pconfig: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchPconfig()
|
||||
},
|
||||
methods: {
|
||||
onCopy() {
|
||||
this.$message({
|
||||
message: '复制成功!',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
onError() {
|
||||
this.$message.error('复制失败!')
|
||||
},
|
||||
fetchPconfig() {
|
||||
this.listLoading = true
|
||||
getPconfig().then(response => {
|
||||
this.pconfig = response.pconfig
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.yamlcode {
|
||||
font-family:'Consolas';
|
||||
}
|
||||
pre {
|
||||
max-height: 640px;
|
||||
white-space: pre-wrap;
|
||||
overflow:auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-button v-clipboard:copy="rules" v-clipboard:success="onCopy" v-clipboard:error="onError" class="filter-item" type="warning" icon="el-icon-document-copy">
|
||||
复制配置
|
||||
</el-button>
|
||||
<pre v-highlightjs="rules" style="line-height:120%"><code class="yaml yamlcode" /></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRules } from '@/api/blackbox'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
rules: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchRules()
|
||||
},
|
||||
methods: {
|
||||
onCopy() {
|
||||
this.$message({
|
||||
message: '复制成功!',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
onError() {
|
||||
this.$message.error('复制失败!')
|
||||
},
|
||||
fetchRules() {
|
||||
this.listLoading = true
|
||||
getRules().then(response => {
|
||||
this.rules = response.rules
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.yamlcode {
|
||||
font-family:'Consolas';
|
||||
}
|
||||
pre {
|
||||
max-height: 640px;
|
||||
white-space: pre-wrap;
|
||||
overflow:auto;
|
||||
}
|
||||
</style>
|
|
@ -78,32 +78,32 @@
|
|||
<el-table-column type="expand" width="1">
|
||||
<template slot-scope="{row}">
|
||||
<el-table style="width: 100%" :data="row.meta" row-class-name="success-row" fit border>
|
||||
<el-table-column v-for="{ prop, label } in row.meta_label" :key="prop" :prop="prop" :label="label" />
|
||||
<el-table-column v-for="{ prop, label } in row.meta_label" :key="prop" :prop="prop" :label="label" align="center" />
|
||||
</el-table>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="45%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="newService" label-position="right" label-width="100px" style="width: 500px; margin-left: 50px;">
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="40%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="newService" label-position="right" label-width="auto" style="width: 90%; margin-left: 20px;">
|
||||
<el-form-item label="所属服务组" prop="name">
|
||||
<el-autocomplete v-model="newService.name" :fetch-suggestions="Sugg_name" placeholder="优先选择" clearable style="width: 360px" class="filter-item" />
|
||||
<el-autocomplete v-model="newService.name" :fetch-suggestions="Sugg_name" placeholder="优先选择" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<div v-if="dialogStatus==='update'">
|
||||
<el-form-item label="服务实例ID" prop="ID">
|
||||
<el-input v-model="newService.ID" placeholder="请输入" clearable style="width: 360px" :disabled="true" />
|
||||
<el-input v-model="newService.ID" placeholder="请输入" clearable :disabled="true" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form-item label="服务实例ID" prop="ID">
|
||||
<el-input v-model="newService.ID" placeholder="请输入" clearable style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="newService.ID" placeholder="请输入" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="newService.address" placeholder="请输入" clearable style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="newService.address" placeholder="请输入" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="端口" prop="port">
|
||||
<el-input v-model="newService.port" placeholder="请输入" clearable style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="newService.port" placeholder="请输入" clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Tags" prop="tags">
|
||||
<el-tag v-for="tag in newService.tags" :key="tag" closable :disable-transitions="false" @close="handleClose(tag)">{{ tag }}</el-tag>
|
||||
|
@ -123,7 +123,7 @@
|
|||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<el-input v-model="newService.metaInfo.metaJson" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder='{ "aaa": "bbb", "ccc": "ddd" }' clearable style="width: 360px" class="filter-item" />
|
||||
<el-input v-model="newService.metaInfo.metaJson" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder='{ "aaa": "bbb", "ccc": "ddd" }' clearable class="filter-item" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="coption !== '' && dialogStatus==='update'" label="健康检查操作" prop="coption">
|
||||
|
@ -134,7 +134,7 @@
|
|||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form :inline="true" class="demo-form-inline" label-position="right" label-width="100px">
|
||||
<el-form :inline="true" class="demo-form-inline" label-position="right" label-width="94px">
|
||||
<el-form-item v-if="coption === '' || coption === 'modf'" label="健康检查" prop="isCheck">
|
||||
<el-switch v-model="newService.checkInfo.isCheck" active-text=" " />
|
||||
</el-form-item>
|
||||
|
@ -161,7 +161,7 @@
|
|||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<el-input v-model="newService.checkInfo.caddress" placeholder="请输入" clearable style="width: 360px" />
|
||||
<el-input v-model="newService.checkInfo.caddress" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form v-if="newService.checkInfo.isCheck" :inline="true" class="demo-form-inline" label-position="right" label-width="100px">
|
||||
|
@ -225,6 +225,7 @@ export default {
|
|||
}
|
||||
},
|
||||
coption: '',
|
||||
listLoading: false,
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
|
@ -369,10 +370,8 @@ export default {
|
|||
})
|
||||
},
|
||||
fetchServicesName() {
|
||||
this.listLoading = true
|
||||
getServicesName().then(response => {
|
||||
this.services_name_list = response.services_name
|
||||
this.listLoading = false
|
||||
this.xname = this.load_name()
|
||||
})
|
||||
},
|
||||
|
@ -409,7 +408,6 @@ export default {
|
|||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.coption === 'delete') {
|
||||
delSid(this.newService.ID).then(response => {
|
||||
addSid(this.newService).then(response => {
|
||||
this.fetchServicesName()
|
||||
|
@ -422,18 +420,6 @@ export default {
|
|||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
addSid(this.newService).then(response => {
|
||||
this.fetchServicesName()
|
||||
this.services_name = this.newService.name
|
||||
this.fetchData(this.newService.name)
|
||||
this.dialogFormVisible = false
|
||||
this.$message({
|
||||
message: response.data,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -9,39 +9,41 @@
|
|||
highlight-current-row
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-table-column label="ID" width="73px" align="center">
|
||||
<el-table-column label="ID" width="50px" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.$index+1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Name" label="服务名" sortable align="center">
|
||||
<el-table-column prop="Name" label="服务组" sortable align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-link type="primary" @click="handleInstances(row.Name)">{{ row.Name }}</el-link>
|
||||
<el-link type="primary" style="font-weight:bold" @click="handleInstances(row.Name)"><i class="el-icon-view el-icon--left" />{{ row.Name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Nodes" label="节点" sortable align="center" width="200">
|
||||
<el-table-column prop="Nodes" label="节点" align="center" width="120">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag v-for="atag in row.Nodes" :key="atag" size="mini" effect="dark">{{ atag }}</el-tag>
|
||||
<el-tag v-for="atag in row.Nodes" :key="atag" type="info" size="mini">{{ atag }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Datacenter" label="数据中心" sortable align="center" width="120">
|
||||
<el-table-column prop="Datacenter" label="数据中心" align="center" width="110">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.Datacenter }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="Tags" label="Tags" sortable align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag v-for="atag in row.Tags" :key="atag" size="mini">{{ atag }}</el-tag>
|
||||
<el-tag v-for="atag in row.Tags" :key="atag" size="small">{{ atag }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="InstanceCount" label="实例数" sortable align="center" width="100">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.InstanceCount }}</span>
|
||||
<span style="font-weight:bold">{{ row.InstanceCount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ChecksPassing" label="健康实例" sortable align="center" width="120">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.ChecksPassing - 1 }} </span>
|
||||
<el-tooltip class="item" effect="dark" content="健康检查成功的实例数" placement="top">
|
||||
<el-button size="mini" type="success" icon="el-icon-check" circle>{{ row.ChecksPassing - 1 }}</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ChecksCritical" label="实例状态" sortable align="center" width="120">
|
||||
|
|
|
@ -4,6 +4,25 @@
|
|||
<el-link :underline="false" type="primary" icon="el-icon-star-on" href="https://github.com/starsliao/ConsulManager" target="_blank" class="dashboard-text">StarsL.cn</el-link>
|
||||
</el-badge>
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="2022/4/7" placement="top">
|
||||
<el-card>
|
||||
<h4>v0.5.0</h4>
|
||||
<p>【重要】增加ECS云主机监控:支持自动同步阿里、腾讯、华为云的ECS、分组信息到Consul并接入到Prometheus监控!</p>
|
||||
<p>增加了从Consul同步ECS,站点信息到Prometheus的配置生成界面。</p>
|
||||
<p>增加了Blackbox的配置信息与告警规则信息页面。</p>
|
||||
<p>优化了Blackbox接入Prometheus的配置,只需配置1个job即可接入所有类型的站点监控。</p>
|
||||
<p>更新了站点监控的grafana看板,增加了URL筛选,查询关联所有图表,并支持展示单job的配置方式。</p>
|
||||
<p>更新了主机监控的grafana看板,可匹配自动同步方式采集ECS信息字段的展示, 优化了大量图表,使用新版表格重建,新增健康评分概念,并新增了整体资源消耗信息的一些图表。</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2022/2/23" placement="top">
|
||||
<el-card>
|
||||
<h4>v0.3.1</h4>
|
||||
<p>允许在实例ID字段使用'/',(可以在Consul管理中对blackbox-exporter的监控实例做自定义编辑了,例如:增加标签,增加Meta)。</p>
|
||||
<p>优化了描述和引导使用Blackbox站点监控。</p>
|
||||
<p>增加了Makefile文件,可以使用make update来更新等操作。</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2022/2/10" placement="top">
|
||||
<el-card>
|
||||
<h4>v0.3.0</h4>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
</el-form>
|
||||
<div align="center" class="title-container">
|
||||
<span style="font-size:10px" class="title">v0.3.1</span>
|
||||
<span style="font-size:10px" class="title">v0.5.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,386 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-select v-model="query.vendor" placeholder="云厂商" clearable style="width: 150px" class="filter-item" @change="fetchData(query)">
|
||||
<el-option v-for="item in vendor_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-select v-model="query.account" placeholder="账户" clearable style="width: 150px" class="filter-item" @change="fetchData(query)">
|
||||
<el-option v-for="item in account_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-select v-model="query.itype" placeholder="类型" clearable style="width: 150px" class="filter-item" @change="fetchData(query)">
|
||||
<el-option v-for="item in itype_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
|
||||
<el-tooltip class="item" effect="light" content="清空查询条件" placement="top">
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="info" icon="el-icon-delete" circle @click="resetData" />
|
||||
</el-tooltip>
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
新增同步源
|
||||
</el-button>
|
||||
<div style="float: right;">
|
||||
<el-tooltip class="item" effect="light" content="刷新当前页面" placement="top">
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-refresh" circle @click="fetchData" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="joblist" :row-class-name="tableRowClassName" border fit highlight-current-row style="width: 100%;">
|
||||
<el-table-column type="index" align="center" />
|
||||
<el-table-column prop="vendor" label="云厂商" sortable align="center" />
|
||||
<el-table-column prop="account" label="账户" sortable align="center" />
|
||||
<el-table-column prop="itype" label="资源" sortable align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div v-if="row.itype === 'ecs'" slot="reference" class="name-wrapper">
|
||||
<el-tag size="medium">{{ row.itype.toUpperCase() }}</el-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>{{ row.itype }} </span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="region" label="区域" sortable align="center" />
|
||||
<el-table-column prop="count" label="资源数" sortable align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span style="font-weight:bold">{{ row.count }} </span>
|
||||
<el-tooltip v-if="row.itype === 'ecs'" style="diaplay:inline" effect="dark" placement="top">
|
||||
<div slot="content"> 开机:{{ row.on }},关机:{{ row.off }} </div>
|
||||
<i class="el-icon-info" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="runtime" label="上次同步" sortable align="center" />
|
||||
<el-table-column prop="interval" label="同步间隔" sortable align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.interval }}分钟</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="nextime" label="下次同步" sortable align="center" />
|
||||
<el-table-column label="操作" align="center" width="280" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="success" size="mini" @click="row.itype==='ecs'?handleEcs(row.jobid):handleEnt(row.jobid)">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button type="warning" size="mini" @click="handleRun(row.jobid)">
|
||||
同步
|
||||
</el-button>
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" @click="handleDelete(row.jobid)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="查看组信息" :visible.sync="entFormVisible" width="60%">
|
||||
<el-table v-loading="listLoading" :data="entlist" height="540" border fit highlight-current-row style="width: 100%;">
|
||||
<el-table-column type="index" align="center" />
|
||||
<el-table-column prop="gid" label="组ID" sortable align="center" />
|
||||
<el-table-column prop="gname" label="名称" sortable align="center" />
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="新增同步源" :visible.sync="newFormVisible" width="40%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="ecsJob" label-position="right" label-width="auto" style="width: 90%; margin-left: 1px;">
|
||||
<el-form-item label="云厂商" prop="vendor">
|
||||
<el-select v-model="ecsJob.vendor" placeholder="请选择" @change="ecsJob.region=''">
|
||||
<el-option v-for="item in vendors" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="account">
|
||||
<span slot="label">
|
||||
<span class="span-box">
|
||||
<span>账户</span>
|
||||
<el-tooltip style="diaplay:inline" effect="dark" content="用来区分云厂商不同云账户的标识,支持中文,例如用主账户的名称。" placement="top">
|
||||
<i class="el-icon-info" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<el-input v-model="ecsJob.account" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Access Key" prop="ak">
|
||||
<el-input v-model="ecsJob.ak" placeholder="请输AccessKey ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Secret Key" prop="sk">
|
||||
<el-input v-model="ecsJob.sk" placeholder="请输入AccessKey Secret" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域" prop="region">
|
||||
<el-select v-model="ecsJob.region" placeholder="请选择">
|
||||
<el-option v-for="item in regions[ecsJob.vendor]" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="proj_interval">
|
||||
<span slot="label">
|
||||
<span class="span-box">
|
||||
<span>分组同步间隔(分钟)</span>
|
||||
<el-tooltip style="diaplay:inline" effect="dark" content="分组是采集云厂商用于资源分组的字段,阿里云:资源组,华为云:企业项目,腾讯云:所属项目。请在创建云主机时设置好属组。" placement="top">
|
||||
<i class="el-icon-info" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<el-input v-model="ecsJob.proj_interval" />
|
||||
</el-form-item>
|
||||
<el-form-item label="ECS同步间隔(分钟)" prop="ecs_interval">
|
||||
<el-input v-model="ecsJob.ecs_interval" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="createAndNew()">
|
||||
确认并新增
|
||||
</el-button>
|
||||
<el-button @click="newFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="createData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="更新同步间隔" :visible.sync="upFormVisible" width="30%">
|
||||
<el-form ref="dataForm" :rules="rules" :model="upjob" label-position="right" label-width="130px" style="margin-left: 20px;">
|
||||
<el-form-item label="同步间隔(分钟)" prop="interval">
|
||||
<el-input v-model="upjob.interval" placeholder="请输入" clearable style="width: 150px" class="filter-item" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="upFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAllJobs, PostJob, DelJob, getGroup } from '@/api/node-exporter'
|
||||
export default {
|
||||
data() {
|
||||
const validateInput = (rule, value, callback) => {
|
||||
if (!this.checkSpecialKey(value)) {
|
||||
callback(new Error('不能含有空格或 [ ]`~!#$^&*=|"{}\':/;\\?'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
dialogStatus: '',
|
||||
listLoading: false,
|
||||
joblist: [],
|
||||
entlist: [],
|
||||
job_dict: {},
|
||||
vendor_list: [],
|
||||
account_list: [],
|
||||
itype: [],
|
||||
query: { vendor: '', account: '', itype: '' },
|
||||
rules: {
|
||||
vendor: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }],
|
||||
account: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }],
|
||||
ak: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }],
|
||||
sk: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }],
|
||||
region: [{ required: true, message: '此为必填项', trigger: 'blur' },
|
||||
{ validator: validateInput, trigger: ['blur'] }],
|
||||
proj_interval: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }],
|
||||
ecs_interval: [{ required: true, message: '此为必填项', trigger: 'change' },
|
||||
{ validator: validateInput, trigger: ['blur', 'change'] }]
|
||||
},
|
||||
vendors: [{ value: 'alicloud', label: '阿里云' },
|
||||
{ value: 'tencent_cloud', label: '腾讯云' },
|
||||
{ value: 'huaweicloud', label: '华为云' }],
|
||||
|
||||
regions: {
|
||||
huaweicloud: [
|
||||
{ value: 'cn-east-3', label: '华东-上海一' },
|
||||
{ value: 'cn-east-2', label: '华东-上海二' },
|
||||
{ value: 'cn-south-1', label: '华南-广州' },
|
||||
{ value: 'cn-north-1', label: '华北-北京一' },
|
||||
{ value: 'cn-north-4', label: '华北-北京四' },
|
||||
{ value: 'cn-southwest-2', label: '西南-贵阳一' },
|
||||
{ value: 'ap-southeast-1', label: '中国-香港' }
|
||||
],
|
||||
alicloud: [
|
||||
{ value: 'cn-qingdao', label: '华北1(青岛)' },
|
||||
{ value: 'cn-beijing', label: '华北2(北京)' },
|
||||
{ value: 'cn-zhangjiakou', label: '华北3(张家口)' },
|
||||
{ value: 'cn-huhehaote', label: '华北5(呼和浩特)' },
|
||||
{ value: 'cn-wulanchabu', label: '华北6(乌兰察布)' },
|
||||
{ value: 'cn-hangzhou', label: '华东1(杭州)' },
|
||||
{ value: 'cn-shanghai', label: '华东2(上海)' },
|
||||
{ value: 'cn-shenzhen', label: '华南1(深圳)' },
|
||||
{ value: 'cn-heyuan', label: '华南2(河源)' },
|
||||
{ value: 'cn-guangzhou', label: '华南3(广州)' },
|
||||
{ value: 'cn-chengdu', label: '西南1(成都)' },
|
||||
{ value: 'cn-hongkong', label: '中国(香港)' },
|
||||
{ value: 'cn-nanjing', label: '华东5(南京-本地地域)' }
|
||||
],
|
||||
tencent_cloud: [
|
||||
{ value: 'ap-nanjing', label: '华东地区(南京)' },
|
||||
{ value: 'ap-shanghai', label: '华东地区(上海)' },
|
||||
{ value: 'ap-guangzhou', label: '华南地区(广州)' },
|
||||
{ value: 'ap-beijing', label: '华北地区(北京)' },
|
||||
{ value: 'ap-tianjin', label: '华北地区(天津)' },
|
||||
{ value: 'ap-chengdu', label: '西南地区(成都)' },
|
||||
{ value: 'ap-chongqing', label: '西南地区(重庆)' },
|
||||
{ value: 'ap-hongkong', label: '港澳台地区(中国香港)' }
|
||||
]
|
||||
},
|
||||
|
||||
ecsJob: { vendor: '', ak: '', sk: '', region: '', account: '', proj_interval: 60, ecs_interval: 5 },
|
||||
upjob: { jobid: '', interval: '' },
|
||||
newFormVisible: false,
|
||||
upFormVisible: false,
|
||||
entFormVisible: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
checkSpecialKey(str) {
|
||||
const specialKey = '[]`~!#$^&*/=\\|{}\'":;? '
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
if (specialKey.indexOf(str.substr(i, 1)) !== -1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
if (row.itype === 'ecs') {
|
||||
return 'success-row'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
resetData() {
|
||||
this.query = { vendor: '', account: '', itype: '' }
|
||||
this.fetchData()
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
getAllJobs(this.query).then(response => {
|
||||
this.joblist = response.all_jobs
|
||||
this.vendor_list = response.vendor_list
|
||||
this.account_list = response.account_list
|
||||
this.itype_list = response.itype_list
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleCreate() {
|
||||
this.ecsJob = { vendor: '', ak: '', sk: '', region: '', account: '', proj_interval: 60, ecs_interval: 5 }
|
||||
this.ecsJob.account = this.query.account
|
||||
this.newFormVisible = true
|
||||
},
|
||||
createAndNew() {
|
||||
this.createData()
|
||||
this.newFormVisible = true
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.upFormVisible = false
|
||||
this.listLoading = true
|
||||
this.upjob.dialogStatus = 'update'
|
||||
PostJob(this.upjob).then(response => {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: response.data,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.newFormVisible = false
|
||||
this.listLoading = true
|
||||
this.ecsJob.dialogStatus = 'create'
|
||||
PostJob(this.ecsJob).then(response => {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: response.data,
|
||||
type: 'success'
|
||||
})
|
||||
this.ecsJob.region = ''
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleRun(jobid) {
|
||||
this.$confirm('此操作将立刻同步一次【' + jobid + '】是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.listLoading = true
|
||||
this.dialogStatus = 'run'
|
||||
this.job_dict = { dialogStatus: this.dialogStatus, jobid: jobid }
|
||||
PostJob(this.job_dict).then(response => {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: response.data,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '同步已取消'
|
||||
})
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.upjob.jobid = row.jobid
|
||||
this.upjob.interval = row.interval
|
||||
this.upFormVisible = true
|
||||
},
|
||||
handleEcs(jobid) {
|
||||
this.$router.push({
|
||||
path: '/nodes/lists',
|
||||
query: { job_id: jobid }
|
||||
})
|
||||
},
|
||||
handleEnt(jobid) {
|
||||
getGroup(jobid).then(response => {
|
||||
this.entFormVisible = true
|
||||
this.entlist = response.group
|
||||
})
|
||||
},
|
||||
handleDelete(jobid) {
|
||||
this.$confirm('此操作将删除【' + jobid + '】是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
DelJob(jobid).then(response => {
|
||||
this.fetchData()
|
||||
this.$message({
|
||||
message: response.data,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: '已取消删除'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-table .success-row {
|
||||
background: #f0f9eb;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-select v-model="jobecs_name" placeholder="请选择需要查询的ECS列表" filterable collapse-tags clearable style="width: 350px" class="filter-item" @change="fetchEcs(jobecs_name)">
|
||||
<el-option v-for="item in jobecs_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-tooltip class="item" effect="light" content="刷新当前ECS列表" placement="top">
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-refresh" circle @click="fetchEcs(jobecs_name)" />
|
||||
</el-tooltip>
|
||||
|
||||
<el-table v-loading="listLoading" :data="ecs_list" :default-sort="{ prop: 'group', order: 'ascending' }" border fit highlight-current-row style="width: 100%;">
|
||||
<el-table-column type="index" align="center" />
|
||||
<el-table-column prop="group" label="分组" sortable align="center" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="name" label="名称" sortable align="center" width="280" />
|
||||
<el-table-column prop="instance" label="实例" sortable align="center" width="180" />
|
||||
<el-table-column prop="os" label="系统" sortable align="center" width="100" />
|
||||
<el-table-column prop="cpu" label="CPU" sortable align="center" width="80" />
|
||||
<el-table-column prop="mem" label="内存" sortable align="center" width="80" />
|
||||
<el-table-column prop="exp" label="到期日" sortable align="center" width="120" />
|
||||
<el-table-column prop="iid" label="实例ID" sortable align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getEcsList, getJobEcs } from '@/api/node-exporter'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
jobecs_name: '',
|
||||
jobecs_list: [],
|
||||
ecs_list: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchJobEcs()
|
||||
if (this.$route.query.job_id) {
|
||||
this.fetchEcs(this.$route.query.job_id)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$route.query.job_id) {
|
||||
this.jobecs_name = this.$route.query.job_id
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchJobEcs() {
|
||||
getJobEcs().then(response => {
|
||||
this.jobecs_list = response.jobecs
|
||||
})
|
||||
},
|
||||
fetchEcs(job_id) {
|
||||
this.listLoading = true
|
||||
getEcsList(job_id).then(response => {
|
||||
this.ecs_list = response.ecs_list
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-select v-model="services" multiple placeholder="请选择需要生成配置的服务" filterable collapse-tags clearable style="width: 350px" class="filter-item">
|
||||
<el-option v-for="item in services_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-select v-model="ostype" multiple placeholder="请选择系统" filterable clearable class="filter-item">
|
||||
<el-option v-for="item in ostype_list" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-magic-stick" @click="fetchEcsConfig">
|
||||
生成配置
|
||||
</el-button>
|
||||
<el-button v-clipboard:copy="configs" v-clipboard:success="onCopy" v-clipboard:error="onError" class="filter-item" type="warning" icon="el-icon-document-copy">
|
||||
复制配置
|
||||
</el-button>
|
||||
<pre v-highlightjs="configs" style="line-height:120%"><code class="yaml yamlcode" /></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getServicesList, getConfig } from '@/api/node-exporter'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
services: [],
|
||||
ostype: [],
|
||||
services_list: [],
|
||||
ostype_list: ['linux', 'windows'],
|
||||
services_dict: {},
|
||||
configs: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchEcsList()
|
||||
},
|
||||
methods: {
|
||||
onCopy() {
|
||||
this.$message({
|
||||
message: '复制成功!',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
onError() {
|
||||
this.$message.error('复制失败!')
|
||||
},
|
||||
fetchEcsList() {
|
||||
this.listLoading = true
|
||||
getServicesList().then(response => {
|
||||
this.services_list = response.services_list
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
fetchEcsConfig() {
|
||||
this.listLoading = true
|
||||
this.services_dict.services_list = this.services
|
||||
this.services_dict.ostype_list = this.ostype
|
||||
getConfig(this.services_dict).then(response => {
|
||||
this.configs = response.configs
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.yamlcode {
|
||||
font-family:'Consolas';
|
||||
}
|
||||
pre {
|
||||
max-height: 640px;
|
||||
white-space: pre-wrap;
|
||||
overflow:auto;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue