import linux plugin

This commit is contained in:
jysperm
2014-11-02 16:04:25 +08:00
parent 810066c1cf
commit 846394e6ae
8 changed files with 135 additions and 98 deletions

View File

@@ -6,6 +6,7 @@ app.libs =
_: require 'underscore'
async: require 'async'
bodyParser: require 'body-parser'
child_process: require 'child_process'
cookieParser: require 'cookie-parser'
copy: require 'copy-to'
csrf: require 'csrf'
@@ -21,6 +22,7 @@ app.libs =
mongoose: require 'mongoose'
morgan: require 'morgan'
nodemailer: require 'nodemailer'
os: require 'os'
path: require 'path'
redis: require 'redis'
redisStore: require 'connect-redis'

View File

@@ -119,12 +119,12 @@ exports.joinPlan = (req, account, plan_name, callback) ->
async.each _.difference(account.billing.services, original_account.billing.services), (service_name, callback) ->
async.each pluggable.selectHook(account, "service.#{service_name}.enable"), (hook, callback) ->
hook.action req, callback
hook.filter req, callback
, callback
, ->
unless _.isEqual original_account.resources_limit, account.resources_limit
async.each pluggable.selectHook(account, 'account.resources_limit_changed'), (hook, callback) ->
hook.action account, callback
hook.filter account, callback
, callback
else
callback()
@@ -155,12 +155,12 @@ exports.leavePlan = (req, account, plan_name, callback) ->
async.each leaved_services, (service_name, callback) ->
async.each pluggable.selectHook(original_account, "service.#{service_name}.disable"), (hook, callback) ->
hook.action req, callback
hook.filter req, callback
, callback
, ->
unless _.isEqual original_account.resources_limit, account.resources_limit
async.each pluggable.selectHook(account, 'account.resources_limit_changed'), (hook, callback) ->
hook.action account, callback
hook.filter account, callback
, callback
else
callback()

View File

@@ -1,6 +1,4 @@
_ = require 'underscore'
async = require 'async'
{async, _} = app.libs
{pluggable, config} = app
{requireAuthenticate} = app.middleware
{wrapAsync} = app.utils
@@ -39,8 +37,7 @@ exports.registerHook 'view.panel.widgets',
exports.render 'widget', req,
usage: resources_usage
, (html) ->
callback html
, callback
exports.registerHook 'account.resources_limit_changed',
always_notice: true
@@ -49,29 +46,24 @@ exports.registerHook 'account.resources_limit_changed',
exports.registerServiceHook 'enable',
filter: (req, callback) ->
linux.createUser req.account, ->
linux.setResourceLimit req.account, callback
linux.createUser req.account, callback
exports.registerServiceHook 'disable',
filter: (req, callback) ->
linux.deleteUser req.account, callback
app.get '/public/monitor', requireAuthenticate, (req, res) ->
app.express.get '/public/monitor', requireAuthenticate, (req, res) ->
async.parallel
resources_usage: (callback) ->
linux.getResourceUsageByAccounts (result) ->
console.log result
callback null, result
system: wrapAsync linux.getSystemInfo
storage: wrapAsync linux.getStorageInfo
process_list: wrapAsync linux.getProcessList
memory: wrapAsync linux.getMemoryInfo
x: (callback) ->
callback null, 'test'
, (err, result) ->
console.log arguments
logger.error err if err
exports.render 'monitor', req, result, (html) ->
res.send html

View File

