Commit 3cf01a3e by Dudás Ádám

Merge branch 'master' into dev-siliconbrain

Conflicts:
	cloud/urls.py
	firewall/fw.py
parents 3e045de3 fe568466
SHELL := /bin/bash
default: migrate collectstatic mo restart
pulldef: pull default
pull:
git pull
po:
for i in */; do cd $$i; ../manage.py makemessages --all || true; cd ..; done
for i in */; do cd $$i; ../manage.py makemessages --all -d djangojs || true; cd ..; done
migrate:
./manage.py migrate
collectstatic:
./manage.py collectstatic --noinput
mo:
for i in */locale/*/*/*.po; do echo -ne "$$i:\t"; msgfmt --statistics $$i;done
for i in */; do cd $$i; ls locale &>/dev/null && ../manage.py compilemessages || true; cd ..; done
restart:
sudo /etc/init.d/apache2 reload
......@@ -33,7 +33,7 @@ TIME_ZONE = 'Europe/Budapest'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'hu-hu'
LANGUAGE_CODE = 'hu'
SITE_ID = 1
......@@ -96,6 +96,7 @@ TEMPLATE_LOADERS = (
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.transaction.TransactionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
......@@ -130,6 +131,7 @@ INSTALLED_APPS = (
'south',
'djcelery',
'kombu.transport.django',
'django_extensions',
)
# A sample logging configuration. The only tangible logging
......
......@@ -7,6 +7,10 @@ import one.views
import firewall.views
#import store.views
js_info_dict = {
'packages': ('one', ),
}
urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
......@@ -14,36 +18,79 @@ urlpatterns = patterns('',
url(r'^login/$', 'school.views.login', name='login'),
url(r'^logout/$', 'school.views.logout', name='logout'),
url(r'^vm/new/(?P<template>\d+)/$', 'one.views.vm_new', name='vm_new'),
url(r'^ajax/vm/new/(?P<template>\d+)/$', 'one.views.vm_new_ajax',
name='vm_new_ajax'),
url(r'^vm/new/s(?P<share>\d+)/$', 'one.views.vm_new', name='vm_new'),
url(r'^vm/show/(?P<iid>\d+)/$', 'one.views.vm_show', name='vm_show'),
url(r'^vm/delete/(?P<iid>\d+)/$', 'one.views.vm_delete',
name='vm_delete'),
url(r'^vm/stop/(?P<iid>\d+)/$', 'one.views.vm_stop', name='vm_stop'),
url(r'^vm/unshare/(?P<id>\d+)/$', 'one.views.vm_unshare',
name='vm_unshare'),
url(r'^vm/resume/(?P<iid>\d+)/$', 'one.views.vm_resume',
name='vm_resume'),
url(r'^vm/power_off/(?P<iid>\d+)/$', 'one.views.vm_power_off',
name='vm_power_off'),
url(r'^vm/restart/(?P<iid>\d+)/$', 'one.views.vm_restart',
name='vm_restart'),
url(r'^vm/renew/(?P<which>(suspend|delete))/(?P<iid>\d+)/$',
'one.views.vm_renew', name='vm_renew'),
url(r'^vm/port_add/(?P<iid>\d+)/$', 'one.views.vm_port_add',
name='vm_port_add'),
url(r'^vm/port_del/(?P<iid>\d+)/(?P<proto>tcp|udp)/(?P<public>\d+)/$',
'one.views.vm_port_del', name='vm_port_del'),
url(r'^vm/saveas/(?P<vmid>\d+)$', 'one.views.vm_saveas',
name='vm_saveas'),
url(r'^vm/credentials/(?P<iid>\d+)$', 'one.views.vm_credentials',
name='vm_credentials'),
url(r'^reload/$', 'firewall.views.reload_firewall',
name='reload_firewall'),
url(r'^fwapi/$', 'firewall.views.firewall_api', name='firewall_api'),
url(r'^fwapi/$', 'firewall.views.firewall_api',
name='firewall_api'),
url(r'^store/$', 'store.views.index', name='store_index'),
url(r'^store/gui/$', 'store.views.gui', name='store_gui'),
url(r'^store/top/$', 'store.views.toplist', name='store_top'),
url(r'^ajax/templateWizard$', 'one.views.ajax_template_wizard',
name='ajax_template_wizard'),
url(r'^ajax/share/(?P<id>\d+)/$', 'one.views.ajax_share_wizard',
name='ajax_share_wizard'),
url(r'^ajax/share/(?P<id>\d+)/(?P<gid>\d+)$',
'one.views.ajax_share_wizard', name='ajax_share_wizard'),
url(r'^ajax/template/delete/$', 'one.views.ajax_template_delete',
name='ajax_template_delete'),
url(r'^ajax/template_name_unique/(?P<name>.*)$',
'one.views.ajax_template_name_unique',
name='ajax_template_name_unique'),
url(r'^ajax/store/list$', 'store.views.ajax_listfolder',
name='store_ajax_listfolder'),
name='store_ajax_listfolder'),
url(r'^ajax/store/download$', 'store.views.ajax_download',
name='store_ajax_download'),
name='store_ajax_download'),
url(r'^ajax/store/upload$', 'store.views.ajax_upload',
name='store_ajax_upload'),
name='store_ajax_upload'),
url(r'^ajax/store/delete$', 'store.views.ajax_delete',
name='store_ajax_delete'),
name='store_ajax_delete'),
url(r'^ajax/store/newFolder$', 'store.views.ajax_new_folder',
name='store_ajax_new_folder'),
name='store_ajax_new_folder'),
url(r'^ajax/store/quota$', 'store.views.ajax_quota',
name='store_ajax_quota'),
url(r'^ajax/store/rename$', 'store.views.ajax_rename',
name='store_ajax_rename'),
url(r'^ajax/vm/status/(?P<iid>\d+)$',
'one.views.vm_ajax_instance_status',
name='vm_ajax_instance_status'),
url(r'^language/(?P<lang>[-A-Za-z]+)/$', 'school.views.language',
name='language'),
url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
url(r'^b/(?P<token>.*)/$', 'one.views.boot_token', name='boot_token'),
url(r'^group/show/(?P<gid>\d+)/$', 'school.views.group_show',
name='group_show'),
url(r'^group/new/$', 'school.views.group_new', name='group_new'),
url(r'^ajax/group/(?P<gid>\d+)/add/$',
'school.views.group_ajax_add_new_member',
name='group_ajax_add_new_member'),
url(r'^ajax/group/(?P<gid>\d+)/remove/$',
'school.views.group_ajax_remove_member',
name='group_ajax_remove_member'),
url(r'^ajax/group/delete/$', 'school.views.group_ajax_delete',
name='group_ajax_delete'),
)
......@@ -5,12 +5,12 @@ from firewall.models import *
from django import contrib
class AliasInline(contrib.admin.TabularInline):
model = Alias
class RuleInline(contrib.admin.TabularInline):
model = Rule
class RecordInline(contrib.admin.TabularInline):
model = Record
class HostAdmin(admin.ModelAdmin):
list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac',
'shared_ip', 'owner', 'description', 'reverse', 'groups_l')
......@@ -18,7 +18,7 @@ class HostAdmin(admin.ModelAdmin):
list_filter = ('owner', 'vlan', 'groups')
search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac')
filter_horizontal = ('groups', )
inlines = (AliasInline, RuleInline)
inlines = (RuleInline, RecordInline)
def groups_l(self, instance):
"""Returns instance's groups' names as a comma-separated list."""
......@@ -87,10 +87,28 @@ class GroupAdmin(admin.ModelAdmin):
class FirewallAdmin(admin.ModelAdmin):
inlines = (RuleInline, )
class DomainAdmin(admin.ModelAdmin):
list_display = ('name', 'owner')
class RecordAdmin(admin.ModelAdmin):
list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner')
def address_(self, instance):
a = instance.get_data()
if(a):
return a['address']
def name_(self, instance):
a = instance.get_data()
if(a):
return a['name']
admin.site.register(Host, HostAdmin)
admin.site.register(Vlan, VlanAdmin)
admin.site.register(Rule, RuleAdmin)
admin.site.register(Alias, AliasAdmin)
admin.site.register(Group, GroupAdmin)
admin.site.register(VlanGroup)
admin.site.register(Firewall, FirewallAdmin)
admin.site.register(Domain, DomainAdmin)
admin.site.register(Record, RecordAdmin)
......@@ -9,6 +9,7 @@ mac_re = re.compile(r'^([0-9a-fA-F]{2}([:-]?|$)){6}$')
alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$')
domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$')
ipv4_re = re.compile('^[0-9]+\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
class MACAddressFormField(fields.RegexField):
default_error_messages = {
......@@ -45,6 +46,10 @@ def val_domain(value):
if domain_re.search(value) is None:
raise ValidationError(_(u'%s - invalid domain') % value)
def val_reverse_domain(value):
if not reverse_domain_re.search(value):
raise ValidationError(u'%s - reverse domain' % value)
def ipv4_2_ipv6(ipv4):
"""Convert IPv4 address string to IPv6 address string."""
m = ipv4_re.match(ipv4)
......
from django.contrib import auth
from firewall import models
from modeldict import *
import os
from cloud.settings import firewall_settings as settings
......@@ -376,71 +375,50 @@ def dns():
vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
DNS = []
DNS.append("=cloud.ik.bme.hu:152.66.243.98:600::")
DNS.append(":cloud.ik.bme.hu:28:"
"\040\001\007\070\040\001\100\061\000\002\000\000\000\007\000\000:"
"600")
DNS.append("=r.cloud.ik.bme.hu:152.66.243.62:600::")
DNS.append("Z1.3.0.4.1.0.0.2.8.3.7.0.1.0.0.2.ip6.arpa:"
"dns1.ik.bme.hu:support.ik.bme.hu::::::600") # soa
DNS.append("&1.3.0.4.1.0.0.2.8.3.7.0.1.0.0.2.ip6.arpa::"
"dns1.ik.bme.hu:600::") # ns rekord
DNS.append("&1.3.0.4.1.0.0.2.8.3.7.0.1.0.0.2.ip6.arpa::"
"nic.bme.hu:600::") # ns rekord
for i_vlan in vlans:
m = regex.search(i_vlan.net4)
if i_vlan.name != "DMZ" and i_vlan.name != "PUB":
DNS.append("Z%s.%s.in-addr.arpa:%s:support.ik.bme.hu::::::%s" %
(m.group(2), m.group(1), settings['dns_hostname'],
settings['dns_ttl']))
DNS.append("&%s.%s.in-addr.arpa::%s:%s:" % (m.group(2),
m.group(1), settings['dns_hostname'], settings['dns_ttl']))
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % (i_vlan.domain,
settings['dns_hostname'], settings['dns_ttl']))
DNS.append("&%s::%s:%s" % (i_vlan.domain,
settings['dns_hostname'], settings['dns_ttl']))
if i_vlan.name == "WAR":
DNS.append("Zdns1.%s.%s.%s.in-addr.arpa:%s:"
"support.ik.bme.hu::::::%s" % (m.group(3),
m.group(2), m.group(1),
settings['dns_hostname'], settings['dns_ttl']))
DNS.append("&dns1.%s.%s.%s.in-addr.arpa::%s:%s::" %
(m.group(3), m.group(2), m.group(1),
settings['dns_hostname'], settings['dns_ttl']))
rev = i_vlan.reverse_domain
for i_host in i_vlan.host_set.all():
ipv4 = ( i_host.pub_ipv4 if i_host.pub_ipv4 and not i_host.shared_ip else i_host.ipv4 )
reverse = i_host.reverse if(i_host.reverse and len(i_host.reverse)) else i_host.hostname + u'.' + i_vlan.domain
hostname = i_host.get_fqdn()
ipv4 = (i_host.pub_ipv4 if i_host.pub_ipv4 and
not i_host.shared_ip else i_host.ipv4)
i = ipv4.split('.', 4)
reverse = (i_host.reverse if i_host.reverse and
len(i_host.reverse) else i_host.get_fqdn())
# ipv4
if i_host.ipv4:
# A record
DNS.append("+%s:%s:%s" % (hostname, ipv4,
models.settings['dns_ttl']))
# PTR record 4.3.2.1.in-addr.arpa
DNS.append("^%s:%s:%s" % (ipv4_to_arpa(i_host.ipv4),
DNS.append("^%s:%s:%s" % (
(rev % { 'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]),
'd': int(i[3]) }),
reverse, models.settings['dns_ttl']))
# PTR record 4.dns1.3.2.1.in-addr.arpa
DNS.append("^%s:%s:%s" %
(ipv4_to_arpa(i_host.ipv4, cname=True),
reverse, models.settings['dns_ttl']))
# ipv6
if i_host.ipv6:
# AAAA record
DNS.append(":%s:28:%s:%s" % (hostname,
ipv6_to_octal(i_host.ipv6), settings['dns_ttl']))
# PTR record
DNS.append("^%s:%s:%s" % (ipv6_to_arpa(i_host.ipv6),
reverse, settings['dns_ttl']))
reverse, models.settings['dns_ttl']))
# cname
for i_alias in i_host.alias_set.all():
DNS.append("C%s:%s:%s" % (i_alias.alias, hostname,
settings['dns_ttl']))
for domain in models.Domain.objects.all():
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % (domain.name,
settings['dns_hostname'], models.settings['dns_ttl']))
for r in models.Record.objects.all():
d = r.get_data()
if d['type'] == 'A':
DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'AAAA':
DNS.append(":%s:28:%s:%s" % (d['name'],
ipv6_to_octal(d['address']), d['ttl']))
elif d['type'] == 'NS':
DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'CNAME':
DNS.append("C%s:%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'MX':
mx = d['address'].split(':', 2)
DNS.append("@%(fqdn)s::%(mx)s:%(dist)s:%(ttl)s" %
{'fqdn': d['name'], 'mx': mx[1], 'dist': mx[0],
'ttl': d['ttl']})
process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' %
settings['dns_hostname']], shell=False, stdin=subprocess.PIPE)
......
......@@ -8,6 +8,8 @@ from firewall.fields import *
from south.modelsinspector import add_introspection_rules
from django.core.validators import MinValueValidator, MaxValueValidator
from cloud.settings import firewall_settings as settings
from django.utils.ipv6 import is_valid_ipv6_address
import re
class Rule(models.Model):
CHOICES_type = (('host', 'host'), ('firewall', 'firewall'), ('vlan', 'vlan'))
......@@ -70,7 +72,8 @@ class Vlan(models.Model):
snat_to = models.ManyToManyField('self', symmetrical=False, blank=True, null=True)
description = models.TextField(blank=True)
comment = models.TextField(blank=True)
domain = models.TextField(blank=True, validators=[val_domain])
domain = models.ForeignKey('Domain')
reverse_domain = models.TextField(validators=[val_reverse_domain])
dhcp_pool = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, blank=True, null=True)
......@@ -106,22 +109,6 @@ class Group(models.Model):
def __unicode__(self):
return self.name
class Alias(models.Model):
host = models.ForeignKey('Host')
alias = models.CharField(max_length=40, unique=True, validators=[val_domain])
owner = models.ForeignKey(User, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
def clean(self):
# FIXME later: critical race condition
for h in Host.objects.all():
if h.get_fqdn() == self.alias:
raise ValidationError(_("Host name already used."))
class Meta:
verbose_name_plural = 'aliases'
class Host(models.Model):
hostname = models.CharField(max_length=40, unique=True, validators=[val_alfanum])
reverse = models.CharField(max_length=40, validators=[val_domain], blank=True, null=True)
......@@ -143,6 +130,7 @@ class Host(models.Model):
return self.hostname
def save(self, *args, **kwargs):
id = self.id
if not self.id and self.ipv6 == "auto":
self.ipv6 = ipv4_2_ipv6(self.ipv4)
if not self.shared_ip and self.pub_ipv4 and Host.objects.exclude(id=self.id).filter(pub_ipv4=self.pub_ipv4):
......@@ -151,6 +139,10 @@ class Host(models.Model):
raise ValidationError("Egy masik host natolt cimet nem hasznalhatod sajat ipv4-nek")
self.full_clean()
super(Host, self).save(*args, **kwargs)
if(id is None):
Record(domain=self.vlan.domain, host=self, type='A', owner=self.owner).save()
if self.ipv6:
Record(domain=self.vlan.domain, host=self, type='AAAA', owner=self.owner).save()
def enable_net(self):
self.groups.add(Group.objects.get(name="netezhet"))
......@@ -179,14 +171,7 @@ class Host(models.Model):
self.rules.filter(owner=self.owner).delete()
def get_fqdn(self):
return self.hostname + u'.' + self.vlan.domain
def clean(self):
# FIXME later: critical race condition
for a in Alias.objects.all():
if self.get_fqdn() == a.alias:
raise ValidationError(_("Host name already used as alias."))
return self.hostname + u'.' + unicode(self.vlan.domain)
class Firewall(models.Model):
......@@ -195,3 +180,94 @@ class Firewall(models.Model):
def __unicode__(self):
return self.name
class Domain(models.Model):
name = models.CharField(max_length=40, validators=[val_domain])
owner = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
ttl = models.IntegerField(default=600)
description = models.TextField(blank=True)
def __unicode__(self):
return self.name
class Record(models.Model):
CHOICES_type = (('A', 'A'), ('CNAME', 'CNAME'), ('AAAA', 'AAAA'), ('MX', 'MX'), ('NS', 'NS'), ('PTR', 'PTR'), ('TXT', 'TXT'))
name = models.CharField(max_length=40, validators=[val_domain], blank=True, null=True)
domain = models.ForeignKey('Domain')
host = models.ForeignKey('Host', blank=True, null=True)
type = models.CharField(max_length=6, choices=CHOICES_type)
address = models.CharField(max_length=40, blank=True, null=True)
ttl = models.IntegerField(default=600)
owner = models.ForeignKey(User)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.desc()
def desc(self):
a = self.get_data()
if a:
return a['name'] + u' ' + a['type'] + u' ' + a['address']
return '(nincs)'
def save(self, *args, **kwargs):
self.full_clean()
super(Record, self).save(*args, **kwargs)
def clean(self):
if self.name and self.name.endswith(u'.'):
raise ValidationError(u'a domain nem végződhet pontra')
if self.host and self.type in ['CNAME', 'A', 'AAAA']:
if self.type == 'CNAME':
if not self.name or self.address:
raise ValidationError(u'CNAME rekordnal csak a name legyen kitoltve, ha van host beallitva')
elif self.name or self.address:
raise ValidationError(u'A, AAAA rekord eseten nem szabad megadni name-t, address-t, ha tarsitva van host')
else:
if not self.address:
raise ValidationError(u'address hianyzik')
if self.type == 'A':
if not ipv4_re.match(self.address):
raise ValidationError(u'ez nem ipcim, ez nudli!')
elif self.type in ['CNAME', 'NS', 'PTR', 'TXT']:
if not domain_re.match(self.address):
raise ValidationError(u'ez nem domain, ez nudli!')
elif self.type == 'AAAA':
if not is_valid_ipv6_address(self.address):
raise ValidationError(u'ez nem ipv6cim, ez nudli!')
elif self.type == 'MX':
mx = self.address.split(':', 1)
if not (len(mx) == 2 and mx[0].isdigit() and domain_re.match(mx[1])):
raise ValidationError(u'prioritas:hostname')
else:
raise ValidationError(u'ez ismeretlen rekord, ez nudli!')
def get_data(self):
retval = { 'name': self.name, 'type': self.type, 'ttl': self.ttl, 'address': self.address }
if self.host and self.type in ['CNAME', 'A', 'AAAA']:
if self.type == 'A':
retval['address'] = self.host.pub_ipv4 if self.host.pub_ipv4 and not self.host.shared_ip else self.host.ipv4
retval['name'] = self.host.get_fqdn()
elif self.type == 'AAAA':
if not self.host.ipv6:
return None
retval['address'] = self.host.ipv6
retval['name'] = self.host.get_fqdn()
elif self.type == 'CNAME':
retval['address'] = self.host.get_fqdn()
else:
if not self.name:
retval['name'] = unicode(self.domain)
else:
retval['name'] = self.name + u'.' + unicode(self.domain)
if not (retval['address'] and retval['name']):
return None
return retval
description "cleanup.sh"
start on runlevel [2345]
stop on runlevel [!2345]
post-stop script
/etc/context/cleanup.sh
end script
#!/bin/bash
#rm -f /etc/ssh/ssh_host_*
if [ ! -f /run/context-cleanup ]; then
exit 0
fi
stop rsyslog
rm -fr /home/cloud/.bash_history /root/.bash_history /var/log/*
echo -e "auto lo\niface lo inet loopback\n" > /etc/network/interfaces
sed -i 's/^<volume user=.*//' /etc/security/pam_mount.conf.xml
rm -rf ~cloud/{.bash_history,.ssh/id_rsa}
rm -rf ~root/{.bash_history}
rm -rf /etc/ssh/ssh_host_*
#!/bin/bash
echo "En vagyok a $0 !"
arr=( $((echo 'ibase=16' ; ifconfig eth0 | awk '/HWaddr/ {print $5}' | tr ':a-z' '\nA-Z') | bc ) )
ipv4="${arr[2]}.${arr[3]}.${arr[4]}.${arr[5]}"
gw4="${arr[2]}.${arr[3]}.255.254"
ipv6="2001:738:2001:4031:${arr[3]}:${arr[4]}:${arr[5]}:0"
gw6="2001:738:2001:4031:${arr[3]}:255:254:0"
echo ok "$ipv4 $ipv6 $gw4 $gw6"
/etc/init.d/network-manager stop
ifdown eth0 || ifconfig eth0 0 down
cat > /etc/network/interfaces << EOF
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address $ipv4
netmask 255.255.0.0
gateway $gw4
dns-nameservers 152.66.243.60
iface eth0 inet6 static
address $ipv6
netmask 80
gateway $gw6
EOF
ifup eth0
......@@ -8,4 +8,5 @@ chmod 600 "$HOME/.ssh/id_rsa"
mkdir "$HOME/sshfs"
chown "$USER:$USER" -R "$HOME"
sed -i 's/^<volume user=.*//' /etc/security/pam_mount.conf.xml
sed -i "s/^\(<\/pam_mount>.*\)/<volume user=\"$USER\" fstype=\"fuse\" path=\"sshfs#${NEPTUN}@${SERVER}:home\" mountpoint=\"~\/sshfs\" options=\"nonempty,reconnect,StrictHostKeyChecking=no\" \/>\n\1/" /etc/security/pam_mount.conf.xml
......@@ -2,5 +2,7 @@
echo "En vagyok a $0 !"
echo "export NEPTUN=$NEPTUN" >> $HOME/.profile
if [ "$RECONTEXT" != "YES" ]; then
echo "export NEPTUN=$NEPTUN" >> $HOME/.profile
fi
......@@ -4,21 +4,26 @@ export BASEDIR=$(dirname $0)
export USER="cloud"
export HOME=$(awk -F: -v u=$USER '$1==u{print $6}' /etc/passwd)
mkdir -p "$BASEDIR/mnt"
cd "$BASEDIR"
mount -t iso9660 /dev/cdrom1 "$BASEDIR/mnt" 2> /dev/null
if [ $? -eq 0 -a -f "$BASEDIR/firstrun" -a -f "$BASEDIR/mnt/context.sh" ]; then
. "$BASEDIR/mnt/context.sh"
. "$BASEDIR/mnt/context.sh"
# hogy tudom kiexportalni ennel szebben ezeket a valtozokat?
eval `grep -o '^[a-zA-Z0-9]\+=' "$BASEDIR/mnt/context.sh" | while read x; do echo export ${x%=};done`
if [ "$RECONTEXT" != "YES" ]; then
rm "$BASEDIR/firstrun"
else
touch /run/context-cleanup
fi
run-parts "$BASEDIR/init.d"
for i in $BASEDIR/init.d/*; do
source $i
done
rm "$BASEDIR/firstrun"
else
echo "mar korabban lefutott!"
echo "mar korabban lefutott!"
fi
umount /dev/cdrom1 2>/dev/null
......
#################################################################
##### Windows Powershell Script to configure OpenNebula VMs #####
##### Created by andremonteiro@ua.pt and tsbatista@ua.pt #####
##### DETI/IEETA Universidade de Aveiro 2011 #####
#################################################################
Set-ExecutionPolicy unrestricted -force # not needed if already done once on the VM
[string]$computerName = "$env:computername"
[string]$ConnectionString = "WinNT://$computerName"
[string]$username = "cloud"
function getContext($file) {
$context = @{}
switch -regex -file $file {
'(.+)="(.+)"' {
$name,$value = $matches[1..2]
$context[$name] = $value
}
}
return $context
}
function addLocalUser($context) {
# Create new user
$ADSI = [adsi]$ConnectionString
if(!([ADSI]::Exists("WinNT://$computerName/$username"))) {
$user = $ADSI.Create("user",$username)
$user.setPassword($context["USERPW"])
$user.SetInfo()
}
# Already exists, change password
else{
$admin=[ADSI]("WinNT://$computerName/$username, user")
$admin.psbase.invoke("SetPassword", $context["USERPW"])
}
# Add user to local Administrators
$groups = "Administrators", "Administradores"
foreach ($grp in $groups) {
if([ADSI]::Exists("WinNT://$computerName/$grp,group")) {
$group = [ADSI] "WinNT://$computerName/$grp,group"
if([ADSI]::Exists("WinNT://$computerName/$username")) {
$group.Add("WinNT://$computerName/$username")
}
}
}
}
function renameComputer($context) {
$ComputerInfo = Get-WmiObject -Class Win32_ComputerSystem
$ComputerInfo.rename($context["HOSTNAME"])
}
function enableipv6($context)
{
$interface="BME"
$mac = (gwmi win32_NetworkAdapter -filter "NetConnectionID='BME'").MACAddress.split(':')
$a = [Convert]::ToInt32($mac[3], 16)
$b = [Convert]::ToInt32($mac[4], 16)
$c = [Convert]::ToInt32($mac[5], 16)
$ipv6="2001:738:2001:4031:{0}:{1}:{2}:0" -f $a, $b, $c
$gwv6="2001:738:2001:4031:{0}:{1}:{2}:0" -f $a, 255, 254
netsh interface ipv6 add address "$interface" "$ipv6/80"
netsh interface ipv6 add route ::/0 "$interface" "$gwv6"
}
function enableRemoteDesktop()
{
# Windows 7 only - add firewall exception for RDP
netsh advfirewall Firewall set rule group="Remote Desktop" new enable=yes
# Enable RDP
$Terminal = (Get-WmiObject -Class "Win32_TerminalServiceSetting" -Namespace root\cimv2\terminalservices).SetAllowTsConnections(1)
return $Terminal
}
function setacl($file)
{
$userAccount = "\\someserver\auser"
$aclWork = (Get-Item $file).GetAccessControl("Access")
$netStuff = New-Object system.Security.AccessControl.FileSystemAccessRule($username, "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
$aclWork.SetAccessrule($netStuff)
Set-ACL $file $aclWork
}
function storage($context)
{
$smbpw = $context["SMBPW"]
$smbuser = $context["NEPTUN"]
$server = $context["SERVER"]
$smbpw = $smbpw -replace "`"", "\`""
mkdir c:\Windows\System32\Repl\Import\Scripts
net user $username /scriptpath:$username
echo "net use * /delete /yes `r`n timeout 10 `r`n net use z: \\$server\$smbuser `"$smbpw`" /user:$smbuser `r`n powershell Set-ItemProperty -Path 'HKCU:\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name Personal -Value Z:\" | Out-File -encoding ASCII ("c:\Windows\System32\Repl\Import\Scripts\$username.bat")
setacl("c:\Windows\System32\Repl\Import\Scripts")
if($context["RECONTEXT"] -eq "YES") {
echo "del c:\Windows\System32\Repl\Import\Scripts\$username.bat `r`n net use z: /delete" | Out-File -encoding ASCII ("c:\context\logoff.bat")
} else {
echo " " | Out-File -encoding ASCII ("c:\context\logoff.bat")
}
}
function booturl($context)
{
$token = $context["BOOTURL"]
(new-object System.Net.WebClient).DownloadFile( $token, "c:\context\booturl")
}
# If folder context doesn't exist create it
if (-not (Test-Path "c:\context\")) {
New-Item "C:\context\" -type directory
}
# Execute script
if( -not(Test-Path "c:\context\contextualized") -and (Test-Path "D:\context.sh")) {
$context = @{}
$context = getContext('D:\context.sh')
addLocalUser($context)
# renameComputer($context)
storage($context)
enableipv6($context)
enableRemoteDesktop
booturl($context)
if(-not($context["RECONTEXT"] -eq "YES")) {
echo "contextualized" |Out-File ("c:\context\contextualized")
}
}
Set objShell = CreateObject("Wscript.Shell")
objShell.Run("powershell -NonInteractive -NoProfile -NoLogo -ExecutionPolicy Unrestricted -file C:\context\one-context.ps1")
Dim objFSO, objFolder
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.CreateFolder("C:\context\executedVBScript")
......@@ -9,8 +9,9 @@ xml = base64.b64decode(sys.argv[1])
data = xmltodict.parse(xml)
try:
booturl = data["VM"]["TEMPLATE"]["CONTEXT"]["BOOTURL"]
(drop, b) = booturl.split(".hu", 1)
req=urllib2.Request("http://localhost:8080"+b)
response = urllib2.urlopen(req)
except:
print 'Error'
req=urllib2.Request(booturl)
response = urllib2.urlopen(req)
#!/bin/bash
sudo pip install django_extensions
for i in cloudstore toplist django
do
sudo stop $i || true
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -37,6 +37,9 @@ body {
margin: 0px auto;
display: block;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.3);
border-radius: 5px;
text-align: center;
vertical-align: middle;
}
.contentblock h2 {
background-color: #000;
......
from django.core.management import setup_environ
from cloud import settings
from school.models import Course
setup_environ(settings)
with open('/home/django/targykodok.csv') as f:
for l in f.readlines():
nep, name = l.split("\\")
o, created = Course.objects.get_or_create(code=nep)
try:
o.name = name
o.save()
except:
o.name = "%s (%s)" % (name, nep.replace("BMEVIII", ""))
o.save()
#!/usr/bin/python
import cloudgui
if __name__ == '__main__':
try:
browser = cloudgui.gui.Browser()
browser.main()
finally:
browser.umount_sshfs_folder()
[Desktop Entry]
Version=0.1
Name=Cloud GUI
Comment=Tool to use IK Cloud
Exec=cloud
Icon=/usr/share/icons/cloud.svg
Terminal=false
Type=Application
Categories=Utility;Application;
import gui
import nxkey
import rdp
#!/usr/bin/env python
import gtk
import webkit
import gobject
import base64
import os
import sys
import rdp
from multiprocessing import Process
import subprocess
import tempfile
class KeyGen:
"""Attributes:
private_key
public_key
"""
def __init__(self):
self.private_key, self.public_key = self.keygen(2048)
def keygen(self,length=1024):
"""Generate Keypair for SSH
(private_key, public_key)
"""
import os, base64
from datetime import date
from Crypto.PublicKey import RSA
key = RSA.generate(length, os.urandom)
try:
pub = key.exportKey('OpenSSH')
if not pub.startswith("ssh-"):
raise ValueError(pub)
except:
ssh_rsa = '00000007' + base64.b16encode('ssh-rsa')
exponent = '%x' % (key.e, )
if len(exponent) % 2:
exponent = '0' + exponent
ssh_rsa += '%08x' % (len(exponent) / 2, )
ssh_rsa += exponent
modulus = '%x' % (key.n, )
if len(modulus) % 2:
modulus = '0' + modulus
if modulus[0] in '89abcdef':
modulus = '00' + modulus
ssh_rsa += '%08x' % (len(modulus) / 2, )
ssh_rsa += modulus
pub = 'ssh-rsa %s' % (
base64.b64encode(base64.b16decode(ssh_rsa.upper())), )
return key.exportKey(), "%s %s" % (pub, "cloud-%s" % date.today())
class Browser:
version = "0.1"
mounted = False
neptun = ""
host = ""
private_key_file = ""
public_key_b64 = ""
params = {}
def __init__(self):
#Init window components
gobject.threads_init()
self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
#Register window events
self.window.connect("destroy", self.destroy)
#DEBUG
self.window.set_decorated(True)
self.window.set_title("IK CloudStore Login")
self.window.set_default_size(1024,600)
self.window.set_position(gtk.WIN_POS_CENTER)
#Init browser
self.webview = webkit.WebView()
self.webview.connect('onload-event', self.load_committed_cb)
self.webview.open("https://cloud.ik.bme.hu/store/gui/")
self.webview.connect("navigation-requested", self.on_navigation_requested)
settings = webkit.WebSettings()
settings.set_property('user-agent', 'cloud-gui '+self.version)
settings.set_property('enable-accelerated-compositing', True)
settings.set_property("enable-default-context-menu", False)
self.webview.set_settings(settings)
#Connect things
self.scrolledwindow = gtk.ScrolledWindow()
self.scrolledwindow.add(self.webview)
self.window.add(self.scrolledwindow)
self.window.maximize()
self.window.show_all()
def init_keypair(self):
keygen = KeyGen()
private_key = keygen.private_key
public_key = keygen.public_key
#Saver private_key to KEY_FILE
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
f.write(private_key)
self.private_key_file = f.name
self.public_key_b64 = base64.b64encode(public_key)
def destroy(self, dummy):
try:
os.unlink(self.private_key_file)
except:
pass
try:
self.umount_sshfs_folder()
except:
pass
gtk.main_quit()
def on_navigation_requested(self, view, frame, req, data=None):
uri = req.get_uri()
if uri == "https://login.bme.hu/admin/":
gobject.threads_init()
window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
browser = webkit.WebView()
browser.open(uri)
window.add(browser)
window.show_all()
return True
elif uri == "https://cloud.ik.bme.hu/logout/":
self.umount_sshfs_folder()
try:
scheme, rest = uri.split(":", 1)
if scheme == "nx" or scheme == "rdp" or scheme == "sshterm":
subprocess.Popen(["/usr/local/bin/rdp",uri])
return True
elif scheme == "cloudfile":
file_path = os.path.normpath(rest)
subprocess.call(["xdg-open","file://"+self.folder+file_path])
return True
else:
return False
except:
False
def mount_sshfs_folder(self):
self.folder = os.path.expanduser("~/sshfs")
neptun = self.params["neptun"]
host = self.params["host"]
try:
os.makedirs(self.folder)
except:
pass
result = subprocess.call(['/usr/bin/sshfs', '-o', 'IdentityFile='+self.private_key_file+',StrictHostKeyChecking=no', neptun+"@"+host+":home", self.folder])
#print result
def umount_sshfs_folder(self):
try:
result = subprocess.call(['/bin/fusermount', '-u', self.folder])
except:
pass
def post_key(self,key = None):
if key != None:
js = '''
$.post("/store/gui/", { "KEY" : "%(key)s" },
function (respond) {
window.location = respond;
}
)
.error(function (respond) { alert(JSON.stringify(respond)); });
''' % { "key" : key }
else:
js = '''
$.post("/store/gui/", "",
function (respond) {
window.alert(respond);
}
)
.error(function (respond) { alert(JSON.stringify(respond)); });
'''
self.webview.execute_script(js)
def load_committed_cb(self,web_view, frame):
uri = frame.get_uri()
try:
self.webview.execute_script('document.getElementsByTagName("a")[0].target="";')
except:
pass
### Send keys via JavaScript ###
if uri == "https://cloud.ik.bme.hu/store/gui/":
self.init_keypair()
### JS
self.post_key(self.public_key_b64)
### Parse values and do mounting ###
elif uri.startswith("https://cloud.ik.bme.hu/?"):
if self.mounted != True:
try:
uri, params = uri.split('?', 1)
values = params.split('&')
for p in values:
key, value = p.split('=',1)
self.params[key] = value
try:
self.mount_sshfs_folder()
except Exception as e:
print e
self.mounted = True
except:
pass
finally:
os.unlink(self.private_key_file)
return True
def main(self):
gtk.main()
if __name__ == "__main__":
browser = Browser()
browser.main()
#!/usr/bin/python
import sys
import random
import re
from xml.sax.saxutils import escape
class NXKeyGen:
numValidCharList = 85
dummyString = "{{{{"
def __init__(self, password):
self.password = password
def getEncrypted(self):
return self.scrambleString(self.password)
def getvalidCharList(self, pos):
validcharlist = [
"!", "#", "$", "%", "&", "(", ")", "*", "+", "-",
".", "0", "1", "2", "3", "4", "5", "6", "7", "8",
"9", ":", ";", "<", ">", "?", "@", "A", "B", "C",
"D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "]", "_", "a", "b", "c", "d",
"e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "{", "|", "}"
]
return validcharlist[pos]
def encodePassword(self, p):
sPass = ":"
sTmp = ""
if not p:
return ""
for i in range(len(p)):
c = p[i:i+1]
a = ord(c)
sTmp = str( a + i + 1) + ":"
sPass += sTmp
sTmp = ""
return sPass
def findCharInList(self, c):
i = -1
for j in range(self.numValidCharList):
randchar = self.getvalidCharList(j);
if randchar == c:
i = j
return i
return i
def getRandomValidCharFromList(self):
return self.getvalidCharList(random.randint(0,60))
#return self.getvalidCharList(0)
def scrambleString(self, s):
sRet = ""
if not s:
return s
strp = self.encodePassword(s)
if len(strp) < 32:
sRet += self.dummyString
#print "Added dummy "+sRet
for iR in reversed(range(len(strp))):
sRet += strp[iR:iR+1]
#print "Reverse: "+sRet
if len(sRet) < 32:
sRet += self.dummyString
#print "Added dummy2 "+sRet
app = self.getRandomValidCharFromList()
#print "Random valid char: "+app
k = ord(app)
l = k + len(sRet) - 2
sRet = app + sRet
#print "Random "+sRet+"\n"
for i1 in range(1, len(sRet)):
app2 = sRet[i1 : i1 + 1]
#print "For cycle app2= "+str(app2)
j = self.findCharInList(app2)
#print "For cycle j= "+str(j)
if j == -1:
return sRet
i = (j + l * (i1 + 1)) % self.numValidCharList
#print "For cycle i= "+str(i)
car = self.getvalidCharList(i)
sRet = self.substr_replace(sRet,car,i1,1)
#print "For cycle sRet: "+sRet+"\n"
c = (ord(self.getRandomValidCharFromList())) + 2
c2 = chr(c)
sRet = sRet + c2
return escape(sRet)
def substr_replace(self,in_str,ch,pos,qt):
clist = list(in_str)
count = 0;
tmp_str = '';
for key in clist:
if count != pos:
tmp_str += key
else:
tmp_str += ch
count = count+1
return tmp_str
if __name__ == "__main__":
NXPass = NXKeyGen(sys.argv[1])
print NXPass.password
print NXPass.getEncrypted()
#!/usr/bin/python
import nxkey
import tempfile
import subprocess
import os
import sys
import gtk
import gobject
class RDP:
def __init__(self, uri):
gobject.threads_init()
self.scheme, self.username, self.password, self.host, self.port = uri.split(':',4)
def dialog_box(self,text):
# Window = gtk.Window()
# Window.set_size_request(250, 100)
# Window.set_position(gtk.WIN_POS_CENTER)
# Window.connect("destroy", gtk.main_quit)
# window.set_title("Message dialogs")
md = gtk.MessageDialog(parent=None, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_CLOSE, message_format=text)
md.run()
md.destroy()
def connect(self):
#rdp:cloud:qYSv3eQJYY:152.66.243.62:23037
if self.scheme == "rdp":
self.connect_rdp()
elif self.scheme == "nx":
self.connect_nx()
elif self.scheme == "sshterm":
self.connect_sshterm()
else:
return False
def get_temporary_file(self):
tmpfile = tempfile.NamedTemporaryFile(mode='w', delete=False)
if not "_" in tmpfile.name:
return tmpfile
else:
tmpfile.close()
os.unlink(tmpfile.name)
return self.get_temporary_file()
def connect_sshterm(self):
try:
ssh_subcommand = 'sshpass -p "%(password)s" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null %(username)s@%(host)s -p%(port)s' \
% {'username' : self.username, 'password' : self.password, 'host' : self.host, 'port' : self.port}
ssh_command = ["gnome-terminal", "-e", ssh_subcommand]
proc = subprocess.check_call(ssh_command, stdout = subprocess.PIPE)
except:
self.dialog_box("Unable to connect to host: "+self.host+" at port "+self.port)
def connect_rdp(self):
rdp_command = ["rdesktop", "-khu", "-E", "-P", "-0", "-f", "-u", self.username, "-p", self.password, self.host+":"+self.port]
try:
proc = subprocess.check_call(rdp_command, stdout = subprocess.PIPE)
except subprocess.CalledProcessError as e:
if e.returncode != 1:
print e
print e.returncode
self.dialog_box("Unable to connect to host: "+self.host+" at port "+self.port)
def connect_nx(self):
#Generate temproary config
password_enc = nxkey.NXKeyGen(self.password).getEncrypted()
tmpfile = self.get_temporary_file()
nx_config = """
<!DOCTYPE NXClientSettings>
<NXClientSettings application="nxclient" version="1.3" >
<group name="Advanced" >
<option key="Cache size" value="16" />
<option key="Cache size on disk" value="64" />
<option key="Current keyboard" value="true" />
<option key="Custom keyboard layout" value="" />
<option key="Disable DirectDraw" value="false" />
<option key="Disable ZLIB stream compression" value="false" />
<option key="Disable deferred updates" value="false" />
<option key="Enable HTTP proxy" value="false" />
<option key="Enable SSL encryption" value="true" />
<option key="Enable response time optimisations" value="false" />
<option key="Grab keyboard" value="false" />
<option key="HTTP proxy host" value="" />
<option key="HTTP proxy port" value="8080" />
<option key="HTTP proxy username" value="" />
<option key="Remember HTTP proxy password" value="false" />
<option key="Restore cache" value="true" />
<option key="StreamCompression" value="" />
</group>
<group name="Environment" >
<option key="CUPSD path" value="/usr/sbin/cupsd" />
</group>
<group name="General" >
<option key="Automatic reconnect" value="true" />
<option key="Command line" value="" />
<option key="Custom Unix Desktop" value="console" />
<option key="Desktop" value="gnome" />
<option key="Disable SHM" value="false" />
<option key="Disable emulate shared pixmaps" value="false" />
<option key="Link speed" value="lan" />
<option key="Remember password" value="true" />
<option key="Resolution" value="fullscreen" />
<option key="Resolution height" value="600" />
<option key="Resolution width" value="800" />
<option key="Server host" value="%(host)s" />
<option key="Server port" value="%(port)s" />
<option key="Session" value="unix" />
<option key="Spread over monitors" value="false" />
<option key="Use default image encoding" value="0" />
<option key="Use render" value="true" />
<option key="Use taint" value="true" />
<option key="Virtual desktop" value="false" />
<option key="XAgent encoding" value="true" />
<option key="displaySaveOnExit" value="true" />
<option key="xdm broadcast port" value="177" />
<option key="xdm list host" value="localhost" />
<option key="xdm list port" value="177" />
<option key="xdm mode" value="server decide" />
<option key="xdm query host" value="localhost" />
<option key="xdm query port" value="177" />
</group>
<group name="Images" >
<option key="Disable JPEG Compression" value="0" />
<option key="Disable all image optimisations" value="false" />
<option key="Disable backingstore" value="false" />
<option key="Disable composite" value="false" />
<option key="Image Compression Type" value="3" />
<option key="Image Encoding Type" value="0" />
<option key="Image JPEG Encoding" value="false" />
<option key="JPEG Quality" value="6" />
<option key="RDP Image Encoding" value="3" />
<option key="RDP JPEG Quality" value="6" />
<option key="RDP optimization for low-bandwidth link" value="false" />
<option key="Reduce colors to" value="" />
<option key="Use PNG Compression" value="true" />
<option key="VNC JPEG Quality" value="6" />
<option key="VNC images compression" value="3" />
</group>
<group name="Login" >
<option key="Auth" value="%(password)s" />
<option key="Guest Mode" value="false" />
<option key="Guest password" value="" />
<option key="Guest username" value="" />
<option key="Login Method" value="nx" />
<option key="User" value="%(username)s" />
</group>
<group name="Services" >
<option key="Audio" value="false" />
<option key="IPPPort" value="631" />
<option key="IPPPrinting" value="false" />
<option key="Shares" value="false" />
</group>
<group name="VNC Session" >
<option key="Display" value="0" />
<option key="Remember" value="false" />
<option key="Server" value="" />
</group>
<group name="Windows Session" >
<option key="Application" value="" />
<option key="Authentication" value="2" />
<option key="Color Depth" value="8" />
<option key="Domain" value="" />
<option key="Image Cache" value="true" />
<option key="Password" value="EMPTY_PASSWORD" />
<option key="Remember" value="true" />
<option key="Run application" value="false" />
<option key="Server" value="" />
<option key="User" value="" />
</group>
<group name="share chosen" >
<option key="Share number" value="0" />
</group>
</NXClientSettings>
""" % {'username' : self.username, 'password' : password_enc, 'host' : self.host, 'port' : self.port}
tmpfile.write(nx_config)
tmpfile.close()
try:
subprocess.check_call(["/usr/NX/bin/nxclient", "--session", tmpfile.name])
except:
pass
os.unlink(tmpfile.name)
return
if __name__ == "__main__":
uri = sys.argv[1]
connection = RDP(uri)
connection.connect()
#!/usr/bin/python
import sys
import cloudgui
if __name__ == '__main__':
try:
uri = sys.argv[1]
except:
print "No URI presented!"
sys.exit()
rdp = cloudgui.rdp.RDP(uri)
rdp.connect()
from setuptools import setup, find_packages
setup(
name = "CloudGUI",
version = "0.1",
packages = ['cloudgui',],
scripts = ['cloud','rdp',],
)
#!/usr/bin/env python
import gtk
import webkit
import gobject
import base64
import subprocess
import os
def keygen(length=1024):
import os, base64
from datetime import date
from Crypto.PublicKey import RSA
key = RSA.generate(length, os.urandom)
try:
pub = key.exportKey('OpenSSH')
if not pub.startswith("ssh-"):
raise ValueError(pub)
except:
ssh_rsa = '00000007' + base64.b16encode('ssh-rsa')
exponent = '%x' % (key.e, )
if len(exponent) % 2:
exponent = '0' + exponent
ssh_rsa += '%08x' % (len(exponent) / 2, )
ssh_rsa += exponent
modulus = '%x' % (key.n, )
if len(modulus) % 2:
modulus = '0' + modulus
if modulus[0] in '89abcdef':
modulus = '00' + modulus
ssh_rsa += '%08x' % (len(modulus) / 2, )
ssh_rsa += modulus
pub = 'ssh-rsa %s' % (
base64.b64encode(base64.b16decode(ssh_rsa.upper())), )
return key.exportKey(), "%s %s" % (pub, "cloud-%s" % date.today())
### Settings ###
KEY_DIR = "/tmp/"
KEY_FILE = KEY_DIR+"/id_rsa"
#Initalize keypair
private_key, public_key = keygen(2048)
#Saver private_key
with open(KEY_FILE,'w') as f:
f.write(private_key)
pub_key_string = base64.b64encode(public_key)
class Browser:
neptun = ""
host = ""
def __init__(self):
#Init window components
gobject.threads_init()
self.window = gtk.Window()
self.window.connect("destroy", self.destroy)
self.window.set_title("IK CloudStore Login")
#Init toolbar
self.toolbar = gtk.Toolbar()
#Init browser
self.browser = webkit.WebView()
self.browser.connect('onload-event', self.load_committed_cb)
# self.browser.open("http://10.9.1.86:8080")
self.browser.open("https://cloud.ik.bme.hu/store/gui/")
self.browser.connect("navigation-requested", self.on_navigation_requested)
#self.browser.open("http://index.hu")
#Sample button
self.help_button = gtk.ToolButton(gtk.STOCK_HELP)
self.help_button.connect("clicked",self.hello)
self.store_button = gtk.ToolButton(gtk.STOCK_HOME)
self.store_button.connect("clicked",self.store)
#Connect things
self.toolbar.add(self.store_button)
self.toolbar.add(self.help_button)
self.vbox = gtk.VBox(False, 0)
self.vbox.pack_start(self.toolbar, False, True, 0)
self.vbox.add(self.browser)
self.window.add(self.vbox)
#self.window.add(self.browser)
self.window.show_all()
def destroy(self, dummy):
self.browser.execute_script("resetKey()")
gtk.main_quit()
def on_navigation_requested(self, view, frame, req, data=None):
uri = req.get_uri()
#print "On nav: " + uri
scheme, rest = uri.split(':', 1)
#print scheme
try:
self.neptun, rest = rest.split(':', 1)
#print "Nep: "+neptun
self.host, values = rest.split('?', 1)
#print "Host: "+host
#print "Values: "+values
except:
pass
if scheme == 'login':
self.browser.execute_script("postKey(\"%s\")" % pub_key_string)
self.browser.execute_script("document.getElementById(\"login_button\").hidden=true ;")
self.browser.execute_script("document.getElementById(\"logout_button\").hidden=false ;")
self.browser.execute_script("document.getElementById(\"mount_button\").hidden=false ;")
return True
elif scheme == 'logout':
self.browser.execute_script("resetKey()")
self.browser.execute_script("document.getElementById(\"logout_button\").hidden=true ;")
self.browser.execute_script("document.getElementById(\"login_button\").hidden=false ;")
self.browser.execute_script("document.getElementById(\"mount_button\").hidden=true ;")
return True
elif scheme == "mount":
self.mount_sshfs_folder(self.neptun,self.host)
self.browser.execute_script("document.getElementById(\"mount_button\").hidden=true ;")
self.browser.execute_script("document.getElementById(\"umount_button\").hidden=false ;")
return True
elif scheme == "umount":
self.umount_sshfs_folder()
self.browser.execute_script("document.getElementById(\"mount_button\").hidden=false ;")
self.browser.execute_script("document.getElementById(\"umount_button\").hidden=true ;")
return True
else:
return False
def mount_sshfs_folder(self,neptun,host):
with open(os.devnull, "w") as fnull:
result = subprocess.call(['/usr/bin/sshfs', '-o', 'IdentityFile='+KEY_DIR+"/id_rsa", neptun+"@"+host+":home", "/home/tarokkk/sshfs"])
#print result
def umount_sshfs_folder(self):
with open(os.devnull, "w") as fnull:
result = subprocess.call(['/bin/fusermount', '-u', "/home/tarokkk/sshfs"])
def hello(self, widget):
self.browser.open("https://login.bme.hu/admin/")
def store(self, widget):
self.browser.open("https://cloud.ik.bme.hu/store/gui/")
def load_committed_cb(self,web_view, frame):
self.browser.execute_script('document.getElementsByTagName("a")[0].target="";')
#uri = frame.get_uri()
#print uri
#print web_view.get_title()
return
def main(self):
gtk.main()
if __name__ == "__main__":
browser = Browser()
browser.main()
......@@ -2,7 +2,7 @@
# TODO File permission checks
from bottle import route, run, request, static_file, abort, redirect, app
from bottle import route, run, request, static_file, abort, redirect, app, response
import json, os, shutil
import uuid
import subprocess
......@@ -65,7 +65,7 @@ def neptun_GET(neptun):
if os.path.exists(home_path) != True:
abort(401, 'The requested user does not exist!')
else:
statistics=getQuotaStatus(neptun)
statistics=get_quota(neptun)
return { 'Used' : statistics[0], 'Soft' : statistics[1], 'Hard' : statistics[2]}
COMMANDS = {}
......@@ -165,7 +165,7 @@ def cmd_rename(request, neptun, home_path):
COMMANDS['RENAME'] = cmd_rename
# NEW FOLDER
def cmd_new_folder(request, neptun, home_path):
def cmd_new_folder(request, username, home_path):
dir_path = home_path+'/'+request.json['PATH']
dir_path = os.path.realpath(dir_path)
if not dir_path.startswith(home_path):
......@@ -174,6 +174,7 @@ def cmd_new_folder(request, neptun, home_path):
abort(400, "Directory already exist!")
else:
os.mkdir(dir_path, 0755)
os.chown(dir_path, getpwnam(username).pw_uid, getpwnam(username).pw_gid)
COMMANDS['NEW_FOLDER'] = cmd_new_folder
# REMOVE
......@@ -221,30 +222,48 @@ def set_keys(neptun):
return
elif result == 2:
abort(403, 'User does not exist!')
@route('/quota/<neptun>', method='POST')
@force_ssl
def set_quota(neptun):
try:
quota = request.json['QUOTA']
except:
abort(400, 'Wrong syntax!')
result = subprocess.call([ROOT_BIN_FOLDER+'/'+USER_MANAGER, 'setquota', neptun, str(quota), hard_quota(quota)])
if result == 0:
return
elif result == 2:
abort(403, 'User does not exist!')
@route('/new/<neptun>', method='POST')
@force_ssl
def new_user(neptun):
key_list = []
smbpasswd=''
smbpasswd = ''
quota = ''
try:
smbpasswd = request.json['SMBPASSWD']
quota = request.json['QUOTA']
except:
print "Invalid syntax"
abort(400, 'Invalid syntax')
# Call user creator script
result = subprocess.call([ROOT_BIN_FOLDER+'/'+USER_MANAGER, 'add', neptun, smbpasswd])
result = subprocess.call([ROOT_BIN_FOLDER+'/'+USER_MANAGER, 'add', neptun, smbpasswd, str(quota), hard_quota(quota)])
print "add "+neptun+" "+smbpasswd+" "+str(quota)+" "+hard_quota(quota)
if result == 0:
try:
for key in request.json['KEYS']:
key_list.append(key)
updateSSHAuthorizedKeys(neptun, key_list)
except:
print "SSH error"
abort(400, 'SSH')
return
elif result == 2:
abort(403, 'User already exist!')
else:
print "Error"
abort(400, 'An error occured!')
......@@ -258,6 +277,14 @@ def dl_hash(hash_num):
else:
filename = os.path.basename(os.path.realpath(hash_path+'/'+hash_num))
return static_file(hash_num, root=hash_path, download=filename)
@route('/ul/<hash_num>', method='OPTIONS')
def upload_allow(hash_num):
response.set_header('Access-Control-Allow-Origin', '*')
response.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
response.set_header('Access-Control-Allow-Headers', 'Content-Type, Content-Range, Content-Disposition, Content-Description')
return 'ok'
@route('/ul/<hash_num>', method='POST')
def upload(hash_num):
if not os.path.exists(ROOT_WWW_FOLDER+'/'+hash_num):
......@@ -275,6 +302,7 @@ def upload(hash_num):
# Check if upload path valid
if not up_path.startswith('/home'):
abort(400, 'Invalid path.')
os.remove(ROOT_WWW_FOLDER+'/'+hash_num)
# Get the real upload path
# Delete the hash link
......@@ -284,23 +312,26 @@ def upload(hash_num):
# os.seteuid(getpwnam(username).pw_uid)
# TODO setuid subcommand
# Check if file exist (root can overwrite anything not safe)
f = open(up_path , 'wb')
os.chown(up_path, getpwnam(username).pw_uid, getpwnam(username).pw_gid)
os.chmod(up_path, 0644)
f.close()
with open(up_path , 'wb') as f:
datalength = 0
for chunk in fbuffer(file_data.file):
for chunk in fbuffer(file_data.file, chunk_size=1048576):
f.write(chunk)
datalength += len(chunk)
try:
os.chown(up_path, getpwnam(username).pw_uid, getpwnam(username).pw_gid)
os.chmod(up_path, 0644)
try:
redirect_address = request.headers.get('Referer')
except:
redirect_address = REDIRECT_URL
redirect_address = REDIRECT_URL
response.set_header('Access-Control-Allow-Origin', '*')
response.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
response.set_header('Access-Control-Allow-Headers', 'Content-Type, Content-Range, Content-Disposition, Content-Description')
redirect(redirect_address)
#return 'Upload finished: '+file_name+' - '+str(datalength)+' Byte'
# Return hard quota from quota
def hard_quota(quota):
return str(int(int(quota)*1.25))
# Define filebuffer for big uploads
def fbuffer(f, chunk_size=4096):
......@@ -311,17 +342,12 @@ def fbuffer(f, chunk_size=4096):
# Update users .ssh/authorized_keys
def updateSSHAuthorizedKeys(username, key_list):
user_home_ssh = '/home/'+username+'/home/.ssh'
user_uid=getpwnam(username).pw_uid
user_gid=getpwnam(username).pw_gid
if not os.path.exists(user_home_ssh):
os.mkdir(user_home_ssh, 0700)
os.chown(user_home_ssh, user_uid, user_gid)
auth_file_name = user_home_ssh+'/authorized_keys'
auth_file = open(auth_file_name, 'w')
for key in key_list:
auth_file.write(key+'\n')
auth_file.close()
auth_file_name = "/home/"+username+"/authorized_keys"
with open(auth_file_name, 'w') as auth_file:
for key in key_list:
auth_file.write(key+'\n')
os.chmod(auth_file_name, 0600)
os.chown(auth_file_name, user_uid, user_gid)
return
......@@ -363,10 +389,17 @@ def file_dict(path, home):
'MTIME': os.path.getmtime(path),
'DIR': os.path.relpath(os.path.dirname(path), home)}
def getQuotaStatus(neptun):
def get_quota(neptun):
output=subprocess.check_output([ROOT_BIN_FOLDER+'/'+USER_MANAGER, 'status', neptun], stderr=subprocess.STDOUT)
return output.split()
def set_quota(neptun, quota):
try:
output=subprocess.check_output([ROOT_BIN_FOLDER+'/'+USER_MANAGER, 'setquota', neptun, quota, hard_quota(quota)], stderr=subprocess.STDOUT)
except:
return False
return True
if __name__ == "__main__":
run(host=SITE_HOST, port=SITE_PORT)
else:
......
......@@ -80,7 +80,11 @@ case $COMMAND in
;;
'status')
# echo $(quota -w ${USER_NAME} 2>/dev/null | tail -1 | awk '{ print $2" "$3" "$4 }')
echo "1000 1000 1000"
echo "1916 2097152 2621440"
;;
'setquota')
echo "$1 $2 $3 $4" > /tmp/asd
exit 0
;;
*)
echo "Usage: UserManager.sh COMMAND USER PASSWORD"
......
......@@ -13,14 +13,22 @@ USER_NAME="$2"
SMB_PASSWD="$3"
umask 022
case $COMMAND in
'add')
if [ "x${USER_NAME}" == "x" ]; then
exit 1
fi
case $COMMAND in
'add')
SOFT_QUOTA="$4"
HARD_QUOTA="$5"
if [ "x${SMB_PASSWD}" == "x" ]; then
exit 1
fi
if [ "x${SOFT_QUOTA}" == "x" ]; then
exit 1
fi
if [ "x${HARD_QUOTA}" == "x" ]; then
exit 1
fi
#Check if user already exist
id ${USER_NAME} > /dev/null 2>&1
if [ $? == '0' ]; then
......@@ -40,9 +48,13 @@ case $COMMAND in
echo "User ${USER_NAME} CREATED at `date`" >> /root/users.log
#Set quotas
# Username Soft Hard Inode Dev
setquota ${USER_NAME} 2097152 2621440 0 0 /home
# setquota ${USER_NAME} 2097152 2621440 0 0 /home
setquota ${USER_NAME} ${SOFT_QUOTA} ${HARD_QUOTA} 0 0 /home
;;
'set')
if [ "x${SMB_PASSWD}" == "x" ]; then
exit 1
fi
id ${USER_NAME} > /dev/null 2>&1
if [ $? == '0' ]; then
echo -e "${SMB_PASSWD}\n${SMB_PASSWD}\n" | passwd ${USER_NAME} >/dev/null 2>&1
......@@ -81,6 +93,17 @@ case $COMMAND in
'status')
echo $(quota -w ${USER_NAME} 2>/dev/null | tail -1 | awk '{ print $2" "$3" "$4 }')
;;
'setquota')
SOFT_QUOTA="$3"
HARD_QUOTA="$4"
if [ "x${SOFT_QUOTA}" == "x" ]; then
exit 1
fi
if [ "x${HARD_QUOTA}" == "x" ]; then
exit 1
fi
setquota ${USER_NAME} ${SOFT_QUOTA} ${HARD_QUOTA} 0 0 /home
;;
*)
echo "Usage: UserManager.sh COMMAND USER PASSWORD"
exit 1
......
......@@ -47,22 +47,29 @@ submit_vm.short_description = _('Submit VM')
class TemplateAdmin(contrib.admin.ModelAdmin):
model=models.Template
list_display = ('name', 'state', 'owner', 'system', 'public')
list_filter = ('owner', 'public')
class InstanceAdmin(contrib.admin.ModelAdmin):
model=models.Instance
actions = [update_state,submit_vm]
list_display = ['id', 'name', 'owner', 'state']
readonly_fields = ['ip', 'active_since', 'pw', 'template']
list_filter = ['owner', 'template', 'state']
actions = [update_state, submit_vm]
list_display = ('id', 'name', 'owner', 'state')
readonly_fields = ('ip', 'active_since', 'pw', 'template')
list_filter = ('owner', 'template', 'state')
class DiskAdmin(contrib.admin.ModelAdmin):
model=models.Disk
class NetworkAdmin(contrib.admin.ModelAdmin):
model=models.Network
class ShareAdmin(contrib.admin.ModelAdmin):
model=models.Network
list_filter = ('group', 'template', )
list_display = ('name', 'owner', 'template', 'group', )
contrib.admin.site.register(models.Template, TemplateAdmin)
contrib.admin.site.register(models.Instance, InstanceAdmin)
contrib.admin.site.register(models.Network, NetworkAdmin)
contrib.admin.site.register(models.Disk, DiskAdmin)
contrib.admin.site.register(models.Share, ShareAdmin)
contrib.admin.site.register(models.InstanceType)
from django_extensions.management.jobs import HourlyJob
class Job(HourlyJob):
help = "Suspend/delete expired Instances."
def execute(self):
# executing empty sample job TODO
pass
from one.models import *
from django_extensions.management.jobs import HourlyJob
class Job(HourlyJob):
help = "Update Disks and Networks from OpenNebula."
def execute(self):
Disk.update()
Network.update()
pass
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# , 2013.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-02-12 22:07+0100\n"
"PO-Revision-Date: 2013-02-10 14:27+0100\n"
"Last-Translator: \n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Lokalize 1.4\n"
#: static/cloud.js:159 static/cloud.js.c:657
msgid "Cancel"
msgstr "Mégsem"
#: static/cloud.js:173
#, c-format
msgid "Are you sure stopping %s?"
msgstr "Biztosan felfüggeszti a következőt: %s?"
#: static/cloud.js:174
msgid "Stop"
msgstr "Felfüggesztés"
#: static/cloud.js:183
#, c-format
msgid "Are you sure deleting %s?"
msgstr "Biztosan törli a következőt: %s?"
#: static/cloud.js:184 static/cloud.js.c:258 static/cloud.js.c:347
#: static/cloud.js:657
msgid "Delete"
msgstr "Törlés"
#: static/cloud.js:193
#, c-format
msgid "Are you sure restarting %s?"
msgstr "Biztosan újraindítja a következőt: %s?"
#: static/cloud.js:194
msgid "Restart"
msgstr "Újraindítás"
#: static/cloud.js:257
#, c-format
msgid "Are you sure deleting this %s template?"
msgstr "Biztosan törli a következő sablont: %s?"
#: static/cloud.js:347
#, c-format
msgid "Are you sure deleting <strong>%s</strong>"
msgstr "Törli a következő fájlt: <strong>%s</strong>"
#: static/cloud.js:471 static/cloud.js.c:480 static/cloud.js.c:489
#: static/cloud.js:596 static/cloud.js.c:651
msgid "file"
msgstr "fájl"
#: static/cloud.js:652
#, c-format
msgid "You are removing the file <strong>%s</strong>."
msgstr "Törli a következő fájlt: <strong>%s</strong>."
#: static/cloud.js:654
#, c-format
msgid "You are removing the folder <strong>%s</strong> (and its content)."
msgstr "Törli a következő könyvtárat és tartalmát: <strong>%s</strong>."
#: static/cloud.js:657
msgid "Are you sure?"
msgstr "Biztos benne?"
#: static/cloud.js:809 static/cloud.js.c:811
msgid "Upload"
msgstr "Feltöltés"
#: static/cloud.js:811
msgid "done, processing..."
msgstr "kész, feldolgozás..."
#: static/cloud.js:874
msgid "Please choose a different name."
msgstr "Kérem, válasszon eltérő nevet."
......@@ -43,6 +43,21 @@ body
margin-top:0;
padding:10px;
}
&.wide {
margin-right: 30px;
}
&.note {
background-color: #ffc;
}
ol {
margin-left: 2em;
li.done {
color: #aaa;
}
}
}
.big {
font-size: 2em;
}
.wm-list{
list-style: none;
......@@ -50,87 +65,166 @@ body
ul.messagelist
{
text-align:left;
margin:0;
padding:0 0 5px;
text-align:left;
margin:0;
padding:0 0 5px;
}
ul.messagelist li
{
font-size:12px;
display:block;
border-bottom:1px solid #ddd;
color:#666;
background:#ffc url(admin/img/icon_success.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
font-size:12px;
display:block;
border-bottom:1px solid #ddd;
color:#666;
background:#ffc url(admin/img/icon_success.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
}
ul.messagelist li.warning
{
background-image:url(admin/img/icon_alert.gif);
background-image:url(admin/img/icon_alert.gif);
}
ul.messagelist li.error
{
background-image:url(admin/img/icon_error.gif);
background-image:url(admin/img/icon_error.gif);
}
input.validated {
padding-right: 15px;
background-image: url(admin/img/icon_success.gif);
background-repeat: no-repeat;
background-position: right center;
&.error
{
background:#fcc url(admin/img/icon_error.gif) right center no-repeat;
padding-right: 15px;
}
}
.errornote
{
font-size:12px!important;
display:block;
border:1px solid red;
color:red;
background:#ffc url(admin/img/icon_error.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
font-size:12px!important;
display:block;
border:1px solid red;
color:red;
background:#ffc url(admin/img/icon_error.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
}
.errorlist li
{
font-size:12px!important;
display:block;
border:1px solid red;
color:#FFF;
background:red url(admin/img/icon_alert.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
font-size:12px!important;
display:block;
border:1px solid red;
color:#FFF;
background:red url(admin/img/icon_alert.gif) 5px .3em no-repeat;
margin:0 0 3px;
padding:4px 5px 4px 25px;
}
.errorlist li a
{
color:#FFF;
text-decoration:underline;
color:#FFF;
text-decoration:underline;
}
td ul.errorlist li
{
margin:0!important;
margin:0!important;
}
.errors
{
background:#ffc;
background:#ffc;
}
.errors input,.errors select,.errors textarea
{
border:1px solid red;
border:1px solid red;
}
div.system-message
{
background:#ffc;
font-size:.8em;
margin:10px;
padding:6px 8px;
background:#ffc;
font-size:.8em;
margin:10px;
padding:6px 8px;
}
div.system-message p.system-message-title
{
color:red;
background:#ffc url(admin/img/icon_error.gif) 5px .3em no-repeat;
margin:0;
padding:4px 5px 4px 25px;
color:red;
background:#ffc url(admin/img/icon_error.gif) 5px .3em no-repeat;
margin:0;
padding:4px 5px 4px 25px;
}
small {
font-size: 0.8em;
}
input {
border-radius: 2px;
border: 1px solid #777;
padding: 2px;
margin: 1px 5px;
background: rgba(0,0,0,0.1);
font-family: 'Titillium Web', sans-serif;
outline: none;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 5px rgba(0,0,0,0.2);
-webkit-transition: box-shadow 0.5s;
-moz-transition: box-shadow 0.5s;
-o-transition: box-shadow 0.5s;
transition: box-shadow 0.5s;
&:hover{
border: 1px solid #666;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 5px rgba(255,255,0,0.5);
};
&:focus{
border: 1px solid #555;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 10px rgba(255,255,0,0.8);
}
}
input[type=submit], input[type=button], input[type=reset]{
border: 1px solid #777;
cursor: pointer;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
-webkit-transition: background 0.5s;
-moz-transition: background 0.5s;
-o-transition: background 0.5s;
transition: background 0.5s;
&:hover{
background: rgba(0,0,0,0.2);
};
}
textarea {
border-radius: 2px;
border: 1px solid #777;
padding: 5px;
margin: 10px 5px;
width: 300px;
height: 100px;
background: rgba(0,0,0,0.1);
font-family: 'Titillium Web', sans-serif;
outline: none;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 5px rgba(0,0,0,0.2);
-webkit-transition: box-shadow 0.5s;
-moz-transition: box-shadow 0.5s;
-o-transition: box-shadow 0.5s;
transition: box-shadow 0.5s;
&:hover{
border: 1px solid #666;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 5px rgba(255,255,0,0.5);
};
&:focus{
border: 1px solid #555;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2), 0 0 10px rgba(255,255,0,0.8);
}
}
.hilight {
background-color: #ff6;
}
......@@ -12,13 +12,13 @@
background-color: rgba(0,0,0,0.6);
}
#modal-container{
width: 500px;
min-width: 500px;
position: fixed;
left:50%;
top:50%;
margin-left: -270px;
margin-top: -200px;
min-height: 200px;
min-height: 50px;
background-color: #fff;
border-radius: 4px;
padding: 20px;
......@@ -37,8 +37,8 @@
border-bottom: 1px dotted #999;
}
label{
float: left;
padding: 5px;
display: inline-block;
padding: 3px;
margin: 10px 5px;
}
h2{
......@@ -46,9 +46,13 @@
}
.progress{
text-align: center;
position: relative;
width: 100%;
height: 15px;
.bar{
height: 20px;
background-color: rgb(102, 196, 0);
border-right: 1px solid rgb(70, 134, 0);
}
.bar-container{
border: 1px solid #666;
......@@ -56,38 +60,27 @@
border-radius: 4px;
height: 20px;
position: absolute;
z-index: -1;
width: 500px;
}
h3 {
width: 100%;
position: absolute;
}
}
input[type=text] {
input[type=text], input[type=number], select {
display: block;
float: right;
padding: 5px;
margin: 10px 5px;
width: 200px;
text-align: right;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
border: none;
border-radius: 4px;
margin-top: 8px;
}
input:focus{
box-shadow: 0 0 12px rgb(128,128,255);
outline: none;
input[type=number] {
width: 4em;
}
textarea{
float: right;
padding: 5px;
margin: 10px 5px;
width: 300px;
height: 100px;
font-family:'Metrophobic',sans-serif;
font-size: 12px;
text-align: right;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
border: none;
}
nav{
margin-top: 15px;
a{
text-decoration: underline;
display: block;
......@@ -101,16 +94,26 @@
}
ul.radio{
float: right;
label {
float: left;
margin: 0;
}
}
.radio{
li{
float: left;
padding: 5px;
margin: 10px 5px;
margin: 5px 3px;
border-bottom: none !important;
}
label{
float: none !important;
}
}
.size-summary {
margin: 10px;
}
}
#new-template-name {
input {
margin: 10px 5px;
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 - A manóba</title>
</head>
<body>
<h1>404 - A manóba!</h1>
</body>
</html>
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!doctype html>
<html lang="{{LANGUAGE_CODE}}">
<head>
<meta charset="UTF-8">
<title>404 - {% trans ":(" %}</title>
</head>
<body>
<h1>404 - {% trans ":(" %}</h1>
</body>
</html>
......@@ -6,57 +6,55 @@
<title>{% block title %}IK Cloud{% endblock %}</title>
<link href="https://fonts.googleapis.com/css?family=Titillium+Web&amp;subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link rel="icon" type="image/png" href="/static/favicon.png" />
<link rel="icon" type="image/png" href="one/static/favicon.png">
<link rel="stylesheet/less" href="/static/style.less" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="/static/jquery.min.js"></script>
<script type="text/javascript" src="{% url django.views.i18n.javascript_catalog %}"></script>
<script type="text/javascript">
window.localStorage.clear();
</script>
<script src="/static/less.min.js"></script>
<script src="/static/knockout.min.js"></script>
<script type="text/javascript" src="/static/cloud.js"></script>
{{ form.media }}
{% block js %}{% endblock %}
</head>
<body>
<div id="header">
{% block login %}
<div id="loginblock"><p>
{% if user.is_authenticated %}
Bejelentkezve: {{ user.username }}.
<a href="/logout/">Kijelentkezés</a>.
{% if user.is_staff %}
<a href="/admin/">Admin</a>.
{% endif %}
{% else %}
<a href="/login/">Bejelentkezés</a>.
{% endif %}
<!--{% if lang == 'hu' %}
<a href="/language/en-US/">In English</a>.
{% else %}
<a href="/language/hu/">Magyarul</a>.
{% if autolang %}
<p style="position: absolute; top: 40px; right: 1em;" class="triangle-border top">Böngészője kifejezetten angol tartalmat kért.<br/>A <a href="/language/hu/">magyar változat</a> részletesebb és frissebb!</p>
{% endif %}
{% endif %}-->
</p>
</div>
<div id="loginblock"><p>
{% if user.is_authenticated %}
Bejelentkezve: {{ user.username }}.
<a href="/logout/">Kijelentkezés</a>.
{% if user.is_staff %}
<a href="/admin/">Admin</a>.
{% endif %}
{% else %}
<a href="/login/">Bejelentkezés</a>.
{% endif %}
{% if lang == 'hu' %}
<a href="/language/en/">In English</a>.
{% else %}
<a href="/language/hu/">Magyarul</a>.
{% endif %}
</p>
</div>
{% endblock %}
{% block header %}
{% block header_title %}
<h1><a href="/">IK Cloud <span style="font-size: 21px">&beta;</span></a></h1>
{% endblock %}
{% block header_title %}
<h1><a href="/">IK Cloud</a></h1>
{% endblock %}
{% endblock %}
</div>
{% block messages %}
{% if messages %}
<ul class="messagelist">{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}</ul>
{% endif %}
{% if messages %}
<ul class="messagelist">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock messages %}
<div id="content">
{% block content %}{% endblock %}
<div class="clear"></div>
......
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<div class="boxes" id="groups">
<div class="contentblock">
<h2>{% trans "My Groups" %}</h2>
<div class="content">
<ul class="wm-list">
{% if groups %}
{% for group in groups %}
<li class="wm">
<div class="summary">
<div class="name">{{ group.name }}</div>
<div class="actions">
<a href="#" class="delete" data-id="{{group.id}}" data-name="{{group.name}}">
<img src="/static/icons/minus-circle.png" alt="{% trans "Delete" %}" />
</a>
</div>
<div class="clear"></div>
</div>
<div class="details">
<ul>
<li>
{% trans "Course" %}:
<span class="value">
{% if group.course %}
{{group.course.name}}
{% else %}
{% trans "Not assigned" %}
{% endif %}
</span>
</li>
<li>
{% trans "Semester" %}:
<span class="value">{{group.semester.name}}</span>
</li>
<li>
{% trans "Owner(s)" %}:
<span class="value">{{group.owner_list}}</span>
</li>
<li>
{% trans "Member count" %}:
<span class="value">{{group.member_count}}</span>
</li>
<li>
&nbsp;
<span class="value">
<a href="/group/show/{{group.id}}">{% trans "More details" %}</a>
</span>
</li>
</ul>
</div>
</li>
{% endfor %}
{% else %}
<div id="new-wm-tooltip">
<div id="new-wm-tooltip-container">
<p>{% trans "You have no groups." %}</p>
<p>
{% trans "Create a new one, and add your students to the new group." %}
</p>
<div id="new-wm-tooltip-tail"></div>
</div>
</div>
{% endif %}
<li class="wm" id="new-group">
<div class="summary">
<div class="name">{% trans "Create new group" %}</div>
<div class="clear"></div>
</div>
<div id="new-group-wizard" style="display: none">
<form action="/group/new/" method="POST" class="wizard">
{% csrf_token %}
<h3>{% trans "Create new group" %}</h3>
<ul>
<li>
<label for="new-group-name">{% trans "Group name" %}</label>
<input type="text" name="name" id="new-group-name" />
</li>
<li>
<label for="new-group-semester">{% trans "Semester" %}</label>
<select name="semester" id="new-group-semester">
{% for semester in semesters %}
<option value="{{semester.id}}">{{semester.name}}</option>
{% endfor %}
</select>
</li>
<li>
<label for="new-group-members">{% trans "Members" %}</label>
<textarea name="members" placeholder="{% trans 'Student NEPTUN codes, one per line' %}" id="new-group-members"></textarea>
<div class="clear"></div>
</li>
</ul>
<nav>
<input type="reset" class="prev" value="{% trans "Cancel" %}" />
<input type="submit" class="next" value="{% trans "Done" %}" />
<div class="clear"></div>
</nav>
</form>
</div>
</li>
</ul>
</div>
</div>
</div>
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% if instances %}
{% for i in instances %}
<li class="wm">
<div class="summary">
<!--<div class="id">161</div>-->
<div class="name {% if i.state == 'ACTIVE' %}wm-on{% else %}wm-off{% endif %}">
{{i.name|truncatechars:20}}
</div>
<div class="status">
{{i.state}}
</div>
<div class="actions">
{% if i.state == 'ACTIVE' %}
<a href="{{i.get_connect_uri}}" title="Csatlakozás">
<img src="static/icons/plug.png" alt="connect" />
</a>
<a href="/vm/suspend/{{i.id}}/" onclick="alert('Hamarosan a mozikban.'); return false" title="Felfüggesztés">
<img src="/static/icons/control-pause.png" alt="pause" />
</a>
<a href="/vm/delete/{{i.id}}/" onclick="return confirm('Biztosan törli a gépet?')" title="Törlés">
<img src="/static/icons/minus-circle.png" alt="delete" />
</a>
<a href="/vm/reset/{{i.id}}/" onclick="alert('Hamarosan a mozikban.'); return false" title="Újraindítás">
<img src="/static/icons/arrow-circle-double.png" alt="↺" />
</a>
{% endif %}
{% if i.state == 'PENDING' %}
<img src="/static/load-2.gif" /> indítás...
{% endif %}
{% if i.state == 'SUSPENDED' %}
<a href="/vm/continue/{{i.id}}/" title="Folytatás">
<img src="/static/icons/control.png" alt="resume" />
</a>
<a href="/vm/delete/{{i.id}}/" onclick="return confirm('Biztosan törli a gépet?')" title="Törlés">
<img src="/static/icons/minus-circle.png" alt="delete" />
</a>
{% endif %}
{% if i.state == 'FAILED' %}
<a href="/vm/delete/{{i.id}}/" title="Törlés">
<img src="/static/icons/minus-circle.png" alt="delete" />
</a>
{% endif %}
</div>
<div class="clear"></div>
<li class="wm {% if id == i.id %}opened{% endif %}">
<div class="summary {% if id == i.id %}selected-summary{% endif %} {% if i.template.state == "NEW" %}unfinished{% endif %}">
<!--<div class="id"></div>
-->
<div class="name {% if i.state == 'ACTIVE' %}wm-on{% else %}wm-off{% endif %}">{{i.name|truncatechars:20}}</div>
<div class="status">{{i.state}}</div>
<div class="actions">
{% if i.waiting %}
<img src="/static/load.gif" />
{% elif i.state == 'ACTIVE' %}
<a href="{{i.get_connect_uri}}" data-id="{{ i.id }}" class="connect-vm-button" title="{% trans "Connect" %}">
<img src="/static/icons/plug.png" alt="{% trans "Connect" %}" />
</a>
<a href="#" class="stop-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Pause" %}">
<img src="/static/icons/control-pause.png" alt="{% trans "Pause" %}" />
</span>
<a href="#" class="delete-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Delete" %}">
<img src="/static/icons/minus-circle.png" alt="{% trans "Delete" %}" />
</a>
<a href="#" class="restart-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Restart" %}">
<img src="/static/icons/arrow-circle-double.png" alt="↺" />
</a>
{% elif i.state == 'PENDING' %}
<img src="/static/load.gif" />
{% trans "starting…" %}
{% elif i.state == 'STOPPED' %}
<a href="#" class="resume-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Resume" %}">
<img src="/static/icons/control.png" alt="{% trans "Resume" %}" />
</span>
<a href="#" class="delete-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Delete" %}">
<img src="/static/icons/minus-circle.png" alt="{% trans "Delete" %}" />
</a>
{% elif i.state == 'FAILED' %}
<a href="#" class="delete-vm-button" data-name="{{ i.name}}" data-id="{{ i.id }}" title="{% trans "Delete" %}">
<img src="/static/icons/minus-circle.png" alt="{% trans "Delete" %}" />
</a>
{% endif %}
</div>
<div class="details">
<div class="details-container"><h3>Részletek</h3>
<ul>
<li class="name">Gép neve: <span class="value">{{i.name}}</span></li>
<li class="os-linux">Operációs rendszer: <span class="value">{{i.template.disk.name}}</span></li>
<li class="type">Instance típus: <span class="value">{{i.template.instance_type.name}}</span></li>
<li class="date">Létrehozás dátuma: <span class="value">{{i.created_at}}</span></li>
<li class="date">Lejáratig: <span class="value"><abbr title="1 nap, 5 óra, 34 perc">1 nap</abbr></span></li>
<li>&nbsp;<span class="value"><a href="/vm/show/{{i.id}}/" title="{{i.name}}">További részletek</a></span></li>
</ul>
</div>
<div class="clear"></div>
</div>
<div class="details">
<div class="details-container">
<ul>
<li class="name">
{% trans "Hostname" %}:
<span class="value">{{i.name}}</span>
<div class="clear"></div>
</li>
<li class="os-{{i.template.os_type}}">
{% trans "System" %}:
<span class="value">{{i.template.system}}</span>
<div class="clear"></div>
</li>
<li class="template">
{% trans "Type" %}:
<span class="value">{{i.share.type}}</span>
<div class="clear"></div>
</li>
<li class="template">
{% trans "Share" %}:
<span class="value">{{i.share.name}}</span>
<div class="clear"></div>
</li>
<li class="template">
{% trans "Template" %}:
<span class="value">{{i.template.name}}</span>
<div class="clear"></div>
</li>
<li class="type">
{% trans "Size" %}:
<span class="value">
{{i.template.instance_type.name}}:
<span class="cpu">{{i.template.instance_type.CPU}}</span>
<span class="memory">{{i.template.instance_type.RAM}}</span>
<span class="credit">{{i.template.instance_type.credit}}</span>
</span>
</li>
<li class="date">
{% trans "Created at" %}:
<span class="value">{{i.created_at}}</span>
</li>
{% if i.time_of_suspend %}
<li class="date">
{% trans "time of suspend"|capfirst %}:
<span class="value"> <abbr title="{{i.time_of_suspend}}">{{i.time_of_suspend|timeuntil}}</abbr>
<a href="#" class="renew-vm-button renew-suspend-vm-button" data-id="{{ i.id }}" title="{% trans "Renew suspend time" %}">
<img src="/static/icons/control-double.png" alt="{% trans "Renew suspend time" %}" />
</a>
</span>
</li>
{% endif %}
{% if i.time_of_delete %}
<li class="date">
{% trans "time of delete"|capfirst %}:
<span class="value"> <abbr title="{{i.time_of_delete}}">{{i.time_of_delete|timeuntil}}</abbr>
<a href="#" class="renew-vm-button renew-delete-vm-button" data-id="{{ i.id }}" title="{% trans "Renew deletion time" %}">
<img src="/static/icons/control-double.png" alt="{% trans "Renew deletion time" %}" />
</a>
</span>
</li>
{% endif %}
<li>
&nbsp;
<span class="value">
<a href="/vm/show/{{i.id}}/" title="{{i.name}}">{% trans "More details" %}</a>
</span>
</li>
</ul>
</div>
</div>
</li>
{% endfor %}
{% else %}
<div id="new-wm-tooltip">
<div id="new-wm-tooltip-container">
<p>
Még nem indított egy gépet sem.
</p>
<p>
Válasszon sablont, és septiben használhatja a kiválasztott rendszert.
</p>
</div>
<div id="new-wm-tooltip-container">
<p>{% trans "You have not started any machines yet." %}</p>
<p>
{% trans "Choose a template, and you can use the system in a minute." %}
</p>
<div id="new-wm-tooltip-tail"></div>
</div>
</div>
{% endif %}
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="boxes">
<div class="contentblock" id="state">
<h2>Törlés</h2>
<div class="content">
<form action="{% url vm_delete i.id %}" method="post">
{% csrf_token %}
<p>Biztosan törli a gépet? <input type="submit" value="Törlés" /></p>
</form>
</div>
<div class="contentblock" id="state">
<h2>{% trans "Remove machine" %}</h2>
<div class="content">
<form action="{% url vm_delete i.id %}" method="post">
{% csrf_token %}
<p>
{% trans "Are you sure?" %}
<input type="submit" value="{% trans "Remove" %}" />
</p>
</form>
</div>
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="boxes">
<div class="contentblock" id="project_closed">
<h2>Bejelentkezés</h2>
<div class="content">
<p>A tesztüzem alatt a felhasználók belépése és azonosítása "önbevallásos" alapon működik (a jövőben <a href="http://login.bme.hu/">EduID-s</a> bejelentkezésre lesz lehetőség):</p>
<form action="/login/" method="POST">{% csrf_token %}
<p><label>NEPTUN-kód: <input maxlength="8" size="8" type="text" name="neptun" required pattern="[A-Z][A-Z0-9]{5}" onkeyup="javascript:this.value=this.value.toUpperCase();"/></label></p>
<p><label>Jelszó: <input maxlength="8" size="8" type="password" name="pw" required /></label></p>
<p><input type="hidden" name="next" value="{{nex}}"/><input type="submit" value="Bejelentkezés"/></label></p>
</div>
<h2>Bejelentkezés</h2>
<div class="content">
<p>
A tesztüzem alatt a felhasználók belépése és azonosítása "önbevallásos" alapon működik (a jövőben
<a href="http://login.bme.hu/">EduID-s</a>
bejelentkezésre lesz lehetőség):
</p>
<form action="/login/" method="POST">
{% csrf_token %}
<p>
<label>
NEPTUN-kód:
<input maxlength="8" size="8" type="text" name="neptun" required pattern="[A-Z][A-Z0-9]{5}" onkeyup="javascript:this.value=this.value.toUpperCase();"/>
</label>
</p>
<p>
<label>
Jelszó:
<input maxlength="8" size="8" type="password" name="pw" required />
</label>
</p>
<p>
<input type="hidden" name="next" value="{{nex}}"/>
<input type="submit" value="Bejelentkezés"/>
</p>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<form action="/ajax/share/{{base.id}}/" method="post" id="template-wizard">
{% csrf_token %}
<div id="new-share" class="wizard">
<h2>
{% blocktrans with t=base.name%}Sharing template: {{t}}{% endblocktrans %}
</h2>
{% if not group %}
<div class="hilight">
<p style="margin: 5px">{% trans "Choose a group" %}</p>
{% if not groups %}
<p style="margin: 5px">{% trans "You have no groups." %}</p>
{% endif %}
<ul style="margin: 5px">
<li>
<label for="share-group">{% trans "Group" %}</label>
<select id="share-group-select" name="group"{% if not groups %} disabled=""{%endif%}>
<option value="" selected="" class="dummy">--</option>
{%for i in groups%}
<option value="{{i.id}}">{{i.name}} ({{i.members.count}})</option>
{%endfor%}
</select>
<div class="clear"></div>
</li>
</ul>
</div>
<div class="clear"></div>
{% else %}
<input type="hidden" name="group" value="{{group.id}}" />
{% endif %}
<p>{% trans "Change the parameters as needed." %}</p>
<ul>
<li>
<label for="share-name">{% trans "Name of share" %}</label>
<input type="text" name="name" id="share-name" value="{{base.name}}" />
<div class="clear"></div>
</li>
<li class="li-share-type">
<label for="share-type">{% trans "Type" %}</label>
<ul class="radio">
{% for s in types %}
<li>
<label>
<input type="radio" name="type" value="{{s.id}}" id="share-type-{{s.id}}"
{% if s.default %}checked="checked"{% endif %} />
{{s.verbose_name}}
</label>
</li>
{% endfor %}
</ul>
{% for s in types %}
<p id="share-type-summary-{{s.id}}" class="type-summary clear"
{% if not s.default %}style="display:none"{% endif %}>
{{s.help_text}}
({% if s.suspend %}
<span class="suspend"
title="{% blocktrans with time=s.suspend %}Suspend after {{time}}.{%endblocktrans%}">{{s.suspendx|timeuntil}}</span>
{%endif%}{% if s.delete %}
<span class="delete"
title="{% blocktrans with time=s.delete %}Delete after {{time}}.{%endblocktrans%}">{{s.deletex|timeuntil}}</span>
{%endif%})
</p>
{% endfor %}
<div class="clear"></div>
</li>
<li>
<label for="share-instance-limit">{% trans "Maximal count of instances" %}</label>
<input type="number" name="instance_limit" id="share-instance-limit" value="10" />
<div class="clear"></div>
</li>
<li>
<label for="share-per-user-limit">{% trans "Maximal count of instaces/user" %}</label>
<input type="number" name="per_user_limit" id="share-per-user-limit" value="1" />
<div class="clear"></div>
</li>
<li style="border: none" class="clear">
<label for="share-description">{% trans "Description" %}</label>
<textarea name="description" id="share-description" style="text-align: left">{{base.description}}</textarea>
<div class="clear"></div>
</li>
</ul>
<nav>
<input type="reset" class="prev" value="{% trans "Cancel" %}" />
<input type="submit" value="{% trans "Share" %}" {% if not groups %}{%endif%}/>
<div class="clear"></div>
</nav>
<script type="text/javascript">
$(function(){
{% if not group %}
if (!$("select#share-group-select :selected").val())
$('#new-share input[type=submit]').attr("disabled", "")
else
$('#new-share input[type=submit]').removeAttr("disabled")
$('#new-share select#share-group-select').change(function(e) {
if (!$(":selected", this).val())
$('#new-share input[type=submit]').attr("disabled", "")
else
$('#new-share input[type=submit]').removeAttr("disabled")
})
{% endif %}
$('#new-share nav .prev').click(function(){
$('#modal').hide();
})
$("#new-share input[name='type']").click(function(e){ /* TODO */
var v = $("#new-share input[name='type']:checked").val();
$("p.type-summary").hide();
$("#share-type-summary-" + v).show();
});
})
</script>
</div>
</form>
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<form action="/ajax/templateWizard" method="post" id="template-wizard">
{% csrf_token %}
<div id="new-template-step-1" class="wizard">
<div class="progress">
<div class="bar-container">
<div class="bar" style="width: 33%"></div>
</div>
<h3>
{% blocktrans with step=1 all=3 %}{{step}}/{{all}}{% endblocktrans %}
</h3>
</div>
<h2>{% blocktrans with step=1 %}Step {{step}}{% endblocktrans %}</h2>
<p class="help">
{% trans "Please choose the base system you want to customize." %}
</p>
<div class="container">
<ul class="tpl-list modal">
{% if not templates %}
<p>{% trans "There are no available templates." %}</p>
{% endif %}
{% for m in templates %}
<li class="tpl">
<div class="summary">
<div class="name tpl os-{{m.os_type}}" title="{{m.description}}">
<label>
<input type="radio" name="base" value="{{m.id}}" />
{{m.name}}
{% if m.public %}
<img src="/static/icons/lock-small.png" alt="{% trans "locked" %}"
title="{% trans "This is a shared template." %}" />
{% endif %}
</label>
</div>
<div class="clear"></div>
</div>
</li>
{% endfor %}
</ul>
</div>
<nav>
<input type="reset" class="prev" value="{% trans "Cancel" %}" />
<input type="submit" class="next" value="{% trans "Next &raquo;" %}" />
<div class="clear"></div>
</nav>
<script type="text/javascript">
$(function(){
$('#template-wizard nav .prev').click(function(){
$('#modal').hide();
})
$('#template-wizard').unbind('submit').submit(function(e){
if ($("#template-wizard input[type=radio]:checked").length==0) {
$("#template-wizard .help").addClass('hilight');
}
else {
$.ajax({
'type': 'POST',
'url': '/ajax/templateWizard',
'data': $('#template-wizard').serialize(),
'success': function(data) {
$('#modal-container').html(data);
}
});
}
e.preventDefault();
e.stopPropagation();
return false;
})
})
</script>
</div>
</div>
</form>
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="boxes">
<div class="contentblock" id="state">
<h2>Csatlakozás</h2>
<div class="content">
<p style="font-size:25px; line-height:2em;text-align:center;"><a href="{{uri}}" class="button">Csatlakozás</a></p>
</div>
</div>
{% for box in boxes %}
{% if forloop.counter0|divisibleby:2 %}
<div class="contentblock" id="project_closed">
<h2>{{ box.title }}</h2>
<div class="content">
{{ box.text|safe }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
<div class="boxes">
{% for box in boxes %}
{% if forloop.counter|divisibleby:2 %}
<div class="contentblock" id="project_closed">
<h2>{{ box.title }}</h2>
<div class="content">
{{ box.text|safe }}
</div>
</div>
{% endif %}
{% endfor %}
<div class="contentblock" id="state">
<h2>A cluster állapota</h2>
<div class="content">
<p style="text-align:center"><a href="http://cloud.ik.bme.hu/"><img src="http://cloud.ik.bme.hu/cpu.png" alt="aktuális terhelés" style="border: solid #892034 2px; border-radius:10px;" /><img src="http://cloud.ik.bme.hu/ram.png" alt="aktuális memóriafoglaltság" style="border: solid #892034 2px; border-radius:10px;" /></a></p>
</div>
</div>
{% if user.is_staff %}
<p class="rightbuttons"><a class="button" href="{{ page_edit }}">Szerkesztés</a></p>
{% endif %}
</div>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment