mirror of
https://github.com/HackPlan/RootPanel.git
synced 2026-01-12 22:27:09 +08:00
import linux plugin
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
69
plugin/linux/test/linux.test.coffee
Normal file
69
plugin/linux/test/linux.test.coffee
Normal 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'
|
||||
@@ -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')
|
||||
|
||||
@@ -15,7 +15,7 @@ module.exports =
|
||||
|
||||
plugin:
|
||||
available_extensions: ['bitcoin', 'wiki', 'rpvhost']
|
||||
available_services: []
|
||||
available_services: ['linux']
|
||||
|
||||
billing:
|
||||
currency: 'CNY'
|
||||
|
||||
Reference in New Issue
Block a user