Authored by 陈峰

Merge branch 'feature/page-cache' of http://git.yoho.cn/OPENTECH/yoho-node-ci in…

…to feature/page-cache
  1 +/**
  2 + * 采集数据
  3 + *
  4 + * @class Collect
  5 + * @author shenzm<zhimin.shen@yoho.cn>
  6 + * @date 2016/10/12
  7 + */
  8 +
  9 +import ssh from 'ssh2';
  10 +import Rp from 'request-promise';
  11 +import Trace from '../logger/trace.js';
  12 +import config from '../../config/config.js';
  13 +
  14 +import {
  15 + Server
  16 +} from '../models';
  17 +
  18 +var tracer = new Trace({
  19 + host: config.influxdb.host,
  20 + port: config.influxdb.port
  21 +});
  22 +
  23 +class Collect {
  24 + constructor(host, projectname) {
  25 + this.host = host;
  26 + this.projectname = projectname;
  27 + this.scriptRunning = false;
  28 + this.retry = {};
  29 + }
  30 +
  31 + async collect() {
  32 + let self = this;
  33 + let server = await Server.findByHost(self.host);
  34 + self.server = {
  35 + host: server.host,
  36 + username: server.username,
  37 + password: server.password,
  38 + port: server.port
  39 + }
  40 +
  41 + let obj = {
  42 + 'total': 0,
  43 + 'status': {}
  44 + };
  45 +
  46 + Rp({
  47 + uri: `http://${self.host}:9615`,
  48 + json: true
  49 + }).then(function(data) {
  50 + var processes = data.processes || [];
  51 + processes.forEach(function(p) {
  52 + if (p.name === 'pm2-server-monit') {
  53 + var cpuUsg = p.pm2_env.axm_monitor['CPU usage'].value;
  54 + var freeMen = p.pm2_env.axm_monitor['Free memory'].value;
  55 +
  56 + obj.cpuUsg = cpuUsg ? parseFloat(cpuUsg.replace('%', '')).toFixed(2) : '';
  57 + obj.freeMen = freeMen ? parseFloat(freeMen.replace('%', '')).toFixed(2) : '';
  58 + }
  59 +
  60 + if (p.name === self.projectname) {
  61 + obj.total++;
  62 +
  63 + if (!obj.status[p.pm2_env.status]) {
  64 + obj.status[p.pm2_env.status] = 1;
  65 + } else {
  66 + obj.status[p.pm2_env.status]++;
  67 + }
  68 + }
  69 + });
  70 +
  71 + if (obj.cpuUsg === undefined) { // install server monit
  72 + const script = 'pm2 install pm2-server-monit';
  73 + self.execScript(script);
  74 + }
  75 +
  76 + // add into influxDB todo
  77 + tracer.report('server_data', {
  78 + host: self.host
  79 + }, obj);
  80 + }).catch(function(err) {
  81 + const script = 'pm2 web';
  82 + self.execScript(script);
  83 + });
  84 + }
  85 +
  86 + execScript(script) {
  87 + let self = this;
  88 + if (self.scriptRunning || self.retry[`${self.host}_${script}`] > 5) {
  89 + // 脚本执行中,或者重试次数大于5次以上时,不执行脚本
  90 + return;
  91 + }
  92 +
  93 + let retryCount = self.retry[`${self.host}_${script}`] || 0;
  94 + self.retry[`${self.host}_${script}`] = ++retryCount;
  95 + self.scriptRunning = true;
  96 +
  97 + let conn = new ssh.Client();
  98 + conn.on('ready', () => {
  99 + self._log(`>>>>host:[${self.host}] script[${script}]`);
  100 + conn.exec(`${script}`, (err, stream) => {
  101 + if (err) {
  102 + conn.end();
  103 + self._log(`host:[${self.host}] script:[${script}] exec fail error: ${err}`);
  104 + self.scriptRunning = false;
  105 + } else {
  106 + stream.stdout.on('data', (data) => {
  107 + //self._log(data.toString());
  108 + });
  109 + stream.stderr.on('data', (data) => {
  110 + //self._log(data.toString());
  111 + });
  112 + stream.on('exit', (code) => {
  113 + conn.end();
  114 + if (code === 0) {
  115 + self._log(`host:[${self.host}] script[${script}] exec success`);
  116 + } else {
  117 + self._log(`host:[${self.host}] script[${script}] exec fail`);
  118 + }
  119 + self.scriptRunning = false;
  120 + });
  121 + }
  122 + });
  123 + }).on('error', (err) => {
  124 + self._log(`connect error ${self.host} ${err}`);
  125 + self.scriptRunning = false;
  126 + }).connect(Object.assign(self.server, {
  127 + readyTimeout: 5000
  128 + }));
  129 + }
  130 +
  131 + _log(msg) {
  132 + console.log(msg);
  133 + }
  134 +}
  135 +
  136 +export default Collect;
