Initial commit

master
Sven Slootweg 11 years ago
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,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…
Cancel
Save