Merge remote-tracking branch 'origin/master'
authorMaciej Tronowski <mtro@man.poznan.pl>
Wed, 4 Mar 2015 09:22:22 +0000 (10:22 +0100)
committerMaciej Tronowski <mtro@man.poznan.pl>
Wed, 4 Mar 2015 09:22:22 +0000 (10:22 +0100)
# Conflicts:
# qcg/fields.py
# qcg/forms.py
# qcg/templates/qcg/job_new.html

fabfile.py [new file with mode: 0644]
qcg/fields.py [new file with mode: 0644]
qcg/forms.py
qcg/templates/qcg/job_new.html
qcg/templates/qcg/time_range_field.html [new file with mode: 0644]
requirements_dev.txt
uwsgi.ini [new file with mode: 0644]

diff --git a/fabfile.py b/fabfile.py
new file mode 100644 (file)
index 0000000..1605a4a
--- /dev/null
@@ -0,0 +1,63 @@
+from fabric.api import *
+
+env.user = 'portal'
+env.hosts = ['agave5.man.poznan.pl']
+
+
+#################################################
+# server management (remote)
+#################################################
+
+PID_FILE = '/var/run/uwsgi.pid'
+
+
+@task
+def start():
+    with cd('qcg-portal'):
+        run("uwsgi --ini uwsgi.ini")
+
+
+@task
+def stop():
+    run("uwsgi --stop " + PID_FILE)
+
+
+@task
+def restart():
+    run("uwsgi --reload " + PID_FILE)
+
+
+def is_running():
+    with settings(hide('everything'), warn_only=True):
+        return not run("ps -p `cat {}`".format(PID_FILE), shell_escape=False).failed
+
+
+@task
+def status():
+    print "Server is",
+    print "running." if is_running() else "not running."
+
+
+#################################################
+# deployment (remote)
+#################################################
+
+@task
+def git_status():
+    with cd('qcg-portal'):
+        run("git status")
+
+
+@task
+def deploy(load_ex_data=False):
+    with cd('qcg-portal'):
+        run("git pull")
+
+        run("pip install -r requirements.txt")
+        run("python manage.py migrate qcg")
+        run("python manage.py collectstatic --noinput")
+
+        if is_running:
+            run("uwsgi --reload " + PID_FILE)
+        else:
+            run("uwsgi --ini uwsgi.ini")
\ No newline at end of file
diff --git a/qcg/fields.py b/qcg/fields.py
new file mode 100644 (file)
index 0000000..e067f46
--- /dev/null
@@ -0,0 +1,55 @@
+# coding=utf-8
+from datetime import timedelta
+from django import forms
+from django.core.exceptions import ValidationError
+
+
+class TimeRangeWidget(forms.MultiWidget):
+    def decompress(self, value):
+        if not value:
+            return [0, TimeRangeField.SECONDS]
+
+        seconds = value.seconds + 86400 * value.days
+
+        if seconds % 60 != 0:
+            return [seconds, TimeRangeField.SECONDS]
+        elif seconds % 3600 != 0:
+            return [seconds / 60, TimeRangeField.MINUTES]
+        elif seconds % 86400 != 0:
+            return [seconds / 3600, TimeRangeField.HOURS]
+        else:
+            return [seconds / 86400, TimeRangeField.DAYS]
+
+
+class TimeRangeField(forms.MultiValueField):
+    SECONDS, MINUTES, HOURS, DAYS = range(4)
+    UNIT_CHOICES = (
+        (SECONDS, u"Sekund"),
+        (MINUTES, u"Minut"),
+        (HOURS, u"Godzin"),
+        (DAYS, u"Dni"),
+    )
+
+    value = forms.IntegerField(min_value=0)
+    unit = forms.ChoiceField(choices=UNIT_CHOICES)
+
+    widget = TimeRangeWidget(widgets=(value.widget, unit.widget))
+
+    def __init__(self, *args, **kwargs):
+        super(TimeRangeField, self).__init__((self.value, self.unit), *args, **kwargs)
+
+    def compress(self, data_list):
+        try:
+            unit = int(data_list[1])
+            if unit == self.SECONDS:
+                return timedelta(seconds=data_list[0])
+            elif unit == self.MINUTES:
+                return timedelta(minutes=data_list[0])
+            elif unit == self.HOURS:
+                return timedelta(hours=data_list[0])
+            elif unit == self.DAYS:
+                return timedelta(days=data_list[0])
+            else:
+                raise ValidationError(u"NieprawidÅ‚owa jednostka")
+        except ValueError:
+            raise ValidationError(u"NieprawidÅ‚owa jednostka")
index 7c715d9..b2ad790 100644 (file)
@@ -4,6 +4,7 @@ from django.core.validators import RegexValidator
 from django.template.defaultfilters import capfirst
 from pyqcg.utils import TaskStatus
 