@@ -1,10 +1,5 @@
child_process = require 'child_process'
os = require 'os'
fs = require 'fs'
async = require 'async'
_ = require 'underscore'
{cache} = app
{child_process, os, fs, async, _} = app.libs
{cache, logger} = app
{wrapAsync} = app.utils
monitor = require './monitor'
@@ -18,7 +13,7 @@ exports.createUser = (account, callback) ->
child_process.exec "sudo usermod -G #{account.username} -a www-data", callback
], (err) ->
console.error err if err
logger.error err if err
cache.delete 'linux.getPasswdMap', ->
callback()
@@ -35,7 +30,7 @@ exports.deleteUser = (account, callback) ->
child_process.exec "sudo groupdel #{account.username}", callback
], (err) ->
console.error err if err
logger.error err if err
cache.delete 'linux.getPasswdMap', ->
callback()
@@ -50,32 +45,26 @@ exports.setResourceLimit = (account, callback) ->
hard_inode_limit = (storage_limit * 64 * 1.2).toFixed()
child_process.exec "sudo setquota -u #{account.username} #{soft_limit} #{hard_limit} #{soft_inode_limit} #{hard_inode_limit} -a", (err) ->
console.error err if err
logger.error err if err
callback()
exports.getPasswdMap = (callback) ->
cache.try 'linux.getPasswdMap',
command: cache.SETEX 120
is_json: true
, (callback) ->
cache.try 'linux.getPasswdMap', (SETEX) ->
fs.readFile '/etc/passwd', (err, content) ->
console.error err if err
logger.error err if err
result = {}
for line in _.compact(content.toString().split '\n')
[username, password, uid] = line.split ':'
result[uid] = username
callback result
SETEX result, 120
, callback
exports.getMemoryInfo = (callback) ->
cache.try 'linux.getProcessList',
command: cache.SETEX 3
is_json: true
, (callback) ->
cache.try 'linux.getProcessList', (SETEX) ->
fs.readFile '/proc/meminfo', (err, content) ->
console.error err if err
logger.error err if err
mapping = {}
for line in content.toString().split('\n')
@@ -92,7 +81,7 @@ exports.getMemoryInfo = (callback) ->
swap_free_per = (mapping['SwapFree'] / mapping['SwapTotal'] * 100).toFixed()
swap_used_per = 100 - swap_free_per
callback
SETEX
used: used
cached: mapping['Cached']
buffers: mapping['Buffers']
@@ -109,52 +98,49 @@ exports.getMemoryInfo = (callback) ->
swap_used_per: swap_used_per
swap_free_per: swap_free_per
, 3
, callback
exports.getProcessList = (callback) ->
cache.try 'linux.getProcessList',
command: cache.SETEX 5
is_json: true
, (callback) ->
cache.try 'linux.getProcessList', (SETEX) ->
exports.getPasswdMap (passwd_map) ->
child_process.exec "sudo ps awufxn", (err, stdout) ->
console.error err if err
logger.error err if err
callback _.map stdout.split('\n')[1 ... -1], (item) ->
result = _.map stdout.split('\n')[1 ... -1], (item) ->
result = /^\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/.exec item
return {
user: do ->
if passwd_map[result[1]]
return passwd_map[result[1]]
else
return result[1]
user: do ->
if passwd_map[result[1]]
return passwd_map[result[1]]
else
return result[1]
time: do ->
[minute, second] = result[10].split ':'
return parseInt(minute) * 60 + parseInt(second)
time: do ->
[minute, second] = result[10].split ':'
return parseInt(minute) * 60 + parseInt(second)
pid: parseInt result[2]
cpu_per: parseInt result[3]
mem_per: parseInt result[4]
vsz: parseInt result[5]
rss: parseInt result[6]
tty: result[7]
stat: result[8]
start: result[9]
command: result[11]
pid: parseInt result[2]
cpu_per: parseInt result[3]
mem_per: parseInt result[4]
vsz: parseInt result[5]
rss: parseInt result[6]
tty: result[7]
stat: result[8]
start: result[9]
command: result[11]
}
SETEX result, 5
, callback
exports.getStorageQuota = (callback) ->
cache.try 'linux.getStorageQuota',
command: cache.SETEX 60
is_json: true
, (callback) ->
cache.try 'linux.getStorageQuota', (SETEX) ->
child_process.exec "sudo repquota -a", (err, stdout) ->
console.error err if err
logger.error err if err
lines = _.filter stdout.split('\n')[5...-1], (i) -> i
lines = _.map lines, (line) ->
@@ -170,15 +156,12 @@ exports.getStorageQuota = (callback) ->
inode_used: parseInt inode_used
}
callback _.indexBy lines, 'username'
SETEX _.indexBy(lines, 'username'), 60
, callback
exports.getSystemInfo = (callback) ->
cache.try 'linux.getSystemInfo',
command: cache.SETEX 30
is_json: true
, (callback) ->
cache.try 'linux.getSystemInfo', (SETEX) ->
async.parallel
system: (callback) ->
fs.readFile '/etc/issue', (err, content) ->
@@ -192,27 +175,26 @@ exports.getSystemInfo = (callback) ->
unless item.internal
result.push item.address
callback result
callback null, result
, (err, result) ->
console.error err if err
logger.error err if err
callback _.extend result,
_.extend result,
hostname: os.hostname()
cpu: os.cpus()[0]['model']
uptime: os.uptime()
loadavg: _.map os.loadavg(), (i) -> parseFloat(i.toFixed(2))
time: new Date()
SETEX result, 30
, callback
exports.getStorageInfo = (callback) ->
cache.try 'linux.getStorageInfo',
command: cache.SETEX 30
is_json: true
, (callback) ->
cache.try 'linux.getStorageInfo', (SETEX) ->
child_process.exec "df -h", (err, stdout) ->
console.error err if err
logger.error err if err
disks = {}
for line in stdout.split('\n')
@@ -234,28 +216,25 @@ exports.getStorageInfo = (callback) ->
used_per = (used / total * 100).toFixed()
free_per = 100 - used_per
callback
SETEX
used: used
free: free
total: total
used_per: used_per
free_per: free_per
, 30
, callback
exports.getResourceUsageByAccounts = (callback) ->
cache.try 'linux.getStorageInfo',
command: cache.SETEX 20
is_json: true
, (callback) ->
console.log 'getResourceUsageByAccounts'
cache.try 'linux.getStorageInfo', (SETEX) ->
async.parallel
storage_quota: wrapAsync exports.getStorageQuota
process_list: wrapAsync exports.getProcessList
, (err, result) ->
console.error err if err
logger.error err if err
resources_usage_by_accounts = []
for username, usage of monitor.resources_usage
@@ -266,7 +245,7 @@ exports.getResourceUsageByAccounts = (callback) ->
storage: result.storage_quota[username]?.size_used ? 0
process: _.filter(result.process_list, (i) -> i.user == username).length
callback resources_usage_by_accounts
SETEX resources_usage_by_accounts, 20
, callback