@@ -170,7 +170,6 @@ class Deploy { @@ -170,7 +170,6 @@ class Deploy {
170 } 170 }
171 171
172 _log(msg) { 172 _log(msg) {
173 - console.log(msg)  
174 ws.broadcast(`/deploy/${this.project._id}/log`, { 173 ws.broadcast(`/deploy/${this.project._id}/log`, {
175 host: this.info.host, 174 host: this.info.host,
176 msg: msg 175 msg: msg
1 /** 1 /**
2 * 跟踪监控工具, 将监控数据写入influxdb 2 * 跟踪监控工具, 将监控数据写入influxdb
3 * 3 *
4 - * @usage:  
5 - * <code>  
6 - * let trace = new Trace({  
7 - * host: '54.222.219.223',  
8 - * port: 4444  
9 - * });  
10 - *  
11 - * let testTrace = trace.trace('test_key'); // createOrChoose a measurement  
12 - *  
13 - * // testTrace(someTags, someFields);  
14 - * testTrace({ foo: 'bar', foobar: 'baz2'}, {value: 123, value2: 'aaa 123', value3: 1.3, value4: false});  
15 - *  
16 - * </code>  
17 - *  
18 * @author: jiangfeng<jeff.jiang@yoho.cn> 4 * @author: jiangfeng<jeff.jiang@yoho.cn>
19 * @date: 16/8/1 5 * @date: 16/8/1
20 */ 6 */
@@ -36,79 +22,13 @@ class Trace { @@ -36,79 +22,13 @@ class Trace {
36 this.options = options; 22 this.options = options;
37 } 23 }
38 24
39 - /**  
40 - * create or choose a measurement to write point in.  
41 - * @param name {string} the measurement name  
42 - * @param options {object} some options. the protocol of influxdb connection.  
43 - * @returns {function()} a point write function  
44 - */  
45 - trace(name, options) {  
46 - options = _.assign({  
47 - protocol: 'udp'  
48 - }, options);  
49 -  
50 - let self = this;  
51 -  
52 - if (options.protocol === 'udp') {  
53 - return (key, fields) => {  
54 - return self.udpTrace(name, options, key, fields);  
55 - };  
56 - } else if (options.protocol === 'http') {  
57 - return (key, fields) => {  
58 - return self.httpTrace(name, options, key, fields);  
59 - }; 25 + report(name, keys, data) {
  26 + if (this.options.appName) {
  27 + keys.appName = this.options.appName;
60 } 28 }
61 - }  
62 29
63 - /**  
64 - * write point into influxdb by UDP  
65 - *  
66 - * @param name {string} the measurement name  
67 - * @param options {object}  
68 - * @param key {object} some keys of data  
69 - * @param fields {object} some fields of data  
70 - * @returns {Promise}  
71 - */  
72 - udpTrace(name, options, key, fields) {  
73 -  
74 - if (_.isArray(key)) {  
75 - key.forEach(p => {  
76 - let line = `${this._escape(name)},${this._makeLine(p)}`;  
77 -  
78 - return this._updPostLine(line);  
79 - });  
80 - } else {  
81 - let line = `${this._escape(name)},${this._makeLine(key, false)} ${this._makeLine(fields, true)}`;  
82 -  
83 - return this._updPostLine(line);  
84 - }  
85 - }  
86 -  
87 - /**  
88 - * upd send.  
89 - * @param line {string} @see infulxdb's line protocol  
90 - * @returns {Promise}  
91 - * @private  
92 - */  
93 - _updPostLine(line) {  
94 - let self = this;  
95 -  
96 - return new Promise((resolve, reject) => {  
97 - let socket = dgram.createSocket("udp4");  
98 - let buff = new Buffer(line);  
99 -  
100 -  
101 - socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err, rp) => {  
102 - socket.close();  
103 -  
104 - console.log(rp);  
105 - if (err) {  
106 - reject(err);  
107 - } else {  
108 - resolve();  
109 - }  
110 - });  
111 - }); 30 + let line = `${this._escape(name)},${this._makeLine(keys, false)} ${this._makeLine(data, true)}`;
  31 + return this._send(line);
112 } 32 }
113 33
114 /** 34 /**
@@ -137,79 +57,51 @@ class Trace { @@ -137,79 +57,51 @@ class Trace {
137 } 57 }
138 58
139 /** 59 /**
140 - * data escape with influxdb's line protocol.  
141 - *  
142 - * @param value {*}  
143 - * @param withQuote {boolean}  
144 - * @returns {*} 60 + * upd send.
  61 + * @param line {string} @see infulxdb's line protocol
  62 + * @returns {Promise}
145 * @private 63 * @private
146 */ 64 */
  65 + _send(line) {
  66 + let self = this;
  67 +
  68 + return new Promise((resolve, reject) => {
  69 + let socket = dgram.createSocket('udp4');
  70 + let buff = new Buffer(line);
  71 +
  72 + socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err) => {
  73 + socket.close();
  74 +
  75 + if (err) {
  76 + reject(err);
  77 + } else {
  78 + resolve();
  79 + }
  80 + });
  81 + });
  82 + }
  83 +
147 _escape(value, withQuote) { 84 _escape(value, withQuote) {
148 if (_.isString(value)) { 85 if (_.isString(value)) {
149 - value = _.replace(value, /,/g, '\\,');  
150 - value = _.replace(value, /=/g, '\\=');  
151 -  
152 if (withQuote) { 86 if (withQuote) {
153 value = '"' + value + '"'; 87 value = '"' + value + '"';
154 } else { 88 } else {
  89 + value = _.replace(value, /,/g, '\\,');
  90 + value = _.replace(value, /=/g, '\\=');
155 value = _.replace(value, /\s/g, '\\ '); 91 value = _.replace(value, /\s/g, '\\ ');
156 } 92 }
157 } else if (_.isInteger(value)) { 93 } else if (_.isInteger(value)) {
158 if (withQuote) { 94 if (withQuote) {
159 value = value + 'i'; 95 value = value + 'i';
160 } 96 }
  97 + } else if (_.isObject(value)) {
  98 + value = '"' + _.replace(JSON.stringify(value), /"/g, '\\"') + '"';
  99 + } else if (_.isNull(value) || _.isNil(value)) {
  100 + value = '""';
161 } 101 }
162 102
163 return value; 103 return value;
164 } 104 }
165 -  
166 - /**  
167 - * write point into influxdb by HTTP. use the open source node-influx module.  
168 - * @see https://github.com/node-influx/node-influx  
169 - *  
170 - * @param name {string} the measurement name  
171 - * @param options {object}  
172 - * @param key {object} some keys of data  
173 - * @param fields {object} some fields of data  
174 - * @returns {Promise}  
175 - */  
176 - httpTrace(name, options, key, fields) {  
177 - let client = this.getHttpClient();  
178 -  
179 - return new Promise((resolve, reject) => {  
180 - if (_.isArray(key)) {  
181 - client.writePoints(name, key, options, (err, rp) => {  
182 - if (err) {  
183 - reject(err);  
184 - } else {  
185 - resolve(rp)  
186 - }  
187 - });  
188 - } else {  
189 - client.writePoint(name, fields, key, options, (err, rp) => {  
190 - if (err) {  
191 - reject(err);  
192 - } else {  
193 - resolve(rp)  
194 - }  
195 - });  
196 - }  
197 - });  
198 - }  
199 -  
200 - /**  
201 - * the singleton http client.  
202 - *  
203 - * @returns {*}  
204 - */  
205 - getHttpClient() {  
206 - if (!this.httpClient) {  
207 - this.httpClient = influx(this.options);  
208 - }  
209 -  
210 - return this.httpClient;  
211 - }  
212 } 105 }
213 106
214 -  
215 -module.exports = Trace; 107 +module.exports = Trace;
  1 +'use strict';
  2 +
  3 +import Collect from '../../ci/collect_data';
  4 +
  5 +import {
  6 + Project
  7 +} from '../../models';
  8 +
  9 +export default {
  10 + async collect(ctx) {
  11 + let projects,
  12 + servers = {};
  13 +
  14 + setInterval(async() => {
  15 + if (!projects) projects = await Project.findAll();
  16 + if (!projects || !projects.length) {
  17 + return;
  18 + }
  19 +
  20 + projects.forEach(async(p) => {
  21 + const hosts = p.deploy['production'].target;
  22 + hosts.forEach((host) => {
  23 + if (!servers[host]) {
  24 + servers[host] = new Collect(host, p.name);
  25 + }
  26 +
  27 + servers[host].collect();
  28 + });
  29 + });
  30 + }, 5000);
  31 + }
  32 +};
1 -'use strict';  
2 -  
3 -import Router from 'koa-router';  
4 -import moment from 'moment';  
5 -import _ from 'lodash';  
6 -  
7 -import {Degrade, DegradeServer} from '../../models';  
8 -  
9 -import getter from '../../zookeeper/getter';  
10 -import setter from '../../zookeeper/setter';  
11 -import tester from '../../zookeeper/tester';  
12 -  
13 -const router = new Router();  
14 -  
15 -const ctl = {  
16 - async getServer() {  
17 - let server = await DegradeServer.findAll({});  
18 -  
19 - server = _.last(server);  
20 -  
21 - if (server) {  
22 - return `${server.ip}:${server.port}`;  
23 - }  
24 - },  
25 - async index (ctx) {  
26 - let serverPath = await ctl.getServer();  
27 - let serverSplit = serverPath ? serverPath.split(':') : ['', ''];  
28 -  
29 - await ctx.render('action/degrade', {  
30 - ip: serverSplit[0],  
31 - port: serverSplit[1]  
32 - });  
33 - },  
34 - async connect(ctx) {  
35 - let {ip, port} = ctx.query;  
36 -  
37 - let server = `${ip}:${port}`;  
38 -  
39 - let connected = await tester(server);  
40 -  
41 - // connecting test  
42 - if (!connected) {  
43 - ctx.body = `<p class="connect-err">  
44 - <i class="fa fa-wheelchair" aria-hidden="true"></i>  
45 - Sorry, I can not connect to <span class="server-name">${server}</span>.Please check whether your ip/port is correct or the zookeeper server is running  
46 - </p>`;  
47 - return;  
48 - }  
49 -  
50 - let degrades = await Degrade.findAll();  
51 -  
52 - for(let i of degrades) {  
53 - i.checked = await getter(server, i.path);  
54 - }  
55 -  
56 - let pc = _.filter(degrades, o => _.startsWith(o.path, '/pc'));  
57 - let wap = _.filter(degrades, o => _.startsWith(o.path, '/wap'));  
58 -  
59 - await ctx.render('action/degrade_list', {  
60 - layout: false,  
61 - pc: pc,  
62 - wap: wap  
63 - });  
64 - },  
65 - async server(ctx) {  
66 - let ip = ctx.request.body.ip;  
67 - let port = ctx.request.body.port;  
68 -  
69 - let serverCount = await DegradeServer.count({});  
70 -  
71 - // keep one server  
72 - if (serverCount) {  
73 - let serverConfig = await DegradeServer.findAll({});  
74 - let id = _.last(serverConfig)._id; // get the latest item  
75 -  
76 - await DegradeServer.update({  
77 - _id: id  
78 - }, {  
79 - $set: {  
80 - ip: ip,  
81 - port: port  
82 - }  
83 - });  
84 - } else {  
85 - await DegradeServer.insert({  
86 - ip: ip,  
87 - port: port  
88 - });  
89 - }  
90 -  
91 - ctx.body = {  
92 - code: 200,  
93 - message: `${serverCount ? 'update' : 'new'} server success`  
94 - };  
95 - },  
96 - async setter(ctx) {  
97 - let {checked, id} = ctx.query;  
98 -  
99 - let theDegrade = await Degrade.findById(id);  
100 -  
101 - let path = theDegrade.path;  
102 -  
103 - let serverPath = await ctl.getServer();  
104 -  
105 - await setter(serverPath, path, checked.toString());  
106 -  
107 - ctx.body = {  
108 - code: 200,  
109 - message: 'update success'  
110 - };  
111 - }  
112 -};  
113 -  
114 -router.get('/', ctl.index);  
115 -router.post('/server', ctl.server);  
116 -router.get('/connect', ctl.connect);  
117 -router.get('/setter', ctl.setter);  
118 -  
119 -export default router;  
  1 +'use strict';
  2 +
  3 +import Router from 'koa-router';
  4 +import moment from 'moment';
  5 +import _ from 'lodash';
  6 +import process from 'process';
  7 +
  8 +import {Degrade, DegradeServer} from '../../models';
  9 +
  10 +import getter from '../../zookeeper/getter';
  11 +import setter from '../../zookeeper/setter';
  12 +import tester from '../../zookeeper/tester';
  13 +
  14 +const router = new Router();
  15 +
  16 +const ctl = {
  17 + async index (ctx) {
  18 + let qcloudServer = await DegradeServer.findOne({
  19 + type: 'qcloud'
  20 + });
  21 +
  22 + if (!qcloudServer) {
  23 + qcloudServer = {};
  24 + }
  25 +
  26 + let awsServer = await DegradeServer.findOne({
  27 + type: 'aws'
  28 + });
  29 +
  30 + if (!awsServer) {
  31 + awsServer = {};
  32 + }
  33 +
  34 + await ctx.render('action/degrade', {
  35 + qCloudConfig: {
  36 + ip: qcloudServer.ip,
  37 + port: qcloudServer.port
  38 + },
  39 + awsConfig: {
  40 + ip: awsServer.ip,
  41 + port: awsServer.port
  42 + }
  43 + });
  44 + },
  45 + async connect(ctx) {
  46 + let {ip, port} = ctx.query;
  47 +
  48 + let server = `${ip}:${port}`;
  49 +
  50 + let connected = await tester(server);
  51 +
  52 + // connecting test
  53 + if (!connected) {
  54 + ctx.body = `<p class="connect-err">
  55 + <i class="fa fa-wheelchair" aria-hidden="true"></i>
  56 + Sorry, I can not connect to <span class="server-name">${server}</span>.Please check whether your ip/port is correct or the zookeeper server is running
  57 + </p>`;
  58 + return;
  59 + }
  60 +
  61 + let degrades = await Degrade.findAll();
  62 +
  63 + for(let i of degrades) {
  64 + // 从zookeeper读取配置信息,memcached只做PHP读取使用
  65 + i.checked = await getter(server, i.path);
  66 + }
  67 +
  68 + let pc = _.filter(degrades, o => _.startsWith(o.path, '/pc'));
  69 + let wap = _.filter(degrades, o => _.startsWith(o.path, '/wap'));
  70 +
  71 + await ctx.render('action/degrade_list', {
  72 + layout: false,
  73 + pc: pc,
  74 + wap: wap
  75 + });
  76 + },
  77 + async server(ctx) {
  78 + let {ip, port, type} = ctx.request.body;
  79 +
  80 + let serverCount = await DegradeServer.count({
  81 + type: type
  82 + });
  83 +
  84 + // keep one server
  85 + if (serverCount) {
  86 + let serverConfig = await DegradeServer.findOne({
  87 + type: type
  88 + });
  89 + let id = serverConfig._id; // get the latest item
  90 +
  91 + await DegradeServer.update({
  92 + _id: id
  93 + }, {
  94 + $set: {
  95 + ip: ip,
  96 + port: port
  97 + }
  98 + });
  99 + } else {
  100 + await DegradeServer.insert({
  101 + ip: ip,
  102 + port: port,
  103 + type: type
  104 + });
  105 + }
  106 +
  107 + ctx.body = {
  108 + code: 200,
  109 + message: `${serverCount ? 'update' : 'new'} ${type} server success`
  110 + };
  111 + },
  112 + async setter(ctx) {
  113 + let {checked, id, type} = ctx.query;
  114 +
  115 + let theDegrade = await Degrade.findById(id);
  116 +
  117 + let path = theDegrade.path;
  118 +
  119 + let server = await await DegradeServer.findOne({
  120 + type: type
  121 + });
  122 +
  123 + let result = await setter(`${server.ip}:${server.port}`, path, checked.toString());
  124 +
  125 + // result结果以zookeeper写为准
  126 + if (result) {
  127 + ctx.body = {
  128 + code: 200,
  129 + message: 'update success'
  130 + };
  131 + } else {
  132 + ctx.body = {
  133 + code: 500,
  134 + message: 'update fail,Please retry'
  135 + }
  136 + }
  137 + }
  138 +};
  139 +
  140 +router.get('/', ctl.index);
  141 +router.post('/server', ctl.server);
  142 +router.get('/connect', ctl.connect);
  143 +router.get('/setter', ctl.setter);
  144 +
  145 +export default router;
@@ -61,6 +61,9 @@ const p = { @@ -61,6 +61,9 @@ const p = {
61 deploy.env = env; 61 deploy.env = env;
62 deploy.name = envs[env]; 62 deploy.name = envs[env];
63 63
  64 + if (typeof deploy.target === 'string') {
  65 + deploy.target = [deploy.target];
  66 + }
64 67
65 let promises = deploy.target.map((host) => { 68 let promises = deploy.target.map((host) => {
66 console.log('read host :' + host); 69 console.log('read host :' + host);
@@ -220,6 +223,10 @@ const p = { @@ -220,6 +223,10 @@ const p = {
220 let project = await Project.findById(building.projectId); 223 let project = await Project.findById(building.projectId);
221 let targets = project.deploy[building.env].target; 224 let targets = project.deploy[building.env].target;
222 225
  226 + if (typeof targets === 'string') {
  227 + targets = [targets];
  228 + }
  229 +
223 targets.forEach(async(host) => { 230 targets.forEach(async(host) => {
224 231
225 let doc = await DeployInfo.findOne({ 232 let doc = await DeployInfo.findOne({
@@ -394,7 +401,11 @@ const p = { @@ -394,7 +401,11 @@ const p = {
394 return; 401 return;
395 } 402 }
396 403
397 - const hosts = project.deploy[env].target; 404 + let hosts = project.deploy[env].target;
  405 +
  406 + if (typeof hosts === 'string') {
  407 + hosts = [hosts];
  408 + }
398 hosts.forEach((host) => { 409 hosts.forEach((host) => {
399 var obj = { 410 var obj = {
400 'total': 0, 411 'total': 0,
@@ -10,6 +10,7 @@ import Koa from 'koa'; @@ -10,6 +10,7 @@ import Koa from 'koa';
10 import hbs from '../../middleware/yoho-koa-hbs'; 10 import hbs from '../../middleware/yoho-koa-hbs';
11 import helpers from '../../lib/helpers'; 11 import helpers from '../../lib/helpers';
12 import routers from './routers' 12 import routers from './routers'
  13 +import collectData from './actions/collect_data';
13 14
14 const app = new Koa(); 15 const app = new Koa();
15 16
@@ -29,6 +30,9 @@ const mastersUrl = [ @@ -29,6 +30,9 @@ const mastersUrl = [
29 '/users' 30 '/users'
30 ]; 31 ];
31 32
  33 +// 服务器监控数据采集
  34 +collectData.collect();
  35 +
32 app.use(async(ctx, next) => { 36 app.use(async(ctx, next) => {
33 ctx.locals = { 37 ctx.locals = {
34 title: 'Yoho Node.js 持续集成平台' 38 title: 'Yoho Node.js 持续集成平台'
@@ -38,7 +42,7 @@ app.use(async(ctx, next) => { @@ -38,7 +42,7 @@ app.use(async(ctx, next) => {
38 ctx.locals.layout = null; 42 ctx.locals.layout = null;
39 } 43 }
40 44
41 - if (ctx.session && ctx.session.user ) { 45 + if (ctx.session && ctx.session.user) {
42 ctx.locals.is_master = ctx.session.user.role === '1000'; 46 ctx.locals.is_master = ctx.session.user.role === '1000';
43 ctx.locals.current_user = ctx.session.user; 47 ctx.locals.current_user = ctx.session.user;
44 } 48 }
@@ -35,136 +35,161 @@ @@ -35,136 +35,161 @@
35 float: right; 35 float: right;
36 cursor: pointer; 36 cursor: pointer;
37 } 37 }
  38 +
  39 + .config-panel .server-port {
  40 + width: 100px;
  41 + }
  42 +
  43 + .config-panel .form-inline {
  44 + padding: 20px 0;
  45 + border-bottom: 1px solid #d9edf7;
  46 + }
38 </style> 47 </style>
39 <div class="degrade-page"> 48 <div class="degrade-page">
40 <div class="panel panel-info"> 49 <div class="panel panel-info">
41 - <div class="panel-heading">zookeeper server</div> 50 + <div class="panel-heading">zookeeper configure</div>
42 <div class="panel-body"> 51 <div class="panel-body">
43 - <div class="form-inline">  
44 - <div class="current-server{{#unless ip}} hide{{/unless}}">  
45 - 当前server地址:  
46 - <span id="server-in-use" class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>  
47 - <span id="server-modify-btn" class="ip-port-modify-btn">修改</span>  
48 - </div>  
49 - <div class="server-editor{{#if ip}} hide{{/if}}">  
50 - <div class="form-group">  
51 - <label for="server-ip">Server IP</label>  
52 - <input id="server-ip" class="form-control" type="text" placeholder="input server ip" value="{{ip}}"> 52 + <div class="row">
  53 + <div class="col-md-6 config-panel" data-type="qcloud">
  54 + <div class="panel panel-success">
  55 + <div class="panel-heading">
  56 + 腾讯云
  57 + <span class="refresh">
  58 + <i class="glyphicon glyphicon-refresh"></i>
  59 + </span>
  60 + </div>
  61 + <div class="panel-body">
  62 + {{# qCloudConfig}}
  63 + <div class="form-inline">
  64 + <div class="current-server{{#unless ip}} hide{{/unless}}">
  65 + 当前server地址:
  66 + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>
  67 + <span class="ip-port-modify-btn">修改</span>
  68 + </div>
  69 + <div class="server-editor{{#if ip}} hide{{/if}}">
  70 + <div class="form-group">
  71 + <label>Server IP</label>
  72 + <input class="form-control server-ip" type="text" value="{{ip}}">
  73 + </div>
  74 + <div class="form-group">
  75 + <label for="server-port">Server Port</label>
  76 + <input class="form-control server-port" type="text" value="{{port}}">
  77 + </div>
  78 + <span class="btn btn-default server-sure">保存</span>
  79 + </div>
  80 + </div>
  81 + {{/ qCloudConfig}}
  82 +
  83 + <div class="panel-body list-container">
  84 + Waitting for connecting to server...
  85 + </div>
  86 + </div>
53 </div> 87 </div>
54 - <div class="form-group">  
55 - <label for="server-port">Server Port</label>  
56 - <input id="server-port" class="form-control" type="text" placeholder="input server port" value="{{port}}"> 88 + </div>
  89 + <div class="col-md-6 config-panel" data-type="aws">
  90 + <div class="panel panel-warning">
  91 + <div class="panel-heading">
  92 + AWS
  93 + <span class="refresh">
  94 + <i class="glyphicon glyphicon-refresh"></i>
  95 + </span>
  96 + </div>
  97 + <div class="panel-body">
  98 + {{# awsConfig}}
  99 + <div class="form-inline">
  100 + <div class="current-server{{#unless ip}} hide{{/unless}}">
  101 + 当前server地址:
  102 + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>
  103 + <span class="ip-port-modify-btn">修改</span>
  104 + </div>
  105 + <div class="server-editor{{#if ip}} hide{{/if}}">
  106 + <div class="form-group">
  107 + <label>Server IP</label>
  108 + <input class="form-control server-ip" type="text" value="{{ip}}">
  109 + </div>
  110 + <div class="form-group">
  111 + <label>Server Port</label>
  112 + <input class="form-control server-port" type="text" value="{{port}}">
  113 + </div>
  114 + <span class="btn btn-default server-sure">保存</span>
  115 + </div>
  116 + </div>
  117 + {{/ awsConfig}}
  118 +
  119 + <div class="panel-body list-container">
  120 + Waitting for connecting to server...
  121 + </div>
  122 + </div>
57 </div> 123 </div>
58 - <span id="server-sure" class="btn btn-default">保存</span>  
59 - <span id="err-tip" class="err-tip hide">  
60 - <i class="glyphicon glyphicon-remove-sign"></i>  
61 - 小哥,填错了吧  
62 - </span>  
63 </div> 124 </div>
64 </div> 125 </div>
65 </div> 126 </div>
66 </div> 127 </div>
67 - <div class="panel panel-danger">  
68 - <div class="panel-heading">  
69 - degrade point  
70 - <span id="refresh-connection" class="refresh">  
71 - <i class="glyphicon glyphicon-refresh"></i>  
72 - </span>  
73 - </div>  
74 - <div id="list-container" class="panel-body">  
75 - Waitting for connecting to server...  
76 - </div>  
77 - </div>  
78 </div> 128 </div>
79 129
80 <script> 130 <script>
81 $(function() { 131 $(function() {
82 - var $ip = $('#server-ip');  
83 - var $port = $('#server-port');  
84 - var $tip = $('#err-tip');  
85 -  
86 - var $listContainer = $('#list-container');  
87 -  
88 - function validateIP(ip) {  
89 - if (ip === 'localhost' ||  
90 - /^(\d)+\.(\d)+\.(\d)+\.(\d)+$/.test(ip))  
91 - {  
92 - $ip.closest('.form-group').removeClass('has-error');  
93 - return true;  
94 - }  
95 -  
96 - $ip.closest('.form-group').addClass('has-error');  
97 - return false;  
98 - }  
99 132
100 - function validatePort(port) {  
101 - if (/^[1-9]\d*$/.test(port) && +port >= 1 && +port <= 65535) {  
102 - $port.closest('.form-group').removeClass('has-error');  
103 - return true;  
104 - }  
105 -  
106 - $port.closest('.form-group').addClass('has-error');  
107 - return false;  
108 - } 133 + $('.ip-port-modify-btn').click(function() {
  134 + var $form = $(this).closest('.form-inline');
109 135
110 - $('#server-modify-btn').click(function() {  
111 - $('.server-editor').removeClass('hide');  
112 - $('.current-server').addClass('hide'); 136 + $('.server-editor', $form).removeClass('hide');
  137 + $('.current-server', $form).addClass('hide');
113 }); 138 });
114 139
115 // server 140 // server
116 - $('#server-sure').click(function() {  
117 - var ip = $.trim($ip.val());  
118 - var port = $.trim($port.val()); 141 + $('.server-sure').click(function() {
  142 + var $panel = $(this).closest('.config-panel');
119 143
120 - if (!validateIP(ip) || !validatePort(port)) {  
121 - $tip.removeClass('hide');  
122 - return;  
123 - } 144 + var $ip = $('.server-ip', $panel);
  145 + var $port = $('.server-port', $panel);
124 146
125 - $tip.addClass('hide'); 147 + var ip = $.trim($ip.val());
  148 + var port = $.trim($port.val());
126 149
127 $.ajax({ 150 $.ajax({
128 type: 'POST', 151 type: 'POST',
129 url: '/degrade/server', 152 url: '/degrade/server',
130 data: { 153 data: {
131 ip: ip, 154 ip: ip,
132 - port: port 155 + port: port,
  156 + type: $panel.data('type')
133 } 157 }
134 }).then(function(data) { 158 }).then(function(data) {
135 if (data.code === 200) { 159 if (data.code === 200) {
136 //$.pjax.reload('#pjax-container'); 160 //$.pjax.reload('#pjax-container');
137 - $('.server-editor').addClass('hide');  
138 - $('.current-server').removeClass('hide'); 161 + $('.server-editor', $panel).addClass('hide');
  162 + $('.current-server', $panel).removeClass('hide');
139 163
140 - $('#server-in-use').text(ip + ':' + port).data('ip', ip).data('port', port); 164 + $('.server-in-use', $panel).text(ip + ':' + port).data('ip', ip).data('port', port);
141 165
142 - $('#list-container').html('waiting for connecting server...'); 166 + $('.list-container', $panel).html('waiting for connecting server...');
143 } 167 }
144 }); 168 });
145 }); 169 });
146 170
147 - $listContainer.on('click', '#degrade-tab li', function() { 171 + $('.list-container').on('click', '.degrade-tab li', function() {
148 var $this = $(this); 172 var $this = $(this);
  173 + var $panel = $this.closest('.config-panel');
149 174
150 if ($this.hasClass('active')) { 175 if ($this.hasClass('active')) {
151 return; 176 return;
152 } 177 }
153 178
154 - $('li', $('#degrade-tab')).toggleClass('active'); 179 + $this.parent().children('li').toggleClass('active');
155 180
156 var index = $this.index(); 181 var index = $this.index();
157 182
158 if (index === 0) { 183 if (index === 0) {
159 184
160 //PC active 185 //PC active
161 - $('.pc-degrade').removeClass('hide');  
162 - $('.wap-degrade').addClass('hide'); 186 + $('.pc-degrade', $panel).removeClass('hide');
  187 + $('.wap-degrade', $panel).addClass('hide');
163 } else { 188 } else {
164 189
165 // wap active 190 // wap active
166 - $('.wap-degrade').removeClass('hide');  
167 - $('.pc-degrade').addClass('hide'); 191 + $('.wap-degrade', $panel).removeClass('hide');
  192 + $('.pc-degrade', $panel).addClass('hide');
168 } 193 }
169 }).on('change', '.degrade-content input[type="checkbox"]', function() { 194 }).on('change', '.degrade-content input[type="checkbox"]', function() {
170 var $checkbox = $(this), 195 var $checkbox = $(this),
@@ -180,19 +205,26 @@ @@ -180,19 +205,26 @@
180 checked: checked, 205 checked: checked,
181 id: id 206 id: id
182 } 207 }
  208 + }).then(function(data) {
  209 + if (data.code !== 200) {
  210 + $checkbox.prop('checked', !checked);
  211 + alert(data.message);
  212 + }
183 }); 213 });
184 }); 214 });
185 215
186 - $('#refresh-connection').click(function() { 216 + $('.refresh').click(function() {
  217 + var $panel = $(this).closest('.config-panel');
  218 +
187 $.ajax({ 219 $.ajax({
188 type: 'GET', 220 type: 'GET',
189 url: '/degrade/connect', 221 url: '/degrade/connect',
190 data: { 222 data: {
191 - ip: $('#server-in-use').data('ip'),  
192 - port: $('#server-in-use').data('port') 223 + ip: $('.server-in-use', $panel).data('ip'),
  224 + port: $('.server-in-use', $panel).data('port')
193 } 225 }
194 }).then(function(data) { 226 }).then(function(data) {
195 - $('#list-container').html(data); 227 + $('.list-container', $panel).html(data);
196 }); 228 });
197 }) 229 })
198 }) 230 })
1 -'usu strict';  
2 -  
3 -import _ from 'lodash';  
4 -import zookeeper from 'node-zookeeper-client';  
5 -  
6 -module.exports = (server, path, val) => new Promise((resolve, reject) => {  
7 - const client = zookeeper.createClient(server);  
8 -  
9 - client.once('connected', function () {  
10 - client.setData(path, new Buffer(val.toString()), function(err, data, stat) {  
11 - console.log('path %s data change to', path, val);  
12 - resolve();  
13 - client.close();  
14 - });  
15 - });  
16 -  
17 - client.connect();  
18 -}); 1 +'usu strict';
  2 +
  3 +import _ from 'lodash';
  4 +import zookeeper from 'node-zookeeper-client';
  5 +
  6 +module.exports = (server, path, val) => new Promise((resolve, reject) => {
  7 + const client = zookeeper.createClient(server);
  8 +
  9 + client.once('connected', function () {
  10 + client.setData(path, new Buffer(val.toString()), function(err, data, stat) {
  11 + if (err) {
  12 + console.log('update path %s data error');
  13 + resolve();
  14 + } else {
  15 + console.log('path %s data change to', path, val);
  16 + resolve(true);
  17 + }
  18 + client.close();
  19 + });
  20 + });
  21 +
  22 + client.connect();
  23 +});
@@ -5,15 +5,19 @@ import path from 'path'; @@ -5,15 +5,19 @@ import path from 'path';
5 const env = process.env.NODE_ENV || 'development'; 5 const env = process.env.NODE_ENV || 'development';
6 6
7 const defaults = { 7 const defaults = {
8 - port: 9000,  
9 - buildDir: path.normalize(__dirname + '/../packages/'),  
10 - dbDir: path.normalize(__dirname + '/../db') 8 + port: 9000,
  9 + buildDir: path.normalize(__dirname + '/../packages/'),
  10 + dbDir: path.normalize(__dirname + '/../db'),
  11 + influxdb: {
  12 + host: '54.222.219.223',
  13 + port: 4444
  14 + }
11 }; 15 };
12 16
13 const specific = { 17 const specific = {
14 - development: {},  
15 - test: {},  
16 - production: {} 18 + development: {},
  19 + test: {},
  20 + production: {},
17 }; 21 };
18 22
19 export default Object.assign(defaults, specific[env]); 23 export default Object.assign(defaults, specific[env]);