diff --git a/src/master/bugreporter.py b/src/master/bugreporter.py new file mode 100644 index 0000000..de1c1c0 --- /dev/null +++ b/src/master/bugreporter.py @@ -0,0 +1,53 @@ +from master.settings import settings +import smtplib +from utils.log import logger +from utils import env +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from email.header import Header +from datetime import datetime +import json + +def send_bug_mail(username, bugmessage): + #admin_email_address = env.getenv('ADMIN_EMAIL_ADDRESS') + nulladdr = ['\'\'', '\"\"', ''] + email_from_address = settings.get('EMAIL_FROM_ADDRESS') + admin_email_address = settings.get('ADMIN_EMAIL_ADDRESS') + logger.info("receive bug from %s: %s" % (username, bugmessage)) + if (email_from_address in nulladdr or admin_email_address in nulladdr): + return {'success': 'false'} + #text = 'Dear '+ username + ':\n' + ' Your account in docklet has been activated' + text = '

Dear '+ 'admin' + ':

' + text += '''

      A bug has been report by %s.

+
+   %s   +
+

      Please check it !

+

+

Docklet Team, SEI, PKU

+ ''' % (username, bugmessage) + text += '

'+ str(datetime.utcnow()) + '

' + text += '' + subject = 'A bug of Docklet has been reported' + if admin_email_address[0] == '"': + admins_addr = admin_email_address[1:-1].split(" ") + else: + admins_addr = admin_email_address.split(" ") + alladdr="" + for addr in admins_addr: + alladdr = alladdr+addr+", " + alladdr=alladdr[:-2] + msg = MIMEMultipart() + textmsg = MIMEText(text,'html','utf-8') + msg['Subject'] = Header(subject, 'utf-8') + msg['From'] = email_from_address + msg['To'] = alladdr + msg.attach(textmsg) + s = smtplib.SMTP() + s.connect() + try: + s.sendmail(email_from_address, admins_addr, msg.as_string()) + except Exception as e: + logger.error(e) + s.close() + return {'success':'true'} diff --git a/src/master/vclustermgr.py b/src/master/vclustermgr.py index 54ceea1..caef4ad 100755 --- a/src/master/vclustermgr.py +++ b/src/master/vclustermgr.py @@ -116,6 +116,8 @@ class VclusterMgr(object): def create_cluster(self, clustername, username, image, user_info, setting): if self.is_cluster(clustername, username): return [False, "cluster:%s already exists" % clustername] + if self.imgmgr.get_image_size(image) + 100 > int(setting["disk"]): + return [False, "the size of disk is not big enough for the image"] clustersize = int(self.defaultsize) logger.info ("starting cluster %s with %d containers for %s" % (clustername, int(clustersize), username)) workers = self.nodemgr.get_base_nodeips() @@ -202,6 +204,8 @@ class VclusterMgr(object): def scale_out_cluster(self,clustername,username, image,user_info, setting): if not self.is_cluster(clustername,username): return [False, "cluster:%s not found" % clustername] + if self.imgmgr.get_image_size(image) + 100 > int(setting["disk"]): + return [False, "the size of disk is not big enough for the image"] workers = self.nodemgr.get_base_nodeips() if (len(workers) == 0): logger.warning("no workers to start containers, scale out failed") diff --git a/src/utils/imagemgr.py b/src/utils/imagemgr.py index fcb8873..889023a 100755 --- a/src/utils/imagemgr.py +++ b/src/utils/imagemgr.py @@ -380,6 +380,29 @@ class ImageMgr(): return "" return image.description + def get_image_size(self, image): + imagename = image['name'] + imagetype = image['type'] + imageowner = image['owner'] + if imagename == "base" and imagetype == "base": + return 0 + if imagetype == "private": + imgpath = self.imgpath + "private/" + user + "/" + else: + imgpath = self.imgpath + "public/" + imageowner + "/" + return os.stat(os.path.join(imgpath, imagename)).st_size // (1024*1024) + + + def format_size(self, size_in_byte): + if size_in_byte < 1024: + return str(size_in_byte) + "B" + elif size_in_byte < 1024*1024: + return str(size_in_byte//1024) + "KB" + elif size_in_byte < 1024*1024*1024: + return str(size_in_byte//(1024*1024)) + "MB" + else: + return str(size_in_byte//(1024*1024*1024)) + "GB" + def list_images(self,user): images = {} images["private"] = [] @@ -398,6 +421,9 @@ class ImageMgr(): [time, description] = self.get_image_info(user, imagename, "private") fimage["time"] = time fimage["description"] = description + fimage["size"] = os.stat(os.path.join(imgpath, image)).st_size + fimage["size_format"] = self.format_size(fimage["size"]) + fimage["size_in_mb"] = fimage["size"] // (1024*1024) images["private"].append(fimage) except Exception as e: logger.error(e) @@ -423,6 +449,9 @@ class ImageMgr(): [time, description] = self.get_image_info(public_user, imagename, "public") fimage["time"] = time fimage["description"] = description + fimage["size"] = os.stat(os.path.join(imgpath, image)).st_size + fimage["size_format"] = self.format_size(fimage["size"]) + fimage["size_in_mb"] = fimage["size"] // (1024*1024) images["public"][public_user].append(fimage) except Exception as e: logger.error(e) diff --git a/user/user.py b/user/user.py index 9a38c7f..522aa93 100755 --- a/user/user.py +++ b/user/user.py @@ -34,6 +34,7 @@ from utils.model import User,db from httplib2 import Http from urllib.parse import urlencode from master.settings import settings +from master.bugreporter import send_bug_mail external_login = env.getenv('EXTERNAL_LOGIN') if(external_login == 'TRUE'): @@ -472,6 +473,13 @@ def query_self_notifications_infos(cur_user, user, form): result = G_notificationmgr.query_self_notifications_infos(cur_user=cur_user, form=form) return json.dumps(result) +@app.route("/bug/report/", methods=['POST']) +@login_required +def report_bug(cur_user, user, form): + logger.info("handle request: bug/report") + result = send_bug_mail(user, form.get("bugmessage", None)) + return json.dumps(result) + @app.route("/billing/beans/", methods=['POST']) @auth_key_required def billing_beans(): diff --git a/web/templates/addCluster.html b/web/templates/addCluster.html index 8d24c24..de6ac2e 100644 --- a/web/templates/addCluster.html +++ b/web/templates/addCluster.html @@ -64,6 +64,7 @@ ImageName Type Owner + Size Description Choose @@ -73,16 +74,18 @@ base public docklet + -- A base image for you -
+
{% for image in images['private'] %} {{image['name']}} private {{user}} - {{image['description']}} -
+ {{image['size_format']}} + {{image['description']}} +
{% endfor %} {% for p_user,p_images in images['public'].items() %} @@ -91,8 +94,9 @@ {{image['name']}} public {{p_user}} - {{image['description']}} -
+ {{image['size_format']}} + {{image['description']}} +
{% endfor %} {% endfor %} @@ -125,7 +129,7 @@
-
{{usage['disk']}} MB/{{quota['disk']}}MB +
{{usage['disk']}} MB/{{quota['disk']}}MB (min value is the size of image + 100)
@@ -167,6 +171,12 @@ + - - - + + {% endblock %} - diff --git a/web/templates/opsuccess.html b/web/templates/opsuccess.html index f2bbc84..4783ae0 100644 --- a/web/templates/opsuccess.html +++ b/web/templates/opsuccess.html @@ -19,6 +19,9 @@

SUCCESS



+
{{message}}
+
+
Click Here Back To The Workspace {% endblock %} diff --git a/web/web.py b/web/web.py index 96fa6d7..bb9ad61 100755 --- a/web/web.py +++ b/web/web.py @@ -36,6 +36,7 @@ from webViews.admin import * from webViews.monitor import * from webViews.beansapplication import * from webViews.cloud import * +from webViews.reportbug import * from webViews.authenticate.auth import login_required, administration_required,activated_required from webViews.authenticate.register import registerView from webViews.authenticate.login import loginView, logoutView @@ -113,13 +114,19 @@ def dashboard(): @app.route("/document/", methods=['GET']) def redirect_dochome(): - return redirect("http://unias.github.io/docklet/userguide") + return redirect("https://unias.github.io/docklet/userguide/") @app.route("/config/", methods=['GET']) @login_required def config(): return configView.as_view() +@app.route("/bug/report/", methods=['POST']) +@login_required +def reportBug(): + reportBugView.bugmessage = request.form['bugmessage'] + return reportBugView.as_view() + @app.route("/batch_jobs/", methods=['GET']) @login_required def batch_job(): @@ -632,7 +639,6 @@ def adminpage(): def updatesettings(): return updatesettingsView.as_view() - @app.route('/index/', methods=['GET']) def jupyter_control(): return redirect('/dashboard/') diff --git a/web/webViews/dockletrequest.py b/web/webViews/dockletrequest.py index 8a48790..1d6084d 100644 --- a/web/webViews/dockletrequest.py +++ b/web/webViews/dockletrequest.py @@ -38,7 +38,8 @@ class dockletRequest(): 'user', 'beans', 'notification', - 'settings' + 'settings', + 'bug' } if ":" not in endpoint: endpoint = "http://"+endpoint+":"+master_port diff --git a/web/webViews/reportbug.py b/web/webViews/reportbug.py new file mode 100644 index 0000000..02669d7 --- /dev/null +++ b/web/webViews/reportbug.py @@ -0,0 +1,16 @@ +from flask import session,render_template,request,redirect +from webViews.view import normalView +from webViews.dockletrequest import dockletRequest + + +class reportBugView(normalView): + template_path = "opsuccess.html" + + @classmethod + def get(self): + dockletRequest.post("/bug/report/", {'bugmessage': self.bugmessage}) + return self.render(self.template_path, message="Thank You!") + + @classmethod + def post(self): + return self.get()