Initial commit
commit
aaa3b1a6ec
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, sys, pwd, glob, setuplib, subprocess
|
||||||
|
|
||||||
|
def get_uid_gid():
|
||||||
|
try:
|
||||||
|
target_user = pwd.getpwnam("pylsa")
|
||||||
|
except KeyError, e:
|
||||||
|
raise Exception("The PyLSA installation appears to have been corrupted.")
|
||||||
|
|
||||||
|
return (target_user[2], target_user[3])
|
||||||
|
|
||||||
|
def switch_user():
|
||||||
|
target_uid, target_gid = get_uid_gid()
|
||||||
|
|
||||||
|
os.setgid(target_gid)
|
||||||
|
os.setuid(target_uid)
|
||||||
|
|
||||||
|
def install(as_root=False):
|
||||||
|
try:
|
||||||
|
target_uid, target_gid = get_uid_gid()
|
||||||
|
except Exception, e:
|
||||||
|
target_uid, target_gid = (-1, -1)
|
||||||
|
|
||||||
|
if as_root == True:
|
||||||
|
target_path = "/home/pylsa/.pylsa"
|
||||||
|
else:
|
||||||
|
target_path = os.path.expanduser("~/.pylsa")
|
||||||
|
|
||||||
|
setuplib.create_directory(target_path, True, target_uid, target_gid)
|
||||||
|
setuplib.copy_file("src/daemon.py", "%s/daemon.py" % target_path, False, target_uid, target_gid)
|
||||||
|
setuplib.copy_file("pylsa.conf", "%s/pylsa.conf" % target_path, True, target_uid, target_gid)
|
||||||
|
subprocess.call(["tar", "-xzf", "psutil-0.6.1.tar.gz"])
|
||||||
|
os.chdir("psutil-0.6.1")
|
||||||
|
|
||||||
|
stfu = open("/dev/null", "w")
|
||||||
|
if subprocess.call(["python", "setup.py", "build"], stdout=stfu, stderr=stfu) != 0:
|
||||||
|
sys.stderr.write("An error occurred during compilation.\n")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
build_dir = glob.glob("build/lib.*")[0]
|
||||||
|
except IndexError, e:
|
||||||
|
sys.stderr.write("Compilation appears to have failed, exiting...\n")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
setuplib.copy_directory(build_dir, "%s/lib" % target_path, False, target_uid, target_gid)
|
||||||
|
|
||||||
|
def add_user(uname):
|
||||||
|
# Lock /etc/passwd and /etc/group so we can safely add a user.
|
||||||
|
open("/etc/passwd.lock", "w").close()
|
||||||
|
open("/etc/group.lock", "w").close()
|
||||||
|
|
||||||
|
# Find highest non-reserved UID in the user list
|
||||||
|
passwd = open("/etc/passwd", "r+")
|
||||||
|
highest_uid = 1000
|
||||||
|
|
||||||
|
for line in passwd:
|
||||||
|
username, password, uid, gid, name, homedir, shell = line.split(":")
|
||||||
|
|
||||||
|
if username == uname:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if int(uid) < 32000 and int(uid) > highest_uid:
|
||||||
|
highest_uid = int(uid)
|
||||||
|
|
||||||
|
new_uid = highest_uid + 1
|
||||||
|
|
||||||
|
# Find highest non-reserved GID in the group list - we will assume same restrictions as for UID
|
||||||
|
grp = open("/etc/group", "r+")
|
||||||
|
highest_gid = 1000
|
||||||
|
|
||||||
|
for line in grp:
|
||||||
|
groupname, password, gid, users = line.split(":")
|
||||||
|
|
||||||
|
if groupname == uname:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if int(gid) < 32000 and int(gid) > highest_gid:
|
||||||
|
highest_gid = int(gid)
|
||||||
|
|
||||||
|
new_gid = highest_gid + 1
|
||||||
|
|
||||||
|
# Append new user and group
|
||||||
|
passwd.seek(0, 2)
|
||||||
|
grp.seek(0, 2)
|
||||||
|
|
||||||
|
setuplib.create_directory("/home/%s" % uname, True, new_uid, new_gid, "u+rwx g+rx")
|
||||||
|
passwd.write("%s::%d:%d::/home/cvm:/bin/false\n" % (uname, new_uid, new_gid))
|
||||||
|
grp.write("%s::%d:\n" % (uname, new_gid))
|
||||||
|
|
||||||
|
# We're done with /etc/passwd and /etc/group
|
||||||
|
passwd.close()
|
||||||
|
grp.close()
|
||||||
|
|
||||||
|
# Remove the locks on /etc/passwd and /etc/group
|
||||||
|
os.remove("/etc/passwd.lock")
|
||||||
|
os.remove("/etc/group.lock")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
if os.getuid() == 0:
|
||||||
|
# Script is run as root
|
||||||
|
if os.path.isdir("/home/pylsa/.pylsa"):
|
||||||
|
# Already installed, run as pylsa user
|
||||||
|
switch_user()
|
||||||
|
exit(subprocess.call(["python", "/home/pylsa/.pylsa/daemon.py"]))
|
||||||
|
else:
|
||||||
|
# Not installed yet, install as pylsa user then run
|
||||||
|
add_user("pylsa")
|
||||||
|
install(True)
|
||||||
|
switch_user()
|
||||||
|
exit(subprocess.call(["python", "/home/pylsa/.pylsa/daemon.py"]))
|
||||||
|
else:
|
||||||
|
# Script is run as unprivileged user
|
||||||
|
if os.path.isdir(os.path.expanduser("~/.pylsa")):
|
||||||
|
# Already installed
|
||||||
|
exit(subprocess.call(["python", os.path.expanduser("~/.pylsa/daemon.py")]))
|
||||||
|
else:
|
||||||
|
# Not installed yet
|
||||||
|
install(False)
|
||||||
|
exit(subprocess.call(["python", os.path.expanduser("~/.pylsa/daemon.py")]))
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1,148 @@
|
|||||||
|
import stat, os, shutil, urllib, subprocess
|
||||||
|
from itertools import dropwhile
|
||||||
|
|
||||||
|
def create_directory(path, ignore_failure=True, uid=-1, gid=-1, modes=""):
|
||||||
|
try:
|
||||||
|
os.makedirs(path)
|
||||||
|
except OSError, e:
|
||||||
|
if ignore_failure:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if uid != -1 or gid != -1:
|
||||||
|
os.chown(path, uid, gid)
|
||||||
|
|
||||||
|
if modes != "":
|
||||||
|
set_modes(path, modes)
|
||||||
|
|
||||||
|
def copy_file(source, destination, ignore_failure=True, uid=-1, gid=-1, modes=""):
|
||||||
|
if ignore_failure == False:
|
||||||
|
if os.path.exists(destination):
|
||||||
|
raise Exception("Destination path already exists.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.copy(source, destination)
|
||||||
|
except IOError, e:
|
||||||
|
if ignore_failure:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if uid != -1 or gid != -1:
|
||||||
|
os.chown(destination, uid, gid)
|
||||||
|
|
||||||
|
if modes != "":
|
||||||
|
set_modes(destination, modes)
|
||||||
|
|
||||||
|
def copy_directory(source, destination, ignore_failure=True, uid=-1, gid=-1, modes=""):
|
||||||
|
if ignore_failure == False:
|
||||||
|
if os.path.exists(destination):
|
||||||
|
raise Exception("Destination path already exists.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.copytree(source, destination)
|
||||||
|
except IOError, e:
|
||||||
|
if ignore_failure:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if uid != -1 or gid != -1:
|
||||||
|
os.chown(destination, uid, gid)
|
||||||
|
|
||||||
|
if modes != "":
|
||||||
|
set_modes(destination, modes)
|
||||||
|
|
||||||
|
def create_file(path, contents="", uid=-1, gid=-1, modes=""):
|
||||||
|
f = open(path, "w")
|
||||||
|
|
||||||
|
if contents != "":
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if uid != -1 or gid != -1:
|
||||||
|
os.chown(path, uid, gid)
|
||||||
|
|
||||||
|
if modes != "":
|
||||||
|
set_modes(path, modes)
|
||||||
|
|
||||||
|
def set_modes(path, modes):
|
||||||
|
mode_map = {
|
||||||
|
"u": {
|
||||||
|
"r": stat.S_IRUSR,
|
||||||
|
"w": stat.S_IWUSR,
|
||||||
|
"x": stat.S_IXUSR
|
||||||
|
},
|
||||||
|
"g": {
|
||||||
|
"r": stat.S_IRGRP,
|
||||||
|
"w": stat.S_IWGRP,
|
||||||
|
"x": stat.S_IXGRP
|
||||||
|
},
|
||||||
|
"o": {
|
||||||
|
"r": stat.S_IROTH,
|
||||||
|
"w": stat.S_IWOTH,
|
||||||
|
"x": stat.S_IXOTH
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks = modes.split(" ")
|
||||||
|
mode = 0
|
||||||
|
|
||||||
|
for chunk in chunks:
|
||||||
|
usertype, changes = chunk.split("+")
|
||||||
|
|
||||||
|
if usertype in mode_map:
|
||||||
|
for change in list(changes):
|
||||||
|
if change in mode_map[usertype]:
|
||||||
|
mode = mode | mode_map[usertype][change]
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown permission in modes specification.")
|
||||||
|
elif usertype == "a":
|
||||||
|
for change in list(changes):
|
||||||
|
for i in mode_map:
|
||||||
|
if change in mode_map[i]:
|
||||||
|
mode = mode | mode_map[i][change]
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown user type in modes specification.")
|
||||||
|
|
||||||
|
os.chmod(path, mode)
|
||||||
|
|
||||||
|
def download_file(name, mirrors):
|
||||||
|
try:
|
||||||
|
file_mirrors = mirrors[name]
|
||||||
|
except KeyError, e:
|
||||||
|
raise Exception("No such file exists in the mirror list.")
|
||||||
|
|
||||||
|
for url in file_mirrors:
|
||||||
|
try:
|
||||||
|
urllib.urlretrieve(url, name)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
raise Exception("No functional mirrors found for this file.")
|
||||||
|
|
||||||
|
def install_rpm(path):
|
||||||
|
stfu = open("/dev/null", "wb")
|
||||||
|
|
||||||
|
result = subprocess.call(["yum", "--nogpgcheck", "install", "-y", path], stdout=stfu, stderr=stfu)
|
||||||
|
|
||||||
|
stfu.close()
|
||||||
|
|
||||||
|
if result != 0:
|
||||||
|
raise Exception("Failed to install package.")
|
||||||
|
|
||||||
|
def install_remote_rpm(name, mirrors):
|
||||||
|
download_file(name, mirrors)
|
||||||
|
install_rpm(name)
|
||||||
|
|
||||||
|
def rindex(lst, item):
|
||||||
|
# http://stackoverflow.com/a/6892096/1332715
|
||||||
|
try:
|
||||||
|
return dropwhile(lambda x: lst[x].strip() != item, reversed(xrange(len(lst)))).next()
|
||||||
|
except StopIteration:
|
||||||
|
raise ValueError, "rindex(lst, item): item not in list"
|
@ -0,0 +1,135 @@
|
|||||||
|
import sys, os, json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# We need to set both the Python module path and system environment path, to make sure that the daemon
|
||||||
|
# can find both the psutil library and its compiled libraries.
|
||||||
|
sys.path.insert(0, "%s/lib" % os.path.split(os.path.realpath(__file__))[0])
|
||||||
|
os.environ['PATH'] = "%s/lib:%s" % (os.path.split(os.path.realpath(__file__))[0], os.environ['PATH'])
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
import urlparse
|
||||||
|
import SocketServer, SimpleHTTPServer
|
||||||
|
|
||||||
|
bind_ip = ""
|
||||||
|
port = 8081
|
||||||
|
|
||||||
|
def generate_stats(get_processes):
|
||||||
|
listed_filesystems = ["ext2", "ext3", "ext4", "reiserfs", "removable", "fixed", "simfs"]
|
||||||
|
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
swap = psutil.swap_memory()
|
||||||
|
disks = {}
|
||||||
|
|
||||||
|
for disk in psutil.disk_partitions(True):
|
||||||
|
if disk.fstype in listed_filesystems:
|
||||||
|
usage = psutil.disk_usage(disk.mountpoint)
|
||||||
|
|
||||||
|
disks[disk.mountpoint] = {
|
||||||
|
"device": disk.device,
|
||||||
|
"options": disk.opts,
|
||||||
|
"filesystem": disk.fstype,
|
||||||
|
"total": usage.total,
|
||||||
|
"free": usage.free
|
||||||
|
}
|
||||||
|
|
||||||
|
return_data = {
|
||||||
|
"uptime": (datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds(),
|
||||||
|
"memory": {
|
||||||
|
"total": mem.total,
|
||||||
|
"available": mem.available,
|
||||||
|
"used": mem.used,
|
||||||
|
"unused": mem.free
|
||||||
|
},
|
||||||
|
"swap": {
|
||||||
|
"total": swap.total,
|
||||||
|
"used": swap.used,
|
||||||
|
"unused": swap.free,
|
||||||
|
"in": swap.sin,
|
||||||
|
"out": swap.sout
|
||||||
|
},
|
||||||
|
"disk": disks,
|
||||||
|
"cpu": psutil.cpu_percent(percpu=True)
|
||||||
|
}
|
||||||
|
|
||||||
|
if get_processes:
|
||||||
|
processes = []
|
||||||
|
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
try:
|
||||||
|
cwd = proc.getcwd()
|
||||||
|
except psutil.AccessDenied, e:
|
||||||
|
cwd = None
|
||||||
|
|
||||||
|
processes.append({
|
||||||
|
"pid": proc.pid,
|
||||||
|
"parent": proc.ppid,
|
||||||
|
"name": proc.name,
|
||||||
|
"command": proc.cmdline,
|
||||||
|
"user": proc.username,
|
||||||
|
"status": str(proc.status), # If we don't explicitly use str() here, json module will get confused
|
||||||
|
"cwd": cwd,
|
||||||
|
"cpu": proc.get_cpu_percent(interval=0.1),
|
||||||
|
"rss": proc.get_memory_info()[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
return_data['process'] = processes
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
class StatsHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
req = urlparse.urlparse(self.path)
|
||||||
|
get_params = urlparse.parse_qs(req.query)
|
||||||
|
path = req.path
|
||||||
|
|
||||||
|
try:
|
||||||
|
get_processes = (get_params['processes'][0] == "1")
|
||||||
|
except:
|
||||||
|
get_processes = False
|
||||||
|
|
||||||
|
if path=='/':
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type','text/json')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(generate_stats(get_processes)))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.send_response(404)
|
||||||
|
self.send_header('Content-type','text/plain')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write("404 Not Found")
|
||||||
|
return
|
||||||
|
|
||||||
|
#### Hacky way to daemonize the whole thing
|
||||||
|
|
||||||
|
# Fork away
|
||||||
|
if os.fork(): exit(0)
|
||||||
|
os.umask(0)
|
||||||
|
os.setsid()
|
||||||
|
if os.fork(): exit(0)
|
||||||
|
|
||||||
|
# Write PID to file
|
||||||
|
outfile = open("pylsa.pid", "w")
|
||||||
|
outfile.write('%i' % os.getpid())
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
# Bind the server
|
||||||
|
httpd = SocketServer.ThreadingTCPServer((bind_ip, port), StatsHandler, False)
|
||||||
|
httpd.allow_reuse_address = True
|
||||||
|
httpd.server_bind()
|
||||||
|
httpd.server_activate()
|
||||||
|
|
||||||
|
# If all went well, we'll redirect streams to silence them
|
||||||
|
sys.stdout.flush()
|
||||||
|
sys.stderr.flush()
|
||||||
|
si = file('/dev/null', 'r')
|
||||||
|
so = file('/dev/null', 'a+')
|
||||||
|
se = file('/dev/null', 'a+', 0)
|
||||||
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||||
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||||
|
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
|
# Done!
|
||||||
|
print 'Port=',port
|
||||||
|
httpd.serve_forever()
|
Loading…
Reference in New Issue