Merge remote-tracking branch 'upstream/master'

This commit is contained in:
leebaok 2016-05-17 17:11:16 +08:00
commit d5585e55d3
18 changed files with 822 additions and 562 deletions

View File

@ -41,6 +41,8 @@ lxc.cgroup.memory.limit_in_bytes = %CONTAINER_MEMORY%M
# lxc.cgroup.cpu.cfs_quota_us : quota time of this process
lxc.cgroup.cpu.cfs_quota_us = %CONTAINER_CPU%
lxc.cap.drop = sys_admin net_admin mac_admin mac_override sys_time sys_module
lxc.mount.entry = %FS_PREFIX%/global/users/%USERNAME%/data %ROOTFS%/root/nfs none bind,rw,create=dir 0 0
lxc.mount.entry = %FS_PREFIX%/global/users/%USERNAME%/hosts/%CLUSTERID%.hosts %ROOTFS%/etc/hosts none bind,ro,create=file 0 0
lxc.mount.entry = %FS_PREFIX%/global/users/%USERNAME%/ssh %ROOTFS%/root/.ssh none bind,ro,create=dir 0 0

View File

@ -135,11 +135,13 @@
# default: ""
# ADMIN_EMAIL_ADDRESS=""
# DATA_QUOTA : whether enable the quota of data or not
# default: "NO"
# DATA_QUOTA=""
# DATA_QUOTA : whether enable the quota of data volume or not
# True or False, default: False
# DATA_QUOTA=False
# DATA_QUOTA_CMD : the cmd to set the data quota, the argument of it should be directory name
# and the quota value.
# DATA_QUOTA_CMD : the cmd to set the quota of a given directory. It accepts two arguments:
# arg1: the directory name, relative path from the data volume root, e.g, "/users/bob/data"
# arg2: the quota value in GB of string, e.g., "100"
# default: "gluster volume quota docklet-volume limit-usage %s %s"
# DATA_QUOTA_CMD=""
# DATA_QUOTA_CMD="gluster volume quota docklet-volume limit-usage %s %s"

View File

@ -95,10 +95,8 @@ class Container(object):
else:
logger.error ("get AUTH COOKIE URL failed for jupyter")
authurl = "error"
if (username=='guest'):
cookiename='guest-cookie'
else:
cookiename='docklet-jupyter-cookie'
cookiename='docklet-jupyter-cookie'
rundir = self.lxcpath+'/'+lxc_name+'/rootfs' + self.rundir

View File

@ -51,7 +51,7 @@ def getenv(key):
elif key =="ADMIN_EMAIL_ADDRESS":
return os.environ.get("ADMIN_EMAIL_ADDRESS", "")
elif key =="DATA_QUOTA":
return os.environ.get("DATA_QUOTA", "NO")
return os.environ.get("DATA_QUOTA", "False")
elif key =="DATA_QUOTA_CMD":
return os.environ.get("DATA_QUOTA_CMD", "gluster volume quota docklet-volume limit-usage %s %s")
else:

View File

@ -1,40 +0,0 @@
#!/usr/bin/python3
import os,time,subprocess
import env
import json
class Guest(object):
def __init__(self,vclusterMgr,nodemgr):
self.libpath = env.getenv('DOCKLET_LIB')
self.fspath = env.getenv('FS_PREFIX')
self.lxcpath = "/var/lib/lxc"
self.G_vclustermgr = vclusterMgr
self.nodemgr = nodemgr
def work(self):
image = {}
image['name'] = "base"
image['type'] = "base"
image['owner'] = "docklet"
while len(self.nodemgr.get_rpcs()) < 1:
time.sleep(10)
if not os.path.isdir(self.fspath+"/global/users/guest"):
subprocess.getoutput(self.libpath+"/userinit.sh guest")
user_info = {}
user_info["data"] = {}
user_info["data"]["group"] = "primary"
user_info["data"]["groupinfo"] = {}
user_info["data"]["groupinfo"]["cpu"] = 4
user_info["data"]["groupinfo"]["memory"] = 2000
user_info["data"]["groupinfo"]["disk"] = 2000
user_info = json.dumps(user_info)
self.G_vclustermgr.create_cluster("guestspace", "guest", image, user_info)
while True:
self.G_vclustermgr.start_cluster("guestspace", "guest")
time.sleep(3600)
self.G_vclustermgr.stop_cluster("guestspace", "guest")
fspath = self.fspath + "/local/volume/guest-1-0/"
nfspath = self.fspath + "/global/users/guest/data/"
subprocess.getoutput("(cd %s && rm -rf *)" % fspath)
subprocess.getoutput("(cd %s && rm -rf *)" % nfspath)

View File