View File

@@ -1,11 +1,6 @@
child_process = require 'child_process'
os = require 'os'
fs = require 'fs'
async = require 'async'
_ = require 'underscore'
{child_process, os, fs, async, _} = app.libs
{config} = app
{mAccount} = app.models
{Account} = app.models
linux = require './linux'
@@ -63,7 +58,7 @@ exports.monitoring = (callback) ->
usage.memory = parseFloat (usage.memory / base).toFixed(1)
async.each _.keys(resources_usage), (username, callback) ->
mAccount.search username, (err, account) ->
Account.search username, (err, account) ->
unless account
return callback()

View File

@@ -0,0 +1,69 @@
describe 'plugin/linux', ->
linux = null
agent = null
cache = null
redis = null
before ->
linux = require '../linux'
{cache, redis} = app
{agent} = namespace.accountRouter
describe 'router', ->
it 'GET monitor', (done) ->
agent.get '/public/monitor'
.expect 200
.end done
describe 'createUser', ->
it 'pending'
describe 'deleteUser', ->
it 'pending'
describe 'setResourceLimit', ->
it 'pending'
describe 'getPasswdMap', ->
it 'pending'
describe 'getMemoryInfo', ->
it 'pending'
describe 'getProcessList', ->
it 'pending'
describe 'getStorageQuota', ->
it 'pending'
describe 'getSystemInfo', ->
before (done) ->
cache.delete 'linux.getSystemInfo', done
it 'should success', (done) ->
linux.getSystemInfo (system) ->
system.system.should.match /Ubuntu/
system.hostname.should.be.a 'string'
system.cpu.should.be.a 'string'
system.uptime.should.be.a 'number'
system.loadavg.length.should.be.equal 3
system.time.should.be.exist
for address in system.address
expect(
address.match(/\d+\.\d+\.\d+\.\d+/) or
address.match(/::/)
).to.be.ok
redis.get 'RP:linux.getSystemInfo', (err, system) ->
system.should.be.exist
done()
describe 'getStorageInfo', ->
it 'pending'
describe 'getResourceUsageByAccounts', ->
it 'pending'
describe 'getResourceUsageByAccount', ->
it 'pending'

View File

@@ -1,7 +1,7 @@
extends ../../../core/view/layout
prepend header
title #{t('server_monitor')} | #{t('app.name')}
title #{t('server_monitor')} | #{t(config.web.t_name)}
append header
link(rel='stylesheet', href='/plugin/linux/style/monitor.css')

View File

@@ -15,7 +15,7 @@ module.exports =
plugin:
available_extensions: ['bitcoin', 'wiki', 'rpvhost']
available_services: []
available_services: ['linux']
billing:
currency: 'CNY'