Add administration features for administrators to configure Docklet

This commit is contained in:
Fan Shixiong 2016-05-15 07:12:06 -07:00
parent 4821afe3fb
commit 4400282343
5 changed files with 559 additions and 1 deletions

View File

@ -25,6 +25,7 @@ import nodemgr, vclustermgr, etcdlib, network, imagemgr
import userManager
import monitor
import threading
import sysmgr
#default EXTERNAL_LOGIN=False
external_login = env.getenv('EXTERNAL_LOGIN')
@ -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()

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

@ -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

@ -348,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/')