mirror of https://github.com/midoks/mdserver-web
parent
528094cfa7
commit
5aeaf13f16
@ -0,0 +1,2 @@ |
|||||||
|
## 指定共享内存 |
||||||
|
lua_shared_dict healthcheck 10m; |
@ -0,0 +1,64 @@ |
|||||||
|
location / { |
||||||
|
proxy_pass http://{$UPSTREAM_NAME}; |
||||||
|
|
||||||
|
client_max_body_size 10m; |
||||||
|
client_body_buffer_size 128k; |
||||||
|
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
||||||
|
proxy_set_header X-Real-IP $remote_addr; |
||||||
|
proxy_set_header Host $proxy_host; |
||||||
|
proxy_connect_timeout 90; |
||||||
|
proxy_send_timeout 90; |
||||||
|
proxy_read_timeout 90; |
||||||
|
proxy_buffer_size 4k; |
||||||
|
proxy_buffers 4 32k; |
||||||
|
proxy_busy_buffers_size 64k; |
||||||
|
proxy_temp_file_write_size 64k; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#location /upstream_status { |
||||||
|
# allow 127.0.0.1; |
||||||
|
# deny all; |
||||||
|
# access_log off; |
||||||
|
# default_type text/plain; |
||||||
|
# content_by_lua_block { |
||||||
|
# local hc = require "resty.upstream.healthcheck" |
||||||
|
# ngx.say("OpenResty Worker PID: ", ngx.worker.pid()) |
||||||
|
# ngx.print(hc.status_page()) |
||||||
|
# } |
||||||
|
#} |
||||||
|
|
||||||
|
location /upstream_status_{$UPSTREAM_NAME} { |
||||||
|
allow 127.0.0.1; |
||||||
|
deny all; |
||||||
|
access_log off; |
||||||
|
default_type text/plain; |
||||||
|
content_by_lua_block { |
||||||
|
local json = require "cjson" |
||||||
|
local ok, upstream = pcall(require, "ngx.upstream") |
||||||
|
if not ok then |
||||||
|
ngx.print("[]") |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
local get_primary_peers = upstream.get_primary_peers |
||||||
|
local get_backup_peers = upstream.get_backup_peers |
||||||
|
local upstream_name = "{$UPSTREAM_NAME}" |
||||||
|
|
||||||
|
peers, err = get_primary_peers(upstream_name) |
||||||
|
if not peers then |
||||||
|
ngx.print("[]") |
||||||
|
return |
||||||
|
end |
||||||
|
|
||||||
|
peers_backup, err = get_backup_peers(upstream_name) |
||||||
|
if peers_backup then |
||||||
|
for k, v in pairs(peers_backup) do |
||||||
|
table.insert(peers,v) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
ngx.print(json.encode(peers)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
upstream {$UPSTREAM_NAME} |
||||||
|
{ |
||||||
|
{$NODE_ALGO} |
||||||
|
{$NODE_SERVER_LIST} |
||||||
|
} |
After Width: | Height: | Size: 853 B |
@ -0,0 +1,21 @@ |
|||||||
|
<div class="bt-form"> |
||||||
|
<div class="bt-w-main"> |
||||||
|
<div class="bt-w-menu"> |
||||||
|
<p class="bgw" onclick="pluginService('op_load_balance');">服务</p> |
||||||
|
<p onclick="loadBalanceList();">负载均衡</p> |
||||||
|
</div> |
||||||
|
<div class="bt-w-con pd15"> |
||||||
|
<div class="soft-man-con"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<script type="text/javascript" src="/plugins/file?name=op_load_balance&f=js/app.js"></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
resetPluginWinWidth(800); |
||||||
|
resetPluginWinHeight(300); |
||||||
|
$.getScript( "/plugins/file?name=op_load_balance&f=js/app.js", function(){ |
||||||
|
pluginService('op_load_balance'); |
||||||
|
}); |
||||||
|
</script> |
@ -0,0 +1,474 @@ |
|||||||
|
# coding:utf-8 |
||||||
|
|
||||||
|
import sys |
||||||
|
import io |
||||||
|
import os |
||||||
|
import time |
||||||
|
import subprocess |
||||||
|
import json |
||||||
|
import re |
||||||
|
|
||||||
|
sys.path.append(os.getcwd() + "/class/core") |
||||||
|
import mw |
||||||
|
|
||||||
|
|
||||||
|
app_debug = False |
||||||
|
if mw.isAppleSystem(): |
||||||
|
app_debug = True |
||||||
|
|
||||||
|
|
||||||
|
def getPluginName(): |
||||||
|
return 'op_load_balance' |
||||||
|
|
||||||
|
|
||||||
|
def getPluginDir(): |
||||||
|
return mw.getPluginDir() + '/' + getPluginName() |
||||||
|
|
||||||
|
|
||||||
|
def getServerDir(): |
||||||
|
return mw.getServerDir() + '/' + getPluginName() |
||||||
|
|
||||||
|
|
||||||
|
def getArgs(): |
||||||
|
args = sys.argv[2:] |
||||||
|
tmp = {} |
||||||
|
args_len = len(args) |
||||||
|
|
||||||
|
if args_len == 1: |
||||||
|
t = args[0].strip('{').strip('}') |
||||||
|
if t.strip() == '': |
||||||
|
tmp = [] |
||||||
|
else: |
||||||
|
t = t.split(':') |
||||||
|
tmp[t[0]] = t[1] |
||||||
|
tmp[t[0]] = t[1] |
||||||
|
elif args_len > 1: |
||||||
|
for i in range(len(args)): |
||||||
|
t = args[i].split(':') |
||||||
|
tmp[t[0]] = t[1] |
||||||
|
return tmp |
||||||
|
|
||||||
|
|
||||||
|
def checkArgs(data, ck=[]): |
||||||
|
for i in range(len(ck)): |
||||||
|
if not ck[i] in data: |
||||||
|
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) |
||||||
|
return (True, mw.returnJson(True, 'ok')) |
||||||
|
|
||||||
|
|
||||||
|
def getConf(): |
||||||
|
path = getServerDir() + "/cfg.json" |
||||||
|
|
||||||
|
if not os.path.exists(path): |
||||||
|
mw.writeFile(path, '[]') |
||||||
|
|
||||||
|
c = mw.readFile(path) |
||||||
|
return json.loads(c) |
||||||
|
|
||||||
|
|
||||||
|
def writeConf(data): |
||||||
|
path = getServerDir() + "/cfg.json" |
||||||
|
mw.writeFile(path, json.dumps(data)) |
||||||
|
|
||||||
|
|
||||||
|
def contentReplace(content): |
||||||
|
service_path = mw.getServerDir() |
||||||
|
content = content.replace('{$ROOT_PATH}', mw.getRootDir()) |
||||||
|
content = content.replace('{$SERVER_PATH}', service_path) |
||||||
|
content = content.replace('{$APP_PATH}', app_path) |
||||||
|
return content |
||||||
|
|
||||||
|
|
||||||
|
def restartWeb(): |
||||||
|
mw.opWeb('stop') |
||||||
|
mw.opWeb('start') |
||||||
|
|
||||||
|
|
||||||
|
def loadBalanceConf(): |
||||||
|
path = mw.getServerDir() + '/web_conf/nginx/vhost/load_balance.conf' |
||||||
|
return path |
||||||
|
|
||||||
|
|
||||||
|
def initDreplace(): |
||||||
|
|
||||||
|
dst_conf_tpl = getPluginDir() + '/conf/load_balance.conf' |
||||||
|
dst_conf = loadBalanceConf() |
||||||
|
|
||||||
|
if not os.path.exists(dst_conf): |
||||||
|
con = mw.readFile(dst_conf_tpl) |
||||||
|
mw.writeFile(dst_conf, con) |
||||||
|
|
||||||
|
|
||||||
|
def status(): |
||||||
|
if not mw.getWebStatus(): |
||||||
|
return 'stop' |
||||||
|
|
||||||
|
dst_conf = loadBalanceConf() |
||||||
|
if not os.path.exists(dst_conf): |
||||||
|
return 'stop' |
||||||
|
|
||||||
|
return 'start' |
||||||
|
|
||||||
|
|
||||||
|
def start(): |
||||||
|
initDreplace() |
||||||
|
restartWeb() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def stop(): |
||||||
|
dst_conf = loadBalanceConf() |
||||||
|
os.remove(dst_conf) |
||||||
|
|
||||||
|
deleteLoadBalanceAllCfg() |
||||||
|
restartWeb() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def restart(): |
||||||
|
restartWeb() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def reload(): |
||||||
|
restartWeb() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def installPreInspection(): |
||||||
|
check_op = mw.getServerDir() + "/openresty" |
||||||
|
if not os.path.exists(check_op): |
||||||
|
return "请先安装OpenResty" |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def deleteLoadBalanceAllCfg(): |
||||||
|
cfg = getConf() |
||||||
|
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' |
||||||
|
lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file' |
||||||
|
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' |
||||||
|
vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' |
||||||
|
|
||||||
|
for conf in cfg: |
||||||
|
upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf' |
||||||
|
if os.path.exists(upstream_file): |
||||||
|
os.remove(upstream_file) |
||||||
|
|
||||||
|
lua_file = lua_dir + '/' + conf['upstream_name'] + '.lua' |
||||||
|
if os.path.exists(lua_file): |
||||||
|
os.remove(lua_file) |
||||||
|
|
||||||
|
rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf' |
||||||
|
mw.writeFile(rewrite_file, '') |
||||||
|
|
||||||
|
path = vhost_dir + '/' + conf['domain'] + '.conf' |
||||||
|
|
||||||
|
content = mw.readFile(path) |
||||||
|
content = re.sub('include ' + upstream_file + ';' + "\n", '', content) |
||||||
|
mw.writeFile(path, content) |
||||||
|
|
||||||
|
mw.opLuaInitWorkerFile() |
||||||
|
|
||||||
|
|
||||||
|
def makeConfServerList(data): |
||||||
|
slist = '' |
||||||
|
for x in data: |
||||||
|
slist += 'server ' |
||||||
|
slist += x['ip'] + ':' + x['port'] |
||||||
|
|
||||||
|
if x['state'] == '0': |
||||||
|
slist += ' down;\n\t' |
||||||
|
continue |
||||||
|
|
||||||
|
if x['state'] == '2': |
||||||
|
slist += ' backup;\n\t' |
||||||
|
continue |
||||||
|
|
||||||
|
slist += ' weight=' + x['weight'] |
||||||
|
slist += ' max_fails=' + x['max_fails'] |
||||||
|
slist += ' fail_timeout=' + x['fail_timeout'] + "s;\n\t" |
||||||
|
return slist |
||||||
|
|
||||||
|
|
||||||
|
def makeLoadBalanceAllCfg(row): |
||||||
|
# 生成所有配置 |
||||||
|
cfg = getConf() |
||||||
|
|
||||||
|
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' |
||||||
|
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' |
||||||
|
vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' |
||||||
|
upstream_tpl = getPluginDir() + '/conf/upstream.tpl.conf' |
||||||
|
rewrite_tpl = getPluginDir() + '/conf/rewrite.tpl.conf' |
||||||
|
|
||||||
|
if not os.path.exists(upstream_dir): |
||||||
|
os.makedirs(upstream_dir) |
||||||
|
|
||||||
|
conf = cfg[row] |
||||||
|
|
||||||
|
# replace vhost start |
||||||
|
vhost_file = vhost_dir + '/' + conf['domain'] + '.conf' |
||||||
|
vcontent = mw.readFile(vhost_file) |
||||||
|
|
||||||
|
vhost_find_str = 'upstream/' + conf['upstream_name'] + '.conf' |
||||||
|
vhead = 'include ' + mw.getServerDir() + '/web_conf/nginx/' + \ |
||||||
|
vhost_find_str + ';' |
||||||
|
|
||||||
|
vpos = vcontent.find(vhost_find_str) |
||||||
|
if vpos < 0: |
||||||
|
vcontent = vhead + "\n" + vcontent |
||||||
|
mw.writeFile(vhost_file, vcontent) |
||||||
|
# replace vhost end |
||||||
|
|
||||||
|
# make upstream start |
||||||
|
upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf' |
||||||
|
content = '' |
||||||
|
if len(conf['node_list']) > 0: |
||||||
|
content = mw.readFile(upstream_tpl) |
||||||
|
slist = makeConfServerList(conf['node_list']) |
||||||
|
content = content.replace('{$NODE_SERVER_LIST}', slist) |
||||||
|
content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name']) |
||||||
|
if conf['node_algo'] != 'polling': |
||||||
|
content = content.replace('{$NODE_ALGO}', conf['node_algo'] + ';') |
||||||
|
else: |
||||||
|
content = content.replace('{$NODE_ALGO}', '') |
||||||
|
mw.writeFile(upstream_file, content) |
||||||
|
# make upstream end |
||||||
|
|
||||||
|
# make rewrite start |
||||||
|
rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf' |
||||||
|
rcontent = '' |
||||||
|
if len(conf['node_list']) > 0: |
||||||
|
rcontent = mw.readFile(rewrite_tpl) |
||||||
|
rcontent = rcontent.replace('{$UPSTREAM_NAME}', conf['upstream_name']) |
||||||
|
mw.writeFile(rewrite_file, rcontent) |
||||||
|
# make rewrite end |
||||||
|
|
||||||
|
# health check start |
||||||
|
lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file' |
||||||
|
lua_init_worker_file = lua_dir + '/' + conf['upstream_name'] + '.lua' |
||||||
|
if conf['node_health_check'] == 'ok': |
||||||
|
lua_dir_tpl = getPluginDir() + '/lua/health_check.lua.tpl' |
||||||
|
content = mw.readFile(lua_dir_tpl) |
||||||
|
content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name']) |
||||||
|
content = content.replace('{$DOMAIN}', conf['domain']) |
||||||
|
mw.writeFile(lua_init_worker_file, content) |
||||||
|
else: |
||||||
|
if os.path.exists(lua_init_worker_file): |
||||||
|
os.remove(lua_init_worker_file) |
||||||
|
|
||||||
|
mw.opLuaInitWorkerFile() |
||||||
|
# health check end |
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
def add_load_balance(args): |
||||||
|
|
||||||
|
data = checkArgs( |
||||||
|
args, ['domain', 'upstream_name', 'node_algo', 'node_list', 'node_health_check']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
domain_json = args['domain'] |
||||||
|
|
||||||
|
tmp = json.loads(domain_json) |
||||||
|
domain = tmp['domain'] |
||||||
|
|
||||||
|
cfg = getConf() |
||||||
|
cfg_len = len(cfg) |
||||||
|
tmp = {} |
||||||
|
tmp['domain'] = domain |
||||||
|
tmp['data'] = args['domain'] |
||||||
|
tmp['upstream_name'] = args['upstream_name'] |
||||||
|
tmp['node_algo'] = args['node_algo'] |
||||||
|
tmp['node_list'] = args['node_list'] |
||||||
|
tmp['node_health_check'] = args['node_health_check'] |
||||||
|
cfg.append(tmp) |
||||||
|
writeConf(cfg) |
||||||
|
|
||||||
|
import site_api |
||||||
|
sobj = site_api.site_api() |
||||||
|
domain_path = mw.getWwwDir() + '/' + domain |
||||||
|
|
||||||
|
ps = '负载均衡[' + domain + ']' |
||||||
|
data = sobj.add(domain_json, '80', ps, domain_path, '00') |
||||||
|
|
||||||
|
makeLoadBalanceAllCfg(cfg_len) |
||||||
|
mw.restartWeb() |
||||||
|
return mw.returnJson(True, '添加成功', data) |
||||||
|
|
||||||
|
|
||||||
|
def edit_load_balance(args): |
||||||
|
data = checkArgs( |
||||||
|
args, ['row', 'node_algo', 'node_list', 'node_health_check']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
row = int(args['row']) |
||||||
|
|
||||||
|
cfg = getConf() |
||||||
|
tmp = cfg[row] |
||||||
|
tmp['node_algo'] = args['node_algo'] |
||||||
|
tmp['node_list'] = args['node_list'] |
||||||
|
tmp['node_health_check'] = args['node_health_check'] |
||||||
|
cfg[row] = tmp |
||||||
|
writeConf(cfg) |
||||||
|
|
||||||
|
makeLoadBalanceAllCfg(row) |
||||||
|
mw.restartWeb() |
||||||
|
return mw.returnJson(True, '修改成功', data) |
||||||
|
|
||||||
|
|
||||||
|
def loadBalanceList(): |
||||||
|
cfg = getConf() |
||||||
|
return mw.returnJson(True, 'ok', cfg) |
||||||
|
|
||||||
|
|
||||||
|
def loadBalanceDelete(): |
||||||
|
args = getArgs() |
||||||
|
data = checkArgs(args, ['row']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
row = int(args['row']) |
||||||
|
|
||||||
|
cfg = getConf() |
||||||
|
data = cfg[row] |
||||||
|
|
||||||
|
import site_api |
||||||
|
sobj = site_api.site_api() |
||||||
|
|
||||||
|
sid = mw.M('sites').where('name=?', (data['domain'],)).getField('id') |
||||||
|
|
||||||
|
if type(sid) == list: |
||||||
|
del(cfg[row]) |
||||||
|
writeConf(cfg) |
||||||
|
return mw.returnJson(False, '已经删除了!') |
||||||
|
|
||||||
|
status = sobj.delete(sid, data['domain'], 1) |
||||||
|
status_data = json.loads(status) |
||||||
|
|
||||||
|
if status_data['status']: |
||||||
|
del(cfg[row]) |
||||||
|
writeConf(cfg) |
||||||
|
|
||||||
|
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' |
||||||
|
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' |
||||||
|
|
||||||
|
upstream_file = upstream_dir + '/' + data['upstream_name'] + '.conf' |
||||||
|
if os.path.exists(upstream_file): |
||||||
|
os.remove(upstream_file) |
||||||
|
|
||||||
|
rewrite_file = rewrite_dir + '/' + data['domain'] + '.conf' |
||||||
|
if os.path.exists(rewrite_file): |
||||||
|
mw.writeFile(rewrite_file, '') |
||||||
|
|
||||||
|
return mw.returnJson(status_data['status'], status_data['msg']) |
||||||
|
|
||||||
|
|
||||||
|
def http_get(url): |
||||||
|
ret = re.search(r'https://', url) |
||||||
|
if ret: |
||||||
|
try: |
||||||
|
from gevent import monkey |
||||||
|
monkey.patch_ssl() |
||||||
|
import requests |
||||||
|
ret = requests.get(url=str(url), verify=False, timeout=10) |
||||||
|
status = [200, 301, 302, 404, 403] |
||||||
|
if ret.status_code in status: |
||||||
|
return True |
||||||
|
else: |
||||||
|
return False |
||||||
|
except: |
||||||
|
return False |
||||||
|
else: |
||||||
|
try: |
||||||
|
if sys.version_info[0] == 2: |
||||||
|
import urllib2 |
||||||
|
rec = urllib2.urlopen(url, timeout=3) |
||||||
|
else: |
||||||
|
import urllib.request |
||||||
|
rec = urllib.request.urlopen(url, timeout=3) |
||||||
|
status = [200, 301, 302, 404, 403] |
||||||
|
if rec.getcode() in status: |
||||||
|
return True |
||||||
|
return False |
||||||
|
except: |
||||||
|
return False |
||||||
|
|
||||||
|
|
||||||
|
def checkUrl(): |
||||||
|
args = getArgs() |
||||||
|
data = checkArgs(args, ['ip', 'port', 'path']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
ip = args['ip'] |
||||||
|
port = args['port'] |
||||||
|
path = args['path'] |
||||||
|
|
||||||
|
if port == '443': |
||||||
|
url = 'https://' + str(ip) + ':' + str(port) + str(path.strip()) |
||||||
|
else: |
||||||
|
url = 'http://' + str(ip) + ':' + str(port) + str(path.strip()) |
||||||
|
ret = http_get(url) |
||||||
|
if not ret: |
||||||
|
return mw.returnJson(False, '访问节点[%s]失败' % url) |
||||||
|
return mw.returnJson(True, '访问节点[%s]成功' % url) |
||||||
|
|
||||||
|
|
||||||
|
def getHealthStatus(): |
||||||
|
args = getArgs() |
||||||
|
data = checkArgs(args, ['row']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
row = int(args['row']) |
||||||
|
|
||||||
|
cfg = getConf() |
||||||
|
data = cfg[row] |
||||||
|
|
||||||
|
url = 'http://' + data['domain'] + \ |
||||||
|
'/upstream_status_' + data['upstream_name'] |
||||||
|
|
||||||
|
url_data = mw.httpGet(url) |
||||||
|
return mw.returnJson(True, 'ok', json.loads(url_data)) |
||||||
|
|
||||||
|
|
||||||
|
def getLogs(): |
||||||
|
args = getArgs() |
||||||
|
data = checkArgs(args, ['domain']) |
||||||
|
if not data[0]: |
||||||
|
return data[1] |
||||||
|
|
||||||
|
domain = args['domain'] |
||||||
|
logs = mw.getLogsDir() + '/' + domain + '.log' |
||||||
|
return logs |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
func = sys.argv[1] |
||||||
|
if func == 'status': |
||||||
|
print(status()) |
||||||
|
elif func == 'start': |
||||||
|
print(start()) |
||||||
|
elif func == 'stop': |
||||||
|
print(stop()) |
||||||
|
elif func == 'restart': |
||||||
|
print(restart()) |
||||||
|
elif func == 'reload': |
||||||
|
print(reload()) |
||||||
|
elif func == 'install_pre_inspection': |
||||||
|
print(installPreInspection()) |
||||||
|
elif func == 'add_load_balance': |
||||||
|
print(addLoadBalance()) |
||||||
|
elif func == 'load_balance_list': |
||||||
|
print(loadBalanceList()) |
||||||
|
elif func == 'load_balance_delete': |
||||||
|
print(loadBalanceDelete()) |
||||||
|
elif func == 'check_url': |
||||||
|
print(checkUrl()) |
||||||
|
elif func == 'get_logs': |
||||||
|
print(getLogs()) |
||||||
|
elif func == 'get_health_status': |
||||||
|
print(getHealthStatus()) |
||||||
|
else: |
||||||
|
print('error') |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"title":"OP负载均衡", |
||||||
|
"tip":"soft", |
||||||
|
"name":"op_load_balance", |
||||||
|
"type":"其他插件", |
||||||
|
"install_pre_inspection":true, |
||||||
|
"ps":"基于OpenResty的负载均衡", |
||||||
|
"shell":"install.sh", |
||||||
|
"checks":"server/op_load_balance", |
||||||
|
"path":"server/op_load_balance", |
||||||
|
"author":"midoks", |
||||||
|
"home":"https://github.com/midoks", |
||||||
|
"date":"2023-02-02", |
||||||
|
"pid": "1", |
||||||
|
"versions": ["1.0"] |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
#!/bin/bash |
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin |
||||||
|
export PATH |
||||||
|
|
||||||
|
curPath=`pwd` |
||||||
|
rootPath=$(dirname "$curPath") |
||||||
|
rootPath=$(dirname "$rootPath") |
||||||
|
serverPath=$(dirname "$rootPath") |
||||||
|
|
||||||
|
install_tmp=${rootPath}/tmp/mw_install.pl |
||||||
|
|
||||||
|
action=$1 |
||||||
|
version=$2 |
||||||
|
sys_os=`uname` |
||||||
|
|
||||||
|
if [ -f ${rootPath}/bin/activate ];then |
||||||
|
source ${rootPath}/bin/activate |
||||||
|
fi |
||||||
|
|
||||||
|
if [ "$sys_os" == "Darwin" ];then |
||||||
|
BAK='_bak' |
||||||
|
else |
||||||
|
BAK='' |
||||||
|
fi |
||||||
|
|
||||||
|
Install_App(){ |
||||||
|
echo '正在安装脚本文件...' > $install_tmp |
||||||
|
mkdir -p $serverPath/op_load_balance |
||||||
|
echo "${version}" > $serverPath/op_load_balance/version.pl |
||||||
|
cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py start |
||||||
|
echo 'install ok' > $install_tmp |
||||||
|
} |
||||||
|
|
||||||
|
Uninstall_App(){ |
||||||
|
cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py stop |
||||||
|
rm -rf $serverPath/op_load_balance |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
action=$1 |
||||||
|
if [ "${1}" == 'install' ];then |
||||||
|
Install_App |
||||||
|
else |
||||||
|
Uninstall_App |
||||||
|
fi |
@ -0,0 +1,606 @@ |
|||||||
|
function ooPost(method,args,callback){ |
||||||
|
var _args = null;
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
_args = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
_args = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); |
||||||
|
$.post('/plugins/run', {name:'op_load_balance', func:method, args:_args}, function(data) { |
||||||
|
layer.close(loadT); |
||||||
|
if (!data.status){ |
||||||
|
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
function ooAsyncPost(method,args){ |
||||||
|
var _args = null;
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
_args = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
_args = JSON.stringify(args); |
||||||
|
} |
||||||
|
return syncPost('/plugins/run', {name:'op_load_balance', func:method, args:_args});
|
||||||
|
} |
||||||
|
|
||||||
|
function ooPostCallbak(method, args, callback){ |
||||||
|
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); |
||||||
|
|
||||||
|
var req_data = {}; |
||||||
|
req_data['name'] = 'op_load_balance'; |
||||||
|
req_data['func'] = method; |
||||||
|
args['version'] = '1.0'; |
||||||
|
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
req_data['args'] = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
req_data['args'] = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
$.post('/plugins/callback', req_data, function(data) { |
||||||
|
layer.close(loadT); |
||||||
|
if (!data.status){ |
||||||
|
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
function addNode(){ |
||||||
|
layer.open({ |
||||||
|
type: 1, |
||||||
|
area: ['450px','580px'], |
||||||
|
title: '添加节点', |
||||||
|
closeBtn: 1, |
||||||
|
shift: 5, |
||||||
|
shadeClose: true, |
||||||
|
btn:['提交','关闭'], |
||||||
|
content: "<form class='bt-form pd20'>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>IP地址</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='ip' class='bt-input-text mr5' placeholder='负载名称,可以是英文字母和下划线,不能使用中文' type='text' style='width:250px' value='127.0.0.1'>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>端口</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='port' class='bt-input-text mr5' type='text' style='width:250px' value='80'>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>验证文件路径</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='path' class='bt-input-text mr5' type='text' style='width:250px' value='/'>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点状态</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<select name='state'>\ |
||||||
|
<option value='1'>参与者</option>\ |
||||||
|
<option value='2'>备份</option>\ |
||||||
|
<option value='0'>停用</option>\ |
||||||
|
</select>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>权重</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='weight' class='bt-input-text mr5' type='text' style='width:250px' value='1'>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>阈值</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='max_fails' class='bt-input-text mr5' type='text' style='width:250px' value='2'>次\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>恢复时间</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='fail_timeout' class='bt-input-text mr5' type='text' style='width:250px' value='10'>秒\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<ul style='margin-left:10px' class='help-info-text c7'>\ |
||||||
|
<li>备份状态: 指当其它节点都无法使用时才会使用此节点</li>\ |
||||||
|
<li>参与状态: 正常参与负载均衡,请至少添加1个普通节点</li>\ |
||||||
|
<li>验证文件路径: 用于检查文件路径地址是否可用</li>\ |
||||||
|
<li>IP地址: 仅支持IP地址,否则无法正常参与负载均衡</li>\ |
||||||
|
<li>阈值: 在恢复时间的时间段内,如果OpenResty与节点通信尝试失败的次数达到此值,OpenResty就认为服务器不可用</li>\ |
||||||
|
</ul>\ |
||||||
|
</form>", |
||||||
|
success:function(){ |
||||||
|
}, |
||||||
|
yes:function(index) { |
||||||
|
|
||||||
|
var ip = $('input[name="ip"]').val(); |
||||||
|
var port = $('input[name="port"]').val(); |
||||||
|
var path = $('input[name="path"]').val(); |
||||||
|
var state = $('select[name="state"]').val(); |
||||||
|
var weight = $('input[name="weight"]').val(); |
||||||
|
var max_fails = $('input[name="max_fails"]').val(); |
||||||
|
var fail_timeout = $('input[name="fail_timeout"]').val(); |
||||||
|
|
||||||
|
ooPost('check_url', {ip:ip,port:port,path:path},function(rdata){
|
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
showMsg(rdata.msg, function(){ |
||||||
|
if (rdata.status){ |
||||||
|
layer.close(index); |
||||||
|
$('#nodecon .nulltr').hide(); |
||||||
|
|
||||||
|
var tbody = '<tr>'; |
||||||
|
tbody +='<td>'+ip+'</td>'; |
||||||
|
tbody +='<td>'+port+'</td>'; |
||||||
|
tbody +='<td>'+path+'</td>'; |
||||||
|
|
||||||
|
tbody +="<td><select name='state'>"; |
||||||
|
var state_option_list = { |
||||||
|
'1':'参与者', |
||||||
|
'2':'备份', |
||||||
|
'0':'停用', |
||||||
|
} |
||||||
|
for (i in state_option_list) { |
||||||
|
if (i == state){ |
||||||
|
tbody +="<option value='"+i+"' selected>"+state_option_list[i]+"</option>"; |
||||||
|
} else{ |
||||||
|
tbody +="<option value='"+i+"'>"+state_option_list[i]+"</option>"; |
||||||
|
} |
||||||
|
} |
||||||
|
tbody +="</select></td>"; |
||||||
|
|
||||||
|
tbody +='<td><input type="number" name="weight" value="'+weight+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td><input type="number" name="max_fails" value="'+max_fails+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td><input type="number" name="fail_timeout" value="'+fail_timeout+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td class="text-right" width="50"><a class="btlink minus delete">删除</a></td>'; |
||||||
|
tbody += '</tr>'; |
||||||
|
$('#nodecon').append(tbody); |
||||||
|
|
||||||
|
$('#nodecon .delete').click(function(){ |
||||||
|
$(this).parent().parent().remove(); |
||||||
|
if ($('#nodecon tr').length == 1 ){ |
||||||
|
$('#nodecon .nulltr').show(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
},{ icon: rdata.status ? 1 : 2 }, 2000); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function addBalance(){ |
||||||
|
layer.open({ |
||||||
|
type: 1, |
||||||
|
area: ['750px','460px'], |
||||||
|
title: '创建负载', |
||||||
|
closeBtn: 1, |
||||||
|
shift: 5, |
||||||
|
shadeClose: true, |
||||||
|
btn:['提交','关闭'], |
||||||
|
content: "<form class='bt-form pd20'>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>域名</span>\ |
||||||
|
<div class='info-r'><textarea name='load_domain' class='bt-input-text mr5' placeholder='' style='width:95%;resize: none;height:90px;line-height:20px;'></textarea></div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>负载名称</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input name='upstream_name' class='bt-input-text mr5' placeholder='负载名称,可以是英文字母和下划线,不能使用中文' type='text' style='width:95%' value=''>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点调度</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<select name='node_algo'>\ |
||||||
|
<option value='polling'>轮询[默认]</option>\ |
||||||
|
<option value='ip_hash'>ip_hash</option>\ |
||||||
|
<option value='fair'>fair</option>\ |
||||||
|
<option value='url_hash'>url_hash</option>\ |
||||||
|
<option value='least_conn'>least_conn</option>\ |
||||||
|
</select>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点健康检查</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input type='checkbox' name='node_health_check' checked>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<div class='table-con divtable' style='max-height:120px;overflow:auto; margin-bottom:8px;width:95%'>\ |
||||||
|
<table class='table table-hover' id='fixTable3'>\ |
||||||
|
<thead>\ |
||||||
|
<tr>\ |
||||||
|
<th width='120'>IP地址</th>\ |
||||||
|
<th width='60'>端口</th>\ |
||||||
|
<th width='120'>验证路径</th>\ |
||||||
|
<th width='60'>状态</th>\ |
||||||
|
<th width='60'>权重</th>\ |
||||||
|
<th width='60'>阀值</th>\ |
||||||
|
<th width='120'>恢复时间</th>\ |
||||||
|
<th width='80' class='text-right'>操作</th>\ |
||||||
|
</tr>\ |
||||||
|
</thead>\ |
||||||
|
<tbody id='nodecon'>\ |
||||||
|
<tr class='nulltr'>\ |
||||||
|
<td colspan='8' align='center'>当前节点为空,请至少添加一个普通节点</td>\ |
||||||
|
</tr>\ |
||||||
|
</tbody>\ |
||||||
|
</table>\ |
||||||
|
</div>\ |
||||||
|
<span class='btn btn-success btn-sm add_node' style='vertical-align:0'>添加节点</span>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</form>", |
||||||
|
success:function(){ |
||||||
|
$('textarea[name="load_domain"]').attr('placeholder','每行填写一个域名,默认为80端口。\n泛解析添加方法 *.domain.com\n如另加端口格式为 www.domain.com:88'); |
||||||
|
var rval = getRandomString(6); |
||||||
|
$('input[name="upstream_name"]').val('load_balance_'+rval); |
||||||
|
|
||||||
|
$('.add_node').click(function(){ |
||||||
|
addNode(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
yes:function(index) { |
||||||
|
var data = {}; |
||||||
|
|
||||||
|
var upstream_name = $('input[name="upstream_name"]').val(); |
||||||
|
if (upstream_name == ''){ |
||||||
|
layer.msg('负载名称不能为空!',{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var domain = $('textarea[name="load_domain"]').val().replace('http://','').replace('https://','').split("\n"); |
||||||
|
if (domain[0] == ''){ |
||||||
|
layer.msg('域名不能为空!',{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var domainlist = ''; |
||||||
|
for(var i=1; i<domain.length; i++){ |
||||||
|
domainlist += '"'+domain[i]+'",'; |
||||||
|
} |
||||||
|
domain ='{"domain":"'+domain[0]+'","domainlist":['+domainlist+'],"count":'+domain.length+'}';//拼接json
|
||||||
|
data['domain'] = domain; |
||||||
|
data['upstream_name'] = upstream_name; |
||||||
|
|
||||||
|
data['node_algo'] = $('select[name="node_algo"]').val(); |
||||||
|
|
||||||
|
data['node_health_check'] = 'fail'; |
||||||
|
if ($('input[name="node_health_check"]').prop('checked')){ |
||||||
|
data['node_health_check'] = 'ok'; |
||||||
|
} |
||||||
|
|
||||||
|
var node_list = []; |
||||||
|
$('#nodecon tr').each(function(){ |
||||||
|
|
||||||
|
var ip = $(this).find('td').eq(0).text(); |
||||||
|
var port = $(this).find('td').eq(1).text(); |
||||||
|
|
||||||
|
if (port == ''){return;} |
||||||
|
|
||||||
|
var path = $(this).find('td').eq(2).text(); |
||||||
|
var state = $(this).find('select[name="state"]').val(); |
||||||
|
var weight = $(this).find('input[name="weight"]').val(); |
||||||
|
var max_fails = $(this).find('input[name="max_fails"]').val(); |
||||||
|
var fail_timeout = $(this).find('input[name="fail_timeout"]').val(); |
||||||
|
|
||||||
|
var tmp = { |
||||||
|
ip:ip, |
||||||
|
port:port, |
||||||
|
path:path, |
||||||
|
state:state, |
||||||
|
weight:weight, |
||||||
|
max_fails:max_fails, |
||||||
|
fail_timeout:fail_timeout, |
||||||
|
} |
||||||
|
node_list.push(tmp); |
||||||
|
}); |
||||||
|
data['node_list'] = node_list; |
||||||
|
ooPostCallbak('add_load_balance', data, function(rdata){ |
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
showMsg(rdata.msg, function(){ |
||||||
|
layer.close(index); |
||||||
|
loadBalanceListRender(); |
||||||
|
},{ icon: rdata.status ? 1 : 2 }, 2000); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function editBalance(data, row){ |
||||||
|
layer.open({ |
||||||
|
type: 1, |
||||||
|
area: ['750px','400px'], |
||||||
|
title: '编辑负载', |
||||||
|
closeBtn: 1, |
||||||
|
shift: 5, |
||||||
|
shadeClose: true, |
||||||
|
btn:['提交','关闭'], |
||||||
|
content: "<form class='bt-form pd20'>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>负载名称</span>\ |
||||||
|
<div class='info-r'><input name='upstream_name' class='bt-input-text mr5' type='text' style='width:95%;background: rgb(238, 238, 238);' value='' readonly></div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点调度</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<select name='node_algo'>\ |
||||||
|
<option value='polling'>轮询[默认]</option>\ |
||||||
|
<option value='ip_hash'>ip_hash</option>\ |
||||||
|
<option value='fair'>fair</option>\ |
||||||
|
<option value='url_hash'>url_hash</option>\ |
||||||
|
<option value='least_conn'>least_conn</option>\ |
||||||
|
</select>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点健康检查</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<input type='checkbox' name='node_health_check'>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>节点</span>\ |
||||||
|
<div class='info-r'>\ |
||||||
|
<div class='table-con divtable' style='max-height:120px;overflow:auto; margin-bottom:8px;width:95%'>\ |
||||||
|
<table class='table table-hover' id='fixTable3'>\ |
||||||
|
<thead>\ |
||||||
|
<tr>\ |
||||||
|
<th width='120'>IP地址</th>\ |
||||||
|
<th width='60'>端口</th>\ |
||||||
|
<th width='120'>验证路径</th>\ |
||||||
|
<th width='60'>状态</th>\ |
||||||
|
<th width='60'>权重</th>\ |
||||||
|
<th width='60'>阀值</th>\ |
||||||
|
<th width='120'>恢复时间</th>\ |
||||||
|
<th width='80' class='text-right'>操作</th>\ |
||||||
|
</tr>\ |
||||||
|
</thead>\ |
||||||
|
<tbody id='nodecon'>\ |
||||||
|
<tr class='nulltr'>\ |
||||||
|
<td colspan='8' align='center'>当前节点为空,请至少添加一个普通节点</td>\ |
||||||
|
</tr>\ |
||||||
|
</tbody>\ |
||||||
|
</table>\ |
||||||
|
</div>\ |
||||||
|
<span class='btn btn-success btn-sm add_node' style='vertical-align:0'>添加节点</span>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</form>", |
||||||
|
success:function(){ |
||||||
|
$('input[name="upstream_name"]').val(data['upstream_name']); |
||||||
|
$('select[name="node_algo"]').val(data['node_algo']); |
||||||
|
|
||||||
|
$('input[name="node_health_check"]').prop('checked',false); |
||||||
|
if (data['node_health_check'] == 'ok'){ |
||||||
|
$('input[name="node_health_check"]').prop('checked',true); |
||||||
|
} |
||||||
|
|
||||||
|
var node_list = data['node_list']; |
||||||
|
if (node_list.length>0){ |
||||||
|
$('#nodecon .nulltr').hide(); |
||||||
|
} |
||||||
|
|
||||||
|
var state_option_list = { |
||||||
|
'1':'参与者', |
||||||
|
'2':'备份', |
||||||
|
'0':'停用', |
||||||
|
} |
||||||
|
|
||||||
|
for (var n in node_list) { |
||||||
|
|
||||||
|
var tbody = '<tr>'; |
||||||
|
tbody +='<td>'+node_list[n]['ip']+'</td>'; |
||||||
|
tbody +='<td>'+node_list[n]['port']+'</td>'; |
||||||
|
tbody +='<td>'+node_list[n]['path']+'</td>'; |
||||||
|
|
||||||
|
tbody +="<td><select name='state'>"; |
||||||
|
|
||||||
|
for (i in state_option_list) { |
||||||
|
if (i == node_list[n]['state']){ |
||||||
|
tbody +="<option value='"+i+"' selected>"+state_option_list[i]+"</option>"; |
||||||
|
} else{ |
||||||
|
tbody +="<option value='"+i+"'>"+state_option_list[i]+"</option>"; |
||||||
|
} |
||||||
|
} |
||||||
|
tbody +="</select></td>"; |
||||||
|
|
||||||
|
tbody +='<td><input type="number" name="weight" value="'+node_list[n]['weight']+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td><input type="number" name="max_fails" value="'+node_list[n]['max_fails']+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td><input type="number" name="fail_timeout" value="'+node_list[n]['fail_timeout']+'" style="width:50px"></td>'; |
||||||
|
tbody +='<td class="text-right" width="50"><a class="btlink minus delete">删除</a></td>'; |
||||||
|
tbody += '</tr>'; |
||||||
|
$('#nodecon').append(tbody); |
||||||
|
} |
||||||
|
|
||||||
|
$('#nodecon .delete').click(function(){ |
||||||
|
$(this).parent().parent().remove(); |
||||||
|
if ($('#nodecon tr').length == 1 ){ |
||||||
|
$('#nodecon .nulltr').show(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
$('.add_node').click(function(){ |
||||||
|
addNode(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
yes:function(index) { |
||||||
|
var data = {}; |
||||||
|
|
||||||
|
data['node_algo'] = $('select[name="node_algo"]').val(); |
||||||
|
data['node_health_check'] = 'fail'; |
||||||
|
if ($('input[name="node_health_check"]').prop('checked')){ |
||||||
|
data['node_health_check'] = 'ok'; |
||||||
|
} |
||||||
|
|
||||||
|
var node_list = []; |
||||||
|
$('#nodecon tr').each(function(){ |
||||||
|
|
||||||
|
var ip = $(this).find('td').eq(0).text(); |
||||||
|
var port = $(this).find('td').eq(1).text(); |
||||||
|
|
||||||
|
if (port == ''){return;} |
||||||
|
|
||||||
|
var path = $(this).find('td').eq(2).text(); |
||||||
|
var state = $(this).find('select[name="state"]').val(); |
||||||
|
var weight = $(this).find('input[name="weight"]').val(); |
||||||
|
var max_fails = $(this).find('input[name="max_fails"]').val(); |
||||||
|
var fail_timeout = $(this).find('input[name="fail_timeout"]').val(); |
||||||
|
|
||||||
|
var tmp = { |
||||||
|
ip:ip, |
||||||
|
port:port, |
||||||
|
path:path, |
||||||
|
state:state, |
||||||
|
weight:weight, |
||||||
|
max_fails:max_fails, |
||||||
|
fail_timeout:fail_timeout, |
||||||
|
} |
||||||
|
node_list.push(tmp); |
||||||
|
}); |
||||||
|
data['node_list'] = node_list; |
||||||
|
data['row'] = row; |
||||||
|
ooPostCallbak('edit_load_balance', data, function(rdata){ |
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
showMsg(rdata.msg, function(){ |
||||||
|
layer.close(index); |
||||||
|
loadBalanceListRender(); |
||||||
|
},{ icon: rdata.status ? 1 : 2 }, 2000); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function loadBalanceListRender(){ |
||||||
|
ooPost('load_balance_list', {}, function(rdata){ |
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
var alist = rdata.data; |
||||||
|
|
||||||
|
var tbody = ''; |
||||||
|
for (var i = 0; i < alist.length; i++) { |
||||||
|
tbody += '<tr>'; |
||||||
|
tbody += '<td>'+alist[i]['domain']+'</td>'; |
||||||
|
tbody += '<td>'+alist[i]['upstream_name']+'</td>'; |
||||||
|
tbody += '<td>'+alist[i]['node_list'].length+'</td>'; |
||||||
|
tbody += '<td><a class="btlink log_look" data-row="'+i+'">查看</a></td>'; |
||||||
|
tbody += '<td><a class="btlink health_status" data-row="'+i+'">查看</a></td>'; |
||||||
|
tbody += '<td style="text-align: right;"><a class="btlink edit" data-row="'+i+'">修改</a> | <a class="btlink delete" data-row="'+i+'">删除</a></td>'; |
||||||
|
tbody += '</tr>'; |
||||||
|
} |
||||||
|
|
||||||
|
$('#nodeTable').html(tbody); |
||||||
|
$('.nodeTablePage .Pcount').text('共'+alist.length+'条'); |
||||||
|
$('#nodeTable .edit').click(function(){ |
||||||
|
var row = $(this).data('row'); |
||||||
|
editBalance(alist[row],row); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#nodeTable .log_look').click(function(){ |
||||||
|
var row = $(this).data('row'); |
||||||
|
var args = {'domain':alist[row]['domain']}; |
||||||
|
pluginRollingLogs('op_load_balance','','get_logs',JSON.stringify(args),20); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#nodeTable .health_status').click(function(){ |
||||||
|
var row = $(this).data('row'); |
||||||
|
ooPost('get_health_status', {row:row}, function(rdata){ |
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
|
||||||
|
var tval = ''; |
||||||
|
for (var i = 0; i < rdata.data.length; i++) { |
||||||
|
tval += '<tr>'; |
||||||
|
tval += '<td>'+rdata.data[i]['name']+'</td>'; |
||||||
|
|
||||||
|
if (typeof(rdata.data[i]['down']) != 'undefined' && rdata.data[i]['down']){ |
||||||
|
tval += '<td><span style="color:red;">不正常</span></td>'; |
||||||
|
} else{ |
||||||
|
tval += '<td><span class="btlink">正常</span></td>'; |
||||||
|
} |
||||||
|
tval += '</tr>'; |
||||||
|
} |
||||||
|
|
||||||
|
var tbody = "<div class='bt-form pd20'>\ |
||||||
|
<div>\ |
||||||
|
<div id='gitea_table' class='divtable' style='margin-top:5px;'>\ |
||||||
|
<table class='table table-hover'>\ |
||||||
|
<thead>\ |
||||||
|
<tr>\ |
||||||
|
<th width='120'>地址</th>\ |
||||||
|
<th width='60'>状态</th>\ |
||||||
|
</tr>\ |
||||||
|
</thead>\ |
||||||
|
<tbody>"+tval+"</tbody>\ |
||||||
|
</table>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>"; |
||||||
|
|
||||||
|
layer.open({ |
||||||
|
type: 1, |
||||||
|
area: ['500px','300px'], |
||||||
|
title: '节点状态', |
||||||
|
closeBtn: 1, |
||||||
|
shift: 5, |
||||||
|
shadeClose: true, |
||||||
|
btn:['提交','关闭'], |
||||||
|
content:tbody, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#nodeTable .delete').click(function(){ |
||||||
|
var row = $(this).data('row'); |
||||||
|
ooPost('load_balance_delete', {row:row}, function(rdata){ |
||||||
|
var rdata = $.parseJSON(rdata.data); |
||||||
|
showMsg(rdata.msg, function(){ |
||||||
|
loadBalanceListRender(); |
||||||
|
},{ icon: rdata.status ? 1 : 2 }, 2000); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function loadBalanceList() { |
||||||
|
var body = '<div class="bt-box active" style="display: block;">\ |
||||||
|
<div class="mb10">\ |
||||||
|
<button class="btn btn-success btn-sm" data-index="0" onclick="addBalance()">添加负载</button>\ |
||||||
|
<div class="divtable mt10">\ |
||||||
|
<table class="table table-hover">\ |
||||||
|
<thead>\ |
||||||
|
<tr>\ |
||||||
|
<th>网站</th>\ |
||||||
|
<th>负载名称</th>\ |
||||||
|
<th>节点</th>\ |
||||||
|
<th>日志</th>\ |
||||||
|
<th>状态</th>\ |
||||||
|
<th width="100" style="text-align: right;">操作</th>\ |
||||||
|
</tr>\ |
||||||
|
</thead>\ |
||||||
|
<tbody id="nodeTable"></tbody>\ |
||||||
|
</table>\ |
||||||
|
<div class="nodeTablePage page" data-type="nodePage">\ |
||||||
|
<div><span class="Pcount">共0条</span></div>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>'; |
||||||
|
$(".soft-man-con").html(body); |
||||||
|
loadBalanceListRender(); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
local hc = require "resty.upstream.healthcheck" |
||||||
|
local ok, err = hc.spawn_checker { |
||||||
|
shm = "healthcheck", |
||||||
|
type = "http", |
||||||
|
upstream = "{$UPSTREAM_NAME}", |
||||||
|
http_req = "GET / HTTP/1.0\r\nHost: {$UPSTREAM_NAME}\r\n\r\n", |
||||||
|
interval = 2000, |
||||||
|
timeout = 6000, |
||||||
|
fall = 3, |
||||||
|
rise = 2, |
||||||
|
valid_statuses = {200, 302}, |
||||||
|
concurrency = 20, |
||||||
|
} |
||||||
|
|
||||||
|
if not ok then |
||||||
|
ngx.log(ngx.ERR, "=======> load balance health checker error: ", err) |
||||||
|
return |
||||||
|
end |
Loading…
Reference in new issue