@ -24,7 +24,8 @@ from socketserver import ThreadingMixIn
import nodemgr, vclustermgr, etcdlib, network, imagemgr
import userManager
import monitor
import guest_control, threading
import threading
import sysmgr
#default EXTERNAL_LOGIN=False
external_login = env.getenv('EXTERNAL_LOGIN')
@ -356,7 +357,7 @@ def deleteproxy(cur_user, user, form):
logger.info ("handle request : delete proxy")
clustername = form.get("clustername", None)
G_vclustermgr.deleteproxy(user,clustername)
self.response(200, {'success':'true', 'action':'deleteproxy'})
return json.dumps({'success':'true', 'action':'deleteproxy'})
@app.route("/monitor/hosts/<com_id>/<issue>/", methods=['POST'])
@login_required
@ -365,7 +366,7 @@ def hosts_monitor(cur_user, user, form, com_id, issue):
logger.info("handle request: monitor/hosts")
res = {}
fetcher = monitor.Fetcher(etcdaddr,G_clustername,com_id)
fetcher = monitor.Fetcher(com_id)
if issue == 'meminfo':
res['meminfo'] = fetcher.get_meminfo()
elif issue == 'cpuinfo':
@ -404,7 +405,7 @@ def vnodes_monitor(cur_user, user, form, con_id, issue):
global G_clustername
logger.info("handle request: monitor/vnodes")
res = {}
fetcher = monitor.Container_Fetcher(etcdaddr,G_clustername)
fetcher = monitor.Container_Fetcher()
if issue == 'cpu_use':
res['cpu_use'] = fetcher.get_cpu_use(con_id)
elif issue == 'mem_use':
@ -574,6 +575,84 @@ def selfModify_user(cur_user, user, form):
result = G_usermgr.selfModify(cur_user = cur_user, newValue = form)
return json.dumps(result)
@app.route("/system/parmList/", methods=['POST'])
@login_required
def parmList_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/parmList/")
result = G_sysmgr.getParmList()
return json.dumps(result)
@app.route("/system/modify/", methods=['POST'])
@login_required
def modify_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/modify/")
field = form.get("field", None)
parm = form.get("parm", None)
val = form.get("val", None)
[status, message] = G_sysmgr.modify(field,parm,val)
if status is True:
return json.dumps({'success':'true', 'action':'modify_system'})
else:
return json.dumps({'success':'false', 'message': message})
return json.dumps(result)
@app.route("/system/clear_history/", methods=['POST'])
@login_required
def clear_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/clear_history/")
field = form.get("field", None)
parm = form.get("parm", None)
[status, message] = G_sysmgr.clear(field,parm)
if status is True:
return json.dumps({'success':'true', 'action':'clear_history'})
else:
return json.dumps({'success':'false', 'message': message})
return json.dumps(result)
@app.route("/system/add/", methods=['POST'])
@login_required
def add_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/add/")
field = form.get("field", None)
parm = form.get("parm", None)
val = form.get("val", None)
[status, message] = G_sysmgr.add(field, parm, val)
if status is True:
return json.dumps({'success':'true', 'action':'add_parameter'})
else:
return json.dumps({'success':'false', 'message': message})
return json.dumps(result)
@app.route("/system/delete/", methods=['POST'])
@login_required
def delete_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/delete/")
field = form.get("field", None)
parm = form.get("parm", None)
[status, message] = G_sysmgr.delete(field,parm)
if status is True:
return json.dumps({'success':'true', 'action':'delete_parameter'})
else:
return json.dumps({'success':'false', 'message': message})
return json.dumps(result)
@app.route("/system/reset_all/", methods=['POST'])
@login_required
def resetall_system(cur_user, user, form):
global G_sysmgr
logger.info("handle request: system/reset_all/")
field = form.get("field", None)
[status, message] = G_sysmgr.reset_all(field)
if status is True:
return json.dumps({'success':'true', 'action':'reset_all'})
else:
return json.dumps({'success':'false', 'message': message})
return json.dumps(result)
@app.errorhandler(500)
def internal_server_error(error):
@ -608,6 +687,7 @@ if __name__ == '__main__':
global etcdclient
global G_networkmgr
global G_clustername
global G_sysmgr
# move 'tools.loadenv' to the beginning of this file
fs_path = env.getenv("FS_PREFIX")
@ -687,6 +767,8 @@ if __name__ == '__main__':
clusternet = env.getenv("CLUSTER_NET")
logger.info("using CLUSTER_NET %s" % clusternet)
G_sysmgr = sysmgr.SystemManager()
G_networkmgr = network.NetworkMgr(clusternet, etcdclient, mode)
G_networkmgr.printpools()
@ -697,9 +779,8 @@ if __name__ == '__main__':
logger.info("vclustermgr started")
G_imagemgr = imagemgr.ImageMgr()
logger.info("imagemgr started")
Guest_control = guest_control.Guest(G_vclustermgr,G_nodemgr)
logger.info("guest control started")
threading.Thread(target=Guest_control.work, args=()).start()
master_collector = monitor.Master_Collector(G_nodemgr)
master_collector.start()
logger.info("startting to listen on: ")
masterip = env.getenv('MASTER_IP')

View File

