Commit ac0e2797 by Kálmán Viktor

Merge branch 'master' into network-gui

Conflicts:
	cloud/urls.py
parents 689327cf 0e6e870e
import json
import subprocess
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
def process_debug(req): def process_debug(req):
return {'DEBUG': settings.DEBUG} return {'DEBUG': settings.DEBUG}
def process_stat(req): def process_stat(req):
if settings.STAT_DEBUG: if settings.STAT_DEBUG:
stat = { stat = {
...@@ -28,6 +27,7 @@ def process_stat(req): ...@@ -28,6 +27,7 @@ def process_stat(req):
'cloud_stat': stat, 'cloud_stat': stat,
} }
def process_release(req): def process_release(req):
return { return {
'release': settings.RELEASE, 'release': settings.RELEASE,
......
...@@ -274,11 +274,14 @@ DELETE_VM = True ...@@ -274,11 +274,14 @@ DELETE_VM = True
EMAIL_HOST = '152.66.243.92' # giccero ipv4 EMAIL_HOST = '152.66.243.92' # giccero ipv4
CLOUD_URL = 'https://cloud.ik.bme.hu/' CLOUD_URL = 'https://cloud.ik.bme.hu/'
try: try:
current_dir = os.getcwd()
os.chdir('/opt/webadmin/cloud/') os.chdir('/opt/webadmin/cloud/')
RELEASE = subprocess.check_output( RELEASE = subprocess.check_output(
['/usr/bin/git', 'describe', '--tags', '--abbrev=4']) ['/usr/bin/git', 'describe', '--tags', '--abbrev=4'])
except: except:
RELEASE = 'n/a' RELEASE = 'n/a'
finally:
os.chdir(current_dir)
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
......
# coding=utf8 # coding=utf8
# Django development settings for cloud project. # Django development settings for cloud project.
from .base import * from .base import * # NOQA
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
...@@ -8,11 +8,11 @@ EMAIL_HOST = "localhost" ...@@ -8,11 +8,11 @@ EMAIL_HOST = "localhost"
EMAIL_PORT = 1025 EMAIL_PORT = 1025
ADMINS = ( ADMINS = (
('Ory, Mate', 'orymate@localhost'), ('Ory, Mate', 'orymate@localhost'),
) )
MANAGERS = ( MANAGERS = (
('Ory Mate', 'maat@localhost'), ('Ory Mate', 'maat@localhost'),
) )
INSTALLED_APPS += ("debug_toolbar", ) INSTALLED_APPS += ("debug_toolbar", )
MIDDLEWARE_CLASSES += ("debug_toolbar.middleware.DebugToolbarMiddleware", ) MIDDLEWARE_CLASSES += ("debug_toolbar.middleware.DebugToolbarMiddleware", )
INTERNAL_IPS = [('2001:738:2001:4031:5:253:%d:0' % i) for i in xrange(1, 100)] INTERNAL_IPS = [('2001:738:2001:4031:5:253:%d:0' % i) for i in xrange(1, 100)]
......
# coding=utf8 # coding=utf8
# Django production settings for cloud project. # Django production settings for cloud project.
from .base import * from .base import * # NOQA
DEBUG = False DEBUG = False
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
......
...@@ -3,6 +3,7 @@ from django.conf.urls import patterns, include, url ...@@ -3,6 +3,7 @@ from django.conf.urls import patterns, include, url
from django.contrib import admin from django.contrib import admin
admin.autodiscover() admin.autodiscover()
js_info_dict = { js_info_dict = {
'packages': ('one', ), 'packages': ('one', ),
} }
......
...@@ -23,6 +23,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cloud.settings.prod") ...@@ -23,6 +23,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cloud.settings.prod")
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application() _application = get_wsgi_application()
def application(environ, start_response): def application(environ, start_response):
# copy DJANGO_* wsgi-env vars to process-env # copy DJANGO_* wsgi-env vars to process-env
for i in environ.keys(): for i in environ.keys():
......
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
from django.contrib import admin from django.contrib import admin
from firewall.models import * from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall,
Domain, Record, Blacklist)
from django import contrib from django import contrib
class RuleInline(contrib.admin.TabularInline): class RuleInline(contrib.admin.TabularInline):
model = Rule model = Rule
class RecordInline(contrib.admin.TabularInline): class RecordInline(contrib.admin.TabularInline):
model = Record model = Record
class HostAdmin(admin.ModelAdmin): class HostAdmin(admin.ModelAdmin):
list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac',
'shared_ip', 'owner', 'description', 'reverse', 'list_groups') 'shared_ip', 'owner', 'description', 'reverse',
'list_groups')
ordering = ('hostname', ) ordering = ('hostname', )
list_filter = ('owner', 'vlan', 'groups') list_filter = ('owner', 'vlan', 'groups')
search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac') search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac')
...@@ -26,42 +30,46 @@ class HostAdmin(admin.ModelAdmin): ...@@ -26,42 +30,46 @@ class HostAdmin(admin.ModelAdmin):
names = [group.name for group in instance.groups.all()] names = [group.name for group in instance.groups.all()]
return u', '.join(names) return u', '.join(names)
class HostInline(contrib.admin.TabularInline): class HostInline(contrib.admin.TabularInline):
model = Host model = Host
fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip', fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip',
'owner', 'reverse') 'owner', 'reverse')
class VlanAdmin(admin.ModelAdmin): class VlanAdmin(admin.ModelAdmin):
list_display = ('vid', 'name', 'ipv4', 'net_ipv4', 'ipv6', 'net_ipv6', list_display = ('vid', 'name', 'ipv4', 'net_ipv4', 'ipv6', 'net_ipv6',
'description', 'domain', 'snat_ip', ) 'description', 'domain', 'snat_ip', )
ordering = ('vid', ) ordering = ('vid', )
inlines = (RuleInline, ) inlines = (RuleInline, )
class RuleAdmin(admin.ModelAdmin): class RuleAdmin(admin.ModelAdmin):
list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction', list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction',
'accept', 'proto', 'sport', 'dport', 'nat', 'nat_dport', 'used_in') 'accept', 'proto', 'sport', 'dport', 'nat',
'nat_dport', 'used_in')
list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept', list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept',
'proto', 'nat') 'proto', 'nat')
def color_desc(self, instance): def color_desc(self, instance):
"""Returns a colorful description of the instance.""" """Returns a colorful description of the instance."""
return (u'<span style="color: #FF0000;">[%(type)s]</span> ' return (u'<span style="color: #FF0000;">[%(type)s]</span> '
u'%(src)s<span style="color: #0000FF;"> ▸ </span>%(dst)s ' u'%(src)s<span style="color: #0000FF;"> ▸ </span>%(dst)s '
u'%(para)s %(desc)s') % { u'%(para)s %(desc)s') % {
'type': instance.r_type, 'type': instance.r_type,
'src': (instance.foreign_network.name 'src': (instance.foreign_network.name
if instance.direction == '1' else instance.r_type), if instance.direction == '1' else instance.r_type),
'dst': (instance.r_type if instance.direction == '1' 'dst': (instance.r_type if instance.direction == '1'
else instance.foreign_network.name), else instance.foreign_network.name),
'para': (u'<span style="color: #00FF00;">' + 'para': (u'<span style="color: #00FF00;">' +
(('proto=%s ' % instance.proto) (('proto=%s ' % instance.proto)
if instance.proto else '') + if instance.proto else '') +
(('sport=%s ' % instance.sport) (('sport=%s ' % instance.sport)
if instance.sport else '') + if instance.sport else '') +
(('dport=%s ' % instance.dport) (('dport=%s ' % instance.dport)
if instance.dport else '') + if instance.dport else '') +
'</span>'), '</span>'),
'desc': instance.description} 'desc': instance.description}
color_desc.allow_tags = True color_desc.allow_tags = True
@staticmethod @staticmethod
...@@ -73,7 +81,7 @@ class RuleAdmin(admin.ModelAdmin): ...@@ -73,7 +81,7 @@ class RuleAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def used_in(instance): def used_in(instance):
for field in [instance.vlan, instance.vlangroup, instance.host, for field in [instance.vlan, instance.vlangroup, instance.host,
instance.hostgroup, instance.firewall]: instance.hostgroup, instance.firewall]:
if field: if field:
return unicode(field) + ' ' + field._meta.object_name return unicode(field) + ' ' + field._meta.object_name
...@@ -81,16 +89,20 @@ class RuleAdmin(admin.ModelAdmin): ...@@ -81,16 +89,20 @@ class RuleAdmin(admin.ModelAdmin):
class AliasAdmin(admin.ModelAdmin): class AliasAdmin(admin.ModelAdmin):
list_display = ('alias', 'host') list_display = ('alias', 'host')
class GroupAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin):
list_display = ('name', 'owner', 'description') list_display = ('name', 'owner', 'description')
inlines = (RuleInline, ) inlines = (RuleInline, )
class FirewallAdmin(admin.ModelAdmin): class FirewallAdmin(admin.ModelAdmin):
inlines = (RuleInline, ) inlines = (RuleInline, )
class DomainAdmin(admin.ModelAdmin): class DomainAdmin(admin.ModelAdmin):
list_display = ('name', 'owner') list_display = ('name', 'owner')
class RecordAdmin(admin.ModelAdmin): class RecordAdmin(admin.ModelAdmin):
list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner') list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner')
...@@ -104,6 +116,7 @@ class RecordAdmin(admin.ModelAdmin): ...@@ -104,6 +116,7 @@ class RecordAdmin(admin.ModelAdmin):
a = instance.get_data() a = instance.get_data()
return a['name'] if a else None return a['name'] if a else None
class BlacklistAdmin(admin.ModelAdmin): class BlacklistAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at') list_display = ('ipv4', 'reason', 'created_at', 'modified_at')
...@@ -116,4 +129,3 @@ admin.site.register(Firewall, FirewallAdmin) ...@@ -116,4 +129,3 @@ admin.site.register(Firewall, FirewallAdmin)
admin.site.register(Domain, DomainAdmin) admin.site.register(Domain, DomainAdmin)
admin.site.register(Record, RecordAdmin) admin.site.register(Record, RecordAdmin)
admin.site.register(Blacklist, BlacklistAdmin) admin.site.register(Blacklist, BlacklistAdmin)
...@@ -6,12 +6,14 @@ from django.utils.ipv6 import is_valid_ipv6_address ...@@ -6,12 +6,14 @@ from django.utils.ipv6 import is_valid_ipv6_address
from south.modelsinspector import add_introspection_rules from south.modelsinspector import add_introspection_rules
import re import re
mac_re = re.compile(r'^([0-9a-fA-F]{2}(:|$)){6}$') mac_re = re.compile(r'^([0-9a-fA-F]{2}(:|$)){6}$')
alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$') alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$')
domain_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]+)$') ipv4_re = re.compile('^[0-9]+\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$') reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
class MACAddressFormField(fields.RegexField): class MACAddressFormField(fields.RegexField):
default_error_messages = { default_error_messages = {
'invalid': _(u'Enter a valid MAC address.'), 'invalid': _(u'Enter a valid MAC address.'),
...@@ -20,8 +22,10 @@ class MACAddressFormField(fields.RegexField): ...@@ -20,8 +22,10 @@ class MACAddressFormField(fields.RegexField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MACAddressFormField, self).__init__(mac_re, *args, **kwargs) super(MACAddressFormField, self).__init__(mac_re, *args, **kwargs)
class MACAddressField(models.Field): class MACAddressField(models.Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs['max_length'] = 17 kwargs['max_length'] = 17
super(MACAddressField, self).__init__(*args, **kwargs) super(MACAddressField, self).__init__(*args, **kwargs)
...@@ -35,58 +39,68 @@ class MACAddressField(models.Field): ...@@ -35,58 +39,68 @@ class MACAddressField(models.Field):
return super(MACAddressField, self).formfield(**defaults) return super(MACAddressField, self).formfield(**defaults)
add_introspection_rules([], ["firewall\.fields\.MACAddressField"]) add_introspection_rules([], ["firewall\.fields\.MACAddressField"])
def val_alfanum(value): def val_alfanum(value):
"""Validate whether the parameter is a valid alphanumeric value.""" """Validate whether the parameter is a valid alphanumeric value."""
if not alfanum_re.match(value): if not alfanum_re.match(value):
raise ValidationError(_(u'%s - only letters, numbers, underscores ' raise ValidationError(_(u'%s - only letters, numbers, underscores '
'and hyphens are allowed!') % value) 'and hyphens are allowed!') % value)
def is_valid_domain(value): def is_valid_domain(value):
"""Check whether the parameter is a valid domain name.""" """Check whether the parameter is a valid domain name."""
return domain_re.match(value) is not None return domain_re.match(value) is not None
def val_domain(value): def val_domain(value):
"""Validate whether the parameter is a valid domin name.""" """Validate whether the parameter is a valid domin name."""
if not is_valid_domain(value): if not is_valid_domain(value):
raise ValidationError(_(u'%s - invalid domain name') % value) raise ValidationError(_(u'%s - invalid domain name') % value)
def is_valid_reverse_domain(value): def is_valid_reverse_domain(value):
"""Check whether the parameter is a valid reverse domain name.""" """Check whether the parameter is a valid reverse domain name."""
return reverse_domain_re.match(value) is not None return reverse_domain_re.match(value) is not None
def val_reverse_domain(value): def val_reverse_domain(value):
"""Validate whether the parameter is a valid reverse domain name.""" """Validate whether the parameter is a valid reverse domain name."""
if not is_valid_reverse_domain(value): if not is_valid_reverse_domain(value):
raise ValidationError(u'%s - invalid reverse domain name' % value) raise ValidationError(u'%s - invalid reverse domain name' % value)
def is_valid_ipv4_address(value): def is_valid_ipv4_address(value):
"""Check whether the parameter is a valid IPv4 address.""" """Check whether the parameter is a valid IPv4 address."""
return ipv4_re.match(value) is not None return ipv4_re.match(value) is not None
def val_ipv4(value): def val_ipv4(value):
"""Validate whether the parameter is a valid IPv4 address.""" """Validate whether the parameter is a valid IPv4 address."""
if not is_valid_ipv4_address(value): if not is_valid_ipv4_address(value):
raise ValidationError(_(u'%s - not an IPv4 address') % value) raise ValidationError(_(u'%s - not an IPv4 address') % value)
def val_ipv6(value): def val_ipv6(value):
"""Validate whether the parameter is a valid IPv6 address.""" """Validate whether the parameter is a valid IPv6 address."""
if not is_valid_ipv6_address(value): if not is_valid_ipv6_address(value):
raise ValidationError(_(u'%s - not an IPv6 address') % value) raise ValidationError(_(u'%s - not an IPv6 address') % value)
def val_mx(value): def val_mx(value):
"""Validate whether the parameter is a valid MX address definition. """Validate whether the parameter is a valid MX address definition.
Expected form is <priority>:<hostname>. Expected form is <priority>:<hostname>.
""" """
mx = self.address.split(':', 1) mx = value.split(':', 1)
if not (len(mx) == 2 and mx[0].isdigit() and if not (len(mx) == 2 and mx[0].isdigit() and
domain_re.match(mx[1])): domain_re.match(mx[1])):
raise ValidationError(_("Bad MX address format. " raise ValidationError(_("Bad MX address format. "
"Should be: <priority>:<hostname>")) "Should be: <priority>:<hostname>"))
def ipv4_2_ipv6(ipv4): def ipv4_2_ipv6(ipv4):
"""Convert IPv4 address string to IPv6 address string.""" """Convert IPv4 address string to IPv6 address string."""
val_ipv4(ipv4) val_ipv4(ipv4)
m = ipv4_re.match(ipv4) m = ipv4_re.match(ipv4)
return ("2001:738:2001:4031:%s:%s:%s:0" % return ("2001:738:2001:4031:%s:%s:%s:0" %
(m.group(1), m.group(2), m.group(3))) (m.group(1), m.group(2), m.group(3)))
from django.contrib import auth
from firewall import models from firewall import models
import os
import django.conf import django.conf
import subprocess import subprocess
import re import re
import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.db.models import Q from django.db.models import Q
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
class Firewall: class Firewall:
IPV6=False IPV6 = False
RULES = None RULES = None
RULES_NAT = [] RULES_NAT = []
vlans = None vlans = None
...@@ -29,13 +28,12 @@ class Firewall: ...@@ -29,13 +28,12 @@ class Firewall:
retval += ' --sport %s ' % rule.sport retval += ' --sport %s ' % rule.sport
if rule.dport: if rule.dport:
retval += ' --dport %s ' % (rule.nat_dport retval += ' --dport %s ' % (rule.nat_dport
if (repl and rule.nat and rule.direction == '1') if (repl and rule.nat and rule.direction == '1')
else rule.dport) else rule.dport)
elif rule.proto == 'icmp': elif rule.proto == 'icmp':
retval = '-p %s ' % rule.proto retval = '-p %s ' % rule.proto
return retval return retval
def iptables(self, s): def iptables(self, s):
"""Append rule to filter table.""" """Append rule to filter table."""
self.RULES.append(s) self.RULES.append(s)
...@@ -60,8 +58,8 @@ class Firewall: ...@@ -60,8 +58,8 @@ class Firewall:
if rule.direction == '0' and vlan.name == 'PUB': if rule.direction == '0' and vlan.name == 'PUB':
if rule.dport == 25: if rule.dport == 25:
self.iptables('-A PUB_OUT -s %s %s -p tcp ' self.iptables('-A PUB_OUT -s %s %s -p tcp '
'--dport 25 -j LOG_ACC' % '--dport 25 -j LOG_ACC' %
(ipaddr, rule.extra)) (ipaddr, rule.extra))
break break
action = 'PUB_OUT' action = 'PUB_OUT'
else: else:
...@@ -69,13 +67,14 @@ class Firewall: ...@@ -69,13 +67,14 @@ class Firewall:
else: else:
action = 'LOG_DROP' action = 'LOG_DROP'
if rule.direction == '1': # going TO host if rule.direction == '1': # going TO host
self.iptables('-A %s_%s -d %s %s %s -g %s' % (vlan, self.iptables('-A %s_%s -d %s %s %s -g %s' %
host.vlan, ipaddr, dport_sport, rule.extra, action)) (vlan, host.vlan, ipaddr, dport_sport,
rule.extra, action))
else: else:
self.iptables('-A %s_%s -s %s %s %s -g %s' % (host.vlan, self.iptables('-A %s_%s -s %s %s %s -g %s' %
vlan, ipaddr, dport_sport, rule.extra, action)) (host.vlan, vlan, ipaddr, dport_sport,
rule.extra, action))
def fw2vlan(self, rule): def fw2vlan(self, rule):
if not rule.foreign_network: if not rule.foreign_network:
...@@ -84,14 +83,14 @@ class Firewall: ...@@ -84,14 +83,14 @@ class Firewall:
dport_sport = self.dportsport(rule) dport_sport = self.dportsport(rule)
for vlan in rule.foreign_network.vlans.all(): for vlan in rule.foreign_network.vlans.all():
if rule.direction == '1': # going TO host if rule.direction == '1': # going TO host
self.iptables('-A INPUT -i %s %s %s -g %s' % self.iptables('-A INPUT -i %s %s %s -g %s' %
(vlan.interface, dport_sport, rule.extra, (vlan.interface, dport_sport, rule.extra,
'LOG_ACC' if rule.accept else 'LOG_DROP')) 'LOG_ACC' if rule.accept else 'LOG_DROP'))
else: else:
self.iptables('-A OUTPUT -o %s %s %s -g %s' % self.iptables('-A OUTPUT -o %s %s %s -g %s' %
(vlan.interface, dport_sport, rule.extra, (vlan.interface, dport_sport, rule.extra,
'LOG_ACC' if rule.accept else 'LOG_DROP')) 'LOG_ACC' if rule.accept else 'LOG_DROP'))
def vlan2vlan(self, l_vlan, rule): def vlan2vlan(self, l_vlan, rule):
if not rule.foreign_network: if not rule.foreign_network:
...@@ -108,13 +107,13 @@ class Firewall: ...@@ -108,13 +107,13 @@ class Firewall:
else: else:
action = 'LOG_DROP' action = 'LOG_DROP'
if rule.direction == '1': # going TO host if rule.direction == '1': # going TO host
self.iptables('-A %s_%s %s %s -g %s' % (vlan, l_vlan, self.iptables('-A %s_%s %s %s -g %s' %
dport_sport, rule.extra, action)) (vlan, l_vlan, dport_sport, rule.extra, action))
else: else:
self.iptables('-A %s_%s %s %s -g %s' % (l_vlan, vlan, self.iptables('-A %s_%s %s %s -g %s' % (l_vlan, vlan,
dport_sport, rule.extra, action)) dport_sport,
rule.extra, action))
def prerun(self): def prerun(self):
self.iptables('*filter') self.iptables('*filter')
...@@ -128,39 +127,39 @@ class Firewall: ...@@ -128,39 +127,39 @@ class Firewall:
self.iptables('-A LOG_DROP -p tcp --dport 445 -j DROP') self.iptables('-A LOG_DROP -p tcp --dport 445 -j DROP')
self.iptables('-A LOG_DROP -p udp --dport 137 -j DROP') self.iptables('-A LOG_DROP -p udp --dport 137 -j DROP')
self.iptables('-A LOG_DROP -j LOG --log-level 7 ' self.iptables('-A LOG_DROP -j LOG --log-level 7 '
'--log-prefix "[ipt][drop]"') '--log-prefix "[ipt][drop]"')
self.iptables('-A LOG_DROP -j DROP') self.iptables('-A LOG_DROP -j DROP')
self.iptables('-N LOG_ACC') self.iptables('-N LOG_ACC')
self.iptables('-A LOG_ACC -j LOG --log-level 7 ' self.iptables('-A LOG_ACC -j LOG --log-level 7 '
'--log-prefix "[ipt][isok]"') '--log-prefix "[ipt][isok]"')
self.iptables('-A LOG_ACC -j ACCEPT') self.iptables('-A LOG_ACC -j ACCEPT')
self.iptables('-N PUB_OUT') self.iptables('-N PUB_OUT')
self.iptables('-A FORWARD -m set --match-set blacklist src,dst -j DROP') self.iptables('-A FORWARD -m set --match-set blacklist src,dst '
'-j DROP')
self.iptables('-A FORWARD -m state --state INVALID -g LOG_DROP') self.iptables('-A FORWARD -m state --state INVALID -g LOG_DROP')
self.iptables('-A FORWARD -m state --state ESTABLISHED,RELATED ' self.iptables('-A FORWARD -m state --state ESTABLISHED,RELATED '
'-j ACCEPT') '-j ACCEPT')
self.iptables('-A FORWARD -p icmp --icmp-type echo-request ' self.iptables('-A FORWARD -p icmp --icmp-type echo-request '
'-g LOG_ACC') '-g LOG_ACC')
self.iptables('-A INPUT -m set --match-set blacklist src -j DROP') self.iptables('-A INPUT -m set --match-set blacklist src -j DROP')
self.iptables('-A INPUT -m state --state INVALID -g LOG_DROP') self.iptables('-A INPUT -m state --state INVALID -g LOG_DROP')
self.iptables('-A INPUT -i lo -j ACCEPT') self.iptables('-A INPUT -i lo -j ACCEPT')
self.iptables('-A INPUT -m state --state ESTABLISHED,RELATED ' self.iptables('-A INPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT') '-j ACCEPT')
self.iptables('-A OUTPUT -m state --state INVALID -g LOG_DROP') self.iptables('-A OUTPUT -m state --state INVALID -g LOG_DROP')
self.iptables('-A OUTPUT -o lo -j ACCEPT') self.iptables('-A OUTPUT -o lo -j ACCEPT')
self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED ' self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT') '-j ACCEPT')
def postrun(self): def postrun(self):
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 ' self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 '
'-j LOG_ACC') '-j LOG_ACC')
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 445 ' self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 445 '
'-j LOG_ACC') '-j LOG_ACC')
self.iptables('-A PUB_OUT -p tcp --dport 25 -j LOG_DROP') self.iptables('-A PUB_OUT -p tcp --dport 25 -j LOG_DROP')
self.iptables('-A PUB_OUT -p tcp --dport 445 -j LOG_DROP') self.iptables('-A PUB_OUT -p tcp --dport 445 -j LOG_DROP')
self.iptables('-A PUB_OUT -p udp --dport 445 -j LOG_DROP') self.iptables('-A PUB_OUT -p udp --dport 445 -j LOG_DROP')
...@@ -171,9 +170,6 @@ class Firewall: ...@@ -171,9 +170,6 @@ class Firewall:
self.iptables('-A OUTPUT -g LOG_DROP') self.iptables('-A OUTPUT -g LOG_DROP')
self.iptables('COMMIT') self.iptables('COMMIT')
def ipt_nat(self): def ipt_nat(self):
self.iptablesnat('*nat') self.iptablesnat('*nat')
self.iptablesnat(':PREROUTING ACCEPT [0:0]') self.iptablesnat(':PREROUTING ACCEPT [0:0]')
...@@ -187,34 +183,37 @@ class Firewall: ...@@ -187,34 +183,37 @@ class Firewall:
dport_sport = self.dportsport(rule, False) dport_sport = self.dportsport(rule, False)
if host.vlan.snat_ip: if host.vlan.snat_ip:
self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT ' self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT '
'--to-destination %s:%s' % (host.pub_ipv4, '--to-destination %s:%s' %
dport_sport, rule.extra, host.ipv4, (host.pub_ipv4, dport_sport, rule.extra,
rule.nat_dport)) host.ipv4, rule.nat_dport))
# rules for machines with dedicated public IP # rules for machines with dedicated public IP
for host in self.hosts.exclude(shared_ip=True): for host in self.hosts.exclude(shared_ip=True):
if host.pub_ipv4: if host.pub_ipv4:
self.iptablesnat('-A PREROUTING -d %s -j DNAT ' self.iptablesnat('-A PREROUTING -d %s -j DNAT '
'--to-destination %s' % (host.pub_ipv4, host.ipv4)) '--to-destination %s' %
(host.pub_ipv4, host.ipv4))
self.iptablesnat('-A POSTROUTING -s %s -j SNAT ' self.iptablesnat('-A POSTROUTING -s %s -j SNAT '
'--to-source %s' % (host.ipv4, host.pub_ipv4)) '--to-source %s' %
(host.ipv4, host.pub_ipv4))
# default NAT rules for VLANs # default NAT rules for VLANs
for s_vlan in self.vlans: for s_vlan in self.vlans:
if s_vlan.snat_ip: if s_vlan.snat_ip:
for d_vlan in s_vlan.snat_to.all(): for d_vlan in s_vlan.snat_to.all():
self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT ' self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT '
'--to-source %s' % (s_vlan.net_ipv4(), '--to-source %s' %
d_vlan.interface, s_vlan.snat_ip)) (s_vlan.net_ipv4(), d_vlan.interface,
s_vlan.snat_ip))
# hard-wired rules # hard-wired rules
self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT ' self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT '
'--to-source 10.3.255.254') # man elerheto legyen '--to-source 10.3.255.254') # man elerheto legyen
self.iptablesnat('-A POSTROUTING -o vlan0008 -j SNAT ' self.iptablesnat('-A POSTROUTING -o vlan0008 -j SNAT '
'--to-source 10.0.0.247') # wolf network for printing '--to-source 10.0.0.247') # wolf network for printing
self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -p udp --dport 53 -o vlan0002 -j SNAT ' self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -p udp --dport 53 '
'--to-source %s' % self.pub.ipv4) # kulonben nem megy a dns man-ban '-o vlan0002 -j SNAT ''--to-source %s' %
self.pub.ipv4) # kulonben nem megy a dns man-ban
self.iptablesnat('COMMIT') self.iptablesnat('COMMIT')
...@@ -234,7 +233,8 @@ class Firewall: ...@@ -234,7 +233,8 @@ class Firewall:
for d_vlan in self.vlans: for d_vlan in self.vlans:
self.iptables('-N %s_%s' % (s_vlan, d_vlan)) self.iptables('-N %s_%s' % (s_vlan, d_vlan))
self.iptables('-A FORWARD -i %s -o %s -g %s_%s' % self.iptables('-A FORWARD -i %s -o %s -g %s_%s' %
(s_vlan.interface, d_vlan.interface, s_vlan, d_vlan)) (s_vlan.interface, d_vlan.interface, s_vlan,
d_vlan))
# hosts' rules # hosts' rules
for i_vlan in self.vlans: for i_vlan in self.vlans:
...@@ -263,8 +263,8 @@ class Firewall: ...@@ -263,8 +263,8 @@ class Firewall:
self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES] self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES]
def __init__(self, IPV6=False): def __init__(self, IPV6=False):
self.RULES=[] self.RULES = []
self.RULES_NAT=[] self.RULES_NAT = []
self.IPV6 = IPV6 self.IPV6 = IPV6
self.vlans = models.Vlan.objects.all() self.vlans = models.Vlan.objects.all()
self.hosts = models.Host.objects.all() self.hosts = models.Host.objects.all()
...@@ -277,32 +277,37 @@ class Firewall: ...@@ -277,32 +277,37 @@ class Firewall:
def reload(self): def reload(self):
if self.IPV6: if self.IPV6:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/ip6tables-restore', '-c'], '/usr/bin/sudo',
shell=False, stdin=subprocess.PIPE) '/sbin/ip6tables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n') process.communicate('\n'.join(self.RULES) + '\n')
else: else:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/iptables-restore', '-c'], '/usr/bin/sudo',
shell=False, stdin=subprocess.PIPE) '/sbin/iptables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n' + process.communicate('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n') '\n'.join(self.RULES_NAT) + '\n')
def get(self): def get(self):
if self.IPV6: if self.IPV6:
return { 'filter': self.RULES, } return {'filter': self.RULES, }
else: else:
return { 'filter': self.RULES, 'nat': self.RULES_NAT } return {'filter': self.RULES, 'nat': self.RULES_NAT}
def show(self): def show(self):
if self.IPV6: if self.IPV6:
return '\n'.join(self.RULES) + '\n' return '\n'.join(self.RULES) + '\n'
else: else:
return ('\n'.join(self.RULES) + '\n' + return ('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n') '\n'.join(self.RULES_NAT) + '\n')
def ipset(): def ipset():
week = datetime.now()-timedelta(days=2) week = datetime.now() - timedelta(days=2)
return models.Blacklist.objects.filter(Q(type='tempban', modified_at__gte=week) | Q(type='permban')).values('ipv4', 'reason') filter_ban = (Q(type='tempban', modified_at__gte=week) |
Q(type='permban')).values('ipv4', 'reason')
return models.Blacklist.objects.filter(filter_ban)
def ipv6_to_octal(ipv6): def ipv6_to_octal(ipv6):
...@@ -319,14 +324,16 @@ def ipv6_to_octal(ipv6): ...@@ -319,14 +324,16 @@ def ipv6_to_octal(ipv6):
octets.append(int(part[2:], 16)) octets.append(int(part[2:], 16))
return '\\' + '\\'.join(['%03o' % x for x in octets]) return '\\' + '\\'.join(['%03o' % x for x in octets])
def ipv4_to_arpa(ipv4, cname=False): def ipv4_to_arpa(ipv4, cname=False):
m2 = re.search(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', ipv4) m2 = re.search(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', ipv4)
if cname: if cname:
return ('%s.dns1.%s.%s.%s.in-addr.arpa' % return ('%s.dns1.%s.%s.%s.in-addr.arpa' %
(m2.group(4), m2.group(3), m2.group(2), m2.group(1))) (m2.group(4), m2.group(3), m2.group(2), m2.group(1)))
else: else:
return ('%s.%s.%s.%s.in-addr.arpa' % return ('%s.%s.%s.%s.in-addr.arpa' %
(m2.group(4), m2.group(3), m2.group(2), m2.group(1))) (m2.group(4), m2.group(3), m2.group(2), m2.group(1)))
def ipv6_to_arpa(ipv6): def ipv6_to_arpa(ipv6):
while len(ipv6.split(':')) < 8: while len(ipv6.split(':')) < 8:
...@@ -355,11 +362,11 @@ def ipv6_to_arpa(ipv6): ...@@ -355,11 +362,11 @@ def ipv6_to_arpa(ipv6):
def dns(): def dns():
vlans = models.Vlan.objects.all() vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$') # regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
DNS = [] DNS = []
for i_vlan in vlans: for i_vlan in vlans:
m = regex.search(i_vlan.net4) # m = regex.search(i_vlan.net4)
rev = i_vlan.reverse_domain rev = i_vlan.reverse_domain
for i_host in i_vlan.host_set.all(): for i_host in i_vlan.host_set.all():
...@@ -367,31 +374,32 @@ def dns(): ...@@ -367,31 +374,32 @@ def dns():
not i_host.shared_ip else i_host.ipv4) not i_host.shared_ip else i_host.ipv4)
i = ipv4.split('.', 4) i = ipv4.split('.', 4)
reverse = (i_host.reverse if i_host.reverse and reverse = (i_host.reverse if i_host.reverse and
len(i_host.reverse) else i_host.get_fqdn()) len(i_host.reverse) else i_host.get_fqdn())
# ipv4 # ipv4
if i_host.ipv4: if i_host.ipv4:
DNS.append("^%s:%s:%s" % ( DNS.append("^%s:%s:%s" % (
(rev % { 'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]), (rev % {'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]),
'd': int(i[3]) }), 'd': int(i[3])}),
reverse, models.settings['dns_ttl'])) reverse, models.settings['dns_ttl']))
# ipv6 # ipv6
if i_host.ipv6: if i_host.ipv6:
DNS.append("^%s:%s:%s" % (ipv6_to_arpa(i_host.ipv6), DNS.append("^%s:%s:%s" % (ipv6_to_arpa(i_host.ipv6),
reverse, models.settings['dns_ttl'])) reverse, models.settings['dns_ttl']))
for domain in models.Domain.objects.all(): for domain in models.Domain.objects.all():
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % (domain.name, DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" %
settings['dns_hostname'], models.settings['dns_ttl'])) (domain.name, settings['dns_hostname'],
models.settings['dns_ttl']))
for r in models.Record.objects.all(): for r in models.Record.objects.all():
d = r.get_data() d = r.get_data()
if d['type'] == 'A': if d['type'] == 'A':
DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl'])) DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'AAAA': elif d['type'] == 'AAAA':
DNS.append(":%s:28:%s:%s" % (d['name'], DNS.append(":%s:28:%s:%s" %
ipv6_to_octal(d['address']), d['ttl'])) (d['name'], ipv6_to_octal(d['address']), d['ttl']))
elif d['type'] == 'NS': elif d['type'] == 'NS':
DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl'])) DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'CNAME': elif d['type'] == 'CNAME':
...@@ -399,15 +407,16 @@ def dns(): ...@@ -399,15 +407,16 @@ def dns():
elif d['type'] == 'MX': elif d['type'] == 'MX':
mx = d['address'].split(':', 2) mx = d['address'].split(':', 2)
DNS.append("@%(fqdn)s::%(mx)s:%(dist)s:%(ttl)s" % DNS.append("@%(fqdn)s::%(mx)s:%(dist)s:%(ttl)s" %
{'fqdn': d['name'], 'mx': mx[1], 'dist': mx[0], {'fqdn': d['name'], 'mx': mx[1], 'dist': mx[0],
'ttl': d['ttl']}) 'ttl': d['ttl']})
elif d['type'] == 'PTR': elif d['type'] == 'PTR':
DNS.append("^%s:%s:%s" % (d['name'], d['address'], d['ttl'])) DNS.append("^%s:%s:%s" % (d['name'], d['address'], d['ttl']))
return DNS return DNS
process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' % process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' %
settings['dns_hostname']], shell=False, stdin=subprocess.PIPE) settings['dns_hostname']],
process.communicate("\n".join(DNS)+"\n") shell=False, stdin=subprocess.PIPE)
process.communicate("\n".join(DNS) + "\n")
# print "\n".join(DNS)+"\n" # print "\n".join(DNS)+"\n"
...@@ -420,10 +429,11 @@ def prefix_to_mask(prefix): ...@@ -420,10 +429,11 @@ def prefix_to_mask(prefix):
t[i] = 256 - (2 ** ((i + 1) * 8 - prefix)) t[i] = 256 - (2 ** ((i + 1) * 8 - prefix))
return ".".join([str(i) for i in t]) return ".".join([str(i) for i in t])
def dhcp(): def dhcp():
vlans = models.Vlan.objects.all() vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+\s+' regex = re.compile(r'^([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+\s+'
r'([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+$') r'([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+$')
DHCP = [] DHCP = []
# /tools/dhcp3/dhcpd.conf.generated # /tools/dhcp3/dhcpd.conf.generated
...@@ -432,7 +442,7 @@ def dhcp(): ...@@ -432,7 +442,7 @@ def dhcp():
if(i_vlan.dhcp_pool): if(i_vlan.dhcp_pool):
m = regex.search(i_vlan.dhcp_pool) m = regex.search(i_vlan.dhcp_pool)
if(m or i_vlan.dhcp_pool == "manual"): if(m or i_vlan.dhcp_pool == "manual"):
DHCP.append (''' DHCP.append('''
# %(name)s - %(interface)s # %(name)s - %(interface)s
subnet %(net)s netmask %(netmask)s { subnet %(net)s netmask %(netmask)s {
%(extra)s; %(extra)s;
...@@ -444,7 +454,7 @@ def dhcp(): ...@@ -444,7 +454,7 @@ def dhcp():
authoritative; authoritative;
filename \"pxelinux.0\"; filename \"pxelinux.0\";
allow bootp; allow booting; allow bootp; allow booting;
}''' % { }''' % {
'net': i_vlan.net4, 'net': i_vlan.net4,
'netmask': prefix_to_mask(i_vlan.prefix4), 'netmask': prefix_to_mask(i_vlan.prefix4),
'domain': i_vlan.domain, 'domain': i_vlan.domain,
...@@ -452,14 +462,14 @@ def dhcp(): ...@@ -452,14 +462,14 @@ def dhcp():
'ntp': i_vlan.ipv4, 'ntp': i_vlan.ipv4,
'dnsserver': settings['rdns_ip'], 'dnsserver': settings['rdns_ip'],
'extra': ("range %s" % i_vlan.dhcp_pool 'extra': ("range %s" % i_vlan.dhcp_pool
if m else "deny unknown-clients"), if m else "deny unknown-clients"),
'interface': i_vlan.interface, 'interface': i_vlan.interface,
'name': i_vlan.name, 'name': i_vlan.name,
'tftp': i_vlan.ipv4 'tftp': i_vlan.ipv4
}) })
for i_host in i_vlan.host_set.all(): for i_host in i_vlan.host_set.all():
DHCP.append (''' DHCP.append('''
host %(hostname)s { host %(hostname)s {
hardware ethernet %(mac)s; hardware ethernet %(mac)s;
fixed-address %(ipv4)s; fixed-address %(ipv4)s;
...@@ -471,23 +481,8 @@ def dhcp(): ...@@ -471,23 +481,8 @@ def dhcp():
return DHCP return DHCP
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'cat > /tools/dhcp3/dhcpd.conf.generated;' 'cat > /tools/dhcp3/dhcpd.conf.generated;'
'sudo /etc/init.d/isc-dhcp-server restart'], shell=False, 'sudo /etc/init.d/isc-dhcp-server restart'],
stdin=subprocess.PIPE) shell=False, stdin=subprocess.PIPE)
# print "\n".join(DHCP)+"\n" # print "\n".join(DHCP)+"\n"
process.communicate("\n".join(DHCP)+"\n") process.communicate("\n".join(DHCP) + "\n")
'''
i=2
for mac, name, ipend in [("18:a9:05:64:19:aa", "mega6", 16), ("00:1e:0b:e9:79:1e", "blade1", 21), ("00:22:64:9c:fd:34", "blade2", 22), ("00:1e:0b:ec:65:46", "blade3", 23), ("b4:b5:2f:61:d2:5a", "cloud-man", 1)]:
h1 = models.Host(hostname= name, vlan=models.Vlan.objects.get(vid=3), mac=mac, ipv4="10.3.1.%d" % ipend, ipv6="2001:738:2001:4031:3:1:%d:0" % ipend, owner=auth.models.User.objects.get(username="bd"))
try:
h1.save()
h1.groups.add(models.Group.objects.get(name="netezhet manbol"))
h1.save()
# i = i + 1
except:
print "nemok %s" % name
'''
...@@ -4,7 +4,9 @@ from django.contrib.auth.models import User ...@@ -4,7 +4,9 @@ from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.forms import ValidationError from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from firewall.fields import * from firewall.fields import (MACAddressField, val_alfanum, val_reverse_domain,
val_domain, val_ipv4, val_ipv6, val_mx,
ipv4_2_ipv6)
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
import django.conf import django.conf
from django.db.models.signals import post_save from django.db.models.signals import post_save
...@@ -85,7 +87,7 @@ class Rule(models.Model): ...@@ -85,7 +87,7 @@ class Rule(models.Model):
"(if type is vlan).")) "(if type is vlan)."))
vlangroup = models.ForeignKey('VlanGroup', related_name="rules", vlangroup = models.ForeignKey('VlanGroup', related_name="rules",
blank=True, null=True, verbose_name=_( blank=True, null=True, verbose_name=_(
"vlan group"), "vlan group"),
help_text=_("Group of vlans the rule " help_text=_("Group of vlans the rule "
"applies to (if type is vlan).")) "applies to (if type is vlan)."))
host = models.ForeignKey('Host', related_name="rules", blank=True, host = models.ForeignKey('Host', related_name="rules", blank=True,
...@@ -185,15 +187,17 @@ class Vlan(models.Model): ...@@ -185,15 +187,17 @@ class Vlan(models.Model):
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True, ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True,
verbose_name=_('IPv4 address'), verbose_name=_('IPv4 address'),
help_text=_( help_text=_(
'The IPv4 address of the gateway. ' 'The IPv4 address of the gateway. '
'Recommended value is the last valid ' 'Recommended value is the last '
'address of the subnet, for example ' 'valid address of the subnet, '
'10.4.255.254 for 10.4.0.0/16.')) 'for example '
'10.4.255.254 for 10.4.0.0/16.'))
ipv6 = models.GenericIPAddressField(protocol='ipv6', ipv6 = models.GenericIPAddressField(protocol='ipv6',
unique=True, unique=True,
verbose_name=_('IPv6 address'), verbose_name=_('IPv6 address'),
help_text=_( help_text=_(
'The IPv6 address of the gateway.')) 'The IPv6 address of the '
'gateway.'))
snat_ip = models.GenericIPAddressField(protocol='ipv4', blank=True, snat_ip = models.GenericIPAddressField(protocol='ipv4', blank=True,
null=True, null=True,
verbose_name=_('NAT IP address'), verbose_name=_('NAT IP address'),
......
from celery.task import Task, PeriodicTask from celery.task import Task, PeriodicTask
import celery import celery
from django.core.cache import cache from django.core.cache import cache
import os
import time
from firewall.fw import * from firewall.fw import *
import django.conf import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
@celery.task @celery.task
def reload_dns_task(data): def reload_dns_task(data):
pass pass
@celery.task @celery.task
def reload_firewall_task(data4, data6): def reload_firewall_task(data4, data6):
pass pass
@celery.task @celery.task
def reload_dhcp_task(data): def reload_dhcp_task(data):
pass pass
@celery.task @celery.task
def reload_blacklist_task(data): def reload_blacklist_task(data):
pass pass
class Periodic(PeriodicTask): class Periodic(PeriodicTask):
run_every = timedelta(seconds=10) run_every = timedelta(seconds=10)
...@@ -48,6 +54,7 @@ class Periodic(PeriodicTask): ...@@ -48,6 +54,7 @@ class Periodic(PeriodicTask):
reload_blacklist_task.delay(list(ipset())) reload_blacklist_task.delay(list(ipset()))
print "blacklist ujratoltese kesz" print "blacklist ujratoltese kesz"
class ReloadTask(Task): class ReloadTask(Task):
def run(self, type='Host'): def run(self, type='Host'):
...@@ -64,4 +71,3 @@ class ReloadTask(Task): ...@@ -64,4 +71,3 @@ class ReloadTask(Task):
cache.add("blacklist_lock", "true", 30) cache.add("blacklist_lock", "true", 30)
print type print type
from django.test import TestCase from django.test import TestCase
from admin import HostAdmin from admin import HostAdmin
class MockInstance: class MockInstance:
def __init__(self, groups): def __init__(self, groups):
self.groups = MockGroups(groups) self.groups = MockGroups(groups)
class MockGroup: class MockGroup:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
class MockGroups: class MockGroups:
def __init__(self, groups): def __init__(self, groups):
self.groups = groups self.groups = groups
...@@ -16,6 +19,7 @@ class MockGroups: ...@@ -16,6 +19,7 @@ class MockGroups:
def all(self): def all(self):
return self.groups return self.groups
class HostAdminTestCase(TestCase): class HostAdminTestCase(TestCase):
def test_no_groups(self): def test_no_groups(self):
instance = MockInstance([]) instance = MockInstance([])
...@@ -29,6 +33,6 @@ class HostAdminTestCase(TestCase): ...@@ -29,6 +33,6 @@ class HostAdminTestCase(TestCase):
def test_multiple_groups(self): def test_multiple_groups(self):
instance = MockInstance([MockGroup("alma"), instance = MockInstance([MockGroup("alma"),
MockGroup("korte"), MockGroup("szilva")]) MockGroup("korte"), MockGroup("szilva")])
l = HostAdmin.list_groups(instance) l = HostAdmin.list_groups(instance)
self.assertEqual(l, "alma, korte, szilva") self.assertEqual(l, "alma, korte, szilva")
...@@ -2,12 +2,10 @@ import base64 ...@@ -2,12 +2,10 @@ import base64
import datetime import datetime
import json import json
import re import re
import sys
from django.conf import settings from django.conf import settings
from django.db import IntegrityError from django.db import IntegrityError
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import translation from django.utils import translation
from django.utils.timezone import utc from django.utils.timezone import utc
...@@ -15,13 +13,13 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -15,13 +13,13 @@ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from celery.task.control import inspect
from tasks import * from tasks import *
from firewall.fw import * from firewall.fw import *
from firewall.models import * from firewall.models import *
from one.tasks import SendMailTask from one.tasks import SendMailTask
def reload_firewall(request): def reload_firewall(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
if request.user.is_superuser: if request.user.is_superuser:
...@@ -34,34 +32,46 @@ def reload_firewall(request): ...@@ -34,34 +32,46 @@ def reload_firewall(request):
html = _("Dear anonymous, you've not signed in yet!") html = _("Dear anonymous, you've not signed in yet!")
return HttpResponse(html) return HttpResponse(html)
@csrf_exempt @csrf_exempt
@require_POST @require_POST
def firewall_api(request): def firewall_api(request):
try: try:
data=json.loads(base64.b64decode(request.POST["data"])) data = json.loads(base64.b64decode(request.POST["data"]))
command = request.POST["command"] command = request.POST["command"]
if data["password"] != "bdmegintelrontottaanetet": if data["password"] != "bdmegintelrontottaanetet":
raise Exception(_("Wrong password.")) raise Exception(_("Wrong password."))
if command == "blacklist": if command == "blacklist":
obj, created = Blacklist.objects.get_or_create(ipv4=data["ip"]) obj, created = Blacklist.objects.get_or_create(ipv4=data["ip"])
obj.reason=data["reason"] obj.reason = data["reason"]
obj.snort_message=data["snort_message"] obj.snort_message = data["snort_message"]
if created: if created:
try: try:
obj.host = Host.objects.get(ipv4=data["ip"]) obj.host = Host.objects.get(ipv4=data["ip"])
user = obj.host.owner user = obj.host.owner
lang = user.person_set.all()[0].language lang = user.person_set.all()[0].language
translation.activate(lang) translation.activate(lang)
msg = render_to_string('mails/notification-ban-now.txt', msg = render_to_string(
{ 'user': user, 'mails/notification-ban-now.txt',
'bl': obj, {
'instance:': obj.host.instance_set.get(), 'user': user,
'url': settings.CLOUD_URL} ) 'bl': obj,
SendMailTask.delay(to=obj.host.owner.email, subject='[IK Cloud] %s' % obj.host.instance_set.get().name, msg=msg, sender=u'cloud@ik.bme.hu') 'instance:': obj.host.instance_set.get(),
except (Host.DoesNotExist, ValidationError, IntegrityError, AttributeError): 'url': settings.CLOUD_URL
})
SendMailTask.delay(
to=obj.host.owner.email,
subject='[IK Cloud] %s' %
obj.host.instance_set.get().name,
msg=msg, sender=u'cloud@ik.bme.hu')
except (Host.DoesNotExist, ValidationError,
IntegrityError, AttributeError):
pass pass
if obj.type == 'tempwhite' and obj.modified_at + datetime.timedelta(minutes=1) < datetime.datetime.utcnow().replace(tzinfo=utc):
modified = obj.modified_at + datetime.timedelta(minutes=1)
now = datetime.dateime.utcnow().replace(tzinfo=utc)
if obj.type == 'tempwhite' and modified < now:
obj.type = 'tempban' obj.type = 'tempban'
obj.save() obj.save()
return HttpResponse(unicode(_("OK"))) return HttpResponse(unicode(_("OK")))
...@@ -75,27 +85,26 @@ def firewall_api(request): ...@@ -75,27 +85,26 @@ def firewall_api(request):
data["owner"] = "opennebula" data["owner"] = "opennebula"
owner = auth.models.User.objects.get(username=data["owner"]) owner = auth.models.User.objects.get(username=data["owner"])
host = Host(hostname=data["hostname"], host = Host(hostname=data["hostname"],
vlan=Vlan.objects.get(name=data["vlan"]), vlan=Vlan.objects.get(name=data["vlan"]),
mac=data["mac"], ipv4=data["ip"], owner=owner, mac=data["mac"], ipv4=data["ip"], owner=owner,
description=data["description"], pub_ipv4= description=data["description"], pub_ipv4=
Vlan.objects.get(name=data["vlan"]).snat_ip, Vlan.objects.get(name=data["vlan"]).snat_ip,
shared_ip=True) shared_ip=True)
host.full_clean() host.full_clean()
host.save() host.save()
host.enable_net() host.enable_net()
for p in data["portforward"]: for p in data["portforward"]:
host.add_port(proto=p["proto"], host.add_port(proto=p["proto"], public=int(p["public_port"]),
public=int(p["public_port"]), private=int(p["private_port"]))
private=int(p["private_port"]))
elif command == "destroy": elif command == "destroy":
data["owner"] = "opennebula" data["owner"] = "opennebula"
print data["hostname"] print data["hostname"]
owner = auth.models.User.objects.get(username=data["owner"]) owner = auth.models.User.objects.get(username=data["owner"])
host = Host.objects.get(hostname=data["hostname"], host = Host.objects.get(hostname=data["hostname"],
owner=owner) owner=owner)
host.delete() host.delete()
else: else:
......
...@@ -9,4 +9,4 @@ setuid cloud ...@@ -9,4 +9,4 @@ setuid cloud
env DJANGO_SETTINGS_MODULE=cloud.settings.dev env DJANGO_SETTINGS_MODULE=cloud.settings.dev
env DJANGO_DB_PASSWORD=asjklddfjklqjf env DJANGO_DB_PASSWORD=asjklddfjklqjf
env DJANGO_SECRET_KEY=asjklddfjklqjfasjklddfjklqjfasjklddfjklqjf env DJANGO_SECRET_KEY=asjklddfjklqjfasjklddfjklqjfasjklddfjklqjf
exec /opt/webadmin/cloud/manage.py celery worker --loglevel=info -c 1 -Q local exec /opt/webadmin/cloud/manage.py celery worker --loglevel=info -c 1 -Q local -B
#!/bin/bash #!/bin/bash
nev=dev-$(hostname|tr -dc 0-9)
sudo sed -i /etc/hosts -e "/127.0.1.1/ s/.*/127.0.1.1 $nev.cloud.ik.bme.hu $nev/"
sudo tee /etc/hostname <<<$nev
sudo hostname $nev
sudo /etc/init.d/rabbitmq-server stop || true
sudo /etc/init.d/rabbitmq-server start
sudo pip install django_extensions sudo pip install django_extensions
sudo pip install django-nose sudo pip install django-nose
sudo pip install django-debug-toolbar sudo pip install django-debug-toolbar
...@@ -50,7 +57,13 @@ user_manager = FAKEUserManager.sh ...@@ -50,7 +57,13 @@ user_manager = FAKEUserManager.sh
temp_dir = /tmp/dl temp_dir = /tmp/dl
EOF EOF
for i in cloudstore toplist django #Refresh oned config
sudo cp /opt/webadmin/cloud/miscellaneous/devenv/oned.conf /etc/one/oned.conf
sudo rm -f /opt/update_state
sudo ln -s /opt/webadmin/cloud/miscellaneous/celery/opennebula_celery.py /opt/update_state
sudo /etc/init.d/opennebula restart
for i in cloudstore toplist django celeryone celery
do do
sudo cp /opt/webadmin/cloud/miscellaneous/devenv/$i.conf /etc/init/ sudo cp /opt/webadmin/cloud/miscellaneous/devenv/$i.conf /etc/init/
sudo start $i sudo start $i
......
#*******************************************************************************
# OpenNebula Configuration file
#*******************************************************************************
#*******************************************************************************
# Daemon configuration attributes
#-------------------------------------------------------------------------------
# MANAGER_TIMER: Time in seconds the core uses to evaluate periodical functions.
# HOST_MONITORING_INTERVAL and VM_POLLING_INTERVAL can not have smaller values
# than MANAGER_TIMER.
#
# HOST_MONITORING_INTERVAL: Time in seconds between host monitorization.
# HOST_PER_INTERVAL: Number of hosts monitored in each interval.
# HOST_MONITORING_EXPIRATION_TIME: Time, in seconds, to expire monitoring
# information. Use 0 to disable HOST monitoring recording.
#
# VM_POLLING_INTERVAL: Time in seconds between virtual machine monitorization.
# Use 0 to disable VM monitoring.
# VM_PER_INTERVAL: Number of VMs monitored in each interval.
# VM_MONITORING_EXPIRATION_TIME: Time, in seconds, to expire monitoring
# information. Use 0 to disable VM monitoring recording.
#
# SCRIPTS_REMOTE_DIR: Remote path to store the monitoring and VM management
# scripts.
#
# PORT: Port where oned will listen for xmlrpc calls.
#
# DB: Configuration attributes for the database backend
# backend : can be sqlite or mysql (default is sqlite)
# server : (mysql) host name or an IP address for the MySQL server
# port : (mysql) port for the connection to the server.
# If set to 0, the default port is used.
# user : (mysql) user's MySQL login ID
# passwd : (mysql) the password for user
# db_name : (mysql) the database name
#
# VNC_BASE_PORT: VNC ports for VMs can be automatically set to VNC_BASE_PORT +
# VMID
#
# DEBUG_LEVEL: 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
#*******************************************************************************
#MANAGER_TIMER = 30
HOST_MONITORING_INTERVAL = 600
#HOST_PER_INTERVAL = 15
#HOST_MONITORING_EXPIRATION_TIME = 86400
VM_POLLING_INTERVAL = 600
#VM_PER_INTERVAL = 5
#VM_MONITORING_EXPIRATION_TIME = 86400
SCRIPTS_REMOTE_DIR=/var/tmp/one
PORT = 2633
DB = [ backend = "sqlite" ]
# Sample configuration for MySQL
# DB = [ backend = "mysql",
# server = "localhost",
# port = 0,
# user = "oneadmin",
# passwd = "oneadmin",
# db_name = "opennebula" ]
VNC_BASE_PORT = 5900
DEBUG_LEVEL = 3
#*******************************************************************************
# Physical Networks configuration
#*******************************************************************************
# NETWORK_SIZE: Here you can define the default size for the virtual networks
#
# MAC_PREFIX: Default MAC prefix to be used to create the auto-generated MAC
# addresses is defined here (this can be overrided by the Virtual Network
# template)
#*******************************************************************************
NETWORK_SIZE = 254
MAC_PREFIX = "02:00"
#*******************************************************************************
# DataStore Configuration
#*******************************************************************************
# DATASTORE_LOCATION: *Default* Path for Datastores in the hosts. It IS the
# same for all the hosts in the cluster. DATASTORE_LOCATION IS ONLY FOR THE
# HOSTS AND *NOT* THE FRONT-END. It defaults to /var/lib/one/datastores (or
# $ONE_LOCATION/var/datastores in self-contained mode)
#
# You can define a different DATASTORE_LOCATION in each cluster by updating
# its properties with onecluster update.
#
# DEFAULT_IMAGE_TYPE: This can take values
# OS Image file holding an operating system
# CDROM Image file holding a CDROM
# DATABLOCK Image file holding a datablock,
# always created as an empty block
# DEFAULT_DEVICE_PREFIX: This can be set to
# hd IDE prefix
# sd SCSI
# xvd XEN Virtual Disk
# vd KVM virtual disk
#*******************************************************************************
DATASTORE_LOCATION = /var/lib/one/datastores
DEFAULT_IMAGE_TYPE = "OS"
DEFAULT_DEVICE_PREFIX = "hd"
#*******************************************************************************
# Information Driver Configuration
#*******************************************************************************
# You can add more information managers with different configurations but make
# sure it has different names.
#
# name : name for this information manager
#
# executable: path of the information driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
#
# arguments : for the driver executable, usually a probe configuration file,
# can be an absolute path or relative to $ONE_LOCATION/etc (or
# /etc/one/ if OpenNebula was installed in /)
#*******************************************************************************
#-------------------------------------------------------------------------------
# KVM Information Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
#-------------------------------------------------------------------------------
IM_MAD = [
name = "im_kvm",
executable = "one_im_ssh",
arguments = "-r 0 -t 15 kvm" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# XEN Information Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
#-------------------------------------------------------------------------------
#IM_MAD = [
# name = "im_xen",
# executable = "one_im_ssh",
# arguments = "xen" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# VMware Information Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
#-------------------------------------------------------------------------------
#IM_MAD = [
# name = "im_vmware",
# executable = "one_im_sh",
# arguments = "-c -t 15 -r 0 vmware" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# EC2 Information Driver Manager Configuration
#-------------------------------------------------------------------------------
#IM_MAD = [
# name = "im_ec2",
# executable = "one_im_ec2",
# arguments = "im_ec2/im_ec2.conf" ]
#-------------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Ganglia Information Driver Manager Configuration
#-----------------------------------------------------------------------------
#IM_MAD = [
# name = "im_ganglia",
# executable = "one_im_sh",
# arguments = "ganglia" ]
#-----------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Dummy Information Driver Manager Configuration
#-------------------------------------------------------------------------------
#IM_MAD = [ name="im_dummy", executable="one_im_dummy"]
#-------------------------------------------------------------------------------
#*******************************************************************************
# Virtualization Driver Configuration
#*******************************************************************************
# You can add more virtualization managers with different configurations but
# make sure it has different names.
#
# name : name of the virtual machine manager driver
#
# executable: path of the virtualization driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
#
# arguments : for the driver executable
#
# default : default values and configuration parameters for the driver, can
# be an absolute path or relative to $ONE_LOCATION/etc (or
# /etc/one/ if OpenNebula was installed in /)
#
# type : driver type, supported drivers: xen, kvm, xml
#*******************************************************************************
#-------------------------------------------------------------------------------
# KVM Virtualization Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
# -l <actions[=command_name]> actions executed locally, command can be
# overridden for each action.
# Valid actions: deploy, shutdown, cancel, save, restore, migrate, poll
# An example: "-l migrate,poll=poll_ganglia,save"
#-------------------------------------------------------------------------------
VM_MAD = [
name = "vmm_kvm",
executable = "one_vmm_exec",
arguments = "-t 15 -r 0 kvm",
default = "vmm_exec/vmm_exec_kvm.conf",
type = "kvm" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# XEN Virtualization Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
# -l <actions[=command_name]> actions executed locally, command can be
# overridden for each action.
# Valid actions: deploy, shutdown, cancel, save, restore, migrate, poll
# An example: "-l migrate,poll=poll_ganglia,save"
#-------------------------------------------------------------------------------
#VM_MAD = [
# name = "vmm_xen",
# executable = "one_vmm_exec",
# arguments = "-t 15 -r 0 xen",
# default = "vmm_exec/vmm_exec_xen.conf",
# type = "xen" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# VMware Virtualization Driver Manager Configuration
# -r number of retries when monitoring a host
# -t number of threads, i.e. number of hosts monitored at the same time
#-------------------------------------------------------------------------------
#VM_MAD = [
# name = "vmm_vmware",
# executable = "one_vmm_sh",
# arguments = "-t 15 -r 0 vmware -s sh",
# default = "vmm_exec/vmm_exec_vmware.conf",
# type = "vmware" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# EC2 Virtualization Driver Manager Configuration
# arguments: default values for the EC2 driver, can be an absolute path or
# relative to $ONE_LOCATION/etc (or /etc/one/ if OpenNebula was
# installed in /).
#-------------------------------------------------------------------------------
#VM_MAD = [
# name = "vmm_ec2",
# executable = "one_vmm_ec2",
# arguments = "vmm_ec2/vmm_ec2.conf",
# type = "xml" ]
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Dummy Virtualization Driver Configuration
#-------------------------------------------------------------------------------
#VM_MAD = [ name="vmm_dummy", executable="one_vmm_dummy", type="xml" ]
#-------------------------------------------------------------------------------
#*******************************************************************************
# Transfer Manager Driver Configuration
#*******************************************************************************
# You can add more transfer managers with different configurations but make
# sure it has different names.
# name : name for this transfer driver
#
# executable: path of the transfer driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
# arguments :
# -t: number of threads, i.e. number of transfers made at the same time
# -d: list of transfer drivers separated by commas, if not defined all the
# drivers available will be enabled
#*******************************************************************************
TM_MAD = [
executable = "one_tm",
arguments = "-t 15 -d dummy,lvm,shared,qcow2,ssh,vmfs,iscsi" ]
#*******************************************************************************
# Datastore Driver Configuration
#*******************************************************************************
# Drivers to manage the datastores, specialized for the storage backend
# executable: path of the transfer driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
#
# arguments : for the driver executable
# -t number of threads, i.e. number of repo operations at the same time
# -d datastore mads separated by commas
#*******************************************************************************
DATASTORE_MAD = [
executable = "one_datastore",
arguments = "-t 15 -d dummy,fs,vmware,vmfs,iscsi,lvm"
]
#*******************************************************************************
# Hook Manager Configuration
#*******************************************************************************
# The Driver (HM_MAD)
# -----------------------------------------------
#
# Used to execute the Hooks:
# executable: path of the hook driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
#
# arguments : for the driver executable, can be an absolute path or relative
# to $ONE_LOCATION/etc (or /etc/one/ if OpenNebula was installed
# in /)
#
# Virtual Machine Hooks (VM_HOOK)
# -------------------------------
#
# Defined by:
# name : for the hook, useful to track the hook (OPTIONAL)
# on : when the hook should be executed,
# - CREATE, when the VM is created (onevm create)
# - PROLOG, when the VM is in the prolog state
# - RUNNING, after the VM is successfully booted
# - UNKNOWN, when the VM is in the unknown state
# - SHUTDOWN, after the VM is shutdown
# - STOP, after the VM is stopped (including VM image transfers)
# - DONE, after the VM is deleted or shutdown
# - FAILED, when the VM enters the failed state
# command : path is relative to $ONE_LOCATION/var/remotes/hook
# (self-contained) or to /var/lib/one/remotes/hook (system-wide).
# That directory will be copied on the hosts under
# SCRIPTS_REMOTE_DIR. It can be an absolute path that must exist
# on the target host
# arguments : for the hook. You can access to VM information with $
# - $ID, the ID of the virtual machine
# - $TEMPLATE, the VM template in xml and base64 encoded
# remote : values,
# - YES, The hook is executed in the host where the VM was
# allocated
# - NO, The hook is executed in the OpenNebula server (default)
#
# Host Hooks (HOST_HOOK)
# -------------------------------
#
# Defined by:
# name : for the hook, useful to track the hook (OPTIONAL)
# on : when the hook should be executed,
# - CREATE, when the Host is created (onehost create)
# - ERROR, when the Host enters the error state
# - DISABLE, when the Host is disabled
# command : path is relative to $ONE_LOCATION/var/remotes/hook
# (self-contained) or to /var/lib/one/remotes/hook (system-wide).
# That directory will be copied on the hosts under
# SCRIPTS_REMOTE_DIR. It can be an absolute path that must exist
# on the target host.
# arguments : for the hook. You can use the following Host information:
# - $ID, the ID of the host
# - $TEMPLATE, the Host template in xml and base64 encoded
# remote : values,
# - YES, The hook is executed in the host
# - NO, The hook is executed in the OpenNebula server (default)
#
# Virtual Network (VNET_HOOK)
# User (USER_HOOK)
# Group (GROUP_HOOK)
# Image (IMAGE_HOOK)
# -------------------------------
#
# These hooks are executed when one of the referring entities are created or
# removed. Each hook is defined by:
# name : for the hook, useful to track the hook (OPTIONAL)
# on : when the hook should be executed,
# - CREATE, when the vnet is created
# - REMOVE, when the vnet is removed
# command : path is relative to $ONE_LOCATION/var/remotes/hook
# (self-contained) or to /var/lib/one/remotes/hook (system-wide).
# That directory will be copied on the hosts under
# SCRIPTS_REMOTE_DIR. It can be an absolute path that must exist
# on the target host.
# arguments : for the hook. You can use the following Host information:
# - $ID, the ID of the host
# - $TEMPLATE, the vnet template in xml and base64 encoded
#-------------------------------------------------------------------------------
HM_MAD = [
executable = "one_hm" ]
#*******************************************************************************
# Fault Tolerance Hooks
#*******************************************************************************
# This hook is used to perform recovery actions when a host fails.
# Script to implement host failure tolerance
# It can be set to
# -r resubmit VMs running in the host
# -d delete VMs running in the host
# Additional flags
# -f force resubmission of suspended VMs
# -p <n> avoid resubmission if host comes
# back after n monitoring cycles
#*******************************************************************************
#
#HOST_HOOK = [
# name = "error",
# on = "ERROR",
# command = "ft/host_error.rb",
# arguments = "$ID -r",
# remote = "no" ]
#-------------------------------------------------------------------------------
# These two hooks can be used to automatically delete or resubmit VMs that reach
# the "failed" state. This way, the administrator doesn't have to interact
# manually to release its resources or retry the deployment.
#
#
# Only one of them should be uncommented.
#-------------------------------------------------------------------------------
#
#VM_HOOK = [
# name = "on_failure_delete",
# on = "FAILED",
# command = "/usr/bin/env onevm delete",
# arguments = "$ID" ]
#
VM_HOOK = [
name = "boot_url_hook",
on = "RUNNING",
command = "/opt/boot_url.py",
arguments = "$TEMPLATE" ]
VM_HOOK = [
name = "django",
on = "CREATE",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "RUNNING",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "SHUTDOWN",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "STOP",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "DONE",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "FAILED",
command = "/opt/update_state",
arguments = "$ID"
]
VM_HOOK = [
name = "django",
on = "UNKNOWN",
command = "/opt/update_state",
arguments = "$ID UNKNOWN"
]
#-------------------------------------------------------------------------------
#*******************************************************************************
# Auth Manager Configuration
#*******************************************************************************
# AUTH_MAD: The Driver that will be used to authenticate (authn) and
# authorize (authz) OpenNebula requests. If defined OpenNebula will use the
# built-in auth policies.
#
# executable: path of the auth driver executable, can be an
# absolute path or relative to $ONE_LOCATION/lib/mads (or
# /usr/lib/one/mads/ if OpenNebula was installed in /)
#
# authn : list of authentication modules separated by commas, if not
# defined all the modules available will be enabled
# authz : list of authentication modules separated by commas
#
# SESSION_EXPIRATION_TIME: Time in seconds to keep an authenticated token as
# valid. During this time, the driver is not used. Use 0 to disable session
# caching
#
# ENABLE_OTHER_PERMISSIONS: Whether or not users can set the permissions for
# 'other', so publishing or sharing resources with others. Users in the oneadmin
# group will still be able to change these permissions. Values: YES or NO.
#*******************************************************************************
AUTH_MAD = [
executable = "one_auth_mad",
authn = "ssh,x509,ldap,server_cipher,server_x509"
]
SESSION_EXPIRATION_TIME = 900
#ENABLE_OTHER_PERMISSIONS = "YES"
#*******************************************************************************
# Restricted Attributes Configuration
#*******************************************************************************
# The following attributes are restricted to users outside the oneadmin group
#*******************************************************************************
VM_RESTRICTED_ATTR = "CONTEXT/FILES"
VM_RESTRICTED_ATTR = "NIC/MAC"
VM_RESTRICTED_ATTR = "NIC/VLAN_ID"
VM_RESTRICTED_ATTR = "RANK"
IMAGE_RESTRICTED_ATTR = "SOURCE"
...@@ -64,13 +64,17 @@ class UserCloudDetails(models.Model): ...@@ -64,13 +64,17 @@ class UserCloudDetails(models.Model):
help_text=_('Disk quota in mebibytes.')) help_text=_('Disk quota in mebibytes.'))
def reset_keys(self): def reset_keys(self):
"""Deprecated. Use reset_ssh_keys instead."""
self.reset_ssh_keys()
def reset_ssh_keys(self):
"""Delete old SSH key pair and generate new one.""" """Delete old SSH key pair and generate new one."""
pri, pub = keygen() pri, pub = keygen()
self.ssh_private_key = pri self.ssh_private_key = pri
try: try:
self.ssh_key.key = pub self.ssh_key.key = pub
except: except AttributeError:
self.ssh_key = SshKey(user=self.user, key=pub) self.ssh_key = SshKey(user=self.user, key=pub)
self.ssh_key.save() self.ssh_key.save()
self.ssh_key_id = self.ssh_key.id self.ssh_key_id = self.ssh_key.id
...@@ -79,40 +83,45 @@ class UserCloudDetails(models.Model): ...@@ -79,40 +83,45 @@ class UserCloudDetails(models.Model):
def reset_smb(self): def reset_smb(self):
"""Generate new Samba password.""" """Generate new Samba password."""
self.smb_password = pwgen() self.smb_password = pwgen()
self.save()
def get_weighted_instance_count(self): def get_weighted_instance_count(self):
states = ['ACTIVE', 'PENDING']
credits = [i.template.instance_type.credit credits = [i.template.instance_type.credit
for i in self.user.instance_set.all() for i in self.user.instance_set.filter(state__in=states)]
if i.state in ('ACTIVE', 'PENDING', )]
return sum(credits) return sum(credits)
def get_instance_pc(self): def get_instance_pc(self):
return 100 * self.get_weighted_instance_count() / self.instance_quota """Get what percent of the user's instance quota is in use."""
inst_quota = self.instance_quota
if inst_quota <= 0:
return 100
else:
return 100 * self.get_weighted_instance_count() / inst_quota
def get_weighted_share_count(self): def get_weighted_share_count(self):
c = 0 credits = [s.template.instance_type.credit * s.instance_limit
for i in Share.objects.filter(owner=self.user).all(): for s in Share.objects.filter(owner=self.user)]
c = c + i.template.instance_type.credit * i.instance_limit return sum(credits)
return c
def get_share_pc(self): def get_share_pc(self):
assert self.share_quota > 0 """Get what percent of the user's share quota is in use."""
return 100 * self.get_weighted_share_count() / self.share_quota share_quota = self.share_quota
if share_quota <= 0:
return 100
else:
return 100 * self.get_weighted_share_count() / share_quota
def set_quota(sender, instance, created, **kwargs): def set_quota(sender, instance, created, **kwargs):
try: try:
if not StoreApi.userexist(instance.user.username): if not StoreApi.userexist(instance.user.username):
try: password = instance.smb_password
password = instance.smb_password quota = instance.disk_quota * 1024
quota = instance.disk_quota * 1024 key_list = [k.key for k in instance.user.sshkey_set.all()]
key_list = [key.key for key in instance.user.sshkey_set.all()]
except:
pass
# Create user # Create user
if not StoreApi.createuser(instance.user.username, password, StoreApi.createuser(instance.user.username, password, key_list,
key_list, quota): quota)
pass
else: else:
StoreApi.set_quota(instance.user.username, StoreApi.set_quota(instance.user.username,
instance.disk_quota * 1024) instance.disk_quota * 1024)
...@@ -124,7 +133,7 @@ post_save.connect(set_quota, sender=UserCloudDetails) ...@@ -124,7 +133,7 @@ post_save.connect(set_quota, sender=UserCloudDetails)
def reset_keys(sender, instance, created, **kwargs): def reset_keys(sender, instance, created, **kwargs):
if created: if created:
instance.reset_smb() instance.reset_smb()
instance.reset_keys() instance.reset_ssh_keys()
post_save.connect(reset_keys, sender=UserCloudDetails) post_save.connect(reset_keys, sender=UserCloudDetails)
...@@ -139,7 +148,7 @@ class OpenSshKeyValidator(object): ...@@ -139,7 +148,7 @@ class OpenSshKeyValidator(object):
def __call__(self, value): def __call__(self, value):
try: try:
value = "%s comment" % value value = value + ' comment'
type, key_string, comment = value.split(None, 2) type, key_string, comment = value.split(None, 2)
if type not in self.valid_types: if type not in self.valid_types:
raise ValidationError(_('OpenSSH key type %s is not ' raise ValidationError(_('OpenSSH key type %s is not '
...@@ -205,10 +214,10 @@ class Share(models.Model): ...@@ -205,10 +214,10 @@ class Share(models.Model):
'for this share.')) 'for this share.'))
per_user_limit = models.IntegerField(verbose_name=_('per user limit'), per_user_limit = models.IntegerField(verbose_name=_('per user limit'),
help_text=_('Maximal count of ' help_text=_('Maximal count of '
'instances launchable by ' 'instances launchable '
'a single user.')) 'by a single user.'))
owner = models.ForeignKey( owner = models.ForeignKey(User, null=True, blank=True,
User, null=True, blank=True, related_name='share_set') related_name='share_set')
class Meta: class Meta:
ordering = ['group', 'template', 'owner', ] ordering = ['group', 'template', 'owner', ]
...@@ -217,6 +226,9 @@ class Share(models.Model): ...@@ -217,6 +226,9 @@ class Share(models.Model):
@classmethod @classmethod
def extend_type(cls, t): def extend_type(cls, t):
"""Extend the share's type descriptor with absolute deletion and
suspension time values based on the current time and intervals
already set."""
t['deletex'] = (datetime.now() + td(seconds=1) + t['delete'] t['deletex'] = (datetime.now() + td(seconds=1) + t['delete']
if t['delete'] else None) if t['delete'] else None)
t['suspendx'] = (datetime.now() + td(seconds=1) + t['suspend'] t['suspendx'] = (datetime.now() + td(seconds=1) + t['suspend']
......
from django.test import TestCase
from django.contrib.auth.models import User
from models import Disk, Instance, InstanceType, Network, Template, UserCloudDetails
class ViewsTestCase(TestCase):
def test_index(self):
'''Test whether index is reachable.'''
resp = self.client.get('/', follow=True)
self.assertEqual(resp.status_code, 200)
class UserCloudDetailsTestCase(TestCase):
def setUp(self):
user = User.objects.create(username="testuser",
password="testpass", email="test@mail.com",
first_name="Test", last_name="User")
disk1 = Disk.objects.create(name="testdsk1")
insttype = InstanceType.objects.create(name="testtype", CPU=4,
RAM=4096, credit=4)
ntwrk = Network.objects.create(name="testntwrk", nat=False,
public=True)
tmplt1 = Template.objects.create(name="testtmplt1", disk=disk1,
instance_type=insttype, network=ntwrk, owner=user)
tmplt2 = Template.objects.create(name="testtmplt2", disk=disk1,
instance_type=insttype, network=ntwrk, owner=user)
self.testinst1 = Instance.objects.create(owner=user, template=tmplt1,
state="ACTIVE")
self.testinst2 = Instance.objects.create(owner=user, template=tmplt2,
state="ACTIVE")
self.testdetails = UserCloudDetails.objects.get(user=user)
def test_get_weighted_instance_count(self):
credits = (self.testinst1.template.instance_type.credit +
self.testinst2.template.instance_type.credit)
self.assertEqual(credits, self.testdetails
.get_weighted_instance_count())
self.testinst1.state = "STOPPED"
self.testinst1.save()
self.assertEqual(self.testinst2.template.instance_type.credit,
self.testdetails.get_weighted_instance_count())
self.testinst2.state = "STOPPED"
self.testinst2.save()
self.assertEqual(0, self.testdetails.get_weighted_instance_count())
from datetime import datetime, timedelta
from mock import Mock, patch
from nose import with_setup
from nose.tools import raises
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.test import TestCase
from ..models import (Disk, Instance, InstanceType, Network, Share,
Template, set_quota, reset_keys, OpenSshKeyValidator)
from ..util import keygen
from school.models import Course, Group, Semester
from store.api import StoreApi
class UserCloudDetailsTestCase(TestCase):
def setUp(self):
user = User.objects.create(username="testuser", password="testpass",
email="test@mail.com", first_name="Test",
last_name="User")
disk1 = Disk.objects.create(name="testdsk1")
insttype = InstanceType.objects.create(name="testtype", CPU=4,
RAM=4096, credit=4)
ntwrk = Network.objects.create(name="testntwrk", nat=False,
public=True)
tmplt1 = Template.objects.create(name="testtmplt1", disk=disk1,
instance_type=insttype, owner=user,
network=ntwrk)
tmplt2 = Template.objects.create(name="testtmplt2", disk=disk1,
instance_type=insttype, owner=user,
network=ntwrk)
self.testinst1 = Instance.objects.create(owner=user, state="ACTIVE",
template=tmplt1)
self.testinst2 = Instance.objects.create(owner=user, state="ACTIVE",
template=tmplt2)
self.userdetails = user.cloud_details
date = datetime.now().date()
delta = timedelta(weeks=7)
sem = Semester.objects.create(name="testsem", start=date-delta,
end=date+delta)
course1 = Course.objects.create(code='tccode1', name='testcourse1',
short_name='tc1')
grp1 = Group.objects.create(name="testgroup1", semester=sem,
course=course1)
self.share1 = Share.objects.create(name="testshare1", group=grp1,
template=tmplt1, owner=user,
instance_limit=2,
per_user_limit=1)
def test_reset_keys(self):
private_key = self.userdetails.ssh_private_key
public_key = self.userdetails.ssh_key.key
self.userdetails.reset_keys()
self.assertIsNotNone(self.userdetails.ssh_private_key)
self.assertNotEqual(private_key, self.userdetails.ssh_private_key)
self.assertIsNotNone(self.userdetails.ssh_key.key)
self.assertNotEqual(public_key, self.userdetails.ssh_key.key)
def test_reset_keys_without_key(self):
private_key = self.userdetails.ssh_private_key
self.userdetails.ssh_key = None
self.userdetails.save()
self.userdetails.reset_keys()
self.assertIsNotNone(self.userdetails.ssh_private_key)
self.assertNotEqual(private_key, self.userdetails.ssh_private_key)
self.assertIsNotNone(self.userdetails.ssh_key)
def test_reset_smb(self):
smb_password = self.userdetails.smb_password
self.userdetails.reset_smb()
self.assertIsNotNone(self.userdetails.smb_password)
self.assertNotEqual(smb_password, self.userdetails.smb_password)
def test_get_weighted_instance_count(self):
credits = (self.testinst1.template.instance_type.credit +
self.testinst2.template.instance_type.credit)
self.assertEqual(credits,
self.userdetails.get_weighted_instance_count())
self.testinst1.state = "STOPPED"
self.testinst1.save()
self.assertEqual(self.testinst2.template.instance_type.credit,
self.userdetails.get_weighted_instance_count())
self.testinst2.state = "STOPPED"
self.testinst2.save()
self.assertEqual(0, self.userdetails.get_weighted_instance_count())
def test_get_instance_pc(self):
instance_pc = self.userdetails.get_instance_pc()
self.assertTrue(instance_pc >= 0)
def test_get_instance_pc_with_zero_instance_quota(self):
self.userdetails.instance_quota = 0
self.userdetails.save()
self.assertEqual(100, self.userdetails.get_instance_pc())
def test_get_weighted_share_count(self):
share = self.share1
count = share.template.instance_type.credit * share.instance_limit
self.assertEqual(count, self.userdetails.get_weighted_share_count())
def test_get_share_pc(self):
self.userdetails.share_quota = 50
share_pc = self.userdetails.get_share_pc()
self.assertTrue(share_pc >= 0)
def test_get_share_pc_with_zero_share_quota(self):
self.userdetails.share_quota = 0
self.userdetails.save()
self.assertEqual(100, self.userdetails.get_share_pc())
def create_user():
return User.objects.create(username="testuser", password="testpass",
email="test@mail.com", first_name="Test",
last_name="User")
def delete_user():
User.objects.filter(username="testuser").delete()
@with_setup(create_user, delete_user)
def test_set_quota():
user = User.objects.get(username="testuser")
details = user.cloud_details
set_quota(None, details, None)
assert StoreApi.userexist(user.username)
# TODO check quota value
@with_setup(create_user, delete_user)
@patch('one.models.StoreApi')
def test_set_quota_without_store_user(MockStoreApi):
MockStoreApi.userexist = Mock(return_value=False)
MockStoreApi.createuser = Mock()
user = User.objects.get(username="testuser")
details = user.cloud_details
set_quota(None, details, None)
MockStoreApi.userexist.assert_called_once_with(user.username)
assert MockStoreApi.createuser.called
assert MockStoreApi.createuser.call_count == 1
@with_setup(create_user, delete_user)
def test_reset_keys_when_created():
mock_details = Mock()
mock_details.reset_smb = Mock(return_value=None)
mock_details.reset_ssh_keys = Mock(return_value=None)
reset_keys(None, mock_details, True)
mock_details.reset_smb.assert_called_once_with()
mock_details.reset_ssh_keys.assert_called_once_with()
@with_setup(create_user, delete_user)
def test_reset_keys_when_not_created():
mock_details = Mock()
mock_details.reset_smb = Mock(return_value=None)
mock_details.reset_ssh_keys = Mock(return_value=None)
reset_keys(None, mock_details, False)
assert not mock_details.reset_smb.called
assert not mock_details.reset_ssh_keys.called
def test_OpenSshKeyValidator_init_with_types():
key_types = ['my-key-type']
validator = OpenSshKeyValidator(types=key_types)
assert validator.valid_types == key_types
def test_OpenSshKeyValidator_with_valid_key():
validator = OpenSshKeyValidator()
_, public_key = keygen()
validator(public_key)
@raises(ValidationError)
def test_OpenSshKeyValidator_with_empty_string_as_key():
validator = OpenSshKeyValidator()
public_key = ""
validator(public_key)
@raises(ValidationError)
def test_OpenSshKeyValidator_with_invalid_key_type():
validator = OpenSshKeyValidator()
_, public_key = keygen()
_key_type, rest = public_key.split(None, 1)
public_key = 'my-key-type ' + rest
validator(public_key)
@raises(ValidationError)
def test_OpenSshKeyValidator_with_invalid_key_data():
validator = OpenSshKeyValidator()
_, public_key = keygen()
key_parts = public_key.split(None, 2)
key_parts[1] = key_parts[1][1:]
public_key = ' '.join(key_parts)
validator(public_key)
def test_Share_extend_type():
t = {'delete': timedelta(weeks=2), 'suspend': timedelta(weeks=1)}
Share.extend_type(t)
assert 'deletex' in t
assert 'suspendx' in t
assert t['deletex'] is not None
assert t['suspendx'] is not None
def test_Share_extend_type_with_no_deletion_interval():
t = {'delete': None, 'suspend': timedelta(weeks=1)}
Share.extend_type(t)
assert 'deletex' in t
assert 'suspendx' in t
assert t['deletex'] is None
assert t['suspendx'] is not None
def test_Share_extend_type_with_no_suspension_interval():
t = {'delete': timedelta(weeks=2), 'suspend': None}
Share.extend_type(t)
assert 'deletex' in t
assert 'suspendx' in t
assert t['deletex'] is not None
assert t['suspendx'] is None
from django.core.urlresolvers import reverse
from django.test import TestCase
class ViewsTestCase(TestCase):
def test_index(self):
'''Test whether index is reachable.'''
url = reverse('one.views.index')
resp = self.client.get(url, follow=True)
self.assertEqual(resp.status_code, 200)
from django.contrib import messages
from django.core.exceptions import ValidationError
from django import contrib from django import contrib
from django.utils.translation import ugettext_lazy as _
from school import models from school import models
import string
class GroupInline(contrib.admin.TabularInline): class GroupInline(contrib.admin.TabularInline):
model = models.Group model = models.Group
extra = 3 extra = 3
class CourseAdmin(contrib.admin.ModelAdmin): class CourseAdmin(contrib.admin.ModelAdmin):
model = models.Course model = models.Course
inlines = (GroupInline, ) inlines = (GroupInline, )
...@@ -16,18 +14,20 @@ class CourseAdmin(contrib.admin.ModelAdmin): ...@@ -16,18 +14,20 @@ class CourseAdmin(contrib.admin.ModelAdmin):
list_display = ('code', 'name', 'short_name', 'owner_list') list_display = ('code', 'name', 'short_name', 'owner_list')
list_editable = ('name', 'short_name') list_editable = ('name', 'short_name')
class GroupAdmin(contrib.admin.ModelAdmin): class GroupAdmin(contrib.admin.ModelAdmin):
model = models.Group model = models.Group
filter_horizontal = ('owners', 'members', ) filter_horizontal = ('owners', 'members', )
list_display = ('name', 'course', 'semester', 'owner_list', 'member_count') list_display = ('name', 'course', 'semester', 'owner_list', 'member_count')
list_filter = ('semester', 'course') list_filter = ('semester', 'course')
class SemesterAdmin(contrib.admin.ModelAdmin): class SemesterAdmin(contrib.admin.ModelAdmin):
model = models.Semester model = models.Semester
list_display = ('id', 'name', 'start', 'end') list_display = ('id', 'name', 'start', 'end')
list_editable = ('name', 'start', 'end') list_editable = ('name', 'start', 'end')
contrib.admin.site.register(models.Course, CourseAdmin) contrib.admin.site.register(models.Course, CourseAdmin)
contrib.admin.site.register(models.Semester, SemesterAdmin) contrib.admin.site.register(models.Semester, SemesterAdmin)
contrib.admin.site.register(models.Group, GroupAdmin) contrib.admin.site.register(models.Group, GroupAdmin)
...@@ -14,6 +14,7 @@ LANGUAGE_CHOICES = (('hu', _('Hungarian')), ('en', _('English'))) ...@@ -14,6 +14,7 @@ LANGUAGE_CHOICES = (('hu', _('Hungarian')), ('en', _('English')))
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
""" """
User creation hook. User creation hook.
...@@ -33,32 +34,33 @@ def create_user_profile(sender, instance, created, **kwargs): ...@@ -33,32 +34,33 @@ def create_user_profile(sender, instance, created, **kwargs):
p = Person.objects.create(code=instance.username) p = Person.objects.create(code=instance.username)
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover
logger.warning("Couldn't create profile for user: %(username)s" logger.warning("Couldn't create profile for user: %(username)s"
"\nReason: %(exception)s", "\nReason: %(exception)s",
{"username": instance.username, {"username": instance.username, "exception": e})
"exception": e})
return return
p.clean() p.clean()
p.save() p.save()
post_save.connect(create_user_profile, sender=User) post_save.connect(create_user_profile, sender=User)
class Person(models.Model): class Person(models.Model):
""" """
Personal settings and attributes of a user. Personal settings and attributes of a user.
""" """
user = models.ForeignKey(User, null=True, blank=True, unique=True) user = models.ForeignKey(User, null=True, blank=True, unique=True)
language = models.CharField(verbose_name=_('language'), blank=False, language = models.CharField(verbose_name=_('language'), blank=False,
max_length=10, choices=LANGUAGE_CHOICES, default=LANGUAGE_CODE) max_length=10, choices=LANGUAGE_CHOICES,
default=LANGUAGE_CODE)
code = models.CharField(_('code'), max_length=30, unique=True) code = models.CharField(_('code'), max_length=30, unique=True)
def get_owned_shares(self): def get_owned_shares(self):
"""Get the shares of the groups which the person owns.""" """Get the shares of the groups which the person owns."""
return one.models.Share.objects.filter( return one.models.Share.objects.filter(
group__in=self.owned_groups.all()) group__in=self.owned_groups.all())
def get_shares(self): def get_shares(self):
"""Get the shares of the groups which the person is a member of.""" """Get the shares of the groups which the person is a member of."""
return one.models.Share.objects.filter( return one.models.Share.objects.filter(
group__in=self.course_groups.all()) group__in=self.course_groups.all())
def short_name(self): def short_name(self):
if self.user: if self.user:
...@@ -74,29 +76,35 @@ class Person(models.Model): ...@@ -74,29 +76,35 @@ class Person(models.Model):
if self.user.last_name and self.user.first_name: if self.user.last_name and self.user.first_name:
# TRANSLATORS: full name format used in enumerations # TRANSLATORS: full name format used in enumerations
return _("%(first)s %(last)s") % { return _("%(first)s %(last)s") % {
'first': self.user.first_name, 'first': self.user.first_name,
'last': self.user.last_name} 'last': self.user.last_name}
else: else:
return self.user.username return self.user.username
else: else:
return self.code return self.code
def save(self, *args, **kwargs):
self.full_clean()
super(Person, self).save(*args, **kwargs)
class Meta: class Meta:
verbose_name = _('person') verbose_name = _('person')
verbose_name_plural = _('persons') verbose_name_plural = _('persons')
class Course(models.Model): class Course(models.Model):
code = models.CharField(max_length=20, unique=True, code = models.CharField(max_length=20, unique=True,
verbose_name=_('course code')) verbose_name=_('course code'))
name = models.CharField(max_length=80, null=True, blank=True, name = models.CharField(max_length=80, null=True, blank=True,
verbose_name=_('name')) verbose_name=_('name'))
short_name = models.CharField(max_length=10, null=True, blank=True, short_name = models.CharField(max_length=10, null=True, blank=True,
verbose_name=_('name')) verbose_name=_('name'))
default_group = models.ForeignKey('Group', null=True, blank=True, default_group = models.ForeignKey(
related_name='default_group_of', verbose_name=_('default group'), 'Group', null=True, blank=True, related_name='default_group_of',
help_text=_('New users will be automatically assigned to this group.')) verbose_name=_('default group'), help_text=_('New users will be '
'automatically assigned to this group.'))
owners = models.ManyToManyField(Person, blank=True, null=True, owners = models.ManyToManyField(Person, blank=True, null=True,
verbose_name=_('owners')) verbose_name=_('owners'))
class Meta: class Meta:
verbose_name = _('course') verbose_name = _('course')
...@@ -107,7 +115,8 @@ class Course(models.Model): ...@@ -107,7 +115,8 @@ class Course(models.Model):
return self.default_group return self.default_group
else: else:
default_group = Group(name=_("%s (auto)") % self.short(), default_group = Group(name=_("%s (auto)") % self.short(),
semester=Semester.get_current(), course=self) semester=Semester.get_current(),
course=self)
default_group.save() default_group.save()
self.default_group_id = default_group.id self.default_group_id = default_group.id
self.save() self.save()
...@@ -143,7 +152,7 @@ class Course(models.Model): ...@@ -143,7 +152,7 @@ class Course(models.Model):
class Semester(models.Model): class Semester(models.Model):
name = models.CharField(max_length=20, unique=True, null=False, name = models.CharField(max_length=20, unique=True, null=False,
verbose_name=_('name')) verbose_name=_('name'))
start = models.DateField(verbose_name=_('start')) start = models.DateField(verbose_name=_('start'))
end = models.DateField(verbose_name=_('end')) end = models.DateField(verbose_name=_('end'))
...@@ -171,13 +180,15 @@ class Semester(models.Model): ...@@ -171,13 +180,15 @@ class Semester(models.Model):
class Group(models.Model): class Group(models.Model):
name = models.CharField(max_length=80, verbose_name=_('name')) name = models.CharField(max_length=80, verbose_name=_('name'))
course = models.ForeignKey('Course', null=True, blank=True, course = models.ForeignKey('Course', null=True, blank=True,
verbose_name=_('course')) verbose_name=_('course'))
semester = models.ForeignKey('Semester', null=False, blank=False, semester = models.ForeignKey('Semester', null=False, blank=False,
verbose_name=_('semester')) verbose_name=_('semester'))
owners = models.ManyToManyField(Person, blank=True, null=True, owners = models.ManyToManyField(Person, blank=True, null=True,
related_name='owned_groups', verbose_name=_('owners')) related_name='owned_groups',
verbose_name=_('owners'))
members = models.ManyToManyField(Person, blank=True, null=True, members = models.ManyToManyField(Person, blank=True, null=True,
related_name='course_groups', verbose_name=_('members')) related_name='course_groups',
verbose_name=_('members'))
class Meta: class Meta:
unique_together = (('name', 'course', 'semester', ), ) unique_together = (('name', 'course', 'semester', ), )
......
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.test import TestCase from django.test import TestCase
from django.contrib.auth.models import User, Group as AuthGroup from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from ..models import create_user_profile, Person, Course, Semester, Group from ..models import create_user_profile, Person, Course, Semester, Group
class CreateUserProfileTestCase(TestCase): class CreateUserProfileTestCase(TestCase):
def setUp(self): def setUp(self):
self.user = User(username="testuser", password="testpass", self.user = User(username="testuser", password="testpass",
email="test@mail.com", first_name="Test", last_name="User") email="test@mail.com", first_name="Test",
last_name="User")
Person.objects.all().delete() Person.objects.all().delete()
with self.assertRaises(Person.DoesNotExist): with self.assertRaises(Person.DoesNotExist):
Person.objects.get(code=self.user.username) Person.objects.get(code=self.user.username)
...@@ -23,6 +25,7 @@ class CreateUserProfileTestCase(TestCase): ...@@ -23,6 +25,7 @@ class CreateUserProfileTestCase(TestCase):
create_user_profile(self.user.__class__, self.user, True) create_user_profile(self.user.__class__, self.user, True)
self.assertIsNotNone(Person.objects.get(code=self.user.username)) self.assertIsNotNone(Person.objects.get(code=self.user.username))
class PersonTestCase(TestCase): class PersonTestCase(TestCase):
"""Test 'static' Person facts.""" """Test 'static' Person facts."""
def test_language_code_in_choices(self): def test_language_code_in_choices(self):
...@@ -32,11 +35,13 @@ class PersonTestCase(TestCase): ...@@ -32,11 +35,13 @@ class PersonTestCase(TestCase):
choice_codes = [code for (code, _) in language_field.choices] choice_codes = [code for (code, _) in language_field.choices]
self.assertIn(language_field.default, choice_codes) self.assertIn(language_field.default, choice_codes)
class PersonWithUserTestCase(TestCase): class PersonWithUserTestCase(TestCase):
"""Test Person entities which have their user attribute set.""" """Test Person entities which have their user attribute set."""
def setUp(self): def setUp(self):
self.user = User(username="testuser", password="testpass", self.user = User(username="testuser", password="testpass",
email="test@mail.com", first_name="Test", last_name="User") email="test@mail.com", first_name="Test",
last_name="User")
Person.objects.all().delete() Person.objects.all().delete()
self.person = Person.objects.create(code='testcode', user=self.user) self.person = Person.objects.create(code='testcode', user=self.user)
...@@ -60,6 +65,7 @@ class PersonWithUserTestCase(TestCase): ...@@ -60,6 +65,7 @@ class PersonWithUserTestCase(TestCase):
self.person.user.last_name = None self.person.user.last_name = None
self.assertIsNotNone(self.person.__unicode__()) self.assertIsNotNone(self.person.__unicode__())
class PersonWithoutUserTestCase(TestCase): class PersonWithoutUserTestCase(TestCase):
"""Test Person entities which doesn't have their user attribute set.""" """Test Person entities which doesn't have their user attribute set."""
def setUp(self): def setUp(self):
...@@ -78,6 +84,7 @@ class PersonWithoutUserTestCase(TestCase): ...@@ -78,6 +84,7 @@ class PersonWithoutUserTestCase(TestCase):
def test_unicode(self): def test_unicode(self):
self.assertIsNotNone(self.person.__unicode__()) self.assertIsNotNone(self.person.__unicode__())
class CourseTestCase(TestCase): class CourseTestCase(TestCase):
def setUp(self): def setUp(self):
now = datetime.now() now = datetime.now()
...@@ -85,10 +92,10 @@ class CourseTestCase(TestCase): ...@@ -85,10 +92,10 @@ class CourseTestCase(TestCase):
delta = timedelta(weeks=7) delta = timedelta(weeks=7)
self.testperson1 = Person.objects.create(code="testperson1") self.testperson1 = Person.objects.create(code="testperson1")
self.testperson2 = Person.objects.create(code="testperson2") self.testperson2 = Person.objects.create(code="testperson2")
self.testsemester = Semester.objects.create(name="testsemester", self.testsemester = Semester.objects.create(
start=date-delta, end=date+delta) name="testsemester", start=date-delta, end=date+delta)
self.testcourse = Course.objects.create(code="testcode", self.testcourse = Course.objects.create(
name="testname", short_name="tn") code="testcode", name="testname", short_name="tn")
self.testcourse.owners.add(self.testperson1, self.testperson2) self.testcourse.owners.add(self.testperson1, self.testperson2)
def test_get_or_create_default_group(self): def test_get_or_create_default_group(self):
...@@ -118,18 +125,19 @@ class CourseTestCase(TestCase): ...@@ -118,18 +125,19 @@ class CourseTestCase(TestCase):
self.testcourse.short_name = None self.testcourse.short_name = None
self.assertIsNotNone(self.testcourse.short()) self.assertIsNotNone(self.testcourse.short())
class SemesterTestCase(TestCase): class SemesterTestCase(TestCase):
def setUp(self): def setUp(self):
now = datetime.now() now = datetime.now()
date = now.date() date = now.date()
self.now = now self.now = now
delta = timedelta(weeks=7) delta = timedelta(weeks=7)
self.last_semester = Semester.objects.create(name="testsem1", self.last_semester = Semester.objects.create(
start=date-3*delta, end=date-delta) name="testsem1", start=date-3*delta, end=date-delta)
self.current_semester = Semester.objects.create(name="testsem2", self.current_semester = Semester.objects.create(
start=date-delta, end=date+delta) name="testsem2", start=date-delta, end=date+delta)
self.next_semester = Semester.objects.create(name="testsem3", self.next_semester = Semester.objects.create(
start=date+delta, end=date+3*delta) name="testsem3", start=date+delta, end=date+3*delta)
def test_is_on(self): def test_is_on(self):
self.assertFalse(self.last_semester.is_on(self.now)) self.assertFalse(self.last_semester.is_on(self.now))
...@@ -146,16 +154,17 @@ class SemesterTestCase(TestCase): ...@@ -146,16 +154,17 @@ class SemesterTestCase(TestCase):
def test_unicode(self): def test_unicode(self):
self.current_semester.__unicode__() self.current_semester.__unicode__()
class GroupTestCase(TestCase): class GroupTestCase(TestCase):
def setUp(self): def setUp(self):
date = datetime.now().date() date = datetime.now().date()
delta = timedelta(weeks=7) delta = timedelta(weeks=7)
semester = Semester.objects.create(name="testsem", semester = Semester.objects.create(
start=date-delta, end=date+delta) name="testsem", start=date-delta, end=date+delta)
self.testcourse = Course.objects.create(code="testcode", self.testcourse = Course.objects.create(
name="testname", short_name="tn") code="testcode", name="testname", short_name="tn")
self.testgroup = Group.objects.create(name="testgrp", self.testgroup = Group.objects.create(
semester=semester, course=self.testcourse) name="testgrp", semester=semester, course=self.testcourse)
def test_owner_list(self): def test_owner_list(self):
self.assertIsNotNone(self.testgroup.owner_list()) self.assertIsNotNone(self.testgroup.owner_list())
...@@ -184,4 +193,3 @@ class GroupTestCase(TestCase): ...@@ -184,4 +193,3 @@ class GroupTestCase(TestCase):
def test_get_absolute_url(self): def test_get_absolute_url(self):
self.assertIsNotNone(self.testgroup.get_absolute_url()) self.assertIsNotNone(self.testgroup.get_absolute_url())
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.test import TestCase from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User, Group as AuthGroup from django.contrib.auth.models import User, Group as AuthGroup
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.datastructures import MultiValueDictKeyError from django.utils.datastructures import MultiValueDictKeyError
from ..models import create_user_profile, Person, Course, Semester, Group from ..models import Person, Course, Semester, Group
from one.models import UserCloudDetails from one.models import (UserCloudDetails, Disk, InstanceType, Network,
Template)
import json
class ViewTestCase(TestCase): class ViewTestCase(TestCase):
def setUp(self): def setUp(self):
self.client = Client()
date = datetime.now().date() date = datetime.now().date()
delta = timedelta(weeks=7) delta = timedelta(weeks=7)
semester= Semester.objects.create(name="testsem", start=date-delta, self.semester = Semester.objects.create(
end=date+delta) name="testsem", start=date-delta, end=date+delta)
course1 = Course.objects.create(code='tccode1', course1 = Course.objects.create(
name='testcourse1', short_name='tc1') code='tccode1', name='testcourse1', short_name='tc1')
course2 = Course.objects.create(code='tccode2', course2 = Course.objects.create(
name='testcourse2', short_name='tc2') code='tccode2', name='testcourse2', short_name='tc2')
nonexistent_course_code1 = 'vacationspotscouting' nonexistent_course_code1 = 'vacationspotscouting'
nonexistent_course_code2 = 'caringforunicorns' nonexistent_course_code2 = 'caringforunicorns'
affiliation1 = AuthGroup.objects.create(name='chalktrademanager') affiliation1 = AuthGroup.objects.create(name='chalktrademanager')
self.group1 = Group.objects.create(name=affiliation1.name, self.group1 = Group.objects.create(
semester=semester, course=course1) name=affiliation1.name, semester=self.semester, course=course1)
self.http_headers = { self.http_headers = {
'niifPersonOrgID': 'ABUZER', 'niifPersonOrgID': 'ABUZER',
'niifEduPersonHeldCourse': ';'.join( 'niifEduPersonHeldCourse': ';'.join(
[course1.code, nonexistent_course_code1]), [course1.code, nonexistent_course_code1]),
'niifEduPersonAttendedCourse': ';'.join( 'niifEduPersonAttendedCourse': ';'.join(
[course2.code, nonexistent_course_code2]), [course2.code, nonexistent_course_code2]),
'givenName': 'User', 'givenName': 'Test',
'sn': 'Test', 'sn': 'User',
'email': 'test.user@testsite.hu', 'email': 'test.user@testsite.hu',
'affiliation': ';'.join([affiliation1.name])} 'affiliation': ';'.join([affiliation1.name])}
def login(self, follow=False): def login(self, follow=False):
url = reverse('login') url = reverse('login')
resp = self.client.get(url, follow=follow, **self.http_headers) resp = self.client.get(url, follow=follow, **self.http_headers)
...@@ -48,17 +47,20 @@ class ViewTestCase(TestCase): ...@@ -48,17 +47,20 @@ class ViewTestCase(TestCase):
pass pass
return resp return resp
#
# school.views.logout
#
def test_logout(self): def test_logout(self):
resp = self.client.get(reverse('logout'), follow=False) resp = self.client.get(reverse('logout'), follow=False)
self.assertEqual(302, resp.status_code) self.assertEqual(302, resp.status_code)
#
# school.views.login
#
def test_login(self): def test_login(self):
resp = self.login(follow=True) resp = self.login(follow=True)
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
def test_login_without_id(self): def test_login_without_id(self):
del self.http_headers['niifPersonOrgID'] del self.http_headers['niifPersonOrgID']
resp = self.login(follow=True) resp = self.login(follow=True)
...@@ -66,25 +68,24 @@ class ViewTestCase(TestCase): ...@@ -66,25 +68,24 @@ class ViewTestCase(TestCase):
(url, _) = resp.redirect_chain[0] (url, _) = resp.redirect_chain[0]
self.assertIn('/admin', url) self.assertIn('/admin', url)
def test_login_without_email(self): def test_login_without_email(self):
del self.http_headers['email'] del self.http_headers['email']
resp = self.login(follow=True) resp = self.login(follow=True)
self.assertEqual(403, resp.status_code) self.assertEqual(403, resp.status_code)
def test_login_without_affiliation(self): def test_login_without_affiliation(self):
del self.http_headers['affiliation'] del self.http_headers['affiliation']
resp = self.login(follow=True) resp = self.login(follow=True)
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
def test_login_without_group_for_affiliation(self): def test_login_without_group_for_affiliation(self):
self.group1.delete() self.group1.delete()
resp = self.login(follow=True) resp = self.login(follow=True)
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
#
# school.views.language
#
def test_language(self): def test_language(self):
self.login() self.login()
p = Person.objects.get(user=self.user) p = Person.objects.get(user=self.user)
...@@ -96,18 +97,16 @@ class ViewTestCase(TestCase): ...@@ -96,18 +97,16 @@ class ViewTestCase(TestCase):
p = Person.objects.get(user=self.user) p = Person.objects.get(user=self.user)
self.assertEqual(lang, p.language) self.assertEqual(lang, p.language)
def test_language_with_invalid_parameter(self): def test_language_with_invalid_parameter(self):
self.login() self.login()
lang_before = Person.objects.get(user=self.user).language lang_before = Person.objects.get(user=self.user).language
new_lang = u'nemvanez' # invalid language invalid_lang = u'nemvanez'
url = reverse('school.views.language', kwargs={'lang': new_lang}) url = reverse('school.views.language', kwargs={'lang': invalid_lang})
self.http_headers['HTTP_REFERER'] = '/' self.http_headers['HTTP_REFERER'] = '/'
resp = self.client.get(url, follow=False, **self.http_headers) resp = self.client.get(url, follow=False, **self.http_headers)
self.assertEqual(302, resp.status_code) self.assertEqual(302, resp.status_code)
p = Person.objects.get(user=self.user) p = Person.objects.get(user=self.user)
self.assertEqual(lang_before, p.language) # language didn't change self.assertEqual(lang_before, p.language) # language didn't change
def test_language_without_person_for_user(self): def test_language_without_person_for_user(self):
self.login() self.login()
...@@ -118,19 +117,28 @@ class ViewTestCase(TestCase): ...@@ -118,19 +117,28 @@ class ViewTestCase(TestCase):
resp = self.client.get(url, follow=False, **self.http_headers) resp = self.client.get(url, follow=False, **self.http_headers)
self.assertEqual(302, resp.status_code) self.assertEqual(302, resp.status_code)
#
# school.views.group_show
#
def test_group_show(self): def test_group_show(self):
self.login() self.login()
ucd = UserCloudDetails.objects.get(user=self.user) ucd = UserCloudDetails.objects.get(user=self.user)
ucd.share_quota = 10 ucd.share_quota = 10
ucd.save() ucd.save()
disk = Disk.objects.create(name="testdsk1")
insttype = InstanceType.objects.create(name="testtype", CPU=4,
RAM=4096, credit=4)
ntwrk = Network.objects.create(name="testntwrk", nat=False,
public=True)
Template.objects.create(
name="testtmplt1", disk=disk, instance_type=insttype,
network=ntwrk, owner=self.user, state='READY')
gid = self.group1.id gid = self.group1.id
url = reverse('school.views.group_show', kwargs={'gid': gid}) url = reverse('school.views.group_show', kwargs={'gid': gid})
resp = self.client.get(url) resp = self.client.get(url)
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
def test_group_show_with_nonexistent_group_id(self):
def test_group_show_with_nonexistent_groupid(self):
self.login() self.login()
gid = 1337 # this should be the ID of a non-existent group, gid = 1337 # this should be the ID of a non-existent group,
Group.objects.filter(id=gid).delete() # so if it exists, delete it! Group.objects.filter(id=gid).delete() # so if it exists, delete it!
...@@ -138,57 +146,65 @@ class ViewTestCase(TestCase): ...@@ -138,57 +146,65 @@ class ViewTestCase(TestCase):
resp = self.client.get(url) resp = self.client.get(url)
self.assertEqual(404, resp.status_code) self.assertEqual(404, resp.status_code)
#
# school.views.group_new
#
def test_group_new(self): def test_group_new(self):
self.login() self.login()
url = reverse('school.views.group_new') url = reverse('school.views.group_new')
member1 = Person.objects.create(code="A1B2C3") member1 = Person.objects.create(code="A1B2C3")
members = [member1] members = [member1]
data = { data = {'name': 'myNewGrp',
'name': 'myNewGrp',
'semester': Semester.get_current().id, 'semester': Semester.get_current().id,
'members': '\n'.join([m.code for m in members]), 'members': '\n'.join([m.code for m in members])}
} self.client.post(url, data)
resp = self.client.post(url, data)
group = Group.objects.get(name=data['name']) group = Group.objects.get(name=data['name'])
self.assertEqual(Semester.get_current(), group.semester) self.assertEqual(Semester.get_current(), group.semester)
for member in members: for member in members:
self.assertIn(member, group.members.all()) self.assertIn(member, group.members.all())
def test_group_new_without_members(self): def test_group_new_without_members(self):
self.login() self.login()
url = reverse('school.views.group_new') url = reverse('school.views.group_new')
data = { data = {'name': 'myNewGrp',
'name': 'myNewGrp',
'semester': Semester.get_current().id, 'semester': Semester.get_current().id,
'members': '', 'members': ''}
} self.client.post(url, data)
resp = self.client.post(url, data)
group = Group.objects.get(name=data['name']) group = Group.objects.get(name=data['name'])
self.assertEqual(Semester.get_current(), group.semester) self.assertEqual(Semester.get_current(), group.semester)
self.assertFalse(group.members.exists()) self.assertFalse(group.members.exists())
def test_group_new_with_invalid_neptun(self):
self.login()
url = reverse('school.views.group_new')
data = {'name': 'myNewGrp',
'semester': Semester.get_current().id,
'members': '1ABC123'} # invalid neptun
self.client.post(url, data)
self.assertFalse(Group.objects.filter(name=data['name']).exists())
#
# school.views.group_ajax_add_new_member
#
def test_group_ajax_add_new_member(self): def test_group_ajax_add_new_member(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
url = reverse('school.views.group_ajax_add_new_member', url = reverse('school.views.group_ajax_add_new_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
new_member = Person.objects.get(user=self.user) new_member = Person.objects.get(user=self.user)
data = {'neptun': new_member.code} data = {'neptun': new_member.code}
resp = self.client.post(url, data) resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
group = Group.objects.get(id=group.id) group = Group.objects.get(id=group.id)
self.assertIn(new_member, group.members.all()) self.assertIn(new_member, group.members.all())
def test_group_ajax_add_new_member_with_nonexistent_group_id(self):
def test_group_ajax_add_new_member_with_nonexistent_groupid(self):
self.login() self.login()
gid = 1337 # this should be the ID of a non-existent group, gid = 1337 # this should be the ID of a non-existent group,
Group.objects.filter(id=gid).delete() # so if it exists, delete it! Group.objects.filter(id=gid).delete() # so if it exists, delete it!
url = reverse('school.views.group_ajax_add_new_member', url = reverse('school.views.group_ajax_add_new_member',
kwargs={'gid': gid}) kwargs={'gid': gid})
new_member = Person.objects.get(user=self.user) new_member = Person.objects.get(user=self.user)
data = {'neptun': new_member.code} data = {'neptun': new_member.code}
resp = self.client.post(url, data) resp = self.client.post(url, data)
...@@ -196,10 +212,10 @@ class ViewTestCase(TestCase): ...@@ -196,10 +212,10 @@ class ViewTestCase(TestCase):
def test_group_ajax_add_new_member_without_neptun(self): def test_group_ajax_add_new_member_without_neptun(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
url = reverse('school.views.group_ajax_add_new_member', url = reverse('school.views.group_ajax_add_new_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
new_member = Person.objects.get(user=self.user) new_member = Person.objects.get(user=self.user)
data = {} data = {}
with self.assertRaises(MultiValueDictKeyError): with self.assertRaises(MultiValueDictKeyError):
...@@ -207,13 +223,28 @@ class ViewTestCase(TestCase): ...@@ -207,13 +223,28 @@ class ViewTestCase(TestCase):
group = Group.objects.get(id=group.id) group = Group.objects.get(id=group.id)
self.assertNotIn(new_member, group.members.all()) self.assertNotIn(new_member, group.members.all())
def test_group_ajax_add_new_member_with_invalid_neptun(self):
self.login()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
url = reverse('school.views.group_ajax_add_new_member',
kwargs={'gid': group.id})
new_member = Person.objects.get(user=self.user)
self.assertNotIn(new_member, group.members.all())
data = {'neptun': '1' + new_member.code}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
self.assertEqual('Error', content['status'])
group = Group.objects.get(id=group.id)
self.assertNotIn(new_member, group.members.all())
def test_group_ajax_add_new_member_with_nonexistent_member(self): def test_group_ajax_add_new_member_with_nonexistent_member(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
url = reverse('school.views.group_ajax_add_new_member', url = reverse('school.views.group_ajax_add_new_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
new_member_code = 'ZXY012' # this should be the ID of a new_member_code = 'ZXY012' # this should be the ID of a
# non-existent person, so if it exists, # non-existent person, so if it exists,
Person.objects.filter(code=new_member_code).delete() # delete it! Person.objects.filter(code=new_member_code).delete() # delete it!
...@@ -225,59 +256,219 @@ class ViewTestCase(TestCase): ...@@ -225,59 +256,219 @@ class ViewTestCase(TestCase):
group = Group.objects.get(id=group.id) group = Group.objects.get(id=group.id)
self.assertIn(new_member, group.members.all()) self.assertIn(new_member, group.members.all())
#
# school.views.group_ajax_remove_member
#
def test_group_ajax_remove_member(self): def test_group_ajax_remove_member(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
member = Person.objects.get(user=self.user) member = Person.objects.get(user=self.user)
group.members.add(member) group.members.add(member)
group.save() group.save()
url = reverse('school.views.group_ajax_remove_member', url = reverse('school.views.group_ajax_remove_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
data = {'neptun': member.code} data = {'neptun': member.code}
resp = self.client.post(url, data) resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
group = Group.objects.get(id=group.id) group = Group.objects.get(id=group.id)
self.assertNotIn(member, group.members.all()) self.assertNotIn(member, group.members.all())
def test_group_ajax_remove_member_with_nonexistent_group_id(self):
def test_group_ajax_remove_member_with_nonexistent_groupid(self):
self.login() self.login()
gid = 1337 # this should be the ID of a non-existent group, gid = 1337 # this should be the ID of a non-existent group,
Group.objects.filter(id=gid).delete() # so if it exists, delete it! Group.objects.filter(id=gid).delete() # so if it exists, delete it!
member = Person.objects.get(user=self.user) member = Person.objects.get(user=self.user)
url = reverse('school.views.group_ajax_remove_member', url = reverse('school.views.group_ajax_remove_member',
kwargs={'gid': gid}) kwargs={'gid': gid})
data = {'neptun': member.code} data = {'neptun': member.code}
resp = self.client.post(url, data) resp = self.client.post(url, data)
self.assertEqual(404, resp.status_code) self.assertEqual(404, resp.status_code)
def test_group_ajax_remove_member_without_neptun(self): def test_group_ajax_remove_member_without_neptun(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
member = Person.objects.get(user=self.user) member = Person.objects.get(user=self.user)
group.members.add(member) group.members.add(member)
group.save() group.save()
url = reverse('school.views.group_ajax_remove_member', url = reverse('school.views.group_ajax_remove_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
data = {} data = {}
with self.assertRaises(MultiValueDictKeyError): with self.assertRaises(MultiValueDictKeyError):
self.client.post(url, data) self.client.post(url, data)
group = Group.objects.get(id=group.id) group = Group.objects.get(id=group.id)
self.assertIn(member, group.members.all()) self.assertIn(member, group.members.all())
def test_group_ajax_remove_member_with_invalid_neptun(self):
self.login()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
member = Person.objects.get(user=self.user)
group.members.add(member)
group.save()
url = reverse('school.views.group_ajax_remove_member',
kwargs={'gid': group.id})
data = {'neptun': '1' + member.code} # invalid Neptun code
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
self.assertEqual('Error', content['status'])
group = Group.objects.get(id=group.id)
self.assertIn(member, group.members.all())
def test_group_ajax_remove_member_with_nonexistent_member(self): def test_group_ajax_remove_member_with_nonexistent_member(self):
self.login() self.login()
group = Group.objects.create(name="mytestgroup", group = Group.objects.create(
semester=Semester.get_current()) name="mytestgroup", semester=Semester.get_current())
member_code = 'ZXY012' # this should be the ID of a non-existent member_code = 'ZXY012' # this should be the ID of a non-existent
# person, so if it exists, # person, so if it exists,
Person.objects.filter(code=member_code).delete() # delete it! Person.objects.filter(code=member_code).delete() # delete it!
url = reverse('school.views.group_ajax_remove_member', url = reverse('school.views.group_ajax_remove_member',
kwargs={'gid': group.id}) kwargs={'gid': group.id})
data = {'neptun': member_code} data = {'neptun': member_code}
with self.assertRaises(Person.DoesNotExist): with self.assertRaises(Person.DoesNotExist):
self.client.post(url, data) self.client.post(url, data)
self.assertFalse(Person.objects.filter(code=member_code).exists()) self.assertFalse(Person.objects.filter(code=member_code).exists())
#
# school.views.group_ajax_delete
#
def test_group_ajax_delete(self):
self.login()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
url = reverse('school.views.group_ajax_delete')
data = {'gid': group.id}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
self.assertFalse(Group.objects.filter(id=group.id).exists())
def test_group_ajax_delete_without_gid(self):
self.login()
url = reverse('school.views.group_ajax_delete')
data = {}
with self.assertRaises(MultiValueDictKeyError):
self.client.post(url, data)
def test_group_ajax_delete_with_nonexistent_group_id(self):
self.login()
gid = 1337 # this should be the ID of a non-existent group,
Group.objects.filter(id=gid).delete() # so if it exists, delete it!
url = reverse('school.views.group_ajax_delete')
data = {'gid': gid}
resp = self.client.post(url, data)
self.assertEqual(404, resp.status_code)
#
# school.views.group_ajax_owner_autocomplete
#
def test_group_ajax_owner_autocomplete(self):
self.login()
query = self.user.last_name[:2]
url = reverse('school.views.group_ajax_owner_autocomplete')
data = {'q': query}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
user_data = {'name': self.user.get_full_name(),
'neptun': self.user.username}
self.assertIn(user_data, content)
def test_group_ajax_owner_autocomplete_without_query(self):
self.login()
url = reverse('school.views.group_ajax_owner_autocomplete')
data = {}
with self.assertRaises(MultiValueDictKeyError):
self.client.post(url, data)
#
# school.views.group_ajax_add_new_owner
#
def test_group_ajax_add_new_owner(self):
self.login()
user_details = UserCloudDetails.objects.get(user=self.user)
user_details.share_quota = 10
user_details.save()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
new_owner = Person.objects.get(code=self.user.username)
self.assertNotIn(new_owner, group.owners.all())
url = reverse('school.views.group_ajax_add_new_owner',
kwargs={'gid': group.id})
data = {'neptun': new_owner.code}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
self.assertEqual('OK', content['status'])
group = Group.objects.get(id=group.id)
self.assertIn(new_owner, group.owners.all())
def test_group_ajax_add_new_owner_without_enough_share_quota(self):
self.login()
user_details = UserCloudDetails.objects.get(user=self.user)
user_details.share_quota = 0
user_details.save()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
new_owner = Person.objects.get(code=self.user.username)
self.assertNotIn(new_owner, group.owners.all())
url = reverse('school.views.group_ajax_add_new_owner',
kwargs={'gid': group.id})
data = {'neptun': new_owner.code}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
self.assertEqual('denied', content['status'])
group = Group.objects.get(id=group.id)
self.assertNotIn(new_owner, group.owners.all())
def test_group_ajax_add_new_owner_with_nonexistent_group_id(self):
self.login()
user_details = UserCloudDetails.objects.get(user=self.user)
user_details.share_quota = 10
user_details.save()
gid = 1337 # this should be the ID of a non-existent group,
Group.objects.filter(id=gid).delete() # so if it exists, delete it!
new_owner = Person.objects.get(code=self.user.username)
url = reverse('school.views.group_ajax_add_new_owner',
kwargs={'gid': gid})
data = {'neptun': new_owner.code}
resp = self.client.post(url, data)
self.assertEqual(404, resp.status_code)
def test_group_ajax_add_new_owner_without_neptun(self):
self.login()
user_details = UserCloudDetails.objects.get(user=self.user)
user_details.share_quota = 10
user_details.save()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
new_owner = Person.objects.get(code=self.user.username)
self.assertNotIn(new_owner, group.owners.all())
url = reverse('school.views.group_ajax_add_new_owner',
kwargs={'gid': group.id})
data = {}
with self.assertRaises(MultiValueDictKeyError):
self.client.post(url, data)
group = Group.objects.get(id=group.id)
self.assertNotIn(new_owner, group.owners.all())
def test_group_ajax_add_new_owner_with_invalid_neptun(self):
self.login()
user_details = UserCloudDetails.objects.get(user=self.user)
user_details.share_quota = 10
user_details.save()
group = Group.objects.create(
name="mytestgroup", semester=Semester.get_current())
new_owner = Person.objects.get(code=self.user.username)
self.assertNotIn(new_owner, group.owners.all())
url = reverse('school.views.group_ajax_add_new_owner',
kwargs={'gid': group.id})
data = {'neptun': '1' + new_owner.code}
resp = self.client.post(url, data)
self.assertEqual(200, resp.status_code)
content = json.loads(resp.content)
self.assertEqual('Error', content['status'])
group = Group.objects.get(id=group.id)
self.assertNotIn(new_owner, group.owners.all())
from datetime import datetime
from itertools import chain from itertools import chain
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User, Group as AGroup from django.contrib.auth.models import User, Group as AGroup
from django.contrib import messages from django.contrib import messages
from django.core import signing
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
from django.core.mail import mail_managers, send_mail
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction from django.http import HttpResponse
from django.forms import ModelForm, Textarea from django.shortcuts import (render_to_response, get_object_or_404,
from django.http import Http404 redirect)
from django.shortcuts import render, render_to_response, get_object_or_404, redirect
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import render_to_string
from django.utils.decorators import method_decorator
from django.utils.http import is_safe_url from django.utils.http import is_safe_url
from django.utils.translation import get_language as lang
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import * from one.models import Template, UserCloudDetails
from django.views.generic import * from school.models import Person, Semester, Course, Group
from one.models import *
from school.models import *
import django.contrib.auth as auth import django.contrib.auth as auth
import logging import logging
import json import json
import re import re
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
neptun_re = re.compile('^[a-zA-Z][a-zA-Z0-9]{5}$')
def logout(request): def logout(request):
auth.logout(request) auth.logout(request)
return redirect('/Shibboleth.sso/Logout?return=https%3a%2f%2fcloud.ik.bme.hu%2f') url = '/Shibboleth.sso/Logout?return=https%3a%2f%2fcloud.ik.bme.hu%2f'
return redirect(url)
@ensure_csrf_cookie @ensure_csrf_cookie
...@@ -51,7 +47,9 @@ def login(request): ...@@ -51,7 +47,9 @@ def login(request):
try: try:
user.email = request.META['email'] user.email = request.META['email']
except KeyError: except KeyError:
messages.error(request, _("The identity provider did not pass the mandatory e-mail data.")) messages.error(request,
_("The identity provider did not pass the mandatory "
"e-mail data."))
raise PermissionDenied() raise PermissionDenied()
user.save() user.save()
p, created = Person.objects.get_or_create(code=user.username) p, created = Person.objects.get_or_create(code=user.username)
...@@ -73,12 +71,11 @@ def login(request): ...@@ -73,12 +71,11 @@ def login(request):
try: try:
g.members.add(p) g.members.add(p)
g.save() g.save()
messages.info(request, messages.info(request, _('Course "%s" added.') % g.course)
_('Course "%s" added.') % g.course)
logger.info('Django Course "%s" added.' % g.course) logger.info('Django Course "%s" added.' % g.course)
except Exception as e: except Exception as e:
messages.error(request, messages.error(request,
_('Failed to add course "%s".') % g.course) _('Failed to add course "%s".') % g.course)
logger.warning("Django ex %s" % e) logger.warning("Django ex %s" % e)
held = request.META['niifEduPersonHeldCourse'] held = request.META['niifEduPersonHeldCourse']
...@@ -92,10 +89,11 @@ def login(request): ...@@ -92,10 +89,11 @@ def login(request):
co.owners.add(p) co.owners.add(p)
g.owners.add(p) g.owners.add(p)
messages.info(request, messages.info(request,
_('Course "%s" ownership added.') % g.course) _('Course "%s" ownership added.') % g.course)
except Exception as e: except Exception as e:
messages.error(request, messages.error(request,
_('Failed to add course "%s" ownership.') % g.course) _('Failed to add course "%s" ownership.')
% g.course)
logger.warning("Django ex %s" % e) logger.warning("Django ex %s" % e)
co.save() co.save()
g.save() g.save()
...@@ -116,7 +114,7 @@ def login(request): ...@@ -116,7 +114,7 @@ def login(request):
logger.info("Django affiliation group %s added to %s" % (a, p)) logger.info("Django affiliation group %s added to %s" % (a, p))
except Exception as e: except Exception as e:
logger.warning("Django FAILed to add affiliation group %s to %s." logger.warning("Django FAILed to add affiliation group %s to %s."
" Reason: %s" % (a, p, e)) " Reason: %s" % (a, p, e))
user.save() user.save()
p.save() p.save()
...@@ -155,7 +153,7 @@ def group_show(request, gid): ...@@ -155,7 +153,7 @@ def group_show(request, gid):
user = request.user user = request.user
group = get_object_or_404(Group, id=gid) group = get_object_or_404(Group, id=gid)
mytemplates = Template.objects.filter(owner=request.user, state='READY') mytemplates = Template.objects.filter(owner=user, state='READY')
for t in mytemplates: for t in mytemplates:
t.myshares = t.share_set.filter(group=group) t.myshares = t.share_set.filter(group=group)
...@@ -172,7 +170,7 @@ def group_show(request, gid): ...@@ -172,7 +170,7 @@ def group_show(request, gid):
'mytemplates': mytemplates, 'mytemplates': mytemplates,
'publictemplates': publictemplates, 'publictemplates': publictemplates,
'noshare': not has_share, 'noshare': not has_share,
'userdetails': UserCloudDetails.objects.get(user=request.user), 'userdetails': UserCloudDetails.objects.get(user=user),
'owners': group.owners.all(), 'owners': group.owners.all(),
})) }))
...@@ -185,7 +183,7 @@ def group_new(request): ...@@ -185,7 +183,7 @@ def group_new(request):
members_list = [m for m in members_list if m != ''] members_list = [m for m in members_list if m != '']
members = [] members = []
for member in members_list: for member in members_list:
if re.match('^[a-zA-Z][a-zA-Z0-9]{5}$', member.strip()) is None: if neptun_re.match(member.strip()) is None:
messages.error(request, _('Invalid NEPTUN code found.')) messages.error(request, _('Invalid NEPTUN code found.'))
return redirect('/') return redirect('/')
person, created = Person.objects.get_or_create(code=member) person, created = Person.objects.get_or_create(code=member)
...@@ -207,7 +205,7 @@ def group_new(request): ...@@ -207,7 +205,7 @@ def group_new(request):
def group_ajax_add_new_member(request, gid): def group_ajax_add_new_member(request, gid):
group = get_object_or_404(Group, id=gid) group = get_object_or_404(Group, id=gid)
member = request.POST['neptun'] member = request.POST['neptun']
if re.match('^[a-zA-Z][a-zA-Z0-9]{5}$', member.strip()) is None: if neptun_re.match(member.strip()) is None:
status = json.dumps({'status': 'Error'}) status = json.dumps({'status': 'Error'})
messages.error(request, _('Invalid NEPTUN code')) messages.error(request, _('Invalid NEPTUN code'))
return HttpResponse(status) return HttpResponse(status)
...@@ -223,7 +221,7 @@ def group_ajax_add_new_member(request, gid): ...@@ -223,7 +221,7 @@ def group_ajax_add_new_member(request, gid):
def group_ajax_remove_member(request, gid): def group_ajax_remove_member(request, gid):
group = get_object_or_404(Group, id=gid) group = get_object_or_404(Group, id=gid)
member = request.POST['neptun'] member = request.POST['neptun']
if re.match('^[a-zA-Z][a-zA-Z0-9]{5}$', member) is None: if neptun_re.match(member.strip()) is None:
status = json.dumps({'status': 'Error'}) status = json.dumps({'status': 'Error'})
messages.error(request, _('Invalid NEPTUN code')) messages.error(request, _('Invalid NEPTUN code'))
return HttpResponse(status) return HttpResponse(status)
...@@ -237,7 +235,9 @@ def group_ajax_remove_member(request, gid): ...@@ -237,7 +235,9 @@ def group_ajax_remove_member(request, gid):
@login_required @login_required
def group_ajax_delete(request): def group_ajax_delete(request):
group = get_object_or_404(Group, id=request.POST['gid']) # TODO should take parameter in URL using DELETE command
gid = request.POST['gid']
group = get_object_or_404(Group, id=gid)
group.delete() group.delete()
return HttpResponse(json.dumps({ return HttpResponse(json.dumps({
'status': 'OK' 'status': 'OK'
...@@ -246,30 +246,27 @@ def group_ajax_delete(request): ...@@ -246,30 +246,27 @@ def group_ajax_delete(request):
@login_required @login_required
def group_ajax_owner_autocomplete(request): def group_ajax_owner_autocomplete(request):
# TODO should be renamed to something like 'user_ajax_autocomplete'
users = ( query = request.POST['q']
User.objects.filter(last_name__istartswith=request.POST['q'])[:5] + users = chain(User.objects.filter(last_name__istartswith=query)[:5],
User.objects.filter(first_name__istartswith=request.POST['q'])[:5] + User.objects.filter(first_name__istartswith=query)[:5],
User.objects.filter(username__istartswith=request.POST['q'])[:5]) User.objects.filter(username__istartswith=query)[:5])
results = map(lambda u: { results = [{'name': user.get_full_name(),
'name': u.get_full_name(), 'neptun': user.username} for user in users]
'neptun': u.username}, users)
return HttpResponse(json.dumps(results, ensure_ascii=False)) return HttpResponse(json.dumps(results, ensure_ascii=False))
@login_required @login_required
def group_ajax_add_new_owner(request, gid): def group_ajax_add_new_owner(request, gid):
if request.user.cloud_details.share_quota == 0: if request.user.cloud_details.share_quota <= 0:
return HttpResponse({'status': 'denied'}) return HttpResponse(json.dumps({'status': 'denied'}))
group = get_object_or_404(Group, id=gid) group = get_object_or_404(Group, id=gid)
member = request.POST['neptun'] member = request.POST['neptun']
if re.match('^[a-zA-Z][a-zA-Z0-9]{5}$', member.strip()) is None: if neptun_re.match(member.strip()) is None:
status = json.dumps({'status': 'Error'}) status = json.dumps({'status': 'Error'})
messages.error(request, _('Invalid NEPTUN code')) messages.error(request, _('Invalid NEPTUN code'))
return HttpResponse(status) return HttpResponse(status)
person, created = Person.objects.get_or_create(code=member) person, created = Person.objects.get_or_create(code=member)
group.owners.add(person) group.owners.add(person)
group.save() group.save()
return HttpResponse(json.dumps({ return HttpResponse(json.dumps({'status': 'OK'}))
'status': 'OK'
}))
from django import contrib
# from django.utils.translation import ugettext_lazy as _
from .models import Disk, DataStore
class DiskAdmin(contrib.admin.ModelAdmin):
list_display = ('name', 'datastore')
class DataStoreAdmin(contrib.admin.ModelAdmin):
list_display = ('name', 'path')
contrib.admin.site.register(Disk, DiskAdmin)
contrib.admin.site.register(DataStore, DataStoreAdmin)
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Disk'
db.create_table('storage_disk', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
('path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)),
('format', self.gf('django.db.models.fields.CharField')(max_length=10)),
('size', self.gf('django.db.models.fields.IntegerField')()),
('type', self.gf('django.db.models.fields.CharField')(max_length=10)),
('base', self.gf('django.db.models.fields.related.ForeignKey')(related_name='snapshots', to=orm['storage.Disk'])),
('original_parent', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['storage.Disk'])),
))
db.send_create_signal('storage', ['Disk'])
def backwards(self, orm):
# Deleting model 'Disk'
db.delete_table('storage_disk')
models = {
'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['storage.Disk']"}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'original_parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.Disk']"}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
'size': ('django.db.models.fields.IntegerField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
}
}
complete_apps = ['storage']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'Disk.original_parent'
db.alter_column('storage_disk', 'original_parent_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['storage.Disk'], null=True))
# Changing field 'Disk.base'
db.alter_column('storage_disk', 'base_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['storage.Disk']))
def backwards(self, orm):
# User chose to not deal with backwards NULL issues for 'Disk.original_parent'
raise RuntimeError("Cannot reverse this migration. 'Disk.original_parent' and its values cannot be restored.")
# User chose to not deal with backwards NULL issues for 'Disk.base'
raise RuntimeError("Cannot reverse this migration. 'Disk.base' and its values cannot be restored.")
models = {
'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'snapshots'", 'null': 'True', 'to': "orm['storage.Disk']"}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'original_parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.Disk']", 'null': 'True', 'blank': 'True'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
'size': ('django.db.models.fields.IntegerField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
}
}
complete_apps = ['storage']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'DataStore'
db.create_table('storage_datastore', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
('path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)),
))
db.send_create_signal('storage', ['DataStore'])
# Deleting field 'Disk.path'
db.delete_column('storage_disk', 'path')
# Adding field 'Disk.datastore'
db.add_column('storage_disk', 'datastore',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['storage.DataStore']),
keep_default=False)
def backwards(self, orm):
# Deleting model 'DataStore'
db.delete_table('storage_datastore')
# User chose to not deal with backwards NULL issues for 'Disk.path'
raise RuntimeError("Cannot reverse this migration. 'Disk.path' and its values cannot be restored.")
# Deleting field 'Disk.datastore'
db.delete_column('storage_disk', 'datastore_id')
models = {
'storage.datastore': {
'Meta': {'ordering': "['name']", 'object_name': 'DataStore'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'snapshots'", 'null': 'True', 'to': "orm['storage.Disk']"}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.DataStore']"}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'original_parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.Disk']", 'null': 'True', 'blank': 'True'}),
'size': ('django.db.models.fields.IntegerField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
}
}
complete_apps = ['storage']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Disk.created'
db.add_column('storage_disk', 'created',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Disk.created'
db.delete_column('storage_disk', 'created')
models = {
'storage.datastore': {
'Meta': {'ordering': "['name']", 'object_name': 'DataStore'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'snapshots'", 'null': 'True', 'to': "orm['storage.Disk']"}),
'created': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.DataStore']"}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'original_parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storage.Disk']", 'null': 'True', 'blank': 'True'}),
'size': ('django.db.models.fields.IntegerField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
}
}
complete_apps = ['storage']
\ No newline at end of file
# coding=utf-8
import logging
import jsonpickle
import json
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save, post_delete
from .tasks import StorageDriver
logger = logging.getLogger(__name__)
class DataStore(models.Model):
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
path = models.CharField(max_length=200, unique=True,
verbose_name=_('path'))
class Meta:
ordering = ['name']
verbose_name = _('datastore')
verbose_name_plural = _('datastores')
def __unicode__(self):
return u'%s (%s)' % (self.name, self.path)
class Disk(models.Model):
"""Virtual disks automatically synchronized with OpenNebula."""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
datastore = models.ForeignKey('DataStore')
format = models.CharField(max_length=10,
choices=(('qcow2', 'qcow2'), ('raw', 'raw')))
size = models.IntegerField()
type = models.CharField(max_length=10,
choices=(('snapshot', 'snapshot'),
('normal', 'normal')))
base = models.ForeignKey('Disk', related_name='snapshots',
null=True, blank=True)
original_parent = models.ForeignKey('Disk', null=True, blank=True)
created = models.BooleanField(default=False)
class Meta:
ordering = ['name']
verbose_name = _('disk')
verbose_name_plural = _('disks')
def to_json(self):
self.base_name = self.base.name if self.base else None
self.dir = self.datastore.path
return jsonpickle.encode(self, unpicklable=True)
def __unicode__(self):
return u"%s (#%d)" % (self.name, self.id)
@classmethod
def create_signal(cls, sender, instance, created, **kwargs):
if not instance.created:
StorageDriver.create_disk.delay(instance.to_json()).get()
instance.created = True
instance.save()
@classmethod
def delete_signal(cls, sender, instance, using, **kwargs):
StorageDriver.delete_disk.delay(instance.to_json()).get()
@classmethod
def update_disk(cls, disk):
name = disk['name']
modified = False
try:
base = cls.objects.get(name=disk['base_name'])
except cls.DoesNotExist:
base = None
try:
d = cls.objects.get(name=name)
except Disk.DoesNotExist:
d = Disk(name=name,
created=True,
datastore=DataStore.objects.get(path=disk['dir']),
format=disk['format'],
type=disk['type'])
modified = True
if d.size != disk['size'] or d.base != base:
d.size = disk['size']
d.base = base
modified = True
if modified:
d.full_clean()
d.save()
@classmethod
def update_disks(cls, delete=True):
"""Get and register virtual disks from OpenNebula."""
try:
json_data = StorageDriver.list_disks.delay().get(timeout=10)
disks = json.loads(json_data)
except:
return
with transaction.commit_on_success():
l = []
for disk in disks:
print disk
cls.update_disk(disk)
l.append(disk['name'])
if delete:
cls.objects.exclude(name__in=l).delete()
post_save.connect(Disk.create_signal, sender=Disk)
post_delete.connect(Disk.delete_signal, sender=Disk)
import celery
from celery.contrib.methods import task_method
import logging
logger = logging.getLogger(__name__)
class StorageDriver:
@celery.task(filter=task_method, name='storagedriver.list_disks')
def list_disks():
pass
@celery.task(filter=task_method, name='storagedriver.create_disk')
def create_disk(json_data):
pass
@celery.task(filter=task_method, name='storagedriver.delete_disk')
def delete_disk(json_data):
pass
@celery.task(filter=task_method, name='storagedriver.get_disk')
def get_disk(json_data):
pass
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
# Create your views here.
# Create your views here. # Create your views here.
from django.core.context_processors import csrf
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render_to_response, redirect from django.shortcuts import render_to_response, redirect
from django.template import RequestContext from django.template import RequestContext
from store.api import StoreApi from store.api import StoreApi
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib import auth
from django.views.decorators.csrf import csrf_exempt
import os import os
import json import json
import base64 import base64
def estabilish_store_user(request, user): def estabilish_store_user(request, user):
try: try:
details = request.user.cloud_details details = request.user.cloud_details
...@@ -21,18 +20,19 @@ def estabilish_store_user(request, user): ...@@ -21,18 +20,19 @@ def estabilish_store_user(request, user):
key_list.append(key.key) key_list.append(key.key)
except: except:
return HttpResponse('Can not acces to django database!', status=404) return HttpResponse('Can not acces to django database!', status=404)
#Create user # Create user
if not StoreApi.createuser(user, password, key_list, str(quota)): if not StoreApi.createuser(user, password, key_list, str(quota)):
return HttpResponse('User does not exist on store! And could not create!') return HttpResponse('User does not exist on store!'
'And could not create!')
@login_required @login_required
def index(request): def index(request):
user = request.user.username user = request.user.username
if StoreApi.userexist(user) != True: if StoreApi.userexist(user) is not True:
estabilish_store_user(request, user) estabilish_store_user(request, user)
#UpdateAuthorizationInfo # UpdateAuthorizationInfo
try: try:
auth=request.POST['auth']
try: try:
details = request.user.cloud_details details = request.user.cloud_details
password = details.smb_password password = details.smb_password
...@@ -40,146 +40,181 @@ def index(request): ...@@ -40,146 +40,181 @@ def index(request):
for key in request.user.sshkey_set.all(): for key in request.user.sshkey_set.all():
key_list.append(key.key) key_list.append(key.key)
except: except:
return HttpResponse('Can not acces to django database!', status=404) return HttpResponse('Can not acces to django database!',
status=404)
if not StoreApi.updateauthorizationinfo(user, password, key_list): if not StoreApi.updateauthorizationinfo(user, password, key_list):
return HttpResponse('Can not update authorization information!') return HttpResponse('Can not update authorization information!')
except: except:
pass pass
#Download handler # Download handler
try: try:
dl = request.POST['dl'] dl = request.POST['dl']
return redirect(StoreApi.requestdownload(user, dl)) return redirect(StoreApi.requestdownload(user, dl))
except: except:
pass pass
#Upload handler # Upload handler
try: try:
ul = request.POST['ul'] ul = request.POST['ul']
url = StoreApi.requestupload(user,ul) url = StoreApi.requestupload(user, ul)
return render_to_response('store/upload.html', RequestContext(request,{'URL' : url})) return render_to_response('store/upload.html',
RequestContext(request, {'URL': url}))
except: except:
pass pass
#Remove handler # Remove handler
try: try:
rm = request.POST['rm'] rm = request.POST['rm']
succes = StoreApi.requestremove(user,rm) StoreApi.requestremove(user, rm)
except: except:
pass pass
#Remove handler # Remove handler
try: try:
path = request.POST['path'] path = request.POST['path']
new = request.POST['new'] new = request.POST['new']
succes = StoreApi.requestnewfolder(user,path+'/'+new) StoreApi.requestnewfolder(user, path + '/' + new)
except: except:
pass pass
#Simple file list # Simple file list
path = '/' path = '/'
try: try:
path = request.POST['path'] path = request.POST['path']
except: except:
pass pass
#Normalize path (Need double dirname /folder/ -> /folder -> / # Normalize path (Need double dirname /folder/ -> /folder -> /
backpath = os.path.normpath(os.path.dirname(os.path.dirname(path))) backpath = os.path.normpath(os.path.dirname(os.path.dirname(path)))
file_list = StoreApi.listfolder(user,path) file_list = StoreApi.listfolder(user, path)
quota = StoreApi.requestquota(user) quota = StoreApi.requestquota(user)
return render_to_response('store/list.html', RequestContext(request, {'file_list': file_list, 'path' : path, 'backpath' : backpath, 'username' : user, 'quota' : quota})) return render_to_response('store/list.html',
RequestContext(request,
{
'file_list': file_list,
'path': path,
'backpath': backpath,
'username': user,
'quota': quota
}
))
@login_required @login_required
def ajax_listfolder(request): def ajax_listfolder(request):
user = request.user.username user = request.user.username
if StoreApi.userexist(user) != True: if StoreApi.userexist(user) is not True:
estabilish_store_user(request, user) estabilish_store_user(request, user)
path = '/' path = '/'
try: try:
path = request.POST['path'] path = request.POST['path']
except: except:
pass pass
#Normalize path (Need double dirname /folder/ -> /folder -> / # Normalize path (Need double dirname /folder/ -> /folder -> /
backpath = os.path.normpath(os.path.dirname(os.path.dirname(path))) # backpath = os.path.normpath(os.path.dirname(os.path.dirname(path)))
file_list = StoreApi.listfolder(user,path) file_list = StoreApi.listfolder(user, path)
return HttpResponse(json.dumps(file_list)) return HttpResponse(json.dumps(file_list))
@login_required @login_required
def ajax_quota(request): def ajax_quota(request):
user = request.user.username user = request.user.username
if StoreApi.userexist(user) != True: if StoreApi.userexist(user) is not True:
estabilish_store_user(request, user) estabilish_store_user(request, user)
return HttpResponse(json.dumps(StoreApi.requestquota(user))) return HttpResponse(json.dumps(StoreApi.requestquota(user)))
#return HttpResponse(json.dumps({'Used':20,'Soft':160,'Hard':200})) # return HttpResponse(json.dumps({'Used':20,'Soft':160,'Hard':200}))
@login_required @login_required
def ajax_download(request): def ajax_download(request):
user = request.user.username user = request.user.username
try: try:
dl = request.POST['dl'] dl = request.POST['dl']
return HttpResponse(json.dumps({'url':StoreApi.requestdownload(user,dl)})) return HttpResponse(json.dumps(
{'url': StoreApi.requestdownload(user, dl)}))
except: except:
pass pass
return HttpResponse('File not found!', status=404) return HttpResponse('File not found!', status=404)
@login_required @login_required
def ajax_upload(request): def ajax_upload(request):
user = request.user.username user = request.user.username
try: try:
ul = request.POST['ul'] ul = request.POST['ul']
url = StoreApi.requestupload(user,ul) url = StoreApi.requestupload(user, ul)
return HttpResponse(json.dumps({'url':url})) return HttpResponse(json.dumps({'url': url}))
except: except:
pass pass
return HttpResponse('Error!', status=404) return HttpResponse('Error!', status=404)
@login_required @login_required
def ajax_delete(request): def ajax_delete(request):
user = request.user.username user = request.user.username
try: try:
rm = request.POST['rm'] rm = request.POST['rm']
return HttpResponse(json.dumps({'success':StoreApi.requestremove(user,rm)})) return HttpResponse(json.dumps(
{'success': StoreApi.requestremove(user, rm)}))
except: except:
pass pass
return HttpResponse('File not found!', status=404) return HttpResponse('File not found!', status=404)
@login_required @login_required
def ajax_new_folder(request): def ajax_new_folder(request):
user = request.user.username user = request.user.username
try: try:
path = request.POST['path'] path = request.POST['path']
new = request.POST['new'] new = request.POST['new']
success = StoreApi.requestnewfolder(user,path+'/'+new) success = StoreApi.requestnewfolder(user, path + '/' + new)
return HttpResponse(json.dumps({'success':success})) return HttpResponse(json.dumps({'success': success}))
except: except:
pass pass
return HttpResponse('Error!', status=404) return HttpResponse('Error!', status=404)
@login_required @login_required
def ajax_rename(request): def ajax_rename(request):
user = request.user.username user = request.user.username
try: try:
path = request.POST['path'] path = request.POST['path']
new = request.POST['new'] new = request.POST['new']
success = StoreApi.requestrename(user,path,new) success = StoreApi.requestrename(user, path, new)
return HttpResponse(json.dumps({'success':success})) return HttpResponse(json.dumps({'success': success}))
except: except:
pass pass
return HttpResponse('Error!', status=404) return HttpResponse('Error!', status=404)
@login_required @login_required
def toplist(request): def toplist(request):
user = request.user.username user = request.user.username
path = backpath = '/' path = backpath = '/'
file_list = StoreApi.toplist(user) file_list = StoreApi.toplist(user)
return render_to_response('store/list.html', RequestContext(request, {'file_list': file_list, 'path' : path, 'backpath' : backpath, 'username' : user})) return render_to_response('store/list.html',
RequestContext(request,
{
'file_list': file_list,
'path': path,
'backpath': backpath,
'username': user
}))
@login_required @login_required
def ajax_toplist(request): def ajax_toplist(request):
user = request.user.username user = request.user.username
path = backpath = '/'
file_list = StoreApi.toplist(user) file_list = StoreApi.toplist(user)
return HttpResponse(json.dumps(file_list)) return HttpResponse(json.dumps(file_list))
@login_required @login_required
def gui(request): def gui(request):
user = request.user.username user = request.user.username
if request.method == 'GET': if request.method == 'GET':
return render_to_response('store/gui.html', RequestContext(request, {'username' : user, 'host' : StoreApi.get_host()})) return render_to_response('store/gui.html',
RequestContext(
request,
{
'username': user,
'host': StoreApi.get_host()
}))
elif request.method == 'POST': elif request.method == 'POST':
try: try:
details = request.user.cloud_details details = request.user.cloud_details
...@@ -188,7 +223,8 @@ def gui(request): ...@@ -188,7 +223,8 @@ def gui(request):
for key in request.user.sshkey_set.all(): for key in request.user.sshkey_set.all():
key_list.append(key.key) key_list.append(key.key)
except: except:
return HttpResponse('Can not acces to django database!', status=404) return HttpResponse('Can not acces to django database!',
status=404)
try: try:
lab_key_decoded = base64.b64decode(request.POST['KEY']) lab_key_decoded = base64.b64decode(request.POST['KEY'])
key_list.append(lab_key_decoded) key_list.append(lab_key_decoded)
...@@ -196,14 +232,18 @@ def gui(request): ...@@ -196,14 +232,18 @@ def gui(request):
if StoreApi.updateauthorizationinfo(user, password, key_list): if StoreApi.updateauthorizationinfo(user, password, key_list):
return HttpResponse('Keys resetted succesfully!') return HttpResponse('Keys resetted succesfully!')
else: else:
return HttpResponse('Can not update authorization information!') return HttpResponse('Can not update authorization '
'information!')
if StoreApi.updateauthorizationinfo(user, password, key_list): if StoreApi.updateauthorizationinfo(user, password, key_list):
return HttpResponse('https://cloud.ik.bme.hu/home/'+'?neptun='+user+'&'+'host='+StoreApi.get_host()) return HttpResponse('https://cloud.ik.bme.hu/home/' +
'?neptun=' + user + '&' + 'host=' +
StoreApi.get_host())
else: else:
return HttpResponse('Can not update authorization information!') return HttpResponse('Can not update authorization information!')
else: else:
return HttpResponse('Method not found!', status=404) return HttpResponse('Method not found!', status=404)
def logout(request): def logout(request):
auth.logout(request) auth.logout(request)
return redirect('/') return redirect('/')
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