Commit 883d6de6 by Őry Máté

Merge branch 'master' into acl

Conflicts:
	circle/circle/settings/base.py
parents e1b8a82b 97b69b04
"""Common settings and globals."""
from datetime import timedelta
from os import environ
from os.path import abspath, basename, dirname, join, normpath
from json import loads
# from socket import SOCK_STREAM
from sys import path
# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured
......@@ -311,3 +312,11 @@ AUTHENTICATION_BACKENDS = (
'guardian.backends.ObjectPermissionBackend',
)
ANONYMOUS_USER_ID = -1
# Set up periodic firewall tasks
CELERYBEAT_SCHEDULE = {
'blabla': {
'task': 'firewall.tasks.local_tasks.periodic_task',
'schedule': timedelta(seconds=5),
},
}
from django_tables2 import Table, A
from django_tables2.columns import Column, LinkColumn, TemplateColumn
from django_tables2.columns import LinkColumn, TemplateColumn
from vm.models import Instance
from django.utils.translation import ugettext_lazy as _
class VmListTable(Table):
name = LinkColumn('dashboard.views.detail', args=[A('pk')])
admin = TemplateColumn(template_name='dashboard/vm-list/column-admin.html')
details = TemplateColumn(template_name=
'dashboard/vm-list/column-details.html')
actions = TemplateColumn(template_name=
'dashboard/vm-list/column-actions.html')
pk = TemplateColumn(
template_name='dashboard/vm-list/column-id.html',
verbose_name="ID",
attrs={'th': {'class': 'vm-list-table-thin'}},
)
name = LinkColumn(
'dashboard.views.detail',
args=[A('pk')],
attrs={'class': 'real-link'}
)
admin = TemplateColumn(
template_name='dashboard/vm-list/column-admin.html',
attrs={'th': {'class': 'vm-list-table-admin'}},
)
details = TemplateColumn(
template_name='dashboard/vm-list/column-details.html',
attrs={'th': {'class': 'vm-list-table-thin'}},
)
actions = TemplateColumn(
template_name='dashboard/vm-list/column-actions.html',
attrs={'th': {'class': 'vm-list-table-thin'}},
)
time_of_suspend = TemplateColumn(
'{{ record.time_of_suspend|timesince }}',
'{{ record.time_of_suspend|timeuntil }}',
verbose_name=_("Suspend in"))
time_of_delete = Column(verbose_name=_("Delete in"))
time_of_delete = TemplateColumn(
'{{ record.time_of_delete|timeuntil }}',
verbose_name=_("Delete in"))
class Meta:
model = Instance
......
......@@ -3,7 +3,8 @@
<label for="cpu-priority-slider"><i class="icon-trophy"></i> CPU priority</label>
</div>
<div class="col-sm-9">
<input type="text" data-slider="true" data-slider-highlight="true" data-slider-snap="true" id="cpu-priority-slider" value="0.25">
<input type="text" data-slider="true" data-slider-highlight="true"
data-slider-snap="true" data-slider-range="0,100" data-slider-step="1" id="cpu-priority-slider" value="{{ instance.priority }}">
</div>
</p>
......@@ -13,7 +14,7 @@
<label for="cpu-count-slider"><i class="icon-cogs"></i> CPU count</label>
</div>
<div class="col-sm-9">
<input type="text" value="1" data-slider="true" data-slider-highlight="true" data-slider-range="0,8" data-slider-snap="true" data-slider-step="1" id="cpu-count-slider">
<input type="text" value="{{ instance.num_cores }}" data-slider="true" data-slider-highlight="true" data-slider-range="0,8" data-slider-snap="true" data-slider-step="1" id="cpu-count-slider">
</div>
</p>
......@@ -23,7 +24,7 @@
<label for="ram-slider"><i class="icon-ticket"></i> RAM amount</label>
</div>
<div class="col-sm-9">
<input type="text" data-slider="true" data-slider-highlight="true" id="ram-slider" data-slider-range="128,4096" data-slider-snap="true" data-slider-step="128"> MiB
<input type="text" data-slider="true" data-slider-highlight="true" id="ram-slider" data-slider-range="128,4096" data-slider-snap="true" data-slider-step="128" value="{{ instance.ram_size }}"> MiB
</div>
</p>
......@@ -9,7 +9,7 @@
<div class="row">
<div class="col-md-4" id="vm-info-pane">
<div class="big">
<span class="label label-success ">RUNNING</span>
<span class="label label-success">{{ instance.state }}</span>
<div class="btn-group">
<button type="button" class="btn btn-warning dropdown-toggle" data-toggle="dropdown">Action <i class="icon-caret-down"></i></button>
<ul class="dropdown-menu" role="menu">
......
......@@ -4,7 +4,11 @@
{% block content %}
<div class="alert alert-info">
Tip of the day: you can select multiple vm instances while holding down the <strong>CTRL</strong> button!
Tip #1: you can select multiple vm instances while holding down the <strong>CTRL</strong> key!
</div>
<div class="alert alert-info">
Tip #2: if you want to select multiple instances by one click select an instance then hold down <strong>SHIFT</strong> key and select another one!
</div>
<div class="row">
......@@ -13,6 +17,16 @@
<div class="panel-heading">
<h3 class="no-margin"><i class="icon-desktop"></i> Your virtual machines</h3>
</div>
<div class="panel-body vm-list-group-control">
<p>
<strong>Group actions</strong>
<button class="btn btn-info btn-xs" disabled>Select all</button>
<a class="btn btn-default btn-xs" id="vm-list-group-migrate" disabled><i class="icon-truck"></i> Migrate</a>
<a disabled href="#" class="btn btn-default btn-xs"><i class="icon-refresh"></i> Reboot</a>
<a disabled href="#" class="btn btn-default btn-xs"><i class="icon-off"></i> Shutdown</a>
<a disabled href="#" class="btn btn-danger btn-xs"><i class="icon-remove"></i> Discard</a>
</p>
</div>
<div class="panel-body">
<table class="table table-bordered table-striped table-hover vm-list-table">
<thead>
......@@ -71,27 +85,79 @@
.vm-list-selected td:first-child {
font-weight: bold;
}
.vm-list-table-thin {
width: 10px;
}
.vm-list-table-admin {
width: 130px;
}
</style>
{% endblock %}
{% block extra_js %}
$(function() {
var ctrlDown = false;
var ctrlDown, shiftDown = false;
var ctrlKey = 17;
var shiftKey = 16;
var selected = [];
$(document).keydown(function(e) {
if (e.keyCode == ctrlKey) ctrlDown = true;
if (e.keyCode == shiftKey) shiftDown = true;
}).keyup(function(e) {
if (e.keyCode == ctrlKey) ctrlDown = false;
if (e.keyCode == shiftKey) shiftDown = false;
});
$('.vm-list-table').find('tr').click(function() {
$('.vm-list-table tbody').find('tr').mousedown(function() {
if (ctrlDown) {
setRowColor($(this));
if(!$(this).hasClass('vm-list-selected')) {
selected.splice(selected.indexOf($(this).index()), 1);
} else {
selected.push($(this).index());
}
} else if(shiftDown) {
if(selected.length > 0) {
start = selected[selected.length - 1] + 1;
end = $(this).index();
if(start > end) {
var tmp = start - 1; start = end; end = tmp - 1;
}
for(var i = start; i <= end; i++) {
if(selected.indexOf(i) < 0) {
selected.push(i);
setRowColor($('.vm-list-table tbody tr').eq(i));
}
}
}
} else {
$('.vm-list-selected').removeClass('vm-list-selected');
$(this).addClass('vm-list-selected');
selected = [$(this).index()];
}
// reset btn disables
$('.vm-list-table tbody tr .btn').attr('disabled', false);
// show/hide group controls
if(selected.length > 1) {
$('.vm-list-group-control .btn').attr('disabled', false);
for(var i = 0; i < selected.length; i++) {
$('.vm-list-table tbody tr').eq(selected[i]).find('.btn').attr('disabled', true);
}
} else {
$('.vm-list-group-control .btn').attr('disabled', true);
}
return false;
});
$('#vm-list-group-migrate').click(function() {
console.log(collectIds(selected));
});
$('.vm-list-details').popover({
......@@ -106,25 +172,28 @@ $(function() {
'trigger': 'click'
});
/*
$('#check_all').click(function() {
var checked = $(this).prop('checked');
$('.vm-checkbox').each(function() {
// reverse
// $(this).prop('checked', !$(this).prop('checked'));
// set
$(this).prop('checked', checked);
setRowColor($(this))
})
});
$('tbody a').mousedown(function(e) {
// parent tr doesn't get selected when clicked
e.stopPropagation();
});
$('.vm-checkbox').click(function() {
setRowColor($(this));
$('tbody a').click(function(e) {
// browser doesn't jump to top when clicked the buttons
if(!$(this).hasClass('real-link')) {
return false;
}
});
*/
});
function collectIds(rows) {
var ids = [];
for(var i = 0; i < rows.length; i++) {
var div = $('td:first-child div', $('.vm-list-table tbody tr').eq(rows[i]));
ids.push(div.prop('id').replace('vm-', ''));
}
return ids;
}
function setRowColor(row) {
if(!row.hasClass('vm-list-selected')) {
......
sziahello
<div class="btn-group">
<button type="button" class="btn btn-xs btn-warning dropdown-toggle" data-toggle="dropdown">Action <i class="icon-caret-down"></i></button>
<ul class="dropdown-menu" role="menu">
<li><a href="#"><i class="icon-refresh"></i> Reboot</a></li>
<li><a href="#"><i class="icon-off"></i> Shutdown</a></li>
<li><a href="#"><i class="icon-remove"></i> Discard</a></li>
</ul>
</div>
admin
<a class="btn btn-default btn-xs" title data-original-title="Migrate">
<i class="icon-truck"></i>
</a>
<a class="btn btn-default btn-xs" title data-original-title="Rename">
<i class="icon-pencil"></i>
</a>
<a href="#" class="btn btn-default btn-xs vm-list-connect" data-toggle="popover"
data-content='
Belépés: <input style="width: 300px;" type="text" class="form-control" value="ssh cloud@vm.ik.bme.hu -p22312"/>
Jelszó: <input style="width: 300px;" type="text" class="form-control" value="asdfkicsiasdfkocsi"/>
'>Connect</a>
detail
<a class="btn btn-info btn-xs vm-list-details" href="#" data-toggle="popover"
data-content='
<h4>Quick details</h4>
<dl class="dl-horizontal">
<dt>Number of cores:</dt><dd>{{ record.num_cores }}</dd>
<dt>Memory:</dt> <dd>{{ record.ram_size }} Mebibytes</dd>
<dt>Architecture:</td><dd>{{ record.arch }}</dd>
</dl>
<dl>
<dt>IPv4 address:</dt><dd>{{ record.ipv4 }}10.9.8.7</dd>
<dt>IPv6 address:</dt><dd> 2001:2001:2001:2001:2001:2001::</dd>
<dt>DNS name:</dt><dd>1825.vm.ik.bme.hu</dd>
</ul>
'>Details</a>
<div id="vm-{{ record.pk }}">{{ record.pk }}</div>
<tr>
<!--<td><input type="checkbox"/ class="vm-checkbox" id="vm-1825{{ c }}"></td>-->
<td>182{{ c }}</td>
<td><a href="">network-devenv</a></td>
<td>
<div id="vm-1{{ c }}">1{{ c }}</div>
</td>
<td><a href="" class="real-link">network-devenv</a></td>
<td>running</td>
<td>10 days</td>
<td>1 month</td>
<td>
<span class="btn btn-default btn-xs" title data-original-title="Migrate">
<a class="btn btn-default btn-xs" title data-original-title="Migrate">
<i class="icon-truck"></i>
</span>
<span class="btn btn-default btn-xs" title data-original-title="Rename">
</a>
<a class="btn btn-default btn-xs" title data-original-title="Rename">
<i class="icon-pencil"></i>
</span>
</a>
<a href="#" class="btn btn-default btn-xs vm-list-connect" data-toggle="popover"
data-content='
Belépés: <input style="width: 300px;" type="text" class="form-control" value="ssh cloud@vm.ik.bme.hu -p22312"/>
......
......@@ -74,3 +74,4 @@ class VmList(SingleTableView):
template_name = "dashboard/vm-list.html"
model = Instance
table_class = VmListTable
table_pagination = False
......@@ -49,7 +49,7 @@ class RuleAdmin(admin.ModelAdmin):
list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction',
'accept', 'proto', 'sport', 'dport', 'nat',
'nat_dport', 'used_in')
list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept',
list_filter = ('vlan', 'owner', 'direction', 'accept',
'proto', 'nat')
def color_desc(self, instance):
......
......@@ -64,7 +64,7 @@ class IPNetworkField(models.Field):
description = _('IP Network object')
__metaclass__ = models.SubfieldBase
def __init__(self, version=4, *args, **kwargs):
def __init__(self, version=4, serialize=True, *args, **kwargs):
kwargs['max_length'] = 100
self.version = version
super(IPNetworkField, self).__init__(*args, **kwargs)
......@@ -98,7 +98,7 @@ class IPNetworkField(models.Field):
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_prep_value(value)
return str(self.get_prep_value(value))
def clean(self, value, model_instance):
value = super(IPNetworkField, self).clean(value, model_instance)
......
from firewall import models
import django.conf
import subprocess
import re
from datetime import datetime, timedelta
from django.db.models import Q
......@@ -12,14 +10,6 @@ settings = django.conf.settings.FIREWALL_SETTINGS
class Firewall:
IPV6 = False
RULES = None
RULES_NAT = []
vlans = None
pub = None
hosts = None
fw = None
def dportsport(self, rule, repl=True):
retval = ' '
if rule.proto == 'tcp' or rule.proto == 'udp':
......@@ -46,7 +36,7 @@ class Firewall:
if not rule.foreign_network:
return
if self.IPV6 and host.ipv6:
if self.proto == 6 and host.ipv6:
ipaddr = host.ipv6 + '/112'
else:
ipaddr = host.ipv4
......@@ -156,10 +146,6 @@ class Firewall:
'-j ACCEPT')
def postrun(self):
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 '
'-j LOG_ACC')
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 445 '
'-j LOG_ACC')
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 udp --dport 445 -j LOG_DROP')
......@@ -206,15 +192,6 @@ class Firewall:
(str(s_vlan.network4), d_vlan.interface,
s_vlan.snat_ip))
# hard-wired rules
self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT '
'--to-source 10.3.255.254') # man elerheto legyen
self.iptablesnat('-A POSTROUTING -o vlan0008 -j SNAT '
'--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 ''--to-source %s' %
self.pub.ipv4) # kulonben nem megy a dns man-ban
self.iptablesnat('COMMIT')
def ipt_filter(self):
......@@ -258,45 +235,29 @@ class Firewall:
# post-run stuff
self.postrun()
if self.IPV6:
if self.proto == 6:
self.RULES = [x for x in self.RULES if not ipv4_re.search(x)]
self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES]
def __init__(self, IPV6=False):
def __init__(self, proto=4):
self.RULES = []
self.RULES_NAT = []
self.IPV6 = IPV6
self.proto = proto
self.vlans = models.Vlan.objects.all()
self.hosts = models.Host.objects.all()
self.pub = models.Vlan.objects.get(name='PUB')
self.fw = models.Firewall.objects.all()
self.ipt_filter()
if not self.IPV6:
if self.proto != 6:
self.ipt_nat()
def reload(self):
if self.IPV6:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo',
'/sbin/ip6tables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n')
else:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo',
'/sbin/iptables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n')
def get(self):
if self.IPV6:
if self.proto == 6:
return {'filter': self.RULES, }
else:
return {'filter': self.RULES, 'nat': self.RULES_NAT}
def show(self):
if self.IPV6:
if self.proto == 6:
return '\n'.join(self.RULES) + '\n'
else:
return ('\n'.join(self.RULES) + '\n' +
......@@ -413,11 +374,6 @@ def dns():
DNS.append("^%s:%s:%s" % (d['name'], d['address'], d['ttl']))
return DNS
process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' %
settings['dns_hostname']],
shell=False, stdin=subprocess.PIPE)
process.communicate("\n".join(DNS) + "\n")
# print "\n".join(DNS)+"\n"
def prefix_to_mask(prefix):
......@@ -480,9 +436,11 @@ def dhcp():
})
return DHCP
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'cat > /tools/dhcp3/dhcpd.conf.generated;'
'sudo /etc/init.d/isc-dhcp-server restart'],
shell=False, stdin=subprocess.PIPE)
# print "\n".join(DHCP)+"\n"
process.communicate("\n".join(DHCP) + "\n")
def vlan():
obj = models.Vlan.objects.values('vid', 'name', 'network4', 'network6')
return {x['name']: {'tag': x['vid'],
'addresses': [str(x['network4']),
str(x['network6'])]}
for x in obj}
......@@ -12,6 +12,7 @@ import django.conf
from django.db.models.signals import post_save
import random
from firewall.tasks.local_tasks import reloadtask
settings = django.conf.settings.FIREWALL_SETTINGS
......@@ -62,10 +63,6 @@ class Rule(models.Model):
verbose_name=_("owner"),
help_text=_("The user responsible for "
"this rule."))
r_type = models.CharField(max_length=10, verbose_name=_("Rule type"),
choices=CHOICES_type,
help_text=_("The type of entity the rule "
"belongs to."))
nat = models.BooleanField(default=False, verbose_name=_("NAT"),
help_text=_("If network address translation "
"shoud be done."))
......@@ -129,6 +126,15 @@ class Rule(models.Model):
(("dport=%s " % self.dport) if self.dport else '')),
'desc': self.description}
@property
def r_type(self):
fields = [self.vlan, self.vlangroup, self.host, self.hostgroup,
self.firewall]
for field in fields:
if field is not None:
return field.__class__.__name__.lower()
return None
@models.permalink
def get_absolute_url(self):
return ('network.rule', None, {'pk': self.pk})
......@@ -137,7 +143,6 @@ class Rule(models.Model):
verbose_name = _("rule")
verbose_name_plural = _("rules")
ordering = (
'r_type',
'direction',
'proto',
'sport',
......@@ -278,6 +283,24 @@ class Vlan(models.Model):
def prefix6(self):
return self.network6.prefixlen
def get_new_address(self):
i = 0
hosts = Host.objects.filter(vlan=self)
used_v4 = hosts.values_list('ipv4', flat=True)
used_v6 = hosts.values_list('ipv6', flat=True)
for ipv4 in self.network4.iter_hosts():
i += 1
if i > 10000:
break
ipv4 = str(ipv4)
if ipv4 not in used_v4:
print ipv4
ipv6 = ipv4_2_ipv6(ipv4)
if ipv6 not in used_v6:
return {'ipv4': ipv4, 'ipv6': ipv6}
raise ValidationError(_("All IP addresses are already in use."))
class VlanGroup(models.Model):
"""
......@@ -495,11 +518,11 @@ class Host(models.Model):
if public < 1024:
raise ValidationError(_("Only ports above 1024 can be used."))
rule = Rule(direction='1', owner=self.owner, dport=public,
proto=proto, nat=True, accept=True, r_type="host",
proto=proto, nat=True, accept=True,
nat_dport=private, host=self, foreign_network=vg)
else:
rule = Rule(direction='1', owner=self.owner, dport=public,
proto=proto, nat=False, accept=True, r_type="host",
proto=proto, nat=False, accept=True,
host=self, foreign_network=vg)
rule.full_clean()
......@@ -778,7 +801,6 @@ class Blacklist(models.Model):
def send_task(sender, instance, created, **kwargs):
from firewall.tasks import reloadtask
reloadtask.apply_async(args=[sender.__name__])
......
import celery
from manager.mancelery import celery
from django.core.cache import cache
from firewall.fw import Firewall, dhcp, dns, ipset
import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS
@celery.task
def reload_dns_task(data):
pass
@celery.task
def reload_firewall_task(data4, data6):
pass
@celery.task
def reload_dhcp_task(data):
pass
@celery.task
def reload_blacklist_task(data):
pass
@celery.task
def get_dhcp_clients_task(data):
pass
@celery.task
def periodic_task():
from firewall.fw import Firewall, dhcp, dns, ipset, vlan
import remote_tasks
if cache.get('dns_lock'):
cache.delete("dns_lock")
reload_dns_task.delay(dns())
remote_tasks.reload_dns.apply_async(args=[dns()],
queue='dns')
print "dns ujratoltese kesz"
if cache.get('dhcp_lock'):
cache.delete("dhcp_lock")
reload_dhcp_task.delay(dhcp())
remote_tasks.reload_dhcp.apply_async(args=[dhcp()],
queue='firewall')
print "dhcp ujratoltese kesz"
if cache.get('firewall_lock'):
cache.delete("firewall_lock")
ipv4 = Firewall().get()
ipv6 = Firewall(True).get()
reload_firewall_task.delay(ipv4, ipv6)
ipv4 = Firewall(proto=4).get()
ipv6 = Firewall(proto=6).get()
remote_tasks.reload_firewall.apply_async(args=[ipv4, ipv6],
queue='firewall')
print "firewall ujratoltese kesz"
if cache.get('firewall_vlan_lock'):
cache.delete("firewall_vlan_lock")
remote_tasks.reload_firewall_vlan.apply_async(args=[vlan()],
queue='firewall')
print "firewall_vlan ujratoltese kesz"
if cache.get('blacklist_lock'):
cache.delete("blacklist_lock")
reload_blacklist_task.delay(list(ipset()))
remote_tasks.reload_blacklist.apply_async(args=[list(ipset())],
queue='firewall')
print "blacklist ujratoltese kesz"
......@@ -70,4 +56,7 @@ def reloadtask(type='Host'):
if type == "Blacklist":
cache.add("blacklist_lock", "true", 30)
if type == "Vlan":
cache.add("firewall_vlan_lock", "true", 30)
print type
from manager.mancelery import celery
@celery.task(name='firewall.reload_dns')
def reload_dns(data):
pass
@celery.task(name='firewall.reload_firewall')
def reload_firewall(data4, data6):
pass
@celery.task(name='firewall.reload_firewall_vlan')
def reload_firewall_vlan(data):
pass
@celery.task(name='firewall.reload_dhcp')
def reload_dhcp(data):
pass
@celery.task(name='firewall.reload_blacklist')
def reload_blacklist(data):
pass
@celery.task(name='firewall.get_dhcp_clients')
def get_dhcp_clients(data):
pass
......@@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .tasks import reloadtask
from .tasks.local_tasks import reloadtask
from .models import Blacklist, Host
......@@ -20,6 +20,7 @@ def reload_firewall(request):
html = (_("Dear %s, you've signed in as administrator!<br />"
"Reloading in 10 seconds...") % request.user.username)
reloadtask.delay()
reloadtask.delay('Vlan')
else:
html = (_("Dear %s, you've signed in!") % request.user.username)
else:
......
......@@ -6,7 +6,8 @@ HOSTNAME = "localhost"
celery = Celery('manager', backend='amqp',
broker=getenv("AMQP_URI"),
include=['vm.tasks.local_tasks', 'storage.tasks.local_tasks'])
include=['vm.tasks.local_tasks', 'storage.tasks.local_tasks',
'firewall.tasks.local_tasks'])
celery.conf.update(
CELERY_QUEUES=(
......
......@@ -166,7 +166,6 @@ class RuleForm(ModelForm):
'extra',
'accept',
'owner',
'r_type',
'nat',
'nat_dport',
),
......
......@@ -90,6 +90,9 @@ class SmallHostTable(Table):
class RecordTable(Table):
fqdn = LinkColumn('network.record', args=[A('pk')], orderable=False)
address = TemplateColumn(
template_name="network/columns/records-address.html"
)
class Meta:
model = Record
......
{% if record.host %}
{% if record.type == "A" %}
{{ record.host.ipv4 }}
{% elif record.type == "AAAA" %}
{{ record.host.ipv6 }}
{% elif record.type == "CNAME" %}
{{ record.host.get_fqdn }}
{% elif record.type == "MX" %}
{{ record.host.get_fqdn }}
{% else %}
This should not appear!
{% endif %}
{% else %}
{{ record.address }}
{% endif %}
# -*- 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 'DiskActivity'
db.create_table(u'storage_diskactivity', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)),
('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)),
('activity_code', self.gf('django.db.models.fields.CharField')(max_length=100)),
('task_uuid', self.gf('django.db.models.fields.CharField')(max_length=50, unique=True, null=True, blank=True)),
('disk', self.gf('django.db.models.fields.related.ForeignKey')(related_name='activity_log', to=orm['storage.Disk'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
('started', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
('finished', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
('result', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
('state', self.gf('django.db.models.fields.CharField')(default='PENDING', max_length=50)),
))
db.send_create_signal(u'storage', ['DiskActivity'])
# Adding field 'Disk.removed'
db.add_column(u'storage_disk', 'removed',
self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting model 'DiskActivity'
db.delete_table(u'storage_diskactivity')
# Deleting field 'Disk.removed'
db.delete_column(u'storage_disk', 'removed')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'storage.datastore': {
'Meta': {'ordering': "['name']", 'object_name': 'DataStore'},
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'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'})
},
u'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'derivatives'", 'null': 'True', 'to': u"orm['storage.Disk']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DataStore']"}),
'dev_num': ('django.db.models.fields.CharField', [], {'default': "'a'", 'max_length': '1'}),
'filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'size': ('sizefield.models.FileSizeField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
},
u'storage.diskactivity': {
'Meta': {'object_name': 'DiskActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'disk': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log'", 'to': u"orm['storage.Disk']"}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'result': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '50'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['storage']
\ No newline at end of file
......@@ -6,6 +6,7 @@ import uuid
from django.contrib.auth.models import User
from django.db.models import (Model, BooleanField, CharField, DateTimeField,
ForeignKey, TextField)
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from model_utils.models import TimeStampedModel
from sizefield.models import FileSizeField
......@@ -49,13 +50,31 @@ class Disk(TimeStampedModel):
related_name='derivatives')
ready = BooleanField(default=False)
dev_num = CharField(default='a', max_length=1,
verbose_name="device number")
verbose_name=_("device number"))
removed = DateTimeField(blank=True, default=None, null=True)
class Meta:
ordering = ['name']
verbose_name = _('disk')
verbose_name_plural = _('disks')
class WrongDiskTypeError(Exception):
def __init__(self, type):
self.type = type
def __str__(self):
return ("Operation can't be invoked on a disk of type '%s'." %
self.type)
class DiskInUseError(Exception):
def __init__(self, disk):
self.disk = disk
def __str__(self):
return ("The requested operation can't be performed on disk "
"'%s (%s)' because it is in use." %
(self.disk.name, self.disk.filename))
@property
def path(self):
return self.datastore.path + '/' + self.filename
......@@ -70,39 +89,38 @@ class Disk(TimeStampedModel):
'raw-rw': 'raw',
}[self.type]
class WrongDiskTypeError(Exception):
def __init__(self, type):
self.type = type
@property
def device_type(self):
return {
'qcow2': 'vd',
'raw': 'vd',
'iso': 'hd',
}[self.format]
def __str__(self):
return ("Operation can't be invoked on a disk of type '%s'." %
self.type)
def is_in_use(self):
return self.instance_set.exclude(state='SHUTOFF').exists()
def get_exclusive(self):
"""Get an instance of the disk for exclusive usage.
"""
if self.type in ['qcow2-snap', 'raw-rw']:
raise self.WrongDiskTypeError(self.type)
filename = self.filename if self.type == 'iso' else str(uuid.uuid4())
new_type = {
This method manipulates the database only.
"""
type_mapping = {
'qcow2-norm': 'qcow2-snap',
'iso': 'iso',
'raw-ro': 'raw-rw',
}[self.type]
}
if self.type not in type_mapping.keys():
raise self.WrongDiskTypeError(self.type)
filename = self.filename if self.type == 'iso' else str(uuid.uuid4())
new_type = type_mapping[self.type]
return Disk.objects.create(base=self, datastore=self.datastore,
filename=filename, name=self.name,
size=self.size, type=new_type)
@property
def device_type(self):
return {
'qcow2': 'vd',
'raw': 'vd',
'iso': 'hd',
}[self.format]
def get_vmdisk_desc(self):
return {
'source': self.path,
......@@ -111,10 +129,25 @@ class Disk(TimeStampedModel):
'target_device': self.device_type + self.dev_num
}
def get_disk_desc(self):
return {
'name': self.filename,
'dir': self.datastore.path,
'format': self.format,
'size': self.size,
'base_name': self.base.filename if self.base else None,
'type': 'snapshot' if self.type == 'qcow2-snap' else 'normal'
}
def __unicode__(self):
return u"%s (#%d)" % (self.name, self.id)
def deploy(self):
def clean(self, *args, **kwargs):
if self.size == "" and self.base:
self.size = self.base.size
super(Disk, self).clean(*args, **kwargs)
def deploy(self, user=None, task_uuid=None):
"""Reify the disk model on the associated data store.
:param self: the disk model to reify
......@@ -127,38 +160,96 @@ class Disk(TimeStampedModel):
if self.ready:
return False
disk_desc = {
'name': self.filename,
'dir': self.datastore.path,
'format': self.format,
'size': self.size,
'base_name': self.base.filename if self.base else None,
'type': 'snapshot' if self.type == 'qcow2-snap' else 'normal'
}
act = DiskActivity(activity_code='storage.Disk.deploy')
act.disk = self
act.started = timezone.now()
act.state = 'PENDING'
act.task_uuid = task_uuid
act.user = user
act.save()
# Delegate create / snapshot jobs
queue_name = self.datastore.hostname + ".storage"
disk_desc = self.get_disk_desc()
if self.type == 'qcow2-snap':
remote_tasks.snapshot.apply_async(
args=[disk_desc],
queue=self.datastore.hostname + ".storage").get()
act.update_state('CREATING SNAPSHOT')
remote_tasks.snapshot.apply_async(args=[disk_desc],
queue=queue_name).get()
else:
remote_tasks.create.apply_async(
args=[disk_desc],
queue=self.datastore.hostname + ".storage").get()
act.update_state('CREATING DISK')
remote_tasks.create.apply_async(args=[disk_desc],
queue=queue_name).get()
self.ready = True
self.save()
act.finish('SUCCESS')
return True
def deploy_async(self):
def deploy_async(self, user=None):
"""Execute deploy asynchronously.
"""
local_tasks.deploy.apply_async(self)
local_tasks.deploy.apply_async(args=[self, user],
queue="localhost.man")
def delete(self):
def remove(self, user=None, task_uuid=None):
# TODO add activity logging
self.removed = timezone.now()
self.save()
def remove_async(self, user=None):
local_tasks.remove.apply_async(args=[self, user],
queue='localhost.man')
def restore(self, user=None, task_uuid=None):
"""Restore removed disk.
"""
# TODO
# StorageDriver.delete_disk.delay(instance.to_json()).get()
pass
def restore_async(self, user=None):
local_tasks.restore.apply_async(args=[self, user],
queue='localhost.man')
def save_as(self, name, user=None, task_uuid=None):
mapping = {
'qcow2-snap': ('qcow2-norm', self.base),
}
if self.type not in mapping.keys():
raise self.WrongDiskTypeError(self.type)
if self.is_in_use():
raise self.DiskInUseError(self)
# from this point on, the caller has to guarantee that the disk is not
# going to be used until the operation is complete
act = DiskActivity(activity_code='storage.Disk.save_as')
act.disk = self
act.started = timezone.now()
act.state = 'PENDING'
act.task_uuid = task_uuid
act.user = user
act.save()
filename = str(uuid.uuid4())
new_type, new_base = mapping[self.type]
disk = Disk.objects.create(base=new_base, datastore=self.datastore,
filename=filename, name=name,
size=self.size, type=new_type)
queue_name = self.datastore.hostname + ".storage"
remote_tasks.merge.apply_async(args=[self.get_disk_desc(),
disk.get_disk_desc()],
queue=queue_name).get()
disk.ready = True
disk.save()
act.finish('SUCCESS')
return disk
class DiskActivity(TimeStampedModel):
activity_code = CharField(verbose_name=_('activity_code'), max_length=100)
......@@ -172,3 +263,14 @@ class DiskActivity(TimeStampedModel):
result = TextField(verbose_name=_('result'), blank=True, null=True)
state = CharField(verbose_name=_('state'), default='PENDING',
max_length=50)
def update_state(self, new_state):
self.state = new_state
self.save()
def finish(self, result=None):
if not self.finished:
self.finished = timezone.now()
self.result = result
self.state = 'COMPLETED'
self.save()
......@@ -3,16 +3,14 @@ from manager.mancelery import celery
@celery.task
def deploy(disk, user):
'''Create new virtual machine from VM class.
'''
disk.deploy(task_uuid=deploy.rdiskd, user=user)
disk.deploy(task_uuid=deploy.request.id, user=user)
@celery.task
def delete():
pass
def remove(disk, user):
disk.remove(task_uuid=remove.request.id, user=user)
@celery.task
def save_as():
pass
def restore(disk, user):
disk.restore(task_uuid=restore.request.id, user=user)
......@@ -12,15 +12,20 @@ def create(disk_desc):
@celery.task(name='storagedriver.delete')
def delete(json_data):
def delete(path):
pass
@celery.task(name='storagedriver.snapshot')
def snapshot(json_data):
def snapshot(disk_desc):
pass
@celery.task(name='storagedriver.get')
def get(json_data):
def get(path):
pass
@celery.task(name='storagedriver.merge')
def merge(src_disk_desc, dst_disk_desc):
pass
from django.contrib import admin
from .models import (Instance, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity)
InterfaceTemplate, Lease, NamedBaseResourceConfig, Node,
NodeActivity)
admin.site.register(Instance)
......@@ -10,5 +11,6 @@ admin.site.register(InstanceTemplate)
admin.site.register(Interface)
admin.site.register(InterfaceTemplate)
admin.site.register(Lease)
admin.site.register(NamedBaseResourceConfig)
admin.site.register(Node)
admin.site.register(NodeActivity)
from manager.mancelery import celery
# TODO: Keep syncronhised with Instance funcs
@celery.task
def deploy(instance, user):
'''Create new virtual machine from VM class.
''' Call Insance.deploy() from celery task.
'''
instance.deploy(task_uuid=deploy.request.id, user=user)
def delete():
def destroy():
pass
......
from manager.mancelery import celery
@celery.task(name='netdriver.create')
def create(params):
pass
@celery.task(name='netdriver.delete')
def destroy(params):
pass
......@@ -2,52 +2,47 @@ from manager.mancelery import celery
@celery.task(name='vmdriver.create')
def create(params):
def deploy(params):
pass
@celery.task(name='vmdriver.suspend')
def stop(params):
pass
@celery.task(name='vmdriver.resume')
def resume(params):
@celery.task(name='vmdriver.delete')
def destroy(params):
pass
@celery.task(name='vmdriver.delete')
def poweroff(params):
@celery.task(name='vmdriver.save')
def sleep(params):
pass
@celery.task(name='vmdriver.shutdown')
def shutdown(params):
@celery.task(name='vmdriver.restore')
def wake_up(params):
pass
@celery.task(name='vmdriver.reset')
def reset(params):
@celery.task(name='vmdriver.suspend')
def suspend(params):
pass
@celery.task(name='vmdriver.start')
def restart(params):
@celery.task(name='vmdriver.resume')
def resume(params):
pass
@celery.task(name='vmdriver.reboot')
def reboot(params):
@celery.task(name='vmdriver.shutdown')
def shutdown(params):
pass
@celery.task(name='vmdriver.save')
def save(params):
@celery.task(name='vmdriver.reset')
def reset(params):
pass
@celery.task(name='vmdriver.restore')
def restore(params):
@celery.task(name='vmdriver.reboot')
def reboot(params):
pass
......@@ -64,8 +59,3 @@ def domain_info(params):
@celery.task(name='vmdriver.list_domains')
def list_domains(params):
pass
@celery.task(name='vmdriver.delete')
def delete(params):
pass
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