+from qcg.fields import TimeRangeField
 from qcg.models import Task, Allocation
 
 
index ad5652f..d5b7344 100644 (file)
                 {% bootstrap_field form.modules layout="horizontal" form_group_class="form-group collapse" %}
                 {% bootstrap_field form.procs layout="horizontal" %}
                 {% bootstrap_field form.nodes layout="horizontal" form_group_class="form-group collapse" %}
-                {% bootstrap_field form.wall_time layout="horizontal" %}
+                {% include 'qcg/time_range_field.html' with field_name='wall_time' field=form.wall_time %}
                 {% bootstrap_field form.memory layout="horizontal" form_group_class="form-group collapse" %}
                 {% bootstrap_field form.memory_per_slot layout="horizontal" form_group_class="form-group collapse" %}
                 {% bootstrap_field form.reservation layout="horizontal" form_group_class="form-group collapse" %}
diff --git a/qcg/templates/qcg/time_range_field.html b/qcg/templates/qcg/time_range_field.html
new file mode 100644 (file)
index 0000000..c9570c5
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="form-group">
+    <label class="col-sm-3 col-md-4 control-label" for="id_{{ field_name }}_0">{{ field.label }}</label>
+    <div class="col-sm-7 col-md-4">
+        <input class="form-control" id="id_{{ field_name }}_0" min="0" name="{{ field_name }}_0" value="{{ field.value.0 }}" type="number">
+    </div>
+    <div class="col-sm-2">
+        <select class="form-control" id="id_{{ field_name }}_1" name="{{ field_name }}_1">
+            <option value="0"{% if field.value.1 == 0 %} selected{% endif %}>Sekund</option>
+            <option value="1"{% if field.value.1 == 1 %} selected{% endif %}>Minut</option>
+            <option value="2"{% if field.value.1 == 2 %} selected{% endif %}>Godzin</option>
+            <option value="3"{% if field.value.1 == 3 %} selected{% endif %}>Dni</option>
+        </select>
+    </div>
+</div>
\ No newline at end of file
index 64eaf2e..91510c3 100644 (file)
@@ -1,3 +1,4 @@
 -r requirements.txt
 django-sslserver
-django-debug-toolbar
\ No newline at end of file
+django-debug-toolbar
+Fabric
\ No newline at end of file
diff --git a/uwsgi.ini b/uwsgi.ini
new file mode 100644 (file)
index 0000000..52256df
--- /dev/null
+++ b/uwsgi.ini
@@ -0,0 +1,37 @@
+[uwsgi]
+base-dir = /home/portal/qcg-portal
+
+uid = portal
+gid = portal
+
+cache2 = name=ssl,items=1000,keysize=128,blocksize=4096
+ssl-sessions-use-cache = ssl
+ssl-sessions-timeout = 300
+
+shared-socket = :80
+shared-socket = :443
+
+http-to-https = =0
+https = =1,%(base-dir)/certs/cert.crt,%(base-dir)/certs/cert.key,EECDH+aRSA+AES:EDH+aRSA+AES:kRSA+AES:@STRENGTH
+
+add-header = Strict-Transport-Security: max-age=31536000
+
+workers = 6
+enable-threads = True
+; this do not play well with ssl cache...
+; thunder-lock = True
+
+master = True
+vacuum = True
+umask = 022
+pidfile = /var/run/uwsgi.pid
+logfile-chmod = 644
+daemonize = %(base-dir)/uwsgi.master.log
+stats = %(base-dir)/uwsgi.stats.socket
+
+chdir = %(base-dir)
+virtualenv = /home/portal/venv
+module = plgng.wsgi:application
+env = DJANGO_SETTINGS_MODULE=plgng.settings_prod
+
+static-map = /static=%(base-dir)/server-static