@ -5,13 +5,17 @@ import time,threading,json,traceback,platform
from log import logger
monitor_hosts = {}
monitor_vnodes = {}
workerinfo = {}
workercinfo = {}
class Container_Collector(threading.Thread):
def __init__(self,etcdaddr,cluster_name,host,test=False):
def __init__(self,test=False):
threading.Thread.__init__(self)
self.thread_stop = False
self.host = host
self.etcdser = etcdlib.Client(etcdaddr,"/%s/monitor" % (cluster_name))
self.interval = 2
self.test = test
self.cpu_last = {}
@ -27,6 +31,7 @@ class Container_Collector(threading.Thread):
return containers
def collect_containerinfo(self,container_name):
global workercinfo
output = subprocess.check_output("sudo lxc-info -n %s" % (container_name),shell=True)
output = output.decode('utf-8')
parts = re.split('\n',output)
@ -41,11 +46,11 @@ class Container_Collector(threading.Thread):
basic_info['Name'] = info['Name']
basic_info['State'] = info['State']
if(info['State'] == 'STOPPED'):
self.etcdser.setkey('/vnodes/%s/basic_info'%(container_name), basic_info)
workercinfo[container_name]['basic_info'] = basic_info
return False
basic_info['PID'] = info['PID']
basic_info['IP'] = info['IP']
self.etcdser.setkey('/vnodes/%s/basic_info'%(container_name), basic_info)
workercinfo[container_name]['basic_info'] = basic_info
cpu_parts = re.split(' +',info['CPU use'])
cpu_val = cpu_parts[0].strip()
@ -69,7 +74,7 @@ class Container_Collector(threading.Thread):
self.cpu_quota[container_name] = tmp/100000.0
quota = {'cpu':self.cpu_quota[container_name],'memory':self.mem_quota[container_name]}
#logger.info(quota)
self.etcdser.setkey('/vnodes/%s/quota'%(container_name),quota)
workercinfo[container_name]['quota'] = quota
else:
logger.error("Cant't find config file %s"%(confpath))
return False
@ -82,7 +87,7 @@ class Container_Collector(threading.Thread):
cpu_usedp = 1
cpu_use['usedp'] = cpu_usedp
self.cpu_last[container_name] = cpu_val;
self.etcdser.setkey('/vnodes/%s/cpu_use'%(container_name), cpu_use)
workercinfo[container_name]['cpu_use'] = cpu_use
mem_parts = re.split(' +',info['Memory use'])
mem_val = mem_parts[0].strip()
@ -96,12 +101,14 @@ class Container_Collector(threading.Thread):
mem_val = float(mem_val) * 1024 * 1024
mem_usedp = float(mem_val) / self.mem_quota[container_name]
mem_use['usedp'] = mem_usedp
self.etcdser.setkey('/vnodes/%s/mem_use'%(container_name), mem_use)
workercinfo[container_name]['mem_use'] = mem_use
#print(output)
#print(parts)
return True
def run(self):
global workercinfo
global workerinfo
cnt = 0
while not self.thread_stop:
containers = self.list_container()
@ -110,8 +117,11 @@ class Container_Collector(threading.Thread):
for container in containers:
if not container == '':
conlist.append(container)
if not container in workercinfo.keys():
workercinfo[container] = {}
try:
if(self.collect_containerinfo(container)):
success= self.collect_containerinfo(container)
if(success):
countR += 1
except Exception as err:
logger.warning(traceback.format_exc())
@ -120,10 +130,10 @@ class Container_Collector(threading.Thread):
concnt = {}
concnt['total'] = containers_num
concnt['running'] = countR
self.etcdser.setkey('/hosts/%s/containers'%(self.host), concnt)
workerinfo['containers'] = concnt
time.sleep(self.interval)
if cnt == 0:
self.etcdser.setkey('/hosts/%s/containerslist'%(self.host), conlist)
workerinfo['containerslist'] = conlist
cnt = (cnt+1)%5
if self.test:
break
@ -135,12 +145,9 @@ class Container_Collector(threading.Thread):
class Collector(threading.Thread):
def __init__(self,etcdaddr,cluster_name,host,test=False):
def __init__(self,test=False):
threading.Thread.__init__(self)
self.host = host
self.thread_stop = False
self.etcdser = etcdlib.Client(etcdaddr,"/%s/monitor/hosts/%s" % (cluster_name,host))
self.vetcdser = etcdlib.Client(etcdaddr,"/%s/monitor/vnodes" % (cluster_name))
self.interval = 1
self.test=test
return
@ -154,10 +161,9 @@ class Collector(threading.Thread):
memdict['buffers'] = meminfo.buffers/1024
memdict['cached'] = meminfo.cached/1024
memdict['percent'] = meminfo.percent
self.etcdser.setkey('/meminfo',memdict)
#print(output)
#print(memparts)
return
return memdict
def collect_cpuinfo(self):
cpuinfo = psutil.cpu_times_percent(interval=1,percpu=False)
@ -166,7 +172,6 @@ class Collector(threading.Thread):
cpuset['system'] = cpuinfo.system
cpuset['idle'] = cpuinfo.idle
cpuset['iowait'] = cpuinfo.iowait
self.etcdser.setkey('/cpuinfo',cpuset)
output = subprocess.check_output(["cat /proc/cpuinfo"],shell=True)
output = output.decode('utf-8')
parts = output.split('\n')
@ -182,10 +187,10 @@ class Collector(threading.Thread):
val = key_val[1].lstrip()
if key=='processor' or key=='model name' or key=='core id' or key=='cpu MHz' or key=='cache size' or key=='physical id':
info[idx][key] = val
self.etcdser.setkey('/cpuconfig',info)
return
return [cpuset, info]
def collect_diskinfo(self):
global workercinfo
parts = psutil.disk_partitions()
setval = []
devices = {}
@ -204,15 +209,16 @@ class Collector(threading.Thread):
if(part.mountpoint.startswith('/opt/docklet/local/volume')):
names = re.split('/',part.mountpoint)
container = names[len(names)-1]
self.vetcdser.setkey('/%s/disk_use'%(container), diskval)
if not container in workercinfo.keys():
workercinfo[container] = {}
workercinfo[container]['disk_use'] = diskval
setval.append(diskval)
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
self.etcdser.setkey('/diskinfo', setval)
#print(output)
#print(diskparts)
return
return setval
def collect_osinfo(self):
uname = platform.uname()
@ -224,16 +230,18 @@ class Collector(threading.Thread):
osinfo['version'] = uname.version
osinfo['machine'] = uname.machine
osinfo['processor'] = uname.processor
self.etcdser.setkey('/osinfo',osinfo)
return
return osinfo
def run(self):
self.collect_osinfo()
global workerinfo
workerinfo['osinfo'] = self.collect_osinfo()
while not self.thread_stop:
self.collect_meminfo()
self.collect_cpuinfo()
self.collect_diskinfo()
self.etcdser.setkey('/running','True',6)
workerinfo['meminfo'] = self.collect_meminfo()
[cpuinfo,cpuconfig] = self.collect_cpuinfo()
workerinfo['cpuinfo'] = cpuinfo
workerinfo['cpuconfig'] = cpuconfig
workerinfo['diskinfo'] = self.collect_diskinfo()
workerinfo['running'] = True
time.sleep(self.interval)
if self.test:
break
@ -243,61 +251,96 @@ class Collector(threading.Thread):
def stop(self):
self.thread_stop = True
def workerFetchInfo():
global workerinfo
global workercinfo
return str([workerinfo, workercinfo])
class Master_Collector(threading.Thread):
def __init__(self,nodemgr):
threading.Thread.__init__(self)
self.thread_stop = False
self.nodemgr = nodemgr
return
def run(self):
global monitor_hosts
global monitor_vnodes
while not self.thread_stop:
for worker in monitor_hosts.keys():
monitor_hosts[worker]['running'] = False
workers = self.nodemgr.get_rpcs()
for worker in workers:
try:
ip = self.nodemgr.rpc_to_ip(worker)
#[info,cinfo] = worker.workerFetchInfo()
info = list(eval(worker.workerFetchInfo()))
logger.info(info[1])
monitor_hosts[ip] = info[0]
for container in info[1].keys():
monitor_vnodes[container] = info[1][container]
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
time.sleep(2)
return
def stop(self):
self.thread_stop = True
return
class Container_Fetcher:
def __init__(self,etcdaddr,cluster_name):
self.etcdser = etcdlib.Client(etcdaddr,"/%s/monitor/vnodes" % (cluster_name))
def __init__(self):
return
def get_cpu_use(self,container_name):
res = {}
[ret, ans] = self.etcdser.getkey('/%s/cpu_use'%(container_name))
if ret == True :
res = dict(eval(ans))
[ret,quota] = self.etcdser.getkey('/%s/quota'%(container_name))
if ret == False:
res['quota'] = {'cpu':0}
logger.warning(quota)
res['quota'] = dict(eval(quota))
return res
else:
logger.warning(ans)
return res
global monitor_vnodes
try:
res = monitor_vnodes[container_name]['cpu_use']
res['quota'] = monitor_vnodes[container_name]['quota']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_mem_use(self,container_name):
res = {}
[ret, ans] = self.etcdser.getkey('/%s/mem_use'%(container_name))
if ret == True :
res = dict(eval(ans))
[ret,quota] = self.etcdser.getkey('/%s/quota'%(container_name))
if ret == False:
res['quota'] = {'memory':0}
logger.warning(quota)
res['quota'] = dict(eval(quota))
return res
else:
logger.warning(ans)
return res
global monitor_vnodes
try:
res = monitor_vnodes[container_name]['mem_use']
res['quota'] = monitor_vnodes[container_name]['quota']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_disk_use(self,container_name):
res = {}
[ret, ans] = self.etcdser.getkey('/%s/disk_use'%(container_name))
if ret == True :
res = dict(eval(ans))
else:
logger.warning(ans)
global monitor_vnodes
try:
res = monitor_vnodes[container_name]['disk_use']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_basic_info(self,container_name):
res = self.etcdser.getkey("/%s/basic_info"%(container_name))
if res[0] == False:
return {}
res = dict(eval(res[1]))
global monitor_vnodes
try:
res = monitor_vnodes[container_name]['basic_info']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
class Fetcher:
def __init__(self,etcdaddr,cluster_name,host):
self.etcdser = etcdlib.Client(etcdaddr,"/%s/monitor/hosts/%s" % (cluster_name,host))
def __init__(self,host):
global monitor_hosts
self.info = monitor_hosts[host]
return
#def get_clcnt(self):
@ -310,72 +353,76 @@ class Fetcher:
# return self.get_meminfo_('172.31.0.1')
def get_meminfo(self):
res = {}
[ret, ans] = self.etcdser.getkey('/meminfo')
if ret == True :
res = dict(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['meminfo']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_cpuinfo(self):
res = {}
[ret, ans] = self.etcdser.getkey('/cpuinfo')
if ret == True :
res = dict(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['cpuinfo']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_cpuconfig(self):
res = {}
[ret, ans] = self.etcdser.getkey('/cpuconfig')
if ret == True :
res = list(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['cpuconfig']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_diskinfo(self):
res = []
[ret, ans] = self.etcdser.getkey('/diskinfo')
if ret == True :
res = list(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['diskinfo']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_osinfo(self):
res = {}
[ret, ans] = self.etcdser.getkey('/osinfo')
if ret == True:
res = dict(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['osinfo']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_containers(self):
res = {}
[ret, ans] = self.etcdser.getkey('/containers')
if ret == True:
res = dict(eval(ans))
return res
else:
logger.warning(ans)
return res
try:
res = self.info['containers']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res
def get_status(self):
isexist = self.etcdser.getkey('/running')[0]
try:
isexist = self.info['running']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
isexist = False
if(isexist):
return 'RUNNING'
else:
return 'STOPPED'
def get_containerslist(self):
res = list(eval(self.etcdser.getkey('/containerslist')[1]))
try:
res = self.info['containerslist']
except Exception as err:
logger.warning(traceback.format_exc())
logger.warning(err)
res = {}
return res

164
src/sysmgr.py Normal file
View File

@ -0,0 +1,164 @@
import re, string, os
configPath = {"docklet": os.environ.get("DOCKLET_CONF")+"/docklet.conf",
"container": os.environ.get("DOCKLET_CONF")+"/container.conf"}
#configPath = "../conf/docklet.conf"
#lxcconfigPath = "../conf/container.conf"
defaultPattern = re.compile(u'# *\S+ *= *\S+')
activePattern = re.compile(u'\S+ *= *\S+')
historyPattern = re.compile(u'## *\S+ *= *\S+')
def parse_line(line):
kind = ""
parm = ""
val = ""
if defaultPattern.match(line) != None and not "==" in line:
kind = "default"
elif activePattern.match(line) != None and not "#" in line:
kind = "active"
elif historyPattern.match(line) != None and not "==" in line:
kind = "history"
if kind != "":
line = line.replace("#", "").replace("\n", "")
parm = line[:line.find("=")].strip()
val = line[line.find("=")+1:].strip()
return [kind, parm, val]
class SystemManager():
def getParmList(*args, **kwargs):
result = {"docklet": "", "container": ""}
for field in ["docklet", "container"]:
configFile = open(configPath[field])
lines = configFile.readlines()
configFile.close()
conf = {}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "default":
conf[lineparm] = {"val": lineval, "default": lineval, "history": []}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "active":
try:
conf[lineparm]["val"] = lineval
except:
conf[lineparm] = {"val": lineval, "default": lineval, "history": []}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "history":
conf[lineparm]["history"].append(lineval)
result[field] = [({'parm': parm, 'val': conf[parm]['val'],
'default': conf[parm]['default'], "history": conf[parm]['history']}) for parm in sorted(conf.keys())]
return result
# 1. def and not act 2. act and not def 3. def and act
# have def and act and hist
def modify(self, field, parm, val):
configFile = open(configPath[field])
lines = configFile.readlines()
configFile.close()
finish = False
for i in range(0, len(lines)):
line = lines[i]
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "active" and lineparm == parm:
lines[i] = "## " + parm + "=" + lineval + "\n"
lines.insert(i, parm + "=" + val + "\n")
if i == 0 or not parm in lines[i-1] or not "=" in lines[i-1]:
lines.insert(i, "# " + parm + "=" + lineval + "\n")
finish = True
break
if finish == False:
for i in range(0, len(lines)):
line = lines[i]
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "default" and parm == lineparm:
lines.insert(i+1, "## " + parm + "=" + lineval + "\n")
lines.insert(i+1, parm + "=" + val + "\n")
break
for i in range(0, len(lines)):
line = lines[i]
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "history" and parm == lineparm and val == lineval:
lines.pop(i)
break
configFile = open(configPath[field], "w")
for line in lines:
configFile.write(line)
configFile.close()
return [True, ""]
def clear(self, field, parm):
configFile = open(configPath[field])
lines = configFile.readlines()
configFile.close()
finish = False
for i in range(0, len(lines)):
line = lines[i]
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "history" and parm == lineparm:
lines[i] = ""
configFile = open(configPath[field], "w")
for line in lines:
configFile.write(line)
configFile.close()
return [True, ""]
def add(self, field, parm, val):
configFile = open(configPath[field], "a")
configFile.write("\n" + "# " + parm + "=" + val + "\n" + parm + "=" + val + "\n")
configFile.close()
return [True, ""]
def delete(self, field, parm):
configFile = open(configPath[field])
lines = configFile.readlines()
configFile.close()
for i in range(0, len(lines)):
line = lines[i]
if parm in line:
lines[i] = ""
configFile = open(configPath[field], "w")
for line in lines:
configFile.write(line)
configFile.close()
return [True, ""]
def reset_all(self, field):
configFile = open(configPath[field])
lines = configFile.readlines()
configFile.close()
conf = {}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "default":
conf[lineparm] = {"val": lineval, "default": lineval, "history": []}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "active":
try:
conf[lineparm]["val"] = lineval
except:
conf[lineparm] = {"val": lineval, "default": lineval, "history": []}
for line in lines:
[linekind, lineparm, lineval] = parse_line(line)
if linekind == "history":
conf[lineparm]["history"].append(lineval)
for i in range(0, len(lines)):
line = lines[i]
if activePattern.match(line) != None and not "#" in line:
segs = line.replace("\n", "").split("=")
lines[i] = segs[0].strip() + "=" + conf[segs[0].strip()]["default"] + "\n"
elif historyPattern.match(line) != None and not "==" in line:
lines[i] = ""
configFile = open(configPath[field], "w")
for line in lines:
configFile.write(line)
configFile.close()
return [True, ""]
#sysmgr = SystemManager()
#print(sysmgr.getParmList())

View File

@ -156,13 +156,13 @@ class userManager:
groups.append({'name':'root', 'quotas':{ 'cpu':'4', 'disk':'2000', 'data':'100', 'memory':'2000', 'image':'10', 'idletime':'24', 'vnode':'8' }})
groups.append({'name':'admin', 'quotas':{'cpu':'4', 'disk':'2000', 'data':'100', 'memory':'2000', 'image':'10', 'idletime':'24', 'vnode':'8'}})
groups.append({'name':'primary', 'quotas':{'cpu':'4', 'disk':'2000', 'data':'100', 'memory':'2000', 'image':'10', 'idletime':'24', 'vnode':'8'}})
groups.append({'name':'fundation', 'quotas':{'cpu':'4', 'disk':'2000', 'data':'100', 'memory':'2000', 'image':'10', 'idletime':'24', 'vnode':'8'}})
groups.append({'name':'foundation', 'quotas':{'cpu':'4', 'disk':'2000', 'data':'100', 'memory':'2000', 'image':'10', 'idletime':'24', 'vnode':'8'}})
groupfile.write(json.dumps(groups))
groupfile.close()
if not os.path.exists(fspath+"/global/sys/quotainfo"):
quotafile = open(fspath+"/global/sys/quotainfo",'w')
quotas = {}
quotas['default'] = 'fundation'
quotas['default'] = 'foundation'
quotas['quotainfo'] = []
quotas['quotainfo'].append({'name':'cpu', 'hint':'the cpu quota, number of cores, e.g. 4'})
quotas['quotainfo'].append({'name':'memory', 'hint':'the memory quota, number of MB , e.g. 4000'})
@ -309,16 +309,16 @@ class userManager:
'''
user = User.verify_auth_token(token)
return user
def set_nfs_quota_bygroup(self,groupname, quota):
if not data_quota == "YES":
if not data_quota == "True":
return
users = User.query.filter_by(user_group = groupname).all()
for user in users:
self.set_nfs_quota(user.username, quota)
def set_nfs_quota(self, username, quota):
if not data_quota == "YES":
if not data_quota == "True":
return
nfspath = "/users/%s/data" % username
try:
@ -326,7 +326,7 @@ class userManager:
sys_run(cmd.strip('"'))
except Exception as e:
logger.error(e)
@administration_required
def query(*args, **kwargs):
@ -607,8 +607,8 @@ class userManager:
if (user_modify.status == 'applying' and form.get('status', '') == 'normal'):
send_activated_email(user_modify.e_mail, user_modify.username)
user_modify.status = form.get('status', '')
if (form.get('Chpassword', '') == 'Yes'):
new_password = form.get('password','no_password')
if (form.get('password', '') != ''):
new_password = form.get('password','')
new_password = hashlib.sha512(new_password.encode('utf-8')).hexdigest()
user_modify.password = new_password
#self.chpassword(cur_user = user_modify, password = form.get('password','no_password'))
@ -629,8 +629,8 @@ class userManager:
cur_user = kwargs['cur_user']
cur_user.password = hashlib.sha512(kwargs['password'].encode('utf-8')).hexdigest()
def newuser(*args, **kwargs):
'''
Usage : newuser()

View File

@ -159,7 +159,7 @@ class VclusterMgr(object):
def deleteproxy(self, username, clustername):
[status, clusterinfo] = self.get_clusterinfo(clustername, username)
if 'proxy_ip' not in clusterinfo:
return [False, "proxy not exists"]
return [True, clusterinfo]
clusterinfo.pop('proxy_ip')
clusterfile = open(self.fspath + "/global/users/" + username + "/clusters/" + clustername, 'w')
clusterfile.write(json.dumps(clusterinfo))

View File

@ -120,6 +120,7 @@ class Worker(object):
self.rpcserver = ThreadXMLRPCServer((self.addr, int(self.port)), allow_none=True)
self.rpcserver.register_introspection_functions()
self.rpcserver.register_instance(Containers)
self.rpcserver.register_function(monitor.workerFetchInfo)
# register functions or instances to server for rpc
#self.rpcserver.register_function(function_name)
@ -199,10 +200,6 @@ if __name__ == '__main__':
sys.exit(1)
else:
logger.info("etcd connected")
# init collector to collect monitor infomation
collector = monitor.Collector(etcdaddr,clustername,ipaddr)
collector.start()
cpu_quota = env.getenv('CONTAINER_CPU')
logger.info ("using CONTAINER_CPU %s" % cpu_quota )
@ -213,9 +210,11 @@ if __name__ == '__main__':
worker_port = env.getenv('WORKER_PORT')
logger.info ("using WORKER_PORT %s" % worker_port )
con_collector = monitor.Container_Collector(etcdaddr, clustername,
ipaddr)
# init collector to collect monitor infomation
con_collector = monitor.Container_Collector()
con_collector.start()
collector = monitor.Collector()
collector.start()
logger.info("CPU and Memory usage monitor started")
logger.info("Starting worker")

View File

@ -1,7 +1,8 @@
#!/usr/bin/python3
import os, json, sys
sys.path.append("../src/")
from model import db, User
fspath="/opt/docklet"
def update_quotainfo():
@ -13,7 +14,7 @@ def update_quotainfo():
quotafile.close()
if type(quotas) is list:
new_quotas = {}
new_quotas['default'] = 'fundation'
new_quotas['default'] = 'foundation'
new_quotas['quotainfo'] = quotas
quotas = new_quotas
print("change the type of quotafile from list to dict")
@ -38,6 +39,58 @@ def update_quotainfo():
quotafile = open(fspath+"/global/sys/quotainfo", 'w')
quotafile.write(json.dumps(quotas))
quotafile.close()
if not os.path.exists(fspath+"/global/sys/quota"):
print("quota file not exists, please run docklet to init it")
return False
groupfile = open(fspath+"/global/sys/quota",'r')
groups = json.loads(groupfile.read())
groupfile.close()
for group in groups:
if 'cpu' not in group['quotas'].keys():
group['quotas']['cpu'] = "4"
if 'memory' not in group['quotas'].keys():
group['quotas']['memory'] = "2000"
if 'disk' not in group['quotas'].keys():
group['quotas']['disk'] = "2000"
if 'data' not in group['quotas'].keys():
group['quotas']['data'] = "100"
if 'image' not in group['quotas'].keys():
group['quotas']['image'] = "10"
if 'idletime' not in group['quotas'].keys():
group['quotas']['idletime'] = "24"
if 'vnode' not in group['quotas'].keys():
group['quotas']['vnode'] = "8"
print("quota updated")
groupfile = open(fspath+"/global/sys/quota",'w')
groupfile.write(json.dumps(groups))
groupfile.close()
def name_error():
quotafile = open(fspath+"/global/sys/quotainfo", 'r')
quotas = json.loads(quotafile.read())
quotafile.close()
if quotas['default'] == 'fundation':
quotas['default'] = 'foundation'
quotafile = open(fspath+"/global/sys/quotainfo",'w')
quotafile.write(json.dumps(quotas))
quotafile.close()
groupfile = open(fspath+"/global/sys/quota", 'r')
groups = json.loads(groupfile.read())
groupfile.close()
for group in groups:
if group['name'] == 'fundation':
group['name'] = 'foundation'
groupfile = open(fspath+"/global/sys/quota",'w')
groupfile.write(json.dumps(groups))
groupfile.close()
users = User.query.filter_by(user_group = 'fundation').all()
for user in users:
user.user_group = 'foundation'
db.session.commit()
def allquota():
try:
@ -98,4 +151,6 @@ def enable_gluster_quota():
if __name__ == '__main__':
update_quotainfo()
if "fix-name-error" in sys.argv:
name_error()
# enable_gluster_quota()

View File

@ -154,7 +154,7 @@
<th> {{ group['quotas'][quota['name']] }} </th>
{% endfor %}
<th><a class="btn btn-xs btn-info" data-toggle="modal" data-target="#ModifyGroupModal_{{ group['name'] }}">Edit</a>&nbsp;
{% if group['name'] in [ "root", "primary", "admin", "fundation" ] %}
{% if group['name'] in [ "root", "primary", "admin", "foundation" ] %}
<a class="btn btn-xs btn-default" href="javascript:void(0)">Delete</a>&nbsp;
{% else %}
<a class="btn btn-xs btn-danger" href="/group/delete/{{group['name']}}">Delete</a>&nbsp;
@ -201,7 +201,253 @@
</div>
</div>
</div>
</div>
{% for field in ["docklet", "container"] %}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<div class="box-header with-border">
{% if field == "docklet" %}
<h3 class="box-title">Docklet Config</h3>
{% else %}
<h3 class="box-title">Container Config</h3>
{% endif %}
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="box-body">
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#AddParmModal_{{field}}"><i class="fa fa-plus"></i>Add Parameter</button>
<div class="modal inmodal" id="AddParmModal_{{field}}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<i class="fa fa-laptop modal-icon"></i>
<h4 class="modal-title">Add Parameter</h4>
<small class="font-bold">Add a parameter to Docklet</small>
</div>
<form action="/system/add/" method="POST" >
<div class="modal-body">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="form-group">
<label>Parameter Name</label>
<input type="text" placeholder="Enter Parameter Name" class="form-control" name="parm" value="" />
</div>
<div class="form-group">
<label>Default value</label>
<input type="text" placeholder="Enter Default Value" class="form-control" name="val" value="" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-white" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#ResetAllModal_{{field}}"><i class="fa fa-plus"></i> Reset All to Default</button>
<div class="modal inmodal" id="ResetAllModal_{{field}}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<i class="fa fa-laptop modal-icon"></i>
<h4>Sure to reset all parameters to default ?</h4>
</div>
<form action="/system/resetall/" method="POST">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Yes</button>
<button type="button" class="btn btn-white" data-dismiss="modal">No</button>
</div>
</form>
</div>
</div>
</div>
<div class="table table-responsive">
<table id="myGroupTable" class="table table-striped table-bordered">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
<th>History (Click to Reuse)</th>
<th>Default</th>
<th>Command</th>
</tr>
</thead>
<tbody>
{% for parm in parms[field] %}
<tr>
<th>{{ parm["parm"]|truncate(20) }}</th>
<th>{{ parm["val"]|truncate(20) }}</th>
<th>
{% for history in parm["history"] %}
<a class="btn btn-xs btn-default" data-toggle="modal" data-target="#UseHistoryModal_{{field}}_{{ parm["parm"]|replace(".","") }}_{{ loop.indexo }}">{{ history|truncate(20) }}</a>
<div class="modal inmodal" id="UseHistoryModal_{{field}}_{{ parm["parm"]|replace(".","") }}_{{ loop.indexo }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<i class="fa fa-laptop modal-icon"></i>
<h4>Sure to set <strong> {{ parm["parm"] }} </strong>to <strong>{{ history }} </strong>?</h4>
</div>
<form action="/system/modify/" method="POST">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-body" style="display:none">
<div class="form-group">
<label>Parameter</label>
<input type="text" placeholder="Enter Parameter" class="form-control" name="parm" value="{{ parm['parm'] }}" readonly="true" />
</div>
<div class="form-group">
<label>Value</label>
<input type="text" placeholder="Enter Value" class="form-control" name="val" value="{{ history }}" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Yes</button>
<button type="button" class="btn btn-white" data-dismiss="modal">No</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
</th>
<th><a class="btn btn-xs btn-default" data-toggle="modal" data-target="#UseDefaultModal_{{field}}_{{ parm["parm"]|replace(".","") }}">{{ parm["default"]|truncate(20) }}</a></th>
<th>
<a class="btn btn-xs btn-info" data-toggle="modal" data-target="#ModifyParmModal_{{field}}_{{ parm["parm"]|replace(".","") }}">Edit</a>&nbsp;
<a class="btn btn-xs btn-default" data-toggle="modal" data-target="#ClearHistoryModal_{{field}}_{{ parm["parm"]|replace(".","") }}">Clear History</a>&nbsp;
<a class="btn btn-xs btn-danger" data-toggle="modal" data-target="#DeleteParmModal_{{field}}_{{ parm["parm"]|replace(".","")}}">Delete</a>
</th>
<div class="modal inmodal" id="ModifyParmModal_{{field}}_{{ parm["parm"]|replace(".","") }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<i class="fa fa-laptop modal-icon"></i>
<h4 class="modal-title">Modify Parameter</h4>
<small class="font-bold">Modify a parameter in Docklet</small>
</div>
<form action="/system/modify/" method="POST" >
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-body">
<div class="form-group">
<label>Parameter</label>
<input type="text" placeholder="Enter Parameter" class="form-control" name="parm" value="{{ parm['parm'] }}" readonly="true" />
</div>
<div class="form-group">
<label>Value</label>
<input type="text" placeholder="Enter Value" class="form-control" name="val" value="{{ parm['val'] }}" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-white" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal inmodal" id="UseDefaultModal_{{field}}_{{ parm["parm"]|replace(".","") }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<i class="fa fa-laptop modal-icon"></i>
<h4>Sure to set <strong> {{ parm["parm"] }} </strong> to <strong> {{ parm["default"] }} </strong> ?</h4>
</div>
<form action="/system/modify/" method="POST">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-body" style="display:none">
<div class="form-group">
<label>Parameter</label>
<input type="text" placeholder="Enter Parameter" class="form-control" name="parm" value="{{ parm['parm'] }}" readonly="true" />
</div>
<div class="form-group">
<label>Value</label>
<input type="text" placeholder="Enter Value" class="form-control" name="val" value="{{ parm["default"] }}" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Yes</button>
<button type="button" class="btn btn-white" data-dismiss="modal">No</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal inmodal" id="ClearHistoryModal_{{field}}_{{ parm["parm"]|replace(".","") }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<i class="fa fa-laptop modal-icon"></i>
<h4>Sure to clear history for <strong> {{ parm["parm"] }} </strong> ?</h4>
</div>
<form action="/system/clear_history/" method="POST">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-body" style="display:none">
<div class="form-group">
<label>Parameter</label>
<input type="text" placeholder="Enter Parameter" class="form-control" name="parm" value="{{ parm['parm'] }}" readonly="true" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Yes</button>
<button type="button" class="btn btn-white" data-dismiss="modal">No</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal inmodal" id="DeleteParmModal_{{field}}_{{parm["parm"]|replace(".","") }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<i class="fa fa-laptop modal-icon"></i>
<h4>Sure to delete the parameter <strong> {{ parm["parm"] }} </strong> ?</h4>
</div>
<form action="/system/delete/" method="POST">
<div style="display:none">
<input type="text" placeholder="" class="" name="field" value={{field}} />
</div>
<div class="modal-body" style="display:none">
<div class="form-group">
<label>Parameter</label>
<input type="text" placeholder="Enter Parameter" class="form-control" name="parm" value="{{ parm['parm'] }}" readonly="true" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Yes</button>
<button type="button" class="btn btn-white" data-dismiss="modal">No</button>
</div>
</form>
</div>
</div>
</div>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}

View File

@ -1,329 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Docklet | Dashboard</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="shortcut icon" href="/static/img/favicon.ico">
<link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<!-- Ionicons -->
<link href="//cdn.bootcss.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet">
<link href="//cdn.bootcss.com/animate.css/3.5.1/animate.min.css" rel="stylesheet">
<link href="//cdn.bootcss.com/toastr.js/latest/css/toastr.min.css" rel="stylesheet">
<!-- Theme style -->
<link rel="stylesheet" href="/static/dist/css/AdminLTE.min.css">
<link rel="stylesheet" href="/static/dist/css/skins/skin-blue.min.css">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<!-- Main Header -->
<header class="main-header">
<!-- Logo -->
<a href="" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"></span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>Docklet</b></span>
</a>
<!-- Header Navbar -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<!-- Navbar Right Menu -->
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- Messages: style can be found in dropdown.less-->
<li class="dropdown user user-menu">
<!-- Menu Toggle Button -->
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<!-- The user image in the navbar-->
<img src="{{ mysession['avatar'] }}" class="user-image" alt="User Image">
<!-- hidden-xs hides the username on small devices so only the image appears. -->
<span class="hidden-xs">{{ mysession['nickname'] }}</span>
</a>
<ul class="dropdown-menu">
<!-- The user image in the menu -->
<li class="user-header">
<img src="{{ mysession['avatar'] }}" class="img-circle" alt="User Image">
<p>
{{ mysession['nickname'] }}
<small>{{ mysession['description'] }}</small>
</p>
</li>
<!-- Menu Body -->
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-left">
Profile
</div>
<div class="pull-right">
Sign out
</div>
</li>
</ul>
</li>
<!-- Control Sidebar Toggle Button -->
<li>
<a href="/document/" target="_blank"><strong>Help</strong></a>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel">
<div class="pull-left image">
<img src="{{ mysession['avatar'] }}" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{{ mysession['nickname'] }}</p>
<!-- Status -->
<i class="fa fa-circle text-success"></i> {{ mysession['status']}}
</div>
</div>
<!-- Sidebar Menu -->
<ul class="sidebar-menu">
<li class="header">USER OPERATIONS</li>
<!-- Optionally, you can add icons to the links -->
<li class="active" id="nav_Dashboard">
<a href="javascript:void(0)"><i class="fa fa-th-large"></i> <span class="nav-label">Dashboard</span></a>
</li>
<li id="nav_Config">
<a href="javascript:void(0)"><i class="fa fa-gears"></i> <span class="nav-label">Config</span></a>
</li>
<li id="nav_Status">
<a href="javascript:void(0)"><i class="fa fa-bar-chart"></i> <span class="nav-label">Status</span></a>
</li>
{% if mysession['usergroup'] == 'root' or mysession['usergroup'] == 'admin'%}
<li class="header">ADMIN OPERATIONS</li>
<li id="nav_Hosts">
<i class="fa fa-sitemap"></i> <span class="nav-label">Hosts</span>
</li>
<li id="user_List">
<i class="fa fa-users"></i> <span class="nav-label">Users</span>
</li>
<li id="admin">
<i class="fa fa-gears"></i> <span class="nav-label">Admin</span>
</li>
{% endif %}
</ul>
<!-- /.sidebar-menu -->
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
<strong>Dashboard</strong>
</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-dashboard"></i>Home
</li>
<li class="active">
<strong>Dashboard</strong>
</li>
</ol>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-lg-12">
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Workspaces</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
</div>
</div>
<div class="box-body">
<p>
<button type="button" class="btn btn-primary btn-sm"><i class="fa fa-plus"></i> Add Workspace</button>
</p>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
<th>Operation</th>
<th>WorkSpace</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>guest-1-0</td>
<td><div class="text-success"><i class="fa fa-play"></i> Running</div></td>
<td>
<button type="button" class="btn btn-xs btn-warning"> &nbsp;Stop&nbsp;&nbsp; </button>
<button type="button" class="btn btn-xs btn-default"> Delete </button>
</td>
<td>
<a href="/go/guest/guestspace" target="_blank"><button type="button" class="btn btn-xs btn-success">&nbsp;&nbsp;&nbsp;Go&nbsp;&nbsp;&nbsp;</button></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<!-- Main Footer -->
<footer class="main-footer">
<!-- To the right -->
<div class="pull-right hidden-xs">
<i>Docklet</i> 0.25
</div>
<!-- Default to the left -->
<strong>Copyright</strong>&copy;&nbsp;2016 <a href="http://docklet.unias.org">UniAS</a>@<a href="http://www.sei.pku.edu.cn"> SEI, PKU</a>
</footer>
</div>
<!-- ./wrapper -->
<!-- REQUIRED JS SCRIPTS -->
<!-- jQuery 2.2.1 -->
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
<!-- Bootstrap 3.3.5 -->
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<!-- AdminLTE App -->
<script src="/static/dist/js/app.min.js"></script>
<script src="//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js"></script>
<script src="//cdn.bootcss.com/jQuery-slimScroll/1.3.7/jquery.slimscroll.min.js"></script>
<script src="//cdn.bootcss.com/toastr.js/latest/js/toastr.min.js"></script>
<script type="text/javascript">
var pathname = window.location.pathname;
pathname = pathname.split(/\//);
if(pathname[1] != 'dashboard')
$("#nav_Dashboard").removeClass("active");
if(pathname[1] == 'vclusters')
$("#nav_Status").addClass("active");
else if(pathname[1] == 'hosts')
$("#nav_Hosts").addClass("active");
else if(pathname[1] == 'config')
$("#nav_Config").addClass("active");
else if(pathname[1] == 'user')
{
if (pathname[2] == 'list')
$("#user_List").addClass("active");
}
</script>
{% if mysession['status'] == 'init' %}
<script type="text/javascript">
$(document).ready(function() {
toastr.options = {
"closeButton": false,
"debug": true,
"progressBar": false,
"preventDuplicates": false,
"positionClass": "toast-top-left",
"onclick": function(){
window.location.href="/activate/";
},
"showDuration": "0",
"hideDuration": "0",
"timeOut": "0",
"extendedTimeOut": "0",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
toastr.error("You are not activated. Click this notification to activate your account.");
});
</script>
{% endif %}
{% if mysession['status'] == 'applying' %}
<script type="text/javascript">
$(document).ready(function() {
toastr.options = {
"closeButton": false,
"debug": true,
"progressBar": false,
"preventDuplicates": false,
"positionClass": "toast-top-left",
"onclick": function(){
},
"showDuration": "0",
"hideDuration": "0",
"timeOut": "0",
"extendedTimeOut": "0",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
toastr.warning("You applying is being checked.");
});
</script>
{% endif %}
</body>
</html>

View File

@ -132,16 +132,10 @@
<label>Telephone Number</label>
<input type = "text" placeholder="Enter Telephone Number" class="form-control" name="tel" id="mTel">
</div>
<div class="form-group">
<label>Change Password?</label>
<select class="form-control" name="Chpassword" id="mChpassword">
<option>Yes</option>
<option>No</option>
</select>
</div>
<div class="form-group">
<label>Password</label>
<input type = "text" placeholder="Enter Password" class="form-control" name="password" id="mPassword">
<input type="password" placeholder="Enter Password" class="form-control" name="password" id="mPassword">
</div>
<div class="form-group">
@ -249,8 +243,6 @@
$("#mDepartment").val(result.department);
$("#mStudentNumber").val(result.student_number);
$("#mTel").val(result.tel);
$("#mChpassword").val('No');
$("#mPassword").val(result.password);
$("#mStatus").val(result.status);
$("#mUserGroup").val(result.group);
$("#mAuthMethod").val(result.auth_method);

View File

@ -101,12 +101,6 @@ def activate():
def dashboard():
return dashboardView.as_view()
@app.route("/dashboard_guest/", methods=['GET'])
def dashboard_guest():
resp = make_response(dashboard_guestView.as_view())
resp.set_cookie('guest-cookie', cookie_tool.generate_cookie('guest', app.secret_key))
return resp
@app.route("/document/", methods=['GET'])
def redirect_dochome():
return redirect("http://docklet.unias.org/userguide")
@ -354,6 +348,30 @@ def userinfo():
def userquery():
return userqueryView.as_view()
@app.route("/system/modify/", methods=['POST'])
@administration_required
def systemmodify():
return systemmodifyView.as_view()
@app.route("/system/clear_history/", methods=['POST'])
@administration_required
def systemclearhistory():
return systemclearView.as_view()
@app.route("/system/add/", methods=['POST'])
@administration_required
def systemadd():
return systemaddView.as_view()
@app.route("/system/delete/", methods=['POST'])
@administration_required
def systemdelete():
return systemdeleteView.as_view()
@app.route("/system/resetall/", methods=['POST'])
@administration_required
def systemresetall():
return systemresetallView.as_view()
@app.route("/admin/", methods=['GET', 'POST'])
@administration_required

44
web/webViews/admin.py Executable file → Normal file
View File

@ -13,7 +13,8 @@ class adminView(normalView):
groups = result["groups"]
quotas = result["quotas"]
defaultgroup = result["default"]
return self.render(self.template_path, groups = groups, quotas = quotas, defaultgroup = defaultgroup)
parms = dockletRequest.post('/system/parmList/')
return self.render(self.template_path, groups = groups, quotas = quotas, defaultgroup = defaultgroup, parms = parms)
class groupaddView(normalView):
@classmethod
@ -21,6 +22,36 @@ class groupaddView(normalView):
dockletRequest.post('/user/groupadd/', request.form)
return redirect('/admin/')
class systemmodifyView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/modify/', request.form)
return redirect('/admin/')
class systemclearView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/clear_history/', request.form)
return redirect('/admin/')
class systemaddView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/add/', request.form)
return redirect('/admin/')
class systemdeleteView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/delete/', request.form)
return redirect('/admin/')
class systemresetallView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/reset_all/', request.form)
return redirect('/admin/')
class quotaaddView(normalView):
@classmethod
def post(self):
@ -45,3 +76,14 @@ class groupdelView(normalView):
@classmethod
def get(self):
return self.post()
class chparmView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/chparm/', request.form)
class historydelView(normalView):
@classmethod
def post(self):
dockletRequest.post('/system/historydel/', request.form)
return redirect('/admin/')

View File

@ -34,20 +34,3 @@ class dashboardView(normalView):
@classmethod
def post(self):
return self.get()
class dashboard_guestView(normalView):
template_path = "dashboard_guest.html"
@classmethod
def get(self):
mysession = {}
mysession['avatar'] = "/static/avatar/default.png"
mysession['nickname'] = "guest"
mysession['description'] = "you are a guest"
mysession['status'] = "guest"
mysession['usergroup'] = "normal"
return render_template(self.template_path, mysession = mysession)
@classmethod
def post(self):
return self.get()