Commit cf578809 by Kálmán Viktor

Merge branch 'master' into feature-template-wizard

Conflicts:
	circle/dashboard/static/dashboard/dashboard.css
	circle/dashboard/urls.py
parents 8a94925d dd0e44ed
...@@ -21,6 +21,13 @@ _build ...@@ -21,6 +21,13 @@ _build
celerybeat-schedule celerybeat-schedule
.coverage .coverage
*,cover *,cover
coverage.xml
# Gettext object file: # Gettext object file:
*.mo *.mo
# saml
circle/attribute-maps/
circle/remote_metadata.xml
circle/samlcert.key
circle/samlcert.pem
from collections import deque from collections import deque
from contextlib import contextmanager
from hashlib import sha224 from hashlib import sha224
from logging import getLogger from logging import getLogger
from time import time from time import time
...@@ -32,6 +33,18 @@ def activitycontextimpl(act, on_abort=None, on_commit=None): ...@@ -32,6 +33,18 @@ def activitycontextimpl(act, on_abort=None, on_commit=None):
act.finish(succeeded=True, event_handler=on_commit) act.finish(succeeded=True, event_handler=on_commit)
activity_context = contextmanager(activitycontextimpl)
activity_code_separator = '.'
def join_activity_code(*args):
"""Join the specified parts into an activity code.
"""
return activity_code_separator.join(args)
class ActivityModel(TimeStampedModel): class ActivityModel(TimeStampedModel):
activity_code = CharField(max_length=100, verbose_name=_('activity code')) activity_code = CharField(max_length=100, verbose_name=_('activity code'))
parent = ForeignKey('self', blank=True, null=True, related_name='children') parent = ForeignKey('self', blank=True, null=True, related_name='children')
......
from logging import getLogger
from .models import activity_context
from django.core.exceptions import PermissionDenied
logger = getLogger(__name__)
class Operation(object):
"""Base class for VM operations.
"""
async_queue = 'localhost.man'
required_perms = ()
def __call__(self, **kwargs):
return self.call(**kwargs)
def __init__(self, subject):
"""Initialize a new operation bound to the specified subject.
"""
self.subject = subject
def __unicode__(self):
return self.name
def __prelude(self, kwargs):
"""This method contains the shared prelude of call and async.
"""
skip_checks = kwargs.setdefault('system', False)
user = kwargs.setdefault('user', None)
parent_activity = kwargs.pop('parent_activity', None)
if not skip_checks:
self.check_auth(user)
self.check_precond()
return self.create_activity(parent=parent_activity, user=user)
def _exec_op(self, activity, user, **kwargs):
"""Execute the operation inside the specified activity's context.
"""
with activity_context(activity, on_abort=self.on_abort,
on_commit=self.on_commit):
return self._operation(activity=activity, user=user,
**kwargs)
def _operation(self, activity, user, system, **kwargs):
"""This method is the operation's particular implementation.
Deriving classes should implement this method.
"""
raise NotImplementedError
def async(self, **kwargs):
"""Execute the operation asynchronously.
Only a quick, preliminary check is ran before creating the associated
activity and queuing the job.
The returned value is the handle for the asynchronous job.
For more information, check the synchronous call's documentation.
"""
logger.info("%s called asynchronously with the following parameters: "
"%r", self.__class__.__name__, kwargs)
activity = self.__prelude(kwargs)
return self.async_operation.apply_async(args=(self.id,
self.subject.pk,
activity.pk),
kwargs=kwargs,
queue=self.async_queue)
def call(self, **kwargs):
"""Execute the operation (synchronously).
Anticipated keyword arguments:
* parent_activity: Parent activity for the operation. If this argument
is present, the operation's activity will be created
as a child activity of it.
* system: Indicates that the operation is invoked by the system, not a
User. If this argument is present and has a value of True,
then authorization checks are skipped.
* user: The User invoking the operation. If this argument is not
present, it'll be provided with a default value of None.
"""
logger.info("%s called (synchronously) with the following parameters: "
"%r", self.__class__.__name__, kwargs)
activity = self.__prelude(kwargs)
return self._exec_op(activity=activity, **kwargs)
def check_precond(self):
pass
def check_auth(self, user):
if not user.has_perms(self.required_perms):
raise PermissionDenied("%s doesn't have the required permissions."
% user)
def create_activity(self, parent, user):
raise NotImplementedError
def on_abort(self, activity, error):
"""This method is called when the operation aborts (i.e. raises an
exception).
"""
pass
def on_commit(self, activity):
"""This method is called when the operation executes successfully.
"""
pass
operation_registry_name = '_ops'
class OperatedMixin(object):
def __getattr__(self, name):
# NOTE: __getattr__ is only called if the attribute doesn't already
# exist in your __dict__
cls = self.__class__
ops = getattr(cls, operation_registry_name, {})
op = ops.get(name)
if op:
return op(self)
else:
raise AttributeError("%r object has no attribute %r" %
(self.__class__.__name__, name))
def register_operation(target_cls, op_cls, op_id=None):
"""Register the specified operation with the target class.
You can optionally specify an ID to be used for the registration;
otherwise, the operation class' 'id' attribute will be used.
"""
if op_id is None:
op_id = op_cls.id
if not issubclass(target_cls, OperatedMixin):
raise TypeError("%r is not a subclass of %r" %
(target_cls.__name__, OperatedMixin.__name__))
if not hasattr(target_cls, operation_registry_name):
setattr(target_cls, operation_registry_name, dict())
getattr(target_cls, operation_registry_name)[op_id] = op_cls
from mock import MagicMock, patch
from django.test import TestCase
from ..operations import Operation
class OperationTestCase(TestCase):
def test_activity_created_before_async_job(self):
class AbortEx(Exception):
pass
op = Operation(MagicMock())
op.activity_code_suffix = 'test'
op.id = 'test'
op.async_operation = MagicMock(
apply_async=MagicMock(side_effect=AbortEx))
with patch.object(Operation, 'check_precond'):
with patch.object(Operation, 'create_activity') as create_act:
try:
op.async(system=True)
except AbortEx:
self.assertTrue(create_act.called)
def test_check_precond_called_before_create_activity(self):
class AbortEx(Exception):
pass
op = Operation(MagicMock())
op.activity_code_suffix = 'test'
op.id = 'test'
with patch.object(Operation, 'create_activity', side_effect=AbortEx):
with patch.object(Operation, 'check_precond') as chk_pre:
try:
op.call(system=True)
except AbortEx:
self.assertTrue(chk_pre.called)
def test_auth_check_on_non_system_call(self):
op = Operation(MagicMock())
op.activity_code_suffix = 'test'
op.id = 'test'
user = MagicMock()
with patch.object(Operation, 'check_auth') as check_auth:
with patch.object(Operation, 'check_precond'), \
patch.object(Operation, 'create_activity'), \
patch.object(Operation, '_exec_op'):
op.call(user=user)
check_auth.assert_called_with(user)
def test_no_auth_check_on_system_call(self):
op = Operation(MagicMock())
op.activity_code_suffix = 'test'
op.id = 'test'
with patch.object(Operation, 'check_auth', side_effect=AssertionError):
with patch.object(Operation, 'check_precond'), \
patch.object(Operation, 'create_activity'), \
patch.object(Operation, '_exec_op'):
op.call(system=True)
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
"hostname": "devenv", "hostname": "devenv",
"modified_at": "2014-02-24T15:55:01.412Z", "modified_at": "2014-02-24T15:55:01.412Z",
"location": "", "location": "",
"pub_ipv4": null,
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"shared_ip": false, "shared_ip": false,
"ipv4": "10.7.0.96", "ipv4": "10.7.0.96",
......
...@@ -37,6 +37,7 @@ class VmCustomizeForm(forms.Form): ...@@ -37,6 +37,7 @@ class VmCustomizeForm(forms.Form):
cpu_priority = forms.IntegerField() cpu_priority = forms.IntegerField()
cpu_count = forms.IntegerField() cpu_count = forms.IntegerField()
ram_size = forms.IntegerField() ram_size = forms.IntegerField()
amount = forms.IntegerField(min_value=0, initial=1)
disks = forms.ModelMultipleChoiceField( disks = forms.ModelMultipleChoiceField(
queryset=None, required=True) queryset=None, required=True)
...@@ -72,12 +73,21 @@ class VmCustomizeForm(forms.Form): ...@@ -72,12 +73,21 @@ class VmCustomizeForm(forms.Form):
self.initial['template'] = self.template.pk self.initial['template'] = self.template.pk
self.initial['customized'] = self.template.pk self.initial['customized'] = self.template.pk
# set widget for amount
self.fields['amount'].widget = NumberInput()
self.helper = FormHelper(self) self.helper = FormHelper(self)
self.helper.form_show_labels = False
# don't show labels for the sliders
self.helper.form_show_labels = True
self.fields['cpu_count'].label = ""
self.fields['ram_size'].label = ""
self.fields['cpu_priority'].label = ""
self.helper.layout = Layout( self.helper.layout = Layout(
Field("template", type="hidden"), Field("template", type="hidden"),
Field("customized", type="hidden"), Field("customized", type="hidden"),
Div( # buttons Div(
Div( Div(
AnyTag( # tip: don't try to use Button class AnyTag( # tip: don't try to use Button class
"button", "button",
...@@ -88,16 +98,17 @@ class VmCustomizeForm(forms.Form): ...@@ -88,16 +98,17 @@ class VmCustomizeForm(forms.Form):
HTML(" Start"), HTML(" Start"),
css_id="vm-create-customized-start", css_id="vm-create-customized-start",
css_class="btn btn-success", css_class="btn btn-success",
style="float: right; margin-top: 24px;",
), ),
css_class="col-sm-11 text-right", Field("name", style="max-width: 350px;"),
css_class="col-sm-12",
), ),
css_class="row", css_class="row",
), ),
Div( Div(
Div( Div(
Field("name"), Field("amount", min="1", style="max-width: 60px;"),
css_class="col-sm-5", css_class="col-sm-10",
), ),
css_class="row", css_class="row",
), ),
...@@ -189,32 +200,36 @@ class VmCustomizeForm(forms.Form): ...@@ -189,32 +200,36 @@ class VmCustomizeForm(forms.Form):
HTML(_("No disks are added!")), HTML(_("No disks are added!")),
css_id="vm-create-disk-list", css_id="vm-create-disk-list",
), ),
AnyTag(
"h3",
Div(
AnyTag(
"select",
css_class="form-control",
css_id="vm-create-disk-add-select",
),
Div( Div(
AnyTag( HTML(""),
"a", style="clear: both;",
AnyTag( ),
"i", # AnyTag(
css_class="icon-plus-sign", # "h3",
), # Div(
href="#", # AnyTag(
css_id="vm-create-disk-add-button", # "select",
css_class="btn btn-success", # css_class="form-control",
), # css_id="vm-create-disk-add-select",
css_class="input-group-btn" # ),
), # Div(
css_class="input-group", # AnyTag(
style="max-width: 330px;", # "a",
), # AnyTag(
css_id="vm-create-disk-add", # "i",
), # css_class="icon-plus-sign",
# ),
# href="#",
# css_id="vm-create-disk-add-button",
# css_class="btn btn-success",
# ),
# css_class="input-group-btn"
# ),
# css_class="input-group",
# style="max-width: 330px;",
# ),
# css_id="vm-create-disk-add",
# ),
css_class="no-js-hidden", css_class="no-js-hidden",
), ),
css_class="col-sm-8", css_class="col-sm-8",
......
...@@ -79,6 +79,14 @@ html { ...@@ -79,6 +79,14 @@ html {
color: #fff; color: #fff;
} }
.timeline .activity-active .timeline-icon {
background-color: black!important;
}
.timeline a {
color: black;
}
.timeline-icon.timeline-warning { .timeline-icon.timeline-warning {
border-color: #c09853; border-color: #c09853;
border-style: solid; border-style: solid;
...@@ -100,6 +108,10 @@ html { ...@@ -100,6 +108,10 @@ html {
border-left: 3px solid green; border-left: 3px solid green;
} }
.sub-activity-active {
border-left: 8px solid black;
}
.sub-activity-failed { .sub-activity-failed {
border-left: 3px solid #d9534f; border-left: 3px solid #d9534f;
} }
...@@ -472,3 +484,35 @@ footer a, footer a:hover, footer a:visited { ...@@ -472,3 +484,35 @@ footer a, footer a:hover, footer a:visited {
#vm-details-resources-form { #vm-details-resources-form {
padding: 5px; /* it's nice this way in the tour */ padding: 5px; /* it's nice this way in the tour */
} }
.index-vm-list-name {
display: inline-block;
max-width: 70%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
float: left;
}
#dashboard-vm-list a small {
padding-left: 10px;
}
.index-template-list-name {
display: inline-block;
max-width: 50%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
float: left;
}
#dashboard-template-list a small {
max-width: 50%;
float: left;
padding-top: 2px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding-left: 10px;
}
...@@ -3,7 +3,7 @@ $(function () { ...@@ -3,7 +3,7 @@ $(function () {
var template = $(this).data("template"); var template = $(this).data("template");
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '/dashboard/vm/create/', url: '/dashboard/vm/create/' + (typeof template === "undefined" ? '' : '?template=' + template),
success: function(data) { success: function(data) {
$('body').append(data); $('body').append(data);
vmCreateLoaded(); vmCreateLoaded();
...@@ -12,9 +12,6 @@ $(function () { ...@@ -12,9 +12,6 @@ $(function () {
$('#create-modal').on('hidden.bs.modal', function() { $('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove(); $('#create-modal').remove();
}); });
if(template) {
$('#vm-create-template-select option[value="' + template + '"]').prop("selected", true).trigger("change");
}
} }
}); });
return false; return false;
...@@ -209,6 +206,9 @@ $(function () { ...@@ -209,6 +206,9 @@ $(function () {
'name': result[i].name.toLowerCase(), 'name': result[i].name.toLowerCase(),
'state': result[i].state, 'state': result[i].state,
'fav': result[i].fav, 'fav': result[i].fav,
'host': result[i].host,
'icon': result[i].icon,
'status': result[i].status,
}); });
} }
}); });
...@@ -225,7 +225,9 @@ $(function () { ...@@ -225,7 +225,9 @@ $(function () {
} }
search_result.sort(compareVmByFav); search_result.sort(compareVmByFav);
for(var i=0; i<5 && i<search_result.length; i++) for(var i=0; i<5 && i<search_result.length; i++)
html += generateVmHTML(search_result[i].pk, search_result[i].name, search_result[i].fav); html += generateVmHTML(search_result[i].pk, search_result[i].name,
search_result[i].host, search_result[i].icon,
search_result[i].status, search_result[i].fav);
if(search_result.length == 0) if(search_result.length == 0)
html += '<div class="list-group-item">No result</div>'; html += '<div class="list-group-item">No result</div>';
$("#dashboard-vm-list").html(html); $("#dashboard-vm-list").html(html);
...@@ -251,21 +253,33 @@ $(function () { ...@@ -251,21 +253,33 @@ $(function () {
}); });
}); });
function generateVmHTML(pk, name, fav) { function generateVmHTML(pk, name, host, icon, _status, fav) {
return '<a href="/dashboard/vm/' + pk + '/" class="list-group-item">' + return '<a href="/dashboard/vm/' + pk + '/" class="list-group-item">' +
'<i class="icon-play-sign"></i> ' + name + '<span class="index-vm-list-name">' +
'<div class="pull-right dashboard-vm-favourite" data-vm="' + pk +'">' + '<i class="' + icon + '" title="' + _status + '"></i> ' + name +
'<i class="title-favourite icon-star' + (fav ? "" : "-empty") + ' text-primary" title="" data-original-title="' + '</span>' +
(fav ? "Un": "Mark as ") + 'favourite"></i>' + '<small class="text-muted"> ' + host + '</small>' +
'<div class="pull-right dashboard-vm-favourite" data-vm="' + pk + '">' +
(fav ? '<i class="icon-star text-primary title-favourite" title="Unfavourite"></i>' :
'<i class="icon-star-empty text-primary title-favourite" title="Mark as favorite"></i>' ) +
'</div>' + '</div>' +
'<div style="clear: both;"></div>' +
'</a>'; '</a>';
} }
/* copare vm-s by fav, pk order */
function compareVmByFav(a, b) { function compareVmByFav(a, b) {
if(a.fav) if(a.fav && b.fav) {
return a.pk < b.pk ? -1 : 1;
}
else if(a.fav && !b.fav) {
return -1; return -1;
else }
else if(!a.fav && b.fav) {
return 1; return 1;
}
else
return a.pk < b.pk ? -1 : 1;
} }
function addSliderMiscs() { function addSliderMiscs() {
......
...@@ -14,7 +14,6 @@ function vmCreateLoaded() { ...@@ -14,7 +14,6 @@ function vmCreateLoaded() {
$(".customize-vm").click(function() { $(".customize-vm").click(function() {
var template = $(this).data("template-pk"); var template = $(this).data("template-pk");
console.log(template);
$.get("/dashboard/vm/create/?template=" + template, function(data) { $.get("/dashboard/vm/create/?template=" + template, function(data) {
var r = $('#create-modal'); r.next('div').remove(); r.remove(); var r = $('#create-modal'); r.next('div').remove(); r.remove();
...@@ -143,7 +142,6 @@ function vmCustomizeLoaded() { ...@@ -143,7 +142,6 @@ function vmCustomizeLoaded() {
} }
var html = '<option data-managed="' + (managed ? 1 : 0) + '" value="' + pk + '">' + text + '</option>'; var html = '<option data-managed="' + (managed ? 1 : 0) + '" value="' + pk + '">' + text + '</option>';
if($('#vm-create-network-list span').length < 1) { if($('#vm-create-network-list span').length < 1) {
$("#vm-create-network-list").html(""); $("#vm-create-network-list").html("");
} }
...@@ -152,8 +150,14 @@ function vmCustomizeLoaded() { ...@@ -152,8 +150,14 @@ function vmCustomizeLoaded() {
} else { } else {
$('#vm-create-network-add-select').append(html); $('#vm-create-network-add-select').append(html);
} }
}); });
// if all networks are added add a dummy and disable the add button
if($("#vm-create-network-add-select option").length < 1) {
$("#vm-create-network-add-select").html('<option value="-1">No more networks!</option>');
$('#vm-create-network-add-button').attr('disabled', true);
}
/* build up network list */ /* build up network list */
$('#vm-create-network-add-vlan option').each(function() { $('#vm-create-network-add-vlan option').each(function() {
...@@ -197,23 +201,13 @@ function vmCustomizeLoaded() { ...@@ -197,23 +201,13 @@ function vmCustomizeLoaded() {
/* remove disk */ /* remove disk */
// event for disk remove button (icon, X) // event for disk remove button (icon, X)
$('body').on('click', '.vm-create-remove-disk', function() { $('body').on('click', '.vm-create-remove-disk', function() {
var disk_pk = ($(this).parent('span').prop('id')).replace('vlan-', '') var disk_pk = ($(this).parent('span').prop('id')).replace('disk-', '')
$(this).parent('span').fadeOut(500, function() { $(this).parent('span').fadeOut(500, function() {
/* if ther are no more disks disabled the add button */
if($('#vm-create-disk-add-select option')[0].value == -1) {
$('#vm-create-disk-add-button').attr('disabled', false);
$('#vm-create-disk-add-select').html('');
}
/* remove the disk label */ /* remove the disk label */
$(this).remove(); $(this).remove();
var disk_name = $(this).text(); var disk_name = $(this).text();
$('#vm-create-disk-add-select').append($('<option>', {
value: disk_pk,
text: disk_name
}));
/* remove the selection from the multiple select */ /* remove the selection from the multiple select */
$('#vm-create-disk-add-form option[value="' + disk_pk + '"]').prop('selected', false); $('#vm-create-disk-add-form option[value="' + disk_pk + '"]').prop('selected', false);
...@@ -257,7 +251,11 @@ function vmCustomizeLoaded() { ...@@ -257,7 +251,11 @@ function vmCustomizeLoaded() {
data: $('form').serialize(), data: $('form').serialize(),
success: function(data, textStatus, xhr) { success: function(data, textStatus, xhr) {
if(data.redirect) { if(data.redirect) {
window.location.replace(data.redirect + '#activity'); /* it won't redirect to the same page */
if(window.location.pathname == data.redirect) {
window.location.reload();
}
window.location.href = data.redirect + '#activity';
} }
else { else {
var r = $('#create-modal'); r.next('div').remove(); r.remove(); var r = $('#create-modal'); r.next('div').remove(); r.remove();
...@@ -295,5 +293,6 @@ function vmCreateNetworkLabel(pk, name, managed) { ...@@ -295,5 +293,6 @@ function vmCreateNetworkLabel(pk, name, managed) {
function vmCreateDiskLabel(pk, name) { function vmCreateDiskLabel(pk, name) {
return '<span id="vlan-' + pk + '" class="label label-primary"><i class="icon-file"></i> ' + name + ' <a href="#" class="hover-black vm-create-remove-disk"><i class="icon-remove-sign"></i></a></span> '; var style = "float: left; margin: 5px 5px 5px 0;";
return '<span id="disk-' + pk + '" class="label label-primary" style="' + style + '"><i class="icon-file"></i> ' + name + ' <a href="#" class="hover-black vm-create-remove-disk"><i class="icon-remove-sign"></i></a></span> ';
} }
...@@ -199,24 +199,24 @@ function decideActivityRefresh() { ...@@ -199,24 +199,24 @@ function decideActivityRefresh() {
return check; return check;
} }
function checkNewActivity(only_state, runs) { function checkNewActivity(only_status, runs) {
// set default only_state to false // set default only_status to false
only_state = typeof only_state !== 'undefined' ? only_state : false; only_status = typeof only_status !== 'undefined' ? only_status : false;
var instance = location.href.split('/'); instance = instance[instance.length - 2]; var instance = location.href.split('/'); instance = instance[instance.length - 2];
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '/dashboard/vm/' + instance + '/activity/', url: '/dashboard/vm/' + instance + '/activity/',
data: {'only_state': only_state}, data: {'only_status': only_status},
success: function(data) { success: function(data) {
if(!only_state) { if(!only_status) {
$("#activity-timeline").html(data['activities']); $("#activity-timeline").html(data['activities']);
$("[title]").tooltip(); $("[title]").tooltip();
} }
$("#vm-details-state i").prop("class", data['icon']); $("#vm-details-state i").prop("class", data['icon']);
$("#vm-details-state span").html(data['state']); $("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
if(data['state'] == "RUNNING") { if(data['status'] == "RUNNING") {
$("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled");
} else { } else {
$("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled");
...@@ -224,7 +224,7 @@ function checkNewActivity(only_state, runs) { ...@@ -224,7 +224,7 @@ function checkNewActivity(only_state, runs) {
if(runs > 0 && decideActivityRefresh()) { if(runs > 0 && decideActivityRefresh()) {
setTimeout( setTimeout(
function() {checkNewActivity(only_state, runs + 1)}, function() {checkNewActivity(only_status, runs + 1)},
1000 + Math.exp(runs * 0.05) 1000 + Math.exp(runs * 0.05)
); );
} }
......
...@@ -58,12 +58,17 @@ ...@@ -58,12 +58,17 @@
</li> </li>
</ul> </ul>
<div style="margin-top: 20px; padding: 0 15px; width: 100%"> <div style="margin-top: 20px; padding: 0 15px; width: 100%">
<a class="btn btn-primary btn-xs customize-vm" data-template-pk="{{ t.pk }}" href="{% url "dashboard.views.vm-create" %}?template={{ t.pk }}"><i class="icon-wrench"></i> Customize</a> {% if perms.vm_set_resources %}
<a class="btn btn-primary btn-xs customize-vm" data-template-pk="{{ t.pk }}" href="{% url "dashboard.views.vm-create" %}?template={{ t.pk }}"><i class="icon-wrench"></i> {% trans "Customize" %}</a>
{% endif %}
<form class="pull-right text-right" method="POST" action="{% url "dashboard.views.vm-create" %}"> <form class="pull-right text-right" method="POST" action="{% url "dashboard.views.vm-create" %}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="template" value="{{ t.pk }}"/> <input type="hidden" name="template" value="{{ t.pk }}"/>
<button class="btn btn-success btn-xs vm-create-start" data-template-pk="{{ t.pk }}" type="submit"><i class="icon-play"></i> Start</button> <button class="btn btn-success btn-xs vm-create-start" data-template-pk="{{ t.pk }}" type="submit">
<i class="icon-play"></i> {% trans "Start" %}
</button>
</form> </form>
<div style="clear: both;"></div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
{%endblocktrans%} {%endblocktrans%}
{% endif %} {% endif %}
<div class="pull-right"> <div class="pull-right">
<form action="" method="POST"> <form action="{% url "dashboard.views.status-node" pk=object.pk %}" method="POST">
{% csrf_token %} {% csrf_token %}
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel" %}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel" %}</button>
<input type="hidden" name="change_status" value=""/> <input type="hidden" name="change_status" value=""/>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div class="list-group" id="node-list-view"> <div class="list-group" id="node-list-view">
{% for i in nodes %} {% for i in nodes %}
<a href="{% url "dashboard.views.node-detail" pk=i.pk %}" class="list-group-item"> <a href="{% url "dashboard.views.node-detail" pk=i.pk %}" class="list-group-item">
<i class="icon-{% if i.enabled == True %}play-sign{% else %}pause{% endif %}"></i> {{ i.name }} <div class="pull-right"><i class="icon-star text-primary" title="Mark as favorite."></i></div> <i class="icon-{% if i.enabled == True %}play-sign{% else %}pause{% endif %}"></i> {{ i.name }}
</a> </a>
{% endfor %} {% endfor %}
<div href="#" class="list-group-item list-group-footer"> <div href="#" class="list-group-item list-group-footer">
......
...@@ -7,11 +7,15 @@ ...@@ -7,11 +7,15 @@
<h3 class="no-margin"><i class="icon-puzzle-piece"></i> {% trans "Templates" %} <h3 class="no-margin"><i class="icon-puzzle-piece"></i> {% trans "Templates" %}
</h3> </h3>
</div> </div>
<div class="list-group" id="vm-list-view"> <div class="list-group" id="dashboard-template-list">
{% for t in templates %} {% for t in templates %}
<a href="{% url "dashboard.views.template-detail" pk=t.pk %}" class="list-group-item"> <a href="{% url "dashboard.views.template-detail" pk=t.pk %}" class="list-group-item">
<i class="icon-{{ t.os_type }}"></i> {{ t.name }} <small class="text-muted">{{ t.system }}</small> <span class="index-template-list-name">
<i class="icon-{{ t.os_type }}"></i> {{ t.name }}
</span>
<small class="text-muted index-template-list-system">{{ t.system }}</small>
<div class="pull-right vm-create" data-template="{{ t.pk }}"><i title="{% trans "Start vm instance" %}" class="icon-play"></i></div> <div class="pull-right vm-create" data-template="{{ t.pk }}"><i title="{% trans "Start vm instance" %}" class="icon-play"></i></div>
<div class="clearfix"></div>
</a> </a>
{% empty %} {% empty %}
<div class="alert alert-warning" style="margin: 10px;"> <div class="alert alert-warning" style="margin: 10px;">
......
...@@ -16,8 +16,11 @@ ...@@ -16,8 +16,11 @@
<div id="dashboard-vm-list"> <div id="dashboard-vm-list">
{% for i in instances %} {% for i in instances %}
<a href="{{ i.get_absolute_url }}" class="list-group-item"> <a href="{{ i.get_absolute_url }}" class="list-group-item">
<i class="{{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i> {{ i.name }} <span class="index-vm-list-name">
<small class="text-muted">{{ i.primary_host.hostname }}</small> <i class="{{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }}
</span>
<small class="text-muted"> {{ i.primary_host.hostname }}</small>
<div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}"> <div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %} {% if i.fav %}
<i class="icon-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i> <i class="icon-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
...@@ -25,6 +28,7 @@ ...@@ -25,6 +28,7 @@
<i class="icon-star-empty text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i> <i class="icon-star-empty text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i>
{% endif %} {% endif %}
</div> </div>
<div style="clear: both;"></div>
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
...@@ -50,19 +54,24 @@ ...@@ -50,19 +54,24 @@
<div class="panel-body" id="vm-graph-view" style="display: none"> <div class="panel-body" id="vm-graph-view" style="display: none">
<p class="pull-right"> <input class="knob" data-fgColor="chartreuse" data-thickness=".4" data-max="{{ request.user.profile.instance_limit }}" data-width="100" data-height="100" data-readOnly="true" value="{{ instances|length|add:more_instances }}"></p> <p class="pull-right"> <input class="knob" data-fgColor="chartreuse" data-thickness=".4" data-max="{{ request.user.profile.instance_limit }}" data-width="100" data-height="100" data-readOnly="true" value="{{ instances|length|add:more_instances }}"></p>
<p><span class="bigbig">{% blocktrans with count=running_vm_num %}<big>{{ count }}</big> running{% endblocktrans %}</span> <p><span class="bigbig">{% blocktrans with count=running_vm_num %}<big>{{ count }}</big> running{% endblocktrans %}</span>
<ul class="list-inline"> <ul class="list-inline" style="max-height: 95px; overflow: hidden;">
{% for vm in running_vms %} {% for vm in running_vms %}
<li class="label label-success"> <li style="display: inline-block; padding: 2px;">
<a href="vm.get_absolute_url" title="{{vm.primary_host.get_fqdn}}"><i class="{{vm.get_status_icon}}"></i> {{vm.name}}</a> <a href="{{vm.get_absolute_url}}" title="{{vm.primary_host.get_fqdn}}" class="label label-success">
<i class="{{vm.get_status_icon}}"></i> {{vm.name}}
</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</p> </p>
<p class="big text-warning">{% blocktrans with count=stopped_vm_num %}<big>{{ count }}</big> stopped{% endblocktrans %}</p>
<div class="clearfix"></div> <div class="clearfix"></div>
<div class="text-right"> <div>
<a href="{% url "dashboard.views.vm-list" %}" class="btn btn-primary btn-xs"><i class="icon-chevron-sign-right"></i> <strong>{{ instances|length|add:more_instances }}</strong> machines total</a> <a style="float: right; margin-top: 17px;" href="{% url "dashboard.views.vm-list" %}" class="btn btn-primary btn-xs">
<i class="icon-chevron-sign-right"></i>
<strong>{{ instances|length|add:more_instances }}</strong> machines total
</a>
<p class="big text-warning">{% blocktrans with count=stopped_vm_num %}<big>{{ count }}</big> stopped{% endblocktrans %}</p>
</div> </div>
</div> </div>
</div> </div>
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div class="body-content">
<div class="page-header">
<h1>
{{ object.instance.name }}: {{ object.get_readable_name }}
</h1>
</div>
<div class="row">
<div class="col-md-4" id="vm-info-pane">
<div class="big">
<span id="vm-activity-state" class="label label-{% if object.get_status_id == 'wait' %}info{% else %}{% if object.succeeded %}success{% else %}error{% endif %}{% endif %}">
<span>{{ object.get_status_id|upper }}</span>
</span>
</div>
<div id="vm-activity-context" class="timeline">
{% include "dashboard/vm-detail/_activity-timeline.html" with active=object %}
</div>
</div>
<div class="col-md-8">
<div class="panel panel-default">
<!--<div class="panel-heading"><h2 class="panel-title">{% trans "Activity" %}</h2></div> -->
<div class="panel-body">
<dl>
<dt>{% trans "activity code" %}</dt>
<dd>{{object.activity_code}}</dd>
<dt>{% trans "instance" %}</dt>
<dd><a href="{{object.instance.get_absolute_url}}">{{object.instance}}</a></dd>
<dt>{% trans "time" %}</dt>
<dd>{{object.started|default:'n/a'}} → {{object.finished|default:'n/a'}}</dd>
<dt>{% trans "user" %}</dt>
<dd>{{object.user|default:'(system)'}}</dd>
<dt>{% trans "type" %}</dt>
<dd>
{% if object.parent %}
{% blocktrans with url=object.parent.get_absolute_url name=object.parent %}
subactivity of <a href="{{url}}">{{name}}</a>
{% endblocktrans %}
{% else %}{% trans "top level activity" %}{% endif %}
</dd>
<dt>{% trans "task uuid" %}</dt>
<dd>{{ object.task_uuid|default:'n/a' }}</dd>
<dt>{% trans "status" %}</dt>
<dd>{{ object.get_status_id }}</dd>
<dt>{% trans "result" %}</dt>
<dd><textarea class="form-control">{{object.result}}</textarea></dd>
<dt>{% trans "resultant state" %}</dt>
<dd>{{object.resultant_state|default:'n/a'}}</dd>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
</li> </li>
<li> <li>
<strong>{% trans "Flush" %}:</strong> <strong>{% trans "Flush" %}:</strong>
{% trans "Disable node and move all instances to other ones." %} {% trans "Disable node and move all instances to other one." %}
</li> </li>
<li> <li>
<strong>{% trans "Enable" %}:</strong> <strong>{% trans "Enable" %}:</strong>
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
</li> </li>
<li> <li>
<strong>{% trans "Delete" %}:</strong> <strong>{% trans "Delete" %}:</strong>
{% trans "Remove node and its host." %} {% trans "Remove node and it's host." %}
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
{% include template %} {% include template %}
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}
......
{% load i18n %} {% load i18n %}
{% for a in activities %} {% for a in activities %}
<div class="activity" data-activity-id="{{ a.pk }}"> <div class="activity{% if a.pk == active.pk %} activity-active{%endif%}" data-activity-id="{{ a.pk }}">
<span class="timeline-icon{% if a.has_failed %} timeline-icon-failed{% endif %}"> <span class="timeline-icon{% if a.has_failed %} timeline-icon-failed{% endif %}">
<i class="{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"></i> <i class="{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"></i>
</span> </span>
<strong{% if user.is_superuser and a.result %} title="{{ a.result }}"{% endif %}> <strong{% if user.is_superuser and a.result %} title="{{ a.result }}"{% endif %}>
{{ a.get_readable_name }} {% if user.is_superuser %}<a href="{{ a.get_absolute_url }}">{% endif %}
{{ a.get_readable_name }}{% if user.is_superuser %}</a>{% endif %}
</strong> </strong>
{{ a.started|date:"Y-m-d H:i" }}{% if a.user %}, {{ a.user }}{% endif %} {{ a.started|date:"Y-m-d H:i" }}{% if a.user %}, {{ a.user }}{% endif %}
{% if a.children.count > 0 %} {% if a.children.count > 0 %}
<div class="sub-timeline"> <div class="sub-timeline">
{% for s in a.children.all %} {% for s in a.children.all %}
<div data-activity-id="{{ s.pk }}" class="sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}"> <div data-activity-id="{{ s.pk }}" class="sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}{% if s.pk == active.pk %} sub-activity-active{% endif %}">
<span{% if user.is_superuser and s.result %} title="{{ s.result }}"{% endif %}> <span{% if user.is_superuser and s.result %} title="{{ s.result }}"{% endif %}>
{{ s.get_readable_name }}</span> &ndash; {% if user.is_superuser %}<a href="{{ s.get_absolute_url }}">{% endif %}
{{ s.get_readable_name }}{% if user.is_superuser %}</a>{% endif %}</span> &ndash;
{% if s.finished %} {% if s.finished %}
{{ s.finished|time:"H:i:s" }} {{ s.finished|time:"H:i:s" }}
{% else %} {% else %}
......
import unittest
from factory import Factory, Sequence
from mock import patch, MagicMock
from django.contrib.auth.models import User
# from django.core.exceptions import PermissionDenied
from django.http import HttpRequest, Http404
from dashboard.views import InstanceActivityDetail, InstanceActivity
class ViewUserTestCase(unittest.TestCase):
def test_404(self):
view = InstanceActivityDetail.as_view()
request = FakeRequestFactory(superuser=True)
with self.assertRaises(Http404):
view(request, pk=1234)
def test_not_superuser(self):
request = FakeRequestFactory(superuser=False)
with patch.object(InstanceActivityDetail, 'get_object') as go:
go.return_value = MagicMock(spec=InstanceActivity)
go.return_value._meta.object_name = "InstanceActivity"
view = InstanceActivityDetail.as_view()
self.assertEquals(view(request, pk=1234).status_code, 302)
def test_found(self):
request = FakeRequestFactory(superuser=True)
with patch.object(InstanceActivityDetail, 'get_object') as go:
act = MagicMock(spec=InstanceActivity)
act._meta.object_name = "InstanceActivity"
go.return_value = act
view = InstanceActivityDetail.as_view()
self.assertEquals(view(request, pk=1234).render().status_code, 200)
def FakeRequestFactory(*args, **kwargs):
''' FakeRequestFactory, FakeMessages and FakeRequestContext are good for
mocking out django views; they are MUCH faster than the Django test client.
'''
user = UserFactory()
user.is_authenticated = lambda: kwargs.get('authenticated', True)
user.is_superuser = kwargs.get('superuser', False)
request = HttpRequest()
request.user = user
request.session = kwargs.get('session', {})
if kwargs.get('POST'):
request.method = 'POST'
request.POST = kwargs.get('POST')
else:
request.method = 'GET'
request.POST = kwargs.get('GET', {})
return request
class UserFactory(Factory):
''' using the excellent factory_boy library '''
FACTORY_FOR = User
username = Sequence(lambda i: 'test%d' % i)
first_name = 'John'
last_name = 'Doe'
email = Sequence(lambda i: 'test%d@example.com' % i)
...@@ -5,7 +5,8 @@ from django.core.exceptions import SuspiciousOperation ...@@ -5,7 +5,8 @@ from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from vm.models import Instance, InstanceTemplate, Lease, Node from vm.models import Instance, InstanceTemplate, Lease, Node, Trait
from vm.operations import WakeUpOperation
from ..models import Profile from ..models import Profile
from ..views import VmRenewView from ..views import VmRenewView
from storage.models import Disk from storage.models import Disk
...@@ -487,7 +488,7 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -487,7 +488,7 @@ class VmDetailTest(LoginMixin, TestCase):
def test_permitted_wake_up_wrong_state(self): def test_permitted_wake_up_wrong_state(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(Instance, 'wake_up_async') as mock_method: with patch.object(WakeUpOperation, 'async') as mock_method:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.wake_up mock_method.side_effect = inst.wake_up
inst.manual_state_change('RUNNING') inst.manual_state_change('RUNNING')
...@@ -501,7 +502,7 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -501,7 +502,7 @@ class VmDetailTest(LoginMixin, TestCase):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(Instance, 'select_node', return_value=None): with patch.object(Instance, 'select_node', return_value=None):
with patch.object(Instance, 'wake_up_async') as new_wake_up: with patch.object(WakeUpOperation, 'async') as new_wake_up:
with patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa: with patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
new_wake_up.side_effect = inst.wake_up new_wake_up.side_effect = inst.wake_up
...@@ -530,6 +531,231 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -530,6 +531,231 @@ class VmDetailTest(LoginMixin, TestCase):
response = c.get("/dashboard/template/111111/") response = c.get("/dashboard/template/111111/")
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_permitted_customized_vm_create(self):
c = Client()
self.login(c, "superuser")
instance_count = Instance.objects.all().count()
response = c.post("/dashboard/vm/create/", {
'name': 'vm',
'amount': 2,
'customized': 1,
'template': 1,
'cpu_priority': 1, 'cpu_count': 1, 'ram_size': 1,
'network': [],
'disks': [Disk.objects.get(id=1).pk],
})
self.assertEqual(response.status_code, 302)
self.assertEqual(instance_count + 2, Instance.objects.all().count())
class NodeDetailTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
Instance.get_remote_queue_name = Mock(return_value='test')
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.u2 = User.objects.create(username='user2', is_staff=True)
self.u2.set_password('password')
self.u2.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.user_set.add(self.u1)
self.g1.user_set.add(self.u2)
self.g1.save()
settings["default_vlangroup"] = 'public'
VlanGroup.objects.create(name='public')
node = Node.objects.get(pk=1)
trait, created = Trait.objects.get_or_create(name='testtrait')
node.traits.add(trait)
def tearDown(self):
super(NodeDetailTest, self).tearDown()
self.u1.delete()
self.u2.delete()
self.us.delete()
self.g1.delete()
def test_404_superuser_node_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/node/25555/')
self.assertEqual(response.status_code, 404)
def test_302_user_node_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/node/25555/')
self.assertEqual(response.status_code, 302)
def test_anon_node_page(self):
c = Client()
response = c.get('/dashboard/node/1/')
self.assertEqual(response.status_code, 302)
def test_permitted_node_delete(self):
c = Client()
self.login(c, 'superuser')
response = c.post('/dashboard/node/delete/1/')
self.assertEqual(response.status_code, 302)
def test_not_permitted_node_delete(self):
c = Client()
self.login(c, 'user1')
response = c.post('/dashboard/node/delete/1/')
self.assertEqual(response.status_code, 302)
def test_anon_node_delete(self):
c = Client()
response = c.post('/dashboard/node/delete/1/')
self.assertEqual(response.status_code, 302)
def test_unpermitted_set_name(self):
c = Client()
self.login(c, "user2")
node = Node.objects.get(pk=1)
old_name = node.name
response = c.post("/dashboard/node/1/", {'new_name': 'test1235'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Node.objects.get(pk=1).name, old_name)
def test_permitted_set_name(self):
c = Client()
self.login(c, "superuser")
response = c.post("/dashboard/node/1/", {'new_name': 'test1234'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Node.objects.get(pk=1).name, 'test1234')
def test_permitted_set_name_w_ajax(self):
c = Client()
self.login(c, "superuser")
response = c.post("/dashboard/node/1/", {'new_name': 'test123'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertEqual(Node.objects.get(pk=1).name, 'test123')
def test_unpermitted_add_trait(self):
c = Client()
self.login(c, "user2")
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
response = c.post("/dashboard/node/1/add-trait/",
{'name': 'test1'})
self.assertEqual(response.status_code, 302)
self.assertEqual(len(Node.objects.get(pk=1).traits.all()), trait_count)
def test_anon_add_trait(self):
c = Client()
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
response = c.post("/dashboard/node/1/add-trait/",
{'name': 'test2'})
self.assertEqual(response.status_code, 302)
self.assertEqual(len(Node.objects.get(pk=1).traits.all()), trait_count)
def test_permitted_add_trait(self):
c = Client()
self.login(c, "superuser")
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
response = c.post("/dashboard/node/1/add-trait/", {'name': 'test3'})
self.assertRedirects(response, '/dashboard/node/1/')
self.assertEqual(Node.objects.get(pk=1).traits.count(),
trait_count + 1)
def test_unpermitted_remove_trait(self):
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
traitid = node.traits.get(name='testtrait')
c = Client()
self.login(c, "user2")
response = c.post("/dashboard/node/1/", {'to_remove': traitid})
self.assertEqual(response.status_code, 302)
self.assertEqual(Node.objects.get(pk=1).traits.count(), trait_count)
def test_permitted_remove_trait(self):
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
traitid = node.traits.get(name='testtrait').pk
c = Client()
self.login(c, "superuser")
response = c.post("/dashboard/node/1/", {'to_remove': traitid})
self.assertEqual(response.status_code, 302)
self.assertEqual(Node.objects.get(pk=1).traits.count(),
trait_count - 1)
def test_permitted_remove_trait_w_ajax(self):
node = Node.objects.get(pk=1)
trait_count = Node.objects.get(pk=1).traits.count()
traitid = node.traits.get(name='testtrait').pk
c = Client()
self.login(c, "superuser")
response = c.post("/dashboard/node/1/", {'to_remove': traitid},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertEqual(Node.objects.get(pk=1).
traits.count(), trait_count - 1)
def test_add_too_long_name_trait(self):
c = Client()
self.login(c, "superuser")
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
s = 'x' * 100
response = c.post("/dashboard/node/1/add-trait/", {'name': s})
self.assertEqual(response.status_code, 200)
self.assertEqual(Node.objects.get(pk=1).traits.count(), trait_count)
def test_anon_remove_trait(self):
c = Client()
node = Node.objects.get(pk=1)
trait_count = node.traits.count()
traitid = node.traits.get(name='testtrait').pk
response = c.post("/dashboard/node/1/", {'to_remove': traitid})
self.assertEqual(response.status_code, 302)
self.assertEqual(len(Node.objects.get(pk=1).traits.all()), trait_count)
def test_anon_change_node_status(self):
c = Client()
node = Node.objects.get(pk=1)
node_enabled = node.enabled
response = c.post("/dashboard/node/1/", {'change_status': ''})
self.assertEqual(response.status_code, 302)
self.assertEqual(node_enabled, Node.objects.get(pk=1).enabled)
def test_unpermitted_change_node_status(self):
c = Client()
self.login(c, "user2")
node = Node.objects.get(pk=1)
node_enabled = node.enabled
response = c.post("/dashboard/node/status/1/", {'change_status': ''})
self.assertEqual(response.status_code, 302)
self.assertEqual(node_enabled, Node.objects.get(pk=1).enabled)
def test_permitted_change_node_status(self):
c = Client()
self.login(c, "superuser")
node = Node.objects.get(pk=1)
node_enabled = node.enabled
response = c.post("/dashboard/node/status/1/", {'change_status': ''})
self.assertEqual(response.status_code, 302)
self.assertEqual(node_enabled, not Node.objects.get(pk=1).enabled)
def test_permitted_change_node_status_w_ajax(self):
c = Client()
self.login(c, "superuser")
node = Node.objects.get(pk=1)
node_enabled = node.enabled
response = c.post("/dashboard/node/status/1/", {'change_status': ''},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertEqual(node_enabled, not Node.objects.get(pk=1).enabled)
class VmDetailVncTest(LoginMixin, TestCase): class VmDetailVncTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json'] fixtures = ['test-vm-fixture.json', 'node.json']
......
...@@ -3,15 +3,16 @@ from django.conf.urls import patterns, url ...@@ -3,15 +3,16 @@ from django.conf.urls import patterns, url
from vm.models import Instance from vm.models import Instance
from .views import ( from .views import (
AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete, AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete,
GroupDetailView, GroupList, GroupUserDelete, IndexView, LeaseCreate, GroupDetailView, GroupList, GroupUserDelete, IndexView,
LeaseDelete, LeaseDetail, MyPreferencesView, NodeAddTraitView, NodeCreate, InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail,
NodeDelete, NodeDetailView, NodeFlushView, NodeGraphView, NodeList, MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete,
NodeStatus, NotificationView, PortDelete, TemplateAclUpdateView, NodeDetailView, NodeFlushView, NodeGraphView, NodeList, NodeStatus,
TemplateCreate, TemplateDelete, TemplateDetail, TemplateList, NotificationView, PortDelete, TemplateAclUpdateView, TemplateCreate,
TransferOwnershipConfirmView, TransferOwnershipView, vm_activity, VmCreate, TemplateDelete, TemplateDetail, TemplateList, TransferOwnershipConfirmView,
VmDelete, VmDetailView, VmDetailVncTokenView, VmGraphView, VmList, TransferOwnershipView, vm_activity, VmCreate, VmDelete, VmDetailView,
VmMassDelete, VmMigrateView, VmRenewView, DiskRemoveView, VmDetailVncTokenView, VmGraphView, VmList, VmMassDelete, VmMigrateView,
get_disk_download_status, TemplateChoose, TemplateClone VmRenewView, DiskRemoveView, get_disk_download_status,
TemplateChoose, TemplateClone,
) )
urlpatterns = patterns( urlpatterns = patterns(
...@@ -61,6 +62,8 @@ urlpatterns = patterns( ...@@ -61,6 +62,8 @@ urlpatterns = patterns(
name='dashboard.views.vm-migrate'), name='dashboard.views.vm-migrate'),
url(r'^vm/(?P<pk>\d+)/renew/((?P<key>.*)/?)$', VmRenewView.as_view(), url(r'^vm/(?P<pk>\d+)/renew/((?P<key>.*)/?)$', VmRenewView.as_view(),
name='dashboard.views.vm-renew'), name='dashboard.views.vm-renew'),
url(r'^vm/activity/(?P<pk>\d+)/$', InstanceActivityDetail.as_view(),
name='dashboard.views.vm-activity'),
url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'), url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(), url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
......
...@@ -423,7 +423,7 @@ class VmDetailView(CheckedDetailView): ...@@ -423,7 +423,7 @@ class VmDetailView(CheckedDetailView):
new_name = "Saved from %s (#%d) at %s" % ( new_name = "Saved from %s (#%d) at %s" % (
self.object.name, self.object.pk, date self.object.name, self.object.pk, date
) )
self.object.save_as_template_async(name=new_name, self.object.save_as_template.async(name=new_name,
user=request.user) user=request.user)
messages.success(request, _("Saving instance as template!")) messages.success(request, _("Saving instance as template!"))
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
...@@ -433,7 +433,7 @@ class VmDetailView(CheckedDetailView): ...@@ -433,7 +433,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.shutdown_async(request.user) self.object.shutdown.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __sleep(self, request): def __sleep(self, request):
...@@ -441,7 +441,7 @@ class VmDetailView(CheckedDetailView): ...@@ -441,7 +441,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.sleep_async(request.user) self.object.sleep.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __wake_up(self, request): def __wake_up(self, request):
...@@ -449,7 +449,7 @@ class VmDetailView(CheckedDetailView): ...@@ -449,7 +449,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.wake_up_async(request.user) self.object.wake_up.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __deploy(self, request): def __deploy(self, request):
...@@ -457,7 +457,7 @@ class VmDetailView(CheckedDetailView): ...@@ -457,7 +457,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.deploy_async(request.user) self.object.deploy.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __reset(self, request): def __reset(self, request):
...@@ -465,7 +465,7 @@ class VmDetailView(CheckedDetailView): ...@@ -465,7 +465,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.reset_async(request.user) self.object.reset.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __reboot(self, request): def __reboot(self, request):
...@@ -473,7 +473,7 @@ class VmDetailView(CheckedDetailView): ...@@ -473,7 +473,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.reboot_async(request.user) self.object.reboot.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
def __shut_off(self, request): def __shut_off(self, request):
...@@ -481,7 +481,7 @@ class VmDetailView(CheckedDetailView): ...@@ -481,7 +481,7 @@ class VmDetailView(CheckedDetailView):
if not self.object.has_level(request.user, 'owner'): if not self.object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
self.object.shut_off_async(request.user) self.object.shut_off.async(user=request.user)
return redirect("%s#activity" % self.object.get_absolute_url()) return redirect("%s#activity" % self.object.get_absolute_url())
...@@ -509,8 +509,6 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): ...@@ -509,8 +509,6 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if request.POST.get('new_name'): if request.POST.get('new_name'):
return self.__set_name(request) return self.__set_name(request)
if request.POST.get('change_status') is not None:
return self.__set_status(request)
if request.POST.get('to_remove'): if request.POST.get('to_remove'):
return self.__remove_trait(request) return self.__remove_trait(request)
return redirect(reverse_lazy("dashboard.views.node-detail", return redirect(reverse_lazy("dashboard.views.node-detail",
...@@ -538,27 +536,6 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): ...@@ -538,27 +536,6 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
return redirect(reverse_lazy("dashboard.views.node-detail", return redirect(reverse_lazy("dashboard.views.node-detail",
kwargs={'pk': self.object.pk})) kwargs={'pk': self.object.pk}))
def __set_status(self, request):
self.object = self.get_object()
if not self.object.enabled:
self.object.enable(user=request.user)
else:
self.object.disable(user=request.user)
success_message = _("Node successfully changed status!")
if request.is_ajax():
response = {
'message': success_message,
'node_pk': self.object.pk
}
return HttpResponse(
json.dumps(response),
content_type="application/json"
)
else:
messages.success(request, success_message)
return redirect(reverse_lazy("dashboard.views.node-detail",
kwargs={'pk': self.object.pk}))
def __remove_trait(self, request): def __remove_trait(self, request):
try: try:
to_remove = request.POST.get('to_remove') to_remove = request.POST.get('to_remove')
...@@ -1023,7 +1000,9 @@ class VmList(LoginRequiredMixin, ListView): ...@@ -1023,7 +1000,9 @@ class VmList(LoginRequiredMixin, ListView):
instances = [{ instances = [{
'pk': i.pk, 'pk': i.pk,
'name': i.name, 'name': i.name,
'state': i.state, 'icon': i.get_status_icon(),
'host': "" if not i.primary_host else i.primary_host.hostname,
'status': i.get_status_display(),
'fav': i.pk in favs} for i in instances] 'fav': i.pk in favs} for i in instances]
return HttpResponse( return HttpResponse(
json.dumps(list(instances)), # instances is ValuesQuerySet json.dumps(list(instances)), # instances is ValuesQuerySet
...@@ -1200,9 +1179,9 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1200,9 +1179,9 @@ class VmCreate(LoginRequiredMixin, TemplateView):
if not template.has_level(request.user, 'user'): if not template.has_level(request.user, 'user'):
raise PermissionDenied() raise PermissionDenied()
inst = Instance.create_from_template( instances = [Instance.create_from_template(
template=template, owner=user) template=template, owner=user)]
return self.__deploy(request, inst) return self.__deploy(request, instances)
def __create_customized(self, request, *args, **kwargs): def __create_customized(self, request, *args, **kwargs):
user = request.user user = request.user
...@@ -1231,17 +1210,33 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1231,17 +1210,33 @@ class VmCreate(LoginRequiredMixin, TemplateView):
networks = [InterfaceTemplate(vlan=l, managed=l.managed) networks = [InterfaceTemplate(vlan=l, managed=l.managed)
for l in post['networks']] for l in post['networks']]
disks = post['disks'] disks = post['disks']
inst = Instance.create_from_template(
template=template, owner=user, networks=networks, ikwargs.update({
disks=disks, **ikwargs) 'template': template,
return self.__deploy(request, inst) 'owner': user,
'networks': networks,
'disks': disks,
})
amount = post['amount']
instances = Instance.mass_create_from_template(amount=amount,
**ikwargs)
return self.__deploy(request, instances)
else: else:
raise PermissionDenied() raise PermissionDenied()
def __deploy(self, request, instance, *args, **kwargs): def __deploy(self, request, instances, *args, **kwargs):
instance.deploy_async(user=request.user) for i in instances:
messages.success(request, _('VM successfully created!')) i.deploy.async(user=request.user)
path = instance.get_absolute_url()
if len(instances) > 1:
messages.success(request, _("Successfully created %d VMs!" %
len(instances)))
path = reverse("dashboard.index")
else:
messages.success(request, _("VM successfully created!"))
path = instances[0].get_absolute_url()
if request.is_ajax(): if request.is_ajax():
return HttpResponse(json.dumps({'redirect': path}), return HttpResponse(json.dumps({'redirect': path}),
content_type="application/json") content_type="application/json")
...@@ -1370,7 +1365,7 @@ class VmDelete(LoginRequiredMixin, DeleteView): ...@@ -1370,7 +1365,7 @@ class VmDelete(LoginRequiredMixin, DeleteView):
if not object.has_level(request.user, 'owner'): if not object.has_level(request.user, 'owner'):
raise PermissionDenied() raise PermissionDenied()
object.destroy_async(user=request.user) object.destroy.async(user=request.user)
success_url = self.get_success_url() success_url = self.get_success_url()
success_message = _("VM successfully deleted!") success_message = _("VM successfully deleted!")
...@@ -1503,7 +1498,6 @@ class NodeStatus(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): ...@@ -1503,7 +1498,6 @@ class NodeStatus(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
self.object.enable(user=request.user) self.object.enable(user=request.user)
else: else:
self.object.disable(user=request.user) self.object.disable(user=request.user)
success_message = _("Node successfully changed status!") success_message = _("Node successfully changed status!")
if request.is_ajax(): if request.is_ajax():
...@@ -1550,7 +1544,7 @@ class NodeFlushView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): ...@@ -1550,7 +1544,7 @@ class NodeFlushView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
def __flush(self, request): def __flush(self, request):
self.object = self.get_object() self.object = self.get_object()
self.object.flush_async(user=request.user) self.object.flush.async(user=request.user)
success_message = _("Node successfully flushed!") success_message = _("Node successfully flushed!")
messages.success(request, success_message) messages.success(request, success_message)
return redirect(self.get_success_url()) return redirect(self.get_success_url())
...@@ -1621,7 +1615,7 @@ class VmMassDelete(LoginRequiredMixin, View): ...@@ -1621,7 +1615,7 @@ class VmMassDelete(LoginRequiredMixin, View):
raise PermissionDenied() # no need for rollback or proper raise PermissionDenied() # no need for rollback or proper
# error message, this can't # error message, this can't
# normally happen. # normally happen.
i.destroy_async(request.user) i.destroy.async(user=request.user)
names.append(i.name) names.append(i.name)
success_message = _("Mass delete complete, the following VMs were " success_message = _("Mass delete complete, the following VMs were "
...@@ -1697,11 +1691,12 @@ def vm_activity(request, pk): ...@@ -1697,11 +1691,12 @@ def vm_activity(request, pk):
raise PermissionDenied() raise PermissionDenied()
response = {} response = {}
only_state = request.GET.get("only_state") only_status = request.GET.get("only_status")
response['state'] = instance.state response['human_readable_status'] = instance.get_status_display()
response['status'] = instance.status
response['icon'] = instance.get_status_icon() response['icon'] = instance.get_status_icon()
if only_state is not None and only_state == "false": # instance activity if only_status == "false": # instance activity
context = { context = {
'activities': InstanceActivity.objects.filter( 'activities': InstanceActivity.objects.filter(
instance=instance, parent=None instance=instance, parent=None
...@@ -2149,7 +2144,7 @@ class VmMigrateView(SuperuserRequiredMixin, TemplateView): ...@@ -2149,7 +2144,7 @@ class VmMigrateView(SuperuserRequiredMixin, TemplateView):
if node: if node:
node = Node.objects.get(pk=node) node = Node.objects.get(pk=node)
vm.migrate_async(to_node=node, user=self.request.user) vm.migrate.async(to_node=node, user=self.request.user)
else: else:
messages.error(self.request, _("You didn't select a node!")) messages.error(self.request, _("You didn't select a node!"))
...@@ -2297,3 +2292,16 @@ def get_disk_download_status(request, pk): ...@@ -2297,3 +2292,16 @@ def get_disk_download_status(request, pk):
}), }),
content_type="application/json", content_type="application/json",
) )
class InstanceActivityDetail(SuperuserRequiredMixin, DetailView):
model = InstanceActivity
template_name = 'dashboard/instanceactivity_detail.html'
def get_context_data(self, **kwargs):
ctx = super(InstanceActivityDetail, self).get_context_data(**kwargs)
ctx['activities'] = (
self.object.instance.activity_log.filter(parent=None).
order_by('-started').select_related('user').
prefetch_related('children'))
return ctx
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from django.contrib import admin from django.contrib import admin
from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall, from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall,
Domain, Record, Blacklist, Domain, Record, BlacklistItem,
SwitchPort, EthernetDevice) SwitchPort, EthernetDevice)
from django import contrib from django import contrib
...@@ -16,7 +16,7 @@ class RecordInline(contrib.admin.TabularInline): ...@@ -16,7 +16,7 @@ class RecordInline(contrib.admin.TabularInline):
class HostAdmin(admin.ModelAdmin): class HostAdmin(admin.ModelAdmin):
list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'external_ipv4', 'mac',
'shared_ip', 'owner', 'description', 'reverse', 'shared_ip', 'owner', 'description', 'reverse',
'list_groups') 'list_groups')
ordering = ('hostname', ) ordering = ('hostname', )
...@@ -25,6 +25,10 @@ class HostAdmin(admin.ModelAdmin): ...@@ -25,6 +25,10 @@ class HostAdmin(admin.ModelAdmin):
filter_horizontal = ('groups', ) filter_horizontal = ('groups', )
inlines = (RuleInline, RecordInline) inlines = (RuleInline, RecordInline)
def queryset(self, request):
qs = super(HostAdmin, self).queryset(request)
return qs.prefetch_related('groups')
@staticmethod @staticmethod
def list_groups(instance): def list_groups(instance):
"""Returns instance's groups' names as a comma-separated list.""" """Returns instance's groups' names as a comma-separated list."""
...@@ -48,9 +52,9 @@ class VlanAdmin(admin.ModelAdmin): ...@@ -48,9 +52,9 @@ class VlanAdmin(admin.ModelAdmin):
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', 'action', 'proto', 'sport', 'dport', 'nat',
'nat_dport', 'used_in') 'nat_external_port', 'used_in')
list_filter = ('vlan', 'owner', 'direction', 'accept', list_filter = ('vlan', 'owner', 'direction', 'action',
'proto', 'nat') 'proto', 'nat')
def color_desc(self, instance): def color_desc(self, instance):
...@@ -110,8 +114,8 @@ class RecordAdmin(admin.ModelAdmin): ...@@ -110,8 +114,8 @@ class RecordAdmin(admin.ModelAdmin):
list_display = ('name', 'type', 'address', 'ttl', 'host', 'owner') list_display = ('name', 'type', 'address', 'ttl', 'host', 'owner')
class BlacklistAdmin(admin.ModelAdmin): class BlacklistItemAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at') list_display = ('ipv4', 'type', 'reason', 'created_at', 'modified_at')
class SwitchPortAdmin(admin.ModelAdmin): class SwitchPortAdmin(admin.ModelAdmin):
...@@ -129,6 +133,6 @@ admin.site.register(VlanGroup) ...@@ -129,6 +133,6 @@ admin.site.register(VlanGroup)
admin.site.register(Firewall, FirewallAdmin) 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(BlacklistItem, BlacklistItemAdmin)
admin.site.register(SwitchPort) admin.site.register(SwitchPort)
admin.site.register(EthernetDevice, EthernetDeviceAdmin) admin.site.register(EthernetDevice, EthernetDeviceAdmin)
from firewall import models
import django.conf
import re import re
import logging
from collections import OrderedDict
from netaddr import IPAddress, AddrFormatError
from datetime import datetime, timedelta from datetime import datetime, timedelta
from itertools import product
from .models import (Host, Rule, Vlan, Domain, Record, BlacklistItem,
SwitchPort)
from .iptables import IptRule, IptChain
import django.conf
from django.db.models import Q from django.db.models import Q
from django.template import loader, Context
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
logger = logging.getLogger(__name__)
class Firewall: class BuildFirewall:
def dportsport(self, rule, repl=True):
retval = ' '
if rule.proto == 'tcp' or rule.proto == 'udp':
retval = '-p %s ' % rule.proto
if rule.sport:
retval += ' --sport %s ' % rule.sport
if rule.dport:
retval += ' --dport %s ' % (rule.nat_dport
if (repl and rule.nat and rule.direction == '1')
else rule.dport)
elif rule.proto == 'icmp':
retval = '-p %s ' % rule.proto
return retval
def iptables(self, s):
"""Append rule to filter table."""
self.RULES.append(s)
def iptablesnat(self, s): def __init__(self):
"""Append rule to NAT table.""" self.chains = OrderedDict()
self.RULES_NAT.append(s)
def host2vlan(self, host, rule): def add_rules(self, *args, **kwargs):
if not rule.foreign_network: for chain_name, ipt_rule in kwargs.items():
return if chain_name not in self.chains:
self.create_chain(chain_name)
self.chains[chain_name].add(ipt_rule)
if self.proto == 6 and host.ipv6: def create_chain(self, chain_name):
ipaddr = str(host.ipv6) + '/112' self.chains[chain_name] = IptChain(name=chain_name)
else:
ipaddr = str(host.ipv4)
dport_sport = self.dportsport(rule)
for vlan in rule.foreign_network.vlans.all():
if rule.accept:
if rule.direction == '0' and vlan.name == 'PUB':
if rule.dport == 25:
self.iptables('-A PUB_OUT -s %s %s -p tcp '
'--dport 25 -j LOG_ACC' %
(ipaddr, rule.extra))
break
action = 'PUB_OUT'
else:
action = 'LOG_ACC'
else:
action = 'LOG_DROP'
if rule.direction == '1': # going TO host
self.iptables('-A %s_%s -d %s %s %s -g %s' %
(vlan.name, host.vlan.name, ipaddr, dport_sport,
rule.extra, action))
else:
self.iptables('-A %s_%s -s %s %s %s -g %s' %
(host.vlan.name, vlan.name, ipaddr, dport_sport,
rule.extra, action))
def fw2vlan(self, rule):
if not rule.foreign_network:
return
dport_sport = self.dportsport(rule)
for vlan in rule.foreign_network.vlans.all():
if rule.direction == '1': # going TO host
self.iptables('-A INPUT -i %s %s %s -g %s' %
(vlan.name, dport_sport, rule.extra,
'LOG_ACC' if rule.accept else 'LOG_DROP'))
else:
self.iptables('-A OUTPUT -o %s %s %s -g %s' %
(vlan.name, dport_sport, rule.extra,
'LOG_ACC' if rule.accept else 'LOG_DROP'))
def vlan2vlan(self, l_vlan, rule):
if not rule.foreign_network:
return
dport_sport = self.dportsport(rule)
for vlan in rule.foreign_network.vlans.all():
if rule.accept:
if rule.direction == '0' and vlan.name == 'PUB':
action = 'PUB_OUT'
else:
action = 'LOG_ACC'
else:
action = 'LOG_DROP'
if rule.direction == '1': # going TO host
self.iptables('-A %s_%s %s %s -g %s' %
(vlan.name, l_vlan.name, dport_sport,
rule.extra, action))
else:
self.iptables('-A %s_%s %s %s -g %s' % (l_vlan.name, vlan.name,
dport_sport,
rule.extra, action))
def prerun(self):
self.iptables('*filter')
self.iptables(':INPUT DROP [88:6448]')
self.iptables(':FORWARD DROP [0:0]')
self.iptables(':OUTPUT DROP [50:6936]')
# initialize logging
self.iptables('-N LOG_DROP')
# windows port scan are silently dropped
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 -j LOG --log-level 7 '
'--log-prefix "[ipt][drop]"')
self.iptables('-A LOG_DROP -j DROP')
self.iptables('-N LOG_ACC')
self.iptables('-A LOG_ACC -j LOG --log-level 7 '
'--log-prefix "[ipt][isok]"')
self.iptables('-A LOG_ACC -j ACCEPT')
self.iptables('-N PUB_OUT')
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 ESTABLISHED,RELATED '
'-j ACCEPT')
self.iptables('-A FORWARD -p icmp --icmp-type echo-request '
'-g LOG_ACC')
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 -i lo -j ACCEPT')
self.iptables('-A INPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT')
self.iptables('-A OUTPUT -m state --state INVALID -g LOG_DROP')
self.iptables('-A OUTPUT -o lo -j ACCEPT')
self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT')
def postrun(self):
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')
self.iptables('-A PUB_OUT -g LOG_ACC')
self.iptables('-A FORWARD -g LOG_DROP')
self.iptables('-A INPUT -g LOG_DROP')
self.iptables('-A OUTPUT -g LOG_DROP')
self.iptables('COMMIT')
def ipt_nat(self):
self.iptablesnat('*nat')
self.iptablesnat(':PREROUTING ACCEPT [0:0]')
self.iptablesnat(':INPUT ACCEPT [0:0]')
self.iptablesnat(':OUTPUT ACCEPT [1:708]')
self.iptablesnat(':POSTROUTING ACCEPT [1:708]')
def build_ipt_nat(self):
# portforward # portforward
for host in self.hosts.exclude(pub_ipv4=None): for rule in Rule.objects.filter(
for rule in host.rules.filter(nat=True, direction='1'): action__in=['accept', 'drop'],
dport_sport = self.dportsport(rule, False) nat=True, direction='in').select_related('host'):
if host.vlan.snat_ip: self.add_rules(PREROUTING=IptRule(
self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT ' priority=1000,
'--to-destination %s:%s' % dst=(rule.get_external_ipv4(), None),
(host.pub_ipv4, dport_sport, rule.extra, proto=rule.proto,
host.ipv4, rule.nat_dport)) dport=rule.get_external_port('ipv4'),
extra='-j DNAT --to-destination %s:%s' % (rule.host.ipv4,
# rules for machines with dedicated public IP rule.dport)))
for host in self.hosts.exclude(shared_ip=True):
if host.pub_ipv4: # default outbound NAT rules for VLANs
self.iptablesnat('-A PREROUTING -d %s -j DNAT ' for vl_in in Vlan.objects.exclude(
'--to-destination %s' % snat_ip=None).prefetch_related('snat_to'):
(host.pub_ipv4, host.ipv4)) for vl_out in vl_in.snat_to.all():
self.iptablesnat('-A POSTROUTING -s %s -j SNAT ' self.add_rules(POSTROUTING=IptRule(
'--to-source %s' % priority=1000,
(host.ipv4, host.pub_ipv4)) src=(vl_in.network4, None),
extra='-o %s -j SNAT --to-source %s' % (
# default NAT rules for VLANs vl_out.name, vl_in.snat_ip)))
for s_vlan in self.vlans:
if s_vlan.snat_ip:
for d_vlan in s_vlan.snat_to.all():
self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT '
'--to-source %s' %
(str(s_vlan.network4), d_vlan.name,
s_vlan.snat_ip))
self.iptablesnat('COMMIT')
def ipt_filter(self):
self.prerun()
self.ipt_filter_firewall()
self.ipt_filter_zones()
self.ipt_filter_host_rules()
self.ipt_filter_vlan_rules()
self.ipt_filter_vlan_drop()
self.postrun()
if self.proto == 6: # remove ipv4-specific rules
ipv4_re = re.compile('([0-9]{1,3}\.){3}[0-9]{1,3}')
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 ipt_filter_firewall(self): def ipt_filter_firewall(self):
"""Build firewall's own rules.""" """Build firewall's own rules."""
for f in self.fw: rules = Rule.objects.filter(action__in=['accept', 'drop'])
for rule in f.rules.all(): for rule in rules.exclude(firewall=None).select_related(
self.fw2vlan(rule) 'foreign_network').prefetch_related('foreign_network__vlans'):
self.add_rules(**rule.get_ipt_rules())
def ipt_filter_zones(self):
"""Jumping to chains between zones."""
for s_vlan in self.vlans:
for d_vlan in self.vlans:
self.iptables('-N %s_%s' % (s_vlan.name, d_vlan.name))
self.iptables('-A FORWARD -i %s -o %s -g %s_%s' %
(s_vlan.name, d_vlan.name, s_vlan.name,
d_vlan.name))
def ipt_filter_host_rules(self): def ipt_filter_host_rules(self):
"""Build hosts' rules.""" """Build hosts' rules."""
for i_vlan in self.vlans: # host rules
for i_host in i_vlan.host_set.all(): rules = Rule.objects.filter(action__in=['accept', 'drop'])
for group in i_host.groups.all(): for rule in rules.exclude(host=None).select_related(
for rule in group.rules.all(): 'foreign_network', 'host', 'host__vlan').prefetch_related(
self.host2vlan(i_host, rule) 'foreign_network__vlans'):
for rule in i_host.rules.all(): self.add_rules(**rule.get_ipt_rules(rule.host))
self.host2vlan(i_host, rule) # group rules
for rule in rules.exclude(hostgroup=None).select_related(
'hostgroup', 'foreign_network').prefetch_related(
'hostgroup__host_set__vlan', 'foreign_network__vlans'):
for host in rule.hostgroup.host_set.all():
self.add_rules(**rule.get_ipt_rules(host))
def ipt_filter_vlan_rules(self): def ipt_filter_vlan_rules(self):
"""Enable communication between VLANs.""" """Enable communication between VLANs."""
for s_vlan in self.vlans: rules = Rule.objects.filter(action__in=['accept', 'drop'])
for rule in s_vlan.rules.all(): for rule in rules.exclude(vlan=None).select_related(
self.vlan2vlan(s_vlan, rule) 'vlan', 'foreign_network').prefetch_related(
'foreign_network__vlans'):
self.add_rules(**rule.get_ipt_rules())
def ipt_filter_vlan_drop(self): def ipt_filter_vlan_drop(self):
"""Close intra-VLAN chains.""" """Close intra-VLAN chains."""
for s_vlan in self.vlans: for chain in self.chains.values():
for d_vlan in self.vlans: close_chain_rule = IptRule(priority=1, action='LOG_DROP')
self.iptables('-A %s_%s -g LOG_DROP' % (s_vlan.name, chain.add(close_chain_rule)
d_vlan.name))
def ipt_filter_vlan_jump(self):
def __init__(self, proto=4): """Create intra-VLAN jump rules."""
self.RULES = []
self.RULES_NAT = []
self.proto = proto
self.vlans = models.Vlan.objects.all()
self.hosts = models.Host.objects.all()
self.fw = models.Firewall.objects.all()
self.ipt_filter()
if self.proto != 6:
self.ipt_nat()
def get(self):
if self.proto == 6:
return {'filter': self.RULES, }
else:
return {'filter': self.RULES, 'nat': self.RULES_NAT}
def show(self): vlans = Vlan.objects.all().values_list('name', flat=True)
if self.proto == 6: for vl_in, vl_out in product(vlans, repeat=2):
return '\n'.join(self.RULES) + '\n' name = '%s_%s' % (vl_in, vl_out)
try:
chain = self.chains[name]
except KeyError:
pass
else: else:
return ('\n'.join(self.RULES) + '\n' + jump_rule = IptRule(priority=65535, action=chain.name,
'\n'.join(self.RULES_NAT) + '\n') extra='-i %s -o %s' % (vl_in, vl_out))
self.add_rules(FORWARD=jump_rule)
def build_ipt(self):
"""Build rules."""
self.ipt_filter_firewall()
self.ipt_filter_host_rules()
self.ipt_filter_vlan_rules()
self.ipt_filter_vlan_jump()
self.ipt_filter_vlan_drop()
self.build_ipt_nat()
context = {
'filter': lambda: (chain for name, chain in self.chains.iteritems()
if chain.name not in IptChain.nat_chains),
'nat': lambda: (chain for name, chain in self.chains.iteritems()
if chain.name in IptChain.nat_chains)}
template = loader.get_template('firewall/iptables.conf')
context['proto'] = 'ipv4'
ipv4 = unicode(template.render(Context(context)))
context['proto'] = 'ipv6'
ipv6 = unicode(template.render(Context(context)))
return (ipv4, ipv6)
def ipset(): def ipset():
week = datetime.now() - timedelta(days=2) week = datetime.now() - timedelta(days=2)
filter_ban = (Q(type='tempban', modified_at__gte=week) | filter_ban = (Q(type='tempban', modified_at__gte=week) |
Q(type='permban')).values('ipv4', 'reason') Q(type='permban'))
return models.Blacklist.objects.filter(filter_ban) return BlacklistItem.objects.filter(filter_ban).values('ipv4', 'reason')
def ipv6_to_octal(ipv6): def ipv6_to_octal(ipv6):
while len(ipv6.split(':')) < 8: ipv6 = IPAddress(ipv6, version=6)
ipv6 = ipv6.replace('::', ':::')
octets = [] octets = []
for part in ipv6.split(':'): for part in ipv6.words:
if not part:
octets.extend([0, 0])
else:
# Pad hex part to 4 digits. # Pad hex part to 4 digits.
part = '%04x' % int(part, 16) part = '%04x' % part
octets.append(int(part[:2], 16)) octets.append(int(part[:2], 16))
octets.append(int(part[2:], 16)) octets.append(int(part[2:], 16))
return '\\' + '\\'.join(['%03o' % x for x in octets]) return "".join(r"\%03o" % x for x in octets)
# =fqdn:ip:ttl A, PTR # =fqdn:ip:ttl A, PTR
...@@ -314,25 +163,22 @@ def ipv6_to_octal(ipv6): ...@@ -314,25 +163,22 @@ def ipv6_to_octal(ipv6):
def generate_ptr_records(): def generate_ptr_records():
DNS = [] DNS = []
for host in models.Host.objects.order_by('vlan').all(): for host in Host.objects.order_by('vlan').all():
rev = host.vlan.reverse_domain template = host.vlan.reverse_domain
ipv4 = str(host.pub_ipv4 if host.pub_ipv4 and i = host.get_external_ipv4().words
not host.shared_ip else host.ipv4) reverse = (host.reverse if host.reverse not in [None, '']
i = ipv4.split('.', 4) else host.get_fqdn())
reverse = (host.reverse if host.reverse and
len(host.reverse) else host.get_fqdn())
# ipv4 # ipv4
if host.ipv4: if host.ipv4:
DNS.append("^%s:%s:%s" % ( fqdn = template % {'a': i[0], 'b': i[1], 'c': i[2], 'd': i[3]}
(rev % {'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]), DNS.append("^%s:%s:%s" % (fqdn, reverse, settings['dns_ttl']))
'd': int(i[3])}),
reverse, models.settings['dns_ttl']))
# ipv6 # ipv6
if host.ipv6: if host.ipv6:
DNS.append("^%s:%s:%s" % (host.ipv6.reverse_dns, DNS.append("^%s:%s:%s" % (host.ipv6.reverse_dns,
reverse, models.settings['dns_ttl'])) reverse, settings['dns_ttl']))
return DNS return DNS
...@@ -341,30 +187,32 @@ def txt_to_octal(txt): ...@@ -341,30 +187,32 @@ def txt_to_octal(txt):
def generate_records(): def generate_records():
DNS = [] types = {'A': '+%(fqdn)s:%(address)s:%(ttl)s',
'AAAA': ':%(fqdn)s:28:%(octal)s:%(ttl)s',
for r in models.Record.objects.all(): 'NS': '&%(fqdn)s::%(address)s:%(ttl)s',
if r.type == 'A': 'CNAME': 'C%(fqdn)s:%(address)s:%(ttl)s',
DNS.append("+%s:%s:%s" % (r.fqdn, r.address, r.ttl)) 'MX': '@%(fqdn)s::%(address)s:%(dist)s:%(ttl)s',
elif r.type == 'AAAA': 'PTR': '^%(fqdn)s:%(address)s:%(ttl)s',
DNS.append(":%s:28:%s:%s" % 'TXT': '%(fqdn)s:%(octal)s:%(ttl)s'}
(r.fqdn, ipv6_to_octal(r.address), r.ttl))
elif r.type == 'NS': retval = []
DNS.append("&%s::%s:%s" % (r.fqdn, r.address, r.ttl))
elif r.type == 'CNAME': for r in Record.objects.all():
DNS.append("C%s:%s:%s" % (r.fqdn, r.address, r.ttl)) params = {'fqdn': r.fqdn, 'address': r.address, 'ttl': r.ttl}
elif r.type == 'MX': if r.type == 'MX':
mx = r.address.split(':', 2) params['address'], params['dist'] = r.address.split(':', 2)
DNS.append("@%(fqdn)s::%(mx)s:%(dist)s:%(ttl)s" % if r.type == 'AAAA':
{'fqdn': r.fqdn, 'mx': mx[1], 'dist': mx[0], try:
'ttl': r.ttl}) params['octal'] = ipv6_to_octal(r.address)
elif r.type == 'PTR': except AddrFormatError:
DNS.append("^%s:%s:%s" % (r.fqdn, r.address, r.ttl)) logger.error('Invalid ipv6 address: %s, record: %s',
elif r.type == 'TXT': r.address, r)
DNS.append("'%s:%s:%s" % (r.fqdn, continue
txt_to_octal(r.address), r.ttl)) if r.type == 'TXT':
params['octal'] = txt_to_octal(r.address)
retval.append(types[r.type] % params)
return DNS return retval
def dns(): def dns():
...@@ -374,10 +222,10 @@ def dns(): ...@@ -374,10 +222,10 @@ def dns():
DNS += generate_ptr_records() DNS += generate_ptr_records()
# domain SOA record # domain SOA record
for domain in models.Domain.objects.all(): for domain in Domain.objects.all():
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" %
(domain.name, settings['dns_hostname'], (domain.name, settings['dns_hostname'],
models.settings['dns_ttl'])) settings['dns_ttl']))
# records # records
DNS += generate_records() DNS += generate_records()
...@@ -385,18 +233,24 @@ def dns(): ...@@ -385,18 +233,24 @@ def dns():
return DNS return DNS
class UniqueHostname(object):
"""Append vlan id if hostname already exists."""
def __init__(self):
self.used_hostnames = set()
def get(self, hostname, vlan_id):
if hostname in self.used_hostnames:
hostname = "%s-%s" % (hostname, vlan_id)
self.used_hostnames.add(hostname)
return hostname
def dhcp(): def dhcp():
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 = [] config = []
# /tools/dhcp3/dhcpd.conf.generated
for i_vlan in models.Vlan.objects.all(): VLAN_TEMPLATE = '''
if(i_vlan.dhcp_pool):
m = regex.search(i_vlan.dhcp_pool)
if(m or i_vlan.dhcp_pool == "manual"):
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;
...@@ -408,43 +262,53 @@ def dhcp(): ...@@ -408,43 +262,53 @@ def dhcp():
authoritative; authoritative;
filename \"pxelinux.0\"; filename \"pxelinux.0\";
allow bootp; allow booting; allow bootp; allow booting;
}''' % { }'''
'net': str(i_vlan.network4.network),
'netmask': str(i_vlan.network4.netmask),
'domain': i_vlan.domain,
'router': i_vlan.ipv4,
'ntp': i_vlan.ipv4,
'dnsserver': settings['rdns_ip'],
'extra': ("range %s" % i_vlan.dhcp_pool
if m else "deny unknown-clients"),
'interface': i_vlan.name,
'name': i_vlan.name,
'tftp': i_vlan.ipv4
})
for i_host in i_vlan.host_set.all(): HOST_TEMPLATE = '''
DHCP.append('''
host %(hostname)s { host %(hostname)s {
hardware ethernet %(mac)s; hardware ethernet %(mac)s;
fixed-address %(ipv4)s; fixed-address %(ipv4)s;
}''' % { }'''
'hostname': i_host.hostname,
'mac': i_host.mac, unique_hostnames = UniqueHostname()
'ipv4': i_host.ipv4,
for vlan in Vlan.objects.exclude(
dhcp_pool=None).select_related(
'domain').prefetch_related('host_set'):
m = regex.search(vlan.dhcp_pool)
if(m or vlan.dhcp_pool == "manual"):
config.append(VLAN_TEMPLATE % {
'net': str(vlan.network4.network),
'netmask': str(vlan.network4.netmask),
'domain': vlan.domain,
'router': vlan.network4.ip,
'ntp': vlan.network4.ip,
'dnsserver': settings['rdns_ip'],
'extra': ("range %s" % vlan.dhcp_pool
if m else "deny unknown-clients"),
'interface': vlan.name,
'name': vlan.name,
'tftp': vlan.network4.ip})
for host in vlan.host_set.all():
config.append(HOST_TEMPLATE % {
'hostname': unique_hostnames.get(host.hostname, vlan.vid),
'mac': host.mac,
'ipv4': host.ipv4,
}) })
return DHCP return config
def vlan(): def vlan():
obj = models.Vlan.objects.values('vid', 'name', 'network4', 'network6') obj = Vlan.objects.values('vid', 'name', 'network4', 'network6')
retval = {x['name']: {'tag': x['vid'], retval = {x['name']: {'tag': x['vid'],
'type': 'internal', 'type': 'internal',
'interfaces': [x['name']], 'interfaces': [x['name']],
'addresses': [str(x['network4']), 'addresses': [str(x['network4']),
str(x['network6'])]} str(x['network6'])]}
for x in obj} for x in obj}
for p in models.SwitchPort.objects.all(): for p in SwitchPort.objects.all():
eth_count = p.ethernet_devices.count() eth_count = p.ethernet_devices.count()
if eth_count > 1: if eth_count > 1:
name = 'bond%d' % p.id name = 'bond%d' % p.id
......
import logging
import re
from collections import OrderedDict
logger = logging.getLogger()
ipv4_re = re.compile(
r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}')
class InvalidRuleExcepion(Exception):
pass
class IptRule(object):
def __init__(self, priority=1000, action=None, src=None, dst=None,
proto=None, sport=None, dport=None, extra=None,
ipv4_only=False, comment=None):
if proto not in ['tcp', 'udp', 'icmp', None]:
raise InvalidRuleExcepion()
if proto not in ['tcp', 'udp'] and (sport is not None or
dport is not None):
raise InvalidRuleExcepion()
self.priority = int(priority)
self.action = action
(self.src4, self.src6) = (None, None)
if isinstance(src, tuple):
(self.src4, self.src6) = src
if not self.src6:
ipv4_only = True
(self.dst4, self.dst6) = (None, None)
if isinstance(dst, tuple):
(self.dst4, self.dst6) = dst
if not self.dst6:
ipv4_only = True
self.proto = proto
self.sport = sport
self.dport = dport
self.extra = extra
self.ipv4_only = (ipv4_only or
extra is not None and bool(ipv4_re.search(extra)))
self.comment = comment
def __hash__(self):
return hash(frozenset(self.__dict__.items()))
def __eq__(self, other):
return self.__dict__ == other.__dict__
def __lt__(self, other):
return self.priority < other.priority
def __repr__(self):
return '<IptRule: @%d %s >' % (self.priority, self.compile())
def __unicode__(self):
return self.__repr__()
def compile(self, proto='ipv4'):
opts = OrderedDict([('src4' if proto == 'ipv4' else 'src6', '-s %s'),
('dst4' if proto == 'ipv4' else 'dst6', '-d %s'),
('proto', '-p %s'),
('sport', '--sport %s'),
('dport', '--dport %s'),
('extra', '%s'),
('comment', '-m comment --comment "%s"'),
('action', '-g %s')])
params = [opts[param] % getattr(self, param)
for param in opts
if getattr(self, param) is not None]
return ' '.join(params)
class IptChain(object):
nat_chains = ('PREROUTING', 'POSTROUTING')
builtin_chains = ('FORWARD', 'INPUT', 'OUTPUT') + nat_chains
def __init__(self, name):
self.rules = set()
self.name = name
def add(self, *args, **kwargs):
for rule in args:
self.rules.add(rule)
def sort(self):
return sorted(list(self.rules))
def __len__(self):
return len(self.rules)
def __repr__(self):
return '<IptChain: %s %s>' % (self.name, self.rules)
def __unicode__(self):
return self.__repr__()
def compile(self, proto='ipv4'):
assert proto in ('ipv4', 'ipv6')
prefix = '-A %s ' % self.name
return '\n'.join([prefix + rule.compile(proto)
for rule in self.sort()
if not (proto == 'ipv6' and rule.ipv4_only)])
def compile_v6(self):
return self.compile('ipv6')
# -*- 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):
# Renaming field 'Host.pub_ipv4' to 'Host.external_ipv4'
db.rename_column(u'firewall_host', 'pub_ipv4', 'external_ipv4')
# Renaming field 'Rule.nat_dport' to 'Rule.nat_external_port'
db.rename_column(u'firewall_rule', 'nat_dport', 'nat_external_port')
# Adding field 'Rule.priority'
db.add_column(u'firewall_rule', 'priority',
self.gf('django.db.models.fields.IntegerField')(default=1000, null=True, blank=True),
keep_default=False)
# Adding field 'Rule.nat_external_ipv4'
db.add_column(u'firewall_rule', 'nat_external_ipv4',
self.gf('firewall.fields.IPAddressField')(max_length=100, null=True, blank=True),
keep_default=False)
# Changing field 'Rule.direction'
db.alter_column(u'firewall_rule', 'direction', self.gf('django.db.models.fields.CharField')(max_length=3))
# Migrating data
for rule in orm.Rule.objects.all():
if rule.nat:
# swap
tmp = rule.dport
# rule.dport = rule.nat_external_port
# rule.nat_external_port = tmp
if rule.direction == '0':
rule.direction = 'out'
elif rule.direction == '1':
rule.direction = 'in'
rule.save()
def backwards(self, orm):
# Renaming field 'Host.external_ipv4' to 'Host.pub_ipv4'
db.rename_column(u'firewall_host', 'external_ipv4', 'pub_ipv4')
# Renaming field 'Rule.nat_external_port' to 'Rule.nat_dport'
db.rename_column(u'firewall_rule', 'nat_external_port', 'nat_dport')
# Deleting field 'Rule.priority'
db.delete_column(u'firewall_rule', 'priority')
# Deleting field 'Rule.nat_external_ipv4'
db.delete_column(u'firewall_rule', 'nat_external_ipv4')
# Changing field 'Rule.direction'
db.alter_column(u'firewall_rule', 'direction', self.gf('django.db.models.fields.CharField')(max_length=3))
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'priority': ('django.db.models.fields.IntegerField', [], {'default': '1000', 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
# -*- 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 'Rule.priority'
db.alter_column(u'firewall_rule', 'priority', self.gf('django.db.models.fields.IntegerField')())
def backwards(self, orm):
# Changing field 'Rule.priority'
db.alter_column(u'firewall_rule', 'priority', self.gf('django.db.models.fields.IntegerField')(null=True))
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'priority': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
\ 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):
db.rename_column(u'firewall_rule', 'priority', 'weight')
db.alter_column(u'firewall_rule', 'weight',
self.gf('django.db.models.fields.IntegerField')(default=30000))
def backwards(self, orm):
db.rename_column(u'firewall_rule', 'priority', 'weight')
db.alter_column(u'firewall_rule', 'priority',
self.gf('django.db.models.fields.IntegerField')(default=1000))
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'weight': ('django.db.models.fields.IntegerField', [], {'default': '30000'})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
# -*- 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 'Rule.action'
db.add_column(u'firewall_rule', 'action',
self.gf('django.db.models.fields.CharField')(default='drop', max_length=10),
keep_default=False)
# Migrating data
for rule in orm.Rule.objects.all():
rule.action = 'accept' if rule.accept else 'drop'
rule.save()
def backwards(self, orm):
# Deleting field 'Rule.action'
db.delete_column(u'firewall_rule', 'action')
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'action': ('django.db.models.fields.CharField', [], {'default': "'drop'", 'max_length': '10'}),
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'weight': ('django.db.models.fields.IntegerField', [], {'default': '30000'})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
# -*- 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):
# Deleting field 'Rule.accept'
db.delete_column(u'firewall_rule', 'accept')
def backwards(self, orm):
# Adding field 'Rule.accept'
db.add_column(u'firewall_rule', 'accept',
self.gf('django.db.models.fields.BooleanField')(default=True),
keep_default=False)
# Migrating data
for rule in orm.Rule.objects.all():
rule.accept = rule.action == 'accept'
rule.save()
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'action': ('django.db.models.fields.CharField', [], {'default': "'drop'", 'max_length': '10'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'weight': ('django.db.models.fields.IntegerField', [], {'default': '30000'})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
# -*- 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 'Vlan.host_ipv6_prefixlen'
db.add_column(u'firewall_vlan', 'host_ipv6_prefixlen',
self.gf('django.db.models.fields.IntegerField')(default=112),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Vlan.host_ipv6_prefixlen'
db.delete_column(u'firewall_vlan', 'host_ipv6_prefixlen')
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklist': {
'Meta': {'object_name': 'Blacklist'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'action': ('django.db.models.fields.CharField', [], {'default': "'drop'", 'max_length': '10'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'weight': ('django.db.models.fields.IntegerField', [], {'default': '30000'})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host_ipv6_prefixlen': ('django.db.models.fields.IntegerField', [], {'default': '112'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
\ 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):
db.rename_table(u'firewall_blacklist', 'firewall_blacklistitem')
def backwards(self, orm):
db.rename_table(u'firewall_blacklistitem', 'firewall_blacklist')
models = {
u'acl.level': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Level'},
'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'}),
'weight': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
},
u'acl.objectlevel': {
'Meta': {'unique_together': "(('content_type', 'object_id', 'level'),)", 'object_name': 'ObjectLevel'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['acl.Level']"}),
'object_id': ('django.db.models.fields.IntegerField', [], {}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
},
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'firewall.blacklistitem': {
'Meta': {'object_name': 'BlacklistItem'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'snort_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'default': "'tempban'", 'max_length': '10'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.ethernetdevice': {
'Meta': {'object_name': 'EthernetDevice'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'switch_port': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ethernet_devices'", 'to': u"orm['firewall.SwitchPort']"})
},
u'firewall.firewall': {
'Meta': {'object_name': 'Firewall'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.record': {
'Meta': {'ordering': "('domain', 'name')", 'object_name': 'Record'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '6'})
},
u'firewall.rule': {
'Meta': {'ordering': "('direction', 'proto', 'sport', 'dport', 'nat_external_port', 'host')", 'object_name': 'Rule'},
'action': ('django.db.models.fields.CharField', [], {'default': "'drop'", 'max_length': '10'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'direction': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'dport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'extra': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'firewall': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Firewall']"}),
'foreign_network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ForeignRules'", 'to': u"orm['firewall.VlanGroup']"}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Host']"}),
'hostgroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'nat_external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'nat_external_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'proto': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'sport': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.Vlan']"}),
'vlangroup': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rules'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'weight': ('django.db.models.fields.IntegerField', [], {'default': '30000'})
},
u'firewall.switchport': {
'Meta': {'object_name': 'SwitchPort'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'tagged_vlans': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tagged_ports'", 'null': 'True', 'to': u"orm['firewall.VlanGroup']"}),
'untagged_vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'untagged_ports'", 'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host_ipv6_prefixlen': ('django.db.models.fields.IntegerField', [], {'default': '112'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'firewall.vlangroup': {
'Meta': {'object_name': 'VlanGroup'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'vlans': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['firewall']
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from itertools import islice, ifilter from itertools import islice, ifilter
import logging import logging
from netaddr import IPSet, EUI from netaddr import IPSet, EUI, IPNetwork
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
...@@ -19,6 +19,7 @@ import random ...@@ -19,6 +19,7 @@ import random
from common.models import HumanSortField from common.models import HumanSortField
from firewall.tasks.local_tasks import reloadtask from firewall.tasks.local_tasks import reloadtask
from .iptables import IptRule
from acl.models import AclBase from acl.models import AclBase
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
...@@ -36,9 +37,11 @@ class Rule(models.Model): ...@@ -36,9 +37,11 @@ class Rule(models.Model):
CHOICES_type = (('host', 'host'), ('firewall', 'firewall'), CHOICES_type = (('host', 'host'), ('firewall', 'firewall'),
('vlan', 'vlan')) ('vlan', 'vlan'))
CHOICES_proto = (('tcp', 'tcp'), ('udp', 'udp'), ('icmp', 'icmp')) CHOICES_proto = (('tcp', 'tcp'), ('udp', 'udp'), ('icmp', 'icmp'))
CHOICES_dir = (('0', 'out'), ('1', 'in')) CHOICES_dir = (('out', _('out')), ('in', _('in')))
CHOICES_action = (('accept', _('accept')), ('drop', _('drop')),
('ignore', _('ignore')))
direction = models.CharField(max_length=1, choices=CHOICES_dir, direction = models.CharField(max_length=3, choices=CHOICES_dir,
blank=False, verbose_name=_("direction"), blank=False, verbose_name=_("direction"),
help_text=_("If the rule matches egress " help_text=_("If the rule matches egress "
"or ingress packets.")) "or ingress packets."))
...@@ -58,28 +61,38 @@ class Rule(models.Model): ...@@ -58,28 +61,38 @@ class Rule(models.Model):
blank=True, null=True, verbose_name=_("source port"), blank=True, null=True, verbose_name=_("source port"),
validators=[MinValueValidator(1), MaxValueValidator(65535)], validators=[MinValueValidator(1), MaxValueValidator(65535)],
help_text=_("Source port number of packets that match.")) help_text=_("Source port number of packets that match."))
weight = models.IntegerField(
verbose_name=_("weight"),
validators=[MinValueValidator(1), MaxValueValidator(65535)],
help_text=_("Rule weight"),
default=30000)
proto = models.CharField(max_length=10, choices=CHOICES_proto, proto = models.CharField(max_length=10, choices=CHOICES_proto,
blank=True, null=True, verbose_name=_("protocol"), blank=True, null=True, verbose_name=_("protocol"),
help_text=_("Protocol of packets that match.")) help_text=_("Protocol of packets that match."))
extra = models.TextField(blank=True, verbose_name=_("extra arguments"), extra = models.TextField(blank=True, verbose_name=_("extra arguments"),
help_text=_("Additional arguments passed " help_text=_("Additional arguments passed "
"literally to the iptables-rule.")) "literally to the iptables-rule."))
accept = models.BooleanField(default=False, verbose_name=_("accept"), action = models.CharField(max_length=10, choices=CHOICES_action,
help_text=_("Accept the matching packets " default='drop', verbose_name=_('action'),
"(or deny if not checked).")) help_text=_("Accept, drop or ignore the "
"matching packets."))
owner = models.ForeignKey(User, blank=True, null=True, owner = models.ForeignKey(User, blank=True, null=True,
verbose_name=_("owner"), verbose_name=_("owner"),
help_text=_("The user responsible for " help_text=_("The user responsible for "
"this rule.")) "this rule."))
nat = models.BooleanField(default=False, verbose_name=_("NAT"), nat = models.BooleanField(default=False, verbose_name=_("NAT"),
help_text=_("If network address translation " help_text=_("If network address translation "
"should be done.")) "should be done."))
nat_dport = models.IntegerField(blank=True, null=True, nat_external_port = models.IntegerField(
help_text=_("Rewrite destination port " blank=True, null=True,
"number to this if NAT is " help_text=_("Rewrite destination port number to this if NAT is "
"needed."), "needed."),
validators=[MinValueValidator(1), validators=[MinValueValidator(1), MaxValueValidator(65535)])
MaxValueValidator(65535)]) nat_external_ipv4 = IPAddressField(
version=4, blank=True, null=True,
verbose_name=_('external IPv4 address'))
created_at = models.DateTimeField( created_at = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name=_("created at")) verbose_name=_("created at"))
...@@ -120,14 +133,25 @@ class Rule(models.Model): ...@@ -120,14 +133,25 @@ class Rule(models.Model):
if len(selected_fields) > 1: if len(selected_fields) > 1:
raise ValidationError(_('Only one field can be selected.')) raise ValidationError(_('Only one field can be selected.'))
def get_external_ipv4(self):
return (self.nat_external_ipv4
if self.nat_external_ipv4 else self.host.get_external_ipv4())
def get_external_port(self, proto='ipv4'):
assert proto in ('ipv4', 'ipv6')
if proto == 'ipv4' and self.nat_external_port:
return self.nat_external_port
else:
return self.dport
def desc(self): def desc(self):
"""Return a short string representation of the current rule. """Return a short string representation of the current rule.
""" """
return u'[%(type)s] %(src)s ▸ %(dst)s %(para)s %(desc)s' % { return u'[%(type)s] %(src)s ▸ %(dst)s %(para)s %(desc)s' % {
'type': self.r_type, 'type': self.r_type,
'src': (unicode(self.foreign_network) if self.direction == '1' 'src': (unicode(self.foreign_network) if self.direction == 'in'
else self.r_type), else self.r_type),
'dst': (self.r_type if self.direction == '1' 'dst': (self.r_type if self.direction == 'out'
else unicode(self.foreign_network)), else unicode(self.foreign_network)),
'para': ((("proto=%s " % self.proto) if self.proto else '') + 'para': ((("proto=%s " % self.proto) if self.proto else '') +
(("sport=%s " % self.sport) if self.sport else '') + (("sport=%s " % self.sport) if self.sport else '') +
...@@ -147,6 +171,61 @@ class Rule(models.Model): ...@@ -147,6 +171,61 @@ class Rule(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return ('network.rule', None, {'pk': self.pk}) return ('network.rule', None, {'pk': self.pk})
@staticmethod
def get_chain_name(local, remote, direction):
if direction == 'in':
# remote -> local
return '%s_%s' % (remote, local)
else:
# local -> remote
return '%s_%s' % (local, remote)
def get_ipt_rules(self, host=None):
# action
action = 'LOG_ACC' if self.action == 'accept' else 'LOG_DROP'
# src and dst addresses
src = None
dst = None
if host:
ip = (host.ipv4, host.ipv6_with_prefixlen)
if self.direction == 'in':
dst = ip
else:
src = ip
# src and dst ports
if self.direction == 'in':
dport = self.dport
sport = self.sport
else:
dport = self.sport
sport = self.dport
# 'chain_name': rule dict
retval = {}
# process foreign vlans
for foreign_vlan in self.foreign_network.vlans.all():
r = IptRule(priority=self.weight, action=action,
proto=self.proto, extra=self.extra,
comment='Rule #%s' % self.pk,
src=src, dst=dst, dport=dport, sport=sport)
# host, hostgroup or vlan rule
if host or self.vlan_id:
local_vlan = host.vlan.name if host else self.vlan.name
chain_name = Rule.get_chain_name(local=local_vlan,
remote=foreign_vlan.name,
direction=self.direction)
# firewall rule
elif self.firewall_id:
chain_name = 'INPUT' if self.direction == 'in' else 'OUTPUT'
retval[chain_name] = r
return retval
class Meta: class Meta:
verbose_name = _("rule") verbose_name = _("rule")
verbose_name_plural = _("rules") verbose_name_plural = _("rules")
...@@ -155,7 +234,7 @@ class Rule(models.Model): ...@@ -155,7 +234,7 @@ class Rule(models.Model):
'proto', 'proto',
'sport', 'sport',
'dport', 'dport',
'nat_dport', 'nat_external_port',
'host', 'host',
) )
...@@ -177,7 +256,7 @@ class Vlan(AclBase, models.Model): ...@@ -177,7 +256,7 @@ class Vlan(AclBase, models.Model):
('user', _('user')), ('user', _('user')),
('operator', _('operator')), ('operator', _('operator')),
) )
CHOICES_NETWORK_TYPE = (('public', _('public')), ('dmz', _('dmz')), CHOICES_NETWORK_TYPE = (('public', _('public')),
('portforward', _('portforward'))) ('portforward', _('portforward')))
vid = models.IntegerField(unique=True, vid = models.IntegerField(unique=True,
verbose_name=_('VID'), verbose_name=_('VID'),
...@@ -199,6 +278,12 @@ class Vlan(AclBase, models.Model): ...@@ -199,6 +278,12 @@ class Vlan(AclBase, models.Model):
'valid address of the subnet, ' 'valid address of the subnet, '
'for example ' 'for example '
'10.4.255.254/16 for 10.4.0.0/16.')) '10.4.255.254/16 for 10.4.0.0/16.'))
host_ipv6_prefixlen = models.IntegerField(
verbose_name=_('IPv6 prefixlen/host'),
help_text=_('The prefix length of the subnet assigned to a host. '
'For example /112 = 65536 addresses/host.'),
default=112,
validators=[MinValueValidator(1), MaxValueValidator(128)])
network6 = IPNetworkField(unique=False, network6 = IPNetworkField(unique=False,
version=6, version=6,
null=True, null=True,
...@@ -226,6 +311,7 @@ class Vlan(AclBase, models.Model): ...@@ -226,6 +311,7 @@ class Vlan(AclBase, models.Model):
'of NAT IP address.')) 'of NAT IP address.'))
network_type = models.CharField(choices=CHOICES_NETWORK_TYPE, network_type = models.CharField(choices=CHOICES_NETWORK_TYPE,
verbose_name=_('network type'), verbose_name=_('network type'),
default='portforward',
max_length=20) max_length=20)
managed = models.BooleanField(default=True, verbose_name=_('managed')) managed = models.BooleanField(default=True, verbose_name=_('managed'))
description = models.TextField(blank=True, verbose_name=_('description'), description = models.TextField(blank=True, verbose_name=_('description'),
...@@ -275,30 +361,6 @@ class Vlan(AclBase, models.Model): ...@@ -275,30 +361,6 @@ class Vlan(AclBase, models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return ('network.vlan', None, {'vid': self.vid}) return ('network.vlan', None, {'vid': self.vid})
@property
def net4(self):
return self.network4.network
@property
def ipv4(self):
return self.network4.ip
@property
def prefix4(self):
return self.network4.prefixlen
@property
def net6(self):
return self.network6.network
@property
def ipv6(self):
return self.network6.ip
@property
def prefix6(self):
return self.network6.prefixlen
def get_random_addresses(self, used_v4, buffer_size=100, max_hosts=10000): def get_random_addresses(self, used_v4, buffer_size=100, max_hosts=10000):
addresses = islice(self.network4.iter_hosts(), max_hosts) addresses = islice(self.network4.iter_hosts(), max_hosts)
unused_addresses = list(islice( unused_addresses = list(islice(
...@@ -403,7 +465,7 @@ class Host(models.Model): ...@@ -403,7 +465,7 @@ class Host(models.Model):
verbose_name=_('IPv4 address'), verbose_name=_('IPv4 address'),
help_text=_('The real IPv4 address of the ' help_text=_('The real IPv4 address of the '
'host, for example 10.5.1.34.')) 'host, for example 10.5.1.34.'))
pub_ipv4 = IPAddressField( external_ipv4 = IPAddressField(
version=4, blank=True, null=True, version=4, blank=True, null=True,
verbose_name=_('WAN IPv4 address'), verbose_name=_('WAN IPv4 address'),
help_text=_('The public IPv4 address of the host on the wide ' help_text=_('The public IPv4 address of the host on the wide '
...@@ -449,18 +511,31 @@ class Host(models.Model): ...@@ -449,18 +511,31 @@ class Host(models.Model):
@property @property
def incoming_rules(self): def incoming_rules(self):
return self.rules.filter(direction='1') return self.rules.filter(direction='in')
@property @property
def outgoing_rules(self): def ipv6_with_prefixlen(self):
return self.rules.filter(direction='0') try:
net = IPNetwork(self.ipv6)
net.prefixlen = self.vlan.host_ipv6_prefixlen
return net
except TypeError:
return None
def get_external_ipv4(self):
return self.external_ipv4 if self.external_ipv4 else self.ipv4
@property
def behind_nat(self):
return self.vlan.network_type != 'public'
def clean(self): def clean(self):
if (not self.shared_ip and self.pub_ipv4 and Host.objects. if (self.external_ipv4 and not self.shared_ip and self.behind_nat
exclude(id=self.id).filter(pub_ipv4=self.pub_ipv4)): and Host.objects.exclude(id=self.id).filter(
external_ipv4=self.external_ipv4)):
raise ValidationError(_("If shared_ip has been checked, " raise ValidationError(_("If shared_ip has been checked, "
"pub_ipv4 has to be unique.")) "external_ipv4 has to be unique."))
if Host.objects.exclude(id=self.id).filter(pub_ipv4=self.ipv4): if Host.objects.exclude(id=self.id).filter(external_ipv4=self.ipv4):
raise ValidationError(_("You can't use another host's NAT'd " raise ValidationError(_("You can't use another host's NAT'd "
"address as your own IPv4.")) "address as your own IPv4."))
...@@ -472,36 +547,36 @@ class Host(models.Model): ...@@ -472,36 +547,36 @@ class Host(models.Model):
super(Host, self).save(*args, **kwargs) super(Host, self).save(*args, **kwargs)
# IPv4
if self.ipv4 is not None: if self.ipv4 is not None:
Record.objects.filter(host=self, name=self.hostname, # update existing records
affected_records = Record.objects.filter(
host=self, name=self.hostname,
type='A').update(address=self.ipv4) type='A').update(address=self.ipv4)
record_count = self.record_set.filter(host=self, # create new record
name=self.hostname, if affected_records == 0:
address=self.ipv4,
type='A').count()
if record_count == 0:
Record(host=self, Record(host=self,
name=self.hostname, name=self.hostname,
domain=self.vlan.domain, domain=self.vlan.domain,
address=self.ipv4, address=self.ipv4,
owner=self.owner, owner=self.owner,
description='host.save()', description='created by host.save()',
type='A').save() type='A').save()
if self.ipv6: # IPv6
Record.objects.filter(host=self, name=self.hostname, if self.ipv6 is not None:
# update existing records
affected_records = Record.objects.filter(
host=self, name=self.hostname,
type='AAAA').update(address=self.ipv6) type='AAAA').update(address=self.ipv6)
record_count = self.record_set.filter(host=self, # create new record
name=self.hostname, if affected_records == 0:
address=self.ipv6,
type='AAAA').count()
if record_count == 0:
Record(host=self, Record(host=self,
name=self.hostname, name=self.hostname,
domain=self.vlan.domain, domain=self.vlan.domain,
address=self.ipv6, address=self.ipv6,
owner=self.owner, owner=self.owner,
description='host.save()', description='created by host.save()',
type='AAAA').save() type='AAAA').save()
def enable_net(self): def enable_net(self):
...@@ -517,12 +592,15 @@ class Host(models.Model): ...@@ -517,12 +592,15 @@ class Host(models.Model):
:type proto: str. :type proto: str.
:returns: list -- list of int port numbers used. :returns: list -- list of int port numbers used.
""" """
if self.shared_ip: if self.behind_nat:
ports = Rule.objects.filter(host__pub_ipv4=self.pub_ipv4, ports = Rule.objects.filter(
nat=True, proto=proto) host__external_ipv4=self.external_ipv4,
nat=True,
proto=proto).values_list('nat_external_port', flat=True)
else: else:
ports = self.rules.filter(proto=proto, ) ports = self.rules.filter(proto=proto).values_list(
return set(ports.values_list('dport', flat=True)) 'dport', flat=True)
return set(ports)
def _get_random_port(self, proto, used_ports=None): def _get_random_port(self, proto, used_ports=None):
""" """
...@@ -577,17 +655,15 @@ class Host(models.Model): ...@@ -577,17 +655,15 @@ class Host(models.Model):
logger.error('Host.add_port: default_vlangroup %s missing. %s', logger.error('Host.add_port: default_vlangroup %s missing. %s',
vgname, unicode(e)) vgname, unicode(e))
else: else:
if self.shared_ip: rule = Rule(direction='in', owner=self.owner, dport=private,
proto=proto, nat=False, action='accept',
host=self, foreign_network=vg)
if self.behind_nat:
if public < 1024: if public < 1024:
raise ValidationError( raise ValidationError(
_("Only ports above 1024 can be used.")) _("Only ports above 1024 can be used."))
rule = Rule(direction='1', owner=self.owner, dport=public, rule.nat_external_port = public
proto=proto, nat=True, accept=True, rule.nat = True
nat_dport=private, host=self, foreign_network=vg)
else:
rule = Rule(direction='1', owner=self.owner, dport=private,
proto=proto, nat=False, accept=True,
host=self, foreign_network=vg)
rule.full_clean() rule.full_clean()
rule.save() rule.save()
...@@ -602,10 +678,6 @@ class Host(models.Model): ...@@ -602,10 +678,6 @@ class Host(models.Model):
:param private: Port number of host in subject. :param private: Port number of host in subject.
""" """
if self.shared_ip:
self.rules.filter(owner=self.owner, proto=proto, host=self,
nat_dport=private).delete()
else:
self.rules.filter(owner=self.owner, proto=proto, host=self, self.rules.filter(owner=self.owner, proto=proto, host=self,
dport=private).delete() dport=private).delete()
...@@ -622,11 +694,11 @@ class Host(models.Model): ...@@ -622,11 +694,11 @@ class Host(models.Model):
res = self.record_set.filter(type='AAAA', res = self.record_set.filter(type='AAAA',
address=self.ipv6) address=self.ipv6)
elif proto == 'ipv4': elif proto == 'ipv4':
if self.shared_ip and public: if self.behind_nat and public:
res = Record.objects.filter(type='A', res = Record.objects.filter(
address=self.pub_ipv4) type='A', address=self.get_external_ipv4())
if res.count() < 1: if res.count() < 1:
return unicode(self.pub_ipv4) return unicode(self.get_external_ipv4())
else: else:
res = self.record_set.filter(type='A', res = self.record_set.filter(type='A',
address=self.ipv4) address=self.ipv4)
...@@ -640,27 +712,21 @@ class Host(models.Model): ...@@ -640,27 +712,21 @@ class Host(models.Model):
""" """
retval = [] retval = []
for rule in self.rules.filter(owner=self.owner): for rule in self.rules.filter(owner=self.owner):
private = rule.nat_dport if self.shared_ip else rule.dport
forward = { forward = {
'proto': rule.proto, 'proto': rule.proto,
'private': private, 'private': rule.dport,
} }
if self.shared_ip:
public4 = rule.dport
public6 = rule.nat_dport
else:
public4 = public6 = rule.dport
if True: # ipv4 if True: # ipv4
forward['ipv4'] = { forward['ipv4'] = {
'host': self.get_hostname(proto='ipv4'), 'host': self.get_hostname(proto='ipv4'),
'port': public4, 'port': rule.get_external_port(proto='ipv4'),
'pk': rule.pk, 'pk': rule.pk,
} }
if self.ipv6: # ipv6 if self.ipv6: # ipv6
forward['ipv6'] = { forward['ipv6'] = {
'host': self.get_hostname(proto='ipv6'), 'host': self.get_hostname(proto='ipv6'),
'port': public6, 'port': rule.get_external_port(proto='ipv6'),
'pk': rule.pk, 'pk': rule.pk,
} }
retval.append(forward) retval.append(forward)
...@@ -679,22 +745,16 @@ class Host(models.Model): ...@@ -679,22 +745,16 @@ class Host(models.Model):
""" """
endpoints = {} endpoints = {}
# IPv4 # IPv4
public_ipv4 = self.pub_ipv4 if self.pub_ipv4 else self.ipv4 ports = self.incoming_rules.filter(action='accept', dport=port,
# try get matching port(s) without NAT proto=protocol)
ports = self.incoming_rules.filter(accept=True, dport=port, public_port = (ports[0].get_external_port(proto='ipv4')
nat=False, proto=protocol) if ports.exists() else None)
if ports.exists(): endpoints['ipv4'] = ((self.get_external_ipv4(), public_port)
public_port = ports[0].dport if public_port else
else:
# try get matching port(s) with NAT
ports = self.incoming_rules.filter(accept=True, nat_dport=port,
nat=True, proto=protocol)
public_port = ports[0].dport if ports.exists() else None
endpoints['ipv4'] = ((public_ipv4, public_port) if public_port else
None) None)
# IPv6 # IPv6
blocked = self.incoming_rules.filter(accept=False, dport=port, blocked = self.incoming_rules.exclude(
proto=protocol).exists() action='accept').filter(dport=port, proto=protocol).exists()
endpoints['ipv6'] = (self.ipv6, port) if not blocked else None endpoints['ipv6'] = (self.ipv6, port) if not blocked else None
return endpoints return endpoints
...@@ -863,7 +923,7 @@ class EthernetDevice(models.Model): ...@@ -863,7 +923,7 @@ class EthernetDevice(models.Model):
return self.name return self.name
class Blacklist(models.Model): class BlacklistItem(models.Model):
CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'), CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'),
('whitelist', 'whitelist'), ('tempwhite', 'tempwhite')) ('whitelist', 'whitelist'), ('tempwhite', 'tempwhite'))
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True) ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True)
...@@ -885,11 +945,15 @@ class Blacklist(models.Model): ...@@ -885,11 +945,15 @@ class Blacklist(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.full_clean() self.full_clean()
super(Blacklist, self).save(*args, **kwargs) super(BlacklistItem, self).save(*args, **kwargs)
def __unicode__(self): def __unicode__(self):
return self.ipv4 return self.ipv4
class Meta(object):
verbose_name = _('blacklist item')
verbose_name_plural = _('blacklist')
@models.permalink @models.permalink
def get_absolute_url(self): def get_absolute_url(self):
return ('network.blacklist', None, {'pk': self.pk}) return ('network.blacklist', None, {'pk': self.pk})
...@@ -899,7 +963,7 @@ def send_task(sender, instance, created=False, **kwargs): ...@@ -899,7 +963,7 @@ def send_task(sender, instance, created=False, **kwargs):
reloadtask.apply_async(queue='localhost.man', args=[sender.__name__]) reloadtask.apply_async(queue='localhost.man', args=[sender.__name__])
for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group, Blacklist, for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group,
SwitchPort, EthernetDevice]: BlacklistItem, SwitchPort, EthernetDevice]:
post_save.connect(send_task, sender=sender) post_save.connect(send_task, sender=sender)
post_delete.connect(send_task, sender=sender) post_delete.connect(send_task, sender=sender)
...@@ -26,7 +26,7 @@ def _apply_once(name, queues, task, data): ...@@ -26,7 +26,7 @@ def _apply_once(name, queues, task, data):
@celery.task(ignore_result=True) @celery.task(ignore_result=True)
def periodic_task(): def periodic_task():
from firewall.fw import Firewall, dhcp, dns, ipset, vlan from firewall.fw import BuildFirewall, dhcp, dns, ipset, vlan
from remote_tasks import (reload_dns, reload_dhcp, reload_firewall, from remote_tasks import (reload_dns, reload_dhcp, reload_firewall,
reload_firewall_vlan, reload_blacklist) reload_firewall_vlan, reload_blacklist)
...@@ -40,7 +40,7 @@ def periodic_task(): ...@@ -40,7 +40,7 @@ def periodic_task():
_apply_once('dhcp', firewall_queues, reload_dhcp, _apply_once('dhcp', firewall_queues, reload_dhcp,
lambda: (dhcp(), )) lambda: (dhcp(), ))
_apply_once('firewall', firewall_queues, reload_firewall, _apply_once('firewall', firewall_queues, reload_firewall,
lambda: (Firewall(proto=4).get(), Firewall(proto=6).get())) lambda: (BuildFirewall().build_ipt()))
_apply_once('firewall_vlan', firewall_queues, reload_firewall_vlan, _apply_once('firewall_vlan', firewall_queues, reload_firewall_vlan,
lambda: (vlan(), )) lambda: (vlan(), ))
_apply_once('blacklist', firewall_queues, reload_blacklist, _apply_once('blacklist', firewall_queues, reload_blacklist,
...@@ -48,7 +48,7 @@ def periodic_task(): ...@@ -48,7 +48,7 @@ def periodic_task():
@celery.task @celery.task
def reloadtask(type='Host'): def reloadtask(type='Host', timeout=15):
reload = { reload = {
'Host': ['dns', 'dhcp', 'firewall'], 'Host': ['dns', 'dhcp', 'firewall'],
'Record': ['dns'], 'Record': ['dns'],
......
{% if proto == "ipv4" %}
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
{% for chain in nat %}
{{ chain.compile|safe }}
{% endfor %}
COMMIT
{% endif %}
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
# initialize logging
-N LOG_DROP
# windows port scan are silently dropped
-A LOG_DROP -p tcp --dport 445 -j DROP
-A LOG_DROP -p udp --dport 137 -j DROP
-A LOG_DROP -j LOG --log-level 7 --log-prefix "[ipt][drop]"
-A LOG_DROP -j DROP
-N LOG_ACC
-A LOG_ACC -j LOG --log-level 7 --log-prefix "[ipt][isok]"
-A LOG_ACC -j ACCEPT
# initialize FORWARD chain
-A FORWARD -m set --match-set blacklist src,dst -j DROP
-A FORWARD -m state --state INVALID -g LOG_DROP
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -p icmp --icmp-type echo-request -g LOG_ACC
# initialize INPUT chain
-A INPUT -m set --match-set blacklist src -j DROP
-A INPUT -m state --state INVALID -g LOG_DROP
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# initialize OUTPUT chain
-A OUTPUT -m state --state INVALID -g LOG_DROP
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
{% for chain in filter %}
{% if chain.name not in chain.builtin_chains %}-N {{ chain.name }}{% endif %}
{% if proto == "ipv4" %}
{{ chain.compile|safe }}
{% else %}
{{ chain.compile_v6|safe }}
{% endif %}
{% endfor %}
# close all chains
-A FORWARD -g LOG_DROP
-A INPUT -g LOG_DROP
-A OUTPUT -g LOG_DROP
COMMIT
from netaddr import IPSet from netaddr import IPSet, AddrFormatError
from django.test import TestCase from django.test import TestCase
from django.contrib.auth.models import User from django.contrib.auth.models import User
from ..admin import HostAdmin from ..admin import HostAdmin
from firewall.models import Vlan, Domain, Record, Host from firewall.models import (Vlan, Domain, Record, Host, VlanGroup, Group,
Rule, Firewall)
from firewall.fw import dns, ipv6_to_octal
from firewall.tasks.local_tasks import periodic_task, reloadtask
from django.forms import ValidationError from django.forms import ValidationError
from ..iptables import IptRule, IptChain, InvalidRuleExcepion
from mock import patch
import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS
class MockInstance: class MockInstance:
...@@ -96,12 +104,12 @@ class HostGetHostnameTestCase(TestCase): ...@@ -96,12 +104,12 @@ class HostGetHostnameTestCase(TestCase):
self.vlan.save() self.vlan.save()
self.h = Host(hostname='h', mac='01:02:03:04:05:00', ipv4='10.0.0.1', self.h = Host(hostname='h', mac='01:02:03:04:05:00', ipv4='10.0.0.1',
vlan=self.vlan, owner=self.u1, shared_ip=True, vlan=self.vlan, owner=self.u1, shared_ip=True,
pub_ipv4=self.vlan.snat_ip) external_ipv4=self.vlan.snat_ip)
self.h.save() self.h.save()
def test_issue_93_wo_record(self): def test_issue_93_wo_record(self):
self.assertEqual(self.h.get_hostname(proto='ipv4', public=True), self.assertEqual(self.h.get_hostname(proto='ipv4', public=True),
unicode(self.h.pub_ipv4)) unicode(self.h.external_ipv4))
def test_issue_93_w_record(self): def test_issue_93_w_record(self):
self.r = Record(name='vm', type='A', domain=self.d, owner=self.u1, self.r = Record(name='vm', type='A', domain=self.d, owner=self.u1,
...@@ -109,3 +117,192 @@ class HostGetHostnameTestCase(TestCase): ...@@ -109,3 +117,192 @@ class HostGetHostnameTestCase(TestCase):
self.r.save() self.r.save()
self.assertEqual(self.h.get_hostname(proto='ipv4', public=True), self.assertEqual(self.h.get_hostname(proto='ipv4', public=True),
self.r.fqdn) self.r.fqdn)
class IptablesTestCase(TestCase):
def setUp(self):
self.r = [IptRule(priority=4, action='ACCEPT',
src=('127.0.0.4', None)),
IptRule(priority=4, action='ACCEPT',
src=('127.0.0.4', None)),
IptRule(priority=2, action='ACCEPT',
dst=('127.0.0.2', None),
extra='-p icmp'),
IptRule(priority=6, action='ACCEPT',
dst=('127.0.0.6', None),
proto='tcp', dport=80),
IptRule(priority=1, action='ACCEPT',
dst=('127.0.0.1', None),
proto='udp', dport=53),
IptRule(priority=5, action='ACCEPT',
dst=('127.0.0.5', None),
proto='tcp', dport=443),
IptRule(priority=2, action='ACCEPT',
dst=('127.0.0.2', None),
proto='icmp'),
IptRule(priority=6, action='ACCEPT',
dst=('127.0.0.6', None),
proto='tcp', dport='1337')]
def test_chain_add(self):
ch = IptChain(name='test')
ch.add(*self.r)
self.assertEqual(len(ch), len(self.r) - 1)
def test_rule_compile_ok(self):
assert unicode(self.r[5])
self.assertEqual(self.r[5].compile(),
'-d 127.0.0.5 -p tcp --dport 443 -g ACCEPT')
def test_rule_compile_fail(self):
self.assertRaises(InvalidRuleExcepion,
IptRule, **{'proto': 'test'})
self.assertRaises(InvalidRuleExcepion,
IptRule, **{'priority': 5, 'action': 'ACCEPT',
'dst': '127.0.0.5',
'proto': 'icmp', 'dport': 443})
def test_chain_compile(self):
ch = IptChain(name='test')
ch.add(*self.r)
compiled = ch.compile()
compiled_v6 = ch.compile_v6()
assert unicode(ch)
self.assertEqual(len(compiled.splitlines()), len(ch))
self.assertEqual(len(compiled_v6.splitlines()), 0)
class ReloadTestCase(TestCase):
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.u1.save()
d = Domain.objects.create(name='example.org', owner=self.u1)
self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/29',
snat_ip='152.66.243.99',
network6='2001:738:2001:4031::/80', domain=d,
owner=self.u1, network_type='portforward',
dhcp_pool='manual')
self.vlan.save()
self.vlan2 = Vlan(vid=2, name='pub', network4='10.1.0.0/29',
network6='2001:738:2001:4032::/80', domain=d,
owner=self.u1, network_type='public')
self.vlan2.save()
self.vlan.snat_to.add(self.vlan2)
settings["default_vlangroup"] = 'public'
settings["default_host_groups"] = ['netezhet']
vlg = VlanGroup.objects.create(name='public')
vlg.vlans.add(self.vlan, self.vlan2)
self.hg = Group.objects.create(name='netezhet')
Rule.objects.create(action='accept', hostgroup=self.hg,
foreign_network=vlg)
firewall = Firewall.objects.create(name='fw')
Rule.objects.create(action='accept', firewall=firewall,
foreign_network=vlg)
for i in range(1, 6):
h = Host.objects.create(hostname='h-%d' % i, vlan=self.vlan,
mac='01:02:03:04:05:%02d' % i,
ipv4='10.0.0.%d' % i, owner=self.u1)
h.enable_net()
h.groups.add(self.hg)
if i == 5:
h.vlan = self.vlan2
h.save()
self.h5 = h
if i == 1:
self.h1 = h
self.r1 = Record(name='tst', type='A', address='127.0.0.1',
domain=d, owner=self.u1)
self.rb = Record(name='tst', type='AAAA', address='1.0.0.1',
domain=d, owner=self.u1)
self.r2 = Record(name='ts', type='AAAA', address='2001:123:45::6',
domain=d, owner=self.u1)
self.rm = Record(name='asd', type='MX', address='10:teszthu',
domain=d, owner=self.u1)
self.rt = Record(name='asd', type='TXT', address='ASD',
domain=d, owner=self.u1)
self.r1.save()
self.r2.save()
with patch('firewall.models.Record.clean'):
self.rb.save()
self.rm.save()
self.rt.save()
def test_bad_aaaa_record(self):
self.assertRaises(AddrFormatError, ipv6_to_octal, self.rb.address)
def test_good_aaaa_record(self):
ipv6_to_octal(self.r2.address)
def test_dns_func(self):
records = dns()
self.assertEqual(Host.objects.count() * 2 + # soa
len((self.r1, self.r2, self.rm, self.rt)) + 1,
len(records))
def test_host_add_port(self):
h = self.h1
h.ipv6 = '2001:2:3:4::0'
assert h.behind_nat
h.save()
old_rules = h.rules.count()
h.add_port('tcp', private=22)
new_rules = h.rules.count()
self.assertEqual(new_rules, old_rules + 1)
self.assertEqual(len(h.list_ports()), old_rules + 1)
endp = h.get_public_endpoints(22)
self.assertEqual(endp['ipv4'][0], h.ipv4)
assert int(endp['ipv4'][1])
self.assertEqual(endp['ipv6'][0], h.ipv6)
assert int(endp['ipv6'][1])
def test_host_add_port2(self):
h = self.h5
h.ipv6 = '2001:2:3:4::1'
h.save()
assert not h.behind_nat
old_rules = h.rules.count()
h.add_port('tcp', private=22)
new_rules = h.rules.count()
self.assertEqual(new_rules, old_rules + 1)
self.assertEqual(len(h.list_ports()), old_rules + 1)
endp = h.get_public_endpoints(22)
self.assertEqual(endp['ipv4'][0], h.ipv4)
assert int(endp['ipv4'][1])
self.assertEqual(endp['ipv6'][0], h.ipv6)
assert int(endp['ipv6'][1])
def test_host_del_port(self):
h = self.h1
h.ipv6 = '2001:2:3:4::0'
h.save()
h.add_port('tcp', private=22)
old_rules = h.rules.count()
h.del_port('tcp', private=22)
new_rules = h.rules.count()
self.assertEqual(new_rules, old_rules - 1)
def test_host_add_port_wo_vlangroup(self):
VlanGroup.objects.filter(name='public').delete()
h = self.h1
old_rules = h.rules.count()
h.add_port('tcp', private=22)
new_rules = h.rules.count()
self.assertEqual(new_rules, old_rules)
def test_host_add_port_w_validationerror(self):
h = self.h1
self.assertRaises(ValidationError, h.add_port,
'tcp', public=1000, private=22)
def test_periodic_task(self):
#TODO
with patch('firewall.tasks.local_tasks.cache') as cache:
self.test_host_add_port()
self.test_host_add_port2()
periodic_task()
reloadtask()
assert cache.delete.called
...@@ -11,7 +11,7 @@ from django.views.decorators.csrf import csrf_exempt ...@@ -11,7 +11,7 @@ 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 .tasks.local_tasks import reloadtask from .tasks.local_tasks import reloadtask
from .models import Blacklist, Host from .models import BlacklistItem, Host
def reload_firewall(request): def reload_firewall(request):
...@@ -38,7 +38,7 @@ def firewall_api(request): ...@@ -38,7 +38,7 @@ def firewall_api(request):
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 = BlacklistItem.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:
......
...@@ -5,7 +5,7 @@ from crispy_forms.helper import FormHelper ...@@ -5,7 +5,7 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput
from crispy_forms.bootstrap import FormActions from crispy_forms.bootstrap import FormActions
from firewall.models import (Host, Vlan, Domain, Group, Record, Blacklist, from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem,
Rule, VlanGroup, SwitchPort) Rule, VlanGroup, SwitchPort)
...@@ -26,7 +26,7 @@ class LinkButton(BaseInput): ...@@ -26,7 +26,7 @@ class LinkButton(BaseInput):
super(LinkButton, self).__init__(name, text, *args, **kwargs) super(LinkButton, self).__init__(name, text, *args, **kwargs)
class BlacklistForm(ModelForm): class BlacklistItemForm(ModelForm):
helper = FormHelper() helper = FormHelper()
helper.layout = Layout( helper.layout = Layout(
Div( Div(
...@@ -45,7 +45,7 @@ class BlacklistForm(ModelForm): ...@@ -45,7 +45,7 @@ class BlacklistForm(ModelForm):
) )
class Meta: class Meta:
model = Blacklist model = BlacklistItem
class DomainForm(ModelForm): class DomainForm(ModelForm):
...@@ -106,7 +106,7 @@ class HostForm(ModelForm): ...@@ -106,7 +106,7 @@ class HostForm(ModelForm):
'ipv4', 'ipv4',
'ipv6', 'ipv6',
'shared_ip', 'shared_ip',
'pub_ipv4', 'external_ipv4',
), ),
Fieldset( Fieldset(
'Information', 'Information',
...@@ -162,12 +162,14 @@ class RuleForm(ModelForm): ...@@ -162,12 +162,14 @@ class RuleForm(ModelForm):
'foreign_network', 'foreign_network',
'dport', 'dport',
'sport', 'sport',
'weight',
'proto', 'proto',
'extra', 'extra',
'accept', 'action',
'owner', 'owner',
'nat', 'nat',
'nat_dport', 'nat_external_port',
'nat_external_ipv4',
), ),
Fieldset( Fieldset(
'External', 'External',
...@@ -232,6 +234,7 @@ class VlanForm(ModelForm): ...@@ -232,6 +234,7 @@ class VlanForm(ModelForm):
'IPv6', 'IPv6',
'network6', 'network6',
'ipv6_template', 'ipv6_template',
'host_ipv6_prefixlen',
), ),
Fieldset( Fieldset(
'Domain name service', 'Domain name service',
......
...@@ -4,7 +4,7 @@ from django_tables2.columns import LinkColumn, TemplateColumn ...@@ -4,7 +4,7 @@ from django_tables2.columns import LinkColumn, TemplateColumn
from firewall.models import Host, Vlan, Domain, Group, Record, Rule, SwitchPort from firewall.models import Host, Vlan, Domain, Group, Record, Rule, SwitchPort
class BlacklistTable(Table): class BlacklistItemTable(Table):
ipv4 = LinkColumn('network.blacklist', args=[A('pk')]) ipv4 = LinkColumn('network.blacklist', args=[A('pk')])
class Meta: class Meta:
...@@ -44,7 +44,7 @@ class HostTable(Table): ...@@ -44,7 +44,7 @@ class HostTable(Table):
model = Host model = Host
attrs = {'class': 'table table-striped table-condensed'} attrs = {'class': 'table table-striped table-condensed'}
fields = ('hostname', 'vlan', 'mac', 'ipv4', 'ipv6', fields = ('hostname', 'vlan', 'mac', 'ipv4', 'ipv6',
'pub_ipv4', 'created_at', 'owner', ) 'external_ipv4', 'created_at', 'owner', )
order_by = ('vlan', 'hostname', ) order_by = ('vlan', 'hostname', )
...@@ -128,7 +128,8 @@ class RuleTable(Table): ...@@ -128,7 +128,8 @@ class RuleTable(Table):
model = Rule model = Rule
attrs = {'class': 'table table-striped table-hover table-condensed'} attrs = {'class': 'table table-striped table-hover table-condensed'}
fields = ('r_type', 'color_desc', 'owner', 'extra', 'direction', fields = ('r_type', 'color_desc', 'owner', 'extra', 'direction',
'accept', 'proto', 'sport', 'dport', 'nat', 'nat_dport', ) 'action', 'proto', 'sport', 'dport', 'nat',
'nat_external_port', )
order_by = 'direction' order_by = 'direction'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
{% block content %} {% block content %}
<div class="page-header"> <div class="page-header">
<h2>{% trans "Create a blacklist" %}</h2> <h2>{% trans "Create a blacklist item" %}</h2>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
{% block content %} {% block content %}
<div class="page-header"> <div class="page-header">
<a href="{% url "network.blacklist_delete" pk=blacklist_pk %}" class="btn btn-danger pull-right"><i class="icon-remove-sign"></i> {% trans "Delete this blaclist" %}</a> <a href="{% url "network.blacklist_delete" pk=blacklist_pk %}" class="btn btn-danger pull-right"><i class="icon-remove-sign"></i> {% trans "Delete this blaclist item" %}</a>
<h2>{{ form.ipv4.value }} <small>{{ form.type.value }}</small></h2> <h2>{{ form.ipv4.value }} <small>{{ form.type.value }}</small></h2>
</div> </div>
<div class="row"> <div class="row">
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
{% block content %} {% block content %}
<div class="page-header"> <div class="page-header">
<a href="{% url "network.blacklist_create" %}" class="btn btn-success pull-right"><i class="icon-plus-sign"></i> {% trans "Create a new blacklist" %}</a> <a href="{% url "network.blacklist_create" %}" class="btn btn-success pull-right"><i class="icon-plus-sign"></i> {% trans "Create a new blacklist item" %}</a>
<h1>{% trans "Blacklists" %} <small></small></h1> <h1>{% trans "Blacklist" %} <small></small></h1>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
......
...@@ -33,5 +33,6 @@ ...@@ -33,5 +33,6 @@
{% if record.nat %} {% if record.nat %}
<span class="label label-success">NAT <span class="label label-success">NAT
[ {{ record.dport }} <i class="icon-arrow-right"></i> {{record.nat_dport}} ]</span> [ {{ record.dport }} <i class="icon-arrow-right"></i>
{{record.nat_external_port}} ]</span>
{% endif %} {% endif %}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
{% trans "Records" as t %} {% trans "Records" as t %}
{% include "network/menu-item.html" with href=u text=t %} {% include "network/menu-item.html" with href=u text=t %}
{% url "network.blacklist_list" as u %} {% url "network.blacklist_list" as u %}
{% trans "Blacklists" as t %} {% trans "Blacklist" as t %}
{% include "network/menu-item.html" with href=u text=t %} {% include "network/menu-item.html" with href=u text=t %}
{% url "network.rule_list" as u %} {% url "network.rule_list" as u %}
{% trans "Rules" as t %} {% trans "Rules" as t %}
...@@ -43,4 +43,4 @@ ...@@ -43,4 +43,4 @@
{# <li><a href="/firewalls/">{% trans "Firewalls" %}</a></li> #} {# <li><a href="/firewalls/">{% trans "Firewalls" %}</a></li> #}
{# <li><a href="/domains/">{% trans "Domains" %}</a></li> #} {# <li><a href="/domains/">{% trans "Domains" %}</a></li> #}
{# <li><a href="/records/">{% trans "DNS records" %}</a></li> #} {# <li><a href="/records/">{% trans "DNS records" %}</a></li> #}
{# <li><a href="/blacklists/">{% trans "Blacklists" %}</a></li> #} {# <li><a href="/blacklist/">{% trans "Blacklist" %}</a></li> #}
...@@ -18,13 +18,13 @@ from .views import (IndexView, ...@@ -18,13 +18,13 @@ from .views import (IndexView,
urlpatterns = patterns( urlpatterns = patterns(
'', '',
url('^$', IndexView.as_view(), name='network.index'), url('^$', IndexView.as_view(), name='network.index'),
url('^blacklists/$', BlacklistList.as_view(), url('^blacklist/$', BlacklistList.as_view(),
name='network.blacklist_list'), name='network.blacklist_list'),
url('^blacklists/create$', BlacklistCreate.as_view(), url('^blacklist/create$', BlacklistCreate.as_view(),
name='network.blacklist_create'), name='network.blacklist_create'),
url('^blacklists/(?P<pk>\d+)/$', BlacklistDetail.as_view(), url('^blacklist/(?P<pk>\d+)/$', BlacklistDetail.as_view(),
name='network.blacklist'), name='network.blacklist'),
url('^blacklists/delete/(?P<pk>\d+)/$', BlacklistDelete.as_view(), url('^blacklist/delete/(?P<pk>\d+)/$', BlacklistDelete.as_view(),
name="network.blacklist_delete"), name="network.blacklist_delete"),
url('^domains/$', DomainList.as_view(), name='network.domain_list'), url('^domains/$', DomainList.as_view(), name='network.domain_list'),
url('^domains/create$', DomainCreate.as_view(), url('^domains/create$', DomainCreate.as_view(),
......
...@@ -6,15 +6,15 @@ from django.http import HttpResponse ...@@ -6,15 +6,15 @@ from django.http import HttpResponse
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from firewall.models import (Host, Vlan, Domain, Group, Record, Blacklist, from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem,
Rule, VlanGroup, SwitchPort, EthernetDevice) Rule, VlanGroup, SwitchPort, EthernetDevice)
from vm.models import Interface from vm.models import Interface
from .tables import (HostTable, VlanTable, SmallHostTable, DomainTable, from .tables import (HostTable, VlanTable, SmallHostTable, DomainTable,
GroupTable, RecordTable, BlacklistTable, RuleTable, GroupTable, RecordTable, BlacklistItemTable, RuleTable,
VlanGroupTable, SmallRuleTable, SmallGroupRuleTable, VlanGroupTable, SmallRuleTable, SmallGroupRuleTable,
SmallRecordTable, SwitchPortTable) SmallRecordTable, SwitchPortTable)
from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm, from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm,
BlacklistForm, RuleForm, VlanGroupForm, SwitchPortForm) BlacklistItemForm, RuleForm, VlanGroupForm, SwitchPortForm)
from django.contrib import messages from django.contrib import messages
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
...@@ -51,7 +51,8 @@ class IndexView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): ...@@ -51,7 +51,8 @@ class IndexView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
context = super(IndexView, self).get_context_data(**kwargs) context = super(IndexView, self).get_context_data(**kwargs)
size = 13 size = 13
blacklists = Blacklist.objects.all().order_by('-modified_at')[:size] blacklists = BlacklistItem.objects.all().order_by(
'-modified_at')[:size]
domains = Domain.objects.all().order_by('-modified_at')[:size] domains = Domain.objects.all().order_by('-modified_at')[:size]
groups = Group.objects.all().order_by('-modified_at')[:size] groups = Group.objects.all().order_by('-modified_at')[:size]
hosts = Host.objects.all().order_by('-modified_at')[:size] hosts = Host.objects.all().order_by('-modified_at')[:size]
...@@ -80,18 +81,18 @@ class IndexView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): ...@@ -80,18 +81,18 @@ class IndexView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
class BlacklistList(LoginRequiredMixin, SuperuserRequiredMixin, class BlacklistList(LoginRequiredMixin, SuperuserRequiredMixin,
SingleTableView): SingleTableView):
model = Blacklist model = BlacklistItem
table_class = BlacklistTable table_class = BlacklistItemTable
template_name = "network/blacklist-list.html" template_name = "network/blacklist-list.html"
table_pagination = False table_pagination = False
class BlacklistDetail(LoginRequiredMixin, SuperuserRequiredMixin, class BlacklistDetail(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, UpdateView): SuccessMessageMixin, UpdateView):
model = Blacklist model = BlacklistItem
template_name = "network/blacklist-edit.html" template_name = "network/blacklist-edit.html"
form_class = BlacklistForm form_class = BlacklistItemForm
success_message = _(u'Successfully modified blacklist ' success_message = _(u'Successfully modified blacklist item'
'%(ipv4)s - %(type)s!') '%(ipv4)s - %(type)s!')
def get_success_url(self): def get_success_url(self):
...@@ -106,22 +107,22 @@ class BlacklistDetail(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -106,22 +107,22 @@ class BlacklistDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class BlacklistCreate(LoginRequiredMixin, SuperuserRequiredMixin, class BlacklistCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView): SuccessMessageMixin, CreateView):
model = Blacklist model = BlacklistItem
template_name = "network/blacklist-create.html" template_name = "network/blacklist-create.html"
form_class = BlacklistForm form_class = BlacklistItemForm
success_message = _(u'Successfully created blacklist ' success_message = _(u'Successfully created blacklist item '
'%(ipv4)s - %(type)s!') '%(ipv4)s - %(type)s!')
class BlacklistDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): class BlacklistDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
model = Blacklist model = BlacklistItem
template_name = "network/confirm/base_delete.html" template_name = "network/confirm/base_delete.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" display more information about the object """ """ display more information about the object """
context = super(BlacklistDelete, self).get_context_data(**kwargs) context = super(BlacklistDelete, self).get_context_data(**kwargs)
if 'pk' in self.kwargs: if 'pk' in self.kwargs:
to_delete = Blacklist.objects.get(pk=self.kwargs['pk']) to_delete = BlacklistItem.objects.get(pk=self.kwargs['pk'])
context['object'] = "%s - %s - %s" % (to_delete.ipv4, context['object'] = "%s - %s - %s" % (to_delete.ipv4,
to_delete.reason, to_delete.reason,
to_delete.type) to_delete.type)
...@@ -503,6 +504,11 @@ class RuleList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): ...@@ -503,6 +504,11 @@ class RuleList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
template_name = "network/rule-list.html" template_name = "network/rule-list.html"
table_pagination = False table_pagination = False
def get_table_data(self):
return Rule.objects.select_related('host', 'hostgroup', 'vlan',
'vlangroup', 'firewall',
'foreign_network', 'owner')
class RuleDetail(LoginRequiredMixin, SuperuserRequiredMixin, class RuleDetail(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, UpdateView): SuccessMessageMixin, UpdateView):
......
...@@ -122,7 +122,7 @@ class Disk(AclBase, TimeStampedModel): ...@@ -122,7 +122,7 @@ class Disk(AclBase, TimeStampedModel):
self.disk = disk self.disk = disk
@property @property
def ready(self): def is_ready(self):
""" Returns True if the disk is physically ready on the storage. """ Returns True if the disk is physically ready on the storage.
It needs at least 1 successfull deploy action. It needs at least 1 successfull deploy action.
...@@ -310,7 +310,7 @@ class Disk(AclBase, TimeStampedModel): ...@@ -310,7 +310,7 @@ class Disk(AclBase, TimeStampedModel):
self.destroyed = None self.destroyed = None
self.save() self.save()
if self.ready: if self.is_ready:
return True return True
with disk_activity(code_suffix='deploy', disk=self, with disk_activity(code_suffix='deploy', disk=self,
task_uuid=task_uuid, user=user) as act: task_uuid=task_uuid, user=user) as act:
...@@ -355,7 +355,14 @@ class Disk(AclBase, TimeStampedModel): ...@@ -355,7 +355,14 @@ class Disk(AclBase, TimeStampedModel):
return disk return disk
@classmethod @classmethod
def create_empty(cls, instance=None, user=None, **kwargs): def create_empty_async(cls, instance=None, user=None, **kwargs):
"""Execute deploy asynchronously.
"""
return local_tasks.create_empty.apply_async(
args=[cls, instance, user, kwargs], queue="localhost.man")
@classmethod
def create_empty(cls, instance=None, user=None, task_uuid=None, **kwargs):
"""Create empty Disk object. """Create empty Disk object.
:param instance: Instance or template attach the Disk to. :param instance: Instance or template attach the Disk to.
...@@ -366,6 +373,7 @@ class Disk(AclBase, TimeStampedModel): ...@@ -366,6 +373,7 @@ class Disk(AclBase, TimeStampedModel):
:return: Disk object without a real image, to be .deploy()ed later. :return: Disk object without a real image, to be .deploy()ed later.
""" """
disk = Disk.create(instance, user, **kwargs) disk = Disk.create(instance, user, **kwargs)
disk.deploy(user=user, task_uuid=task_uuid)
return disk return disk
@classmethod @classmethod
...@@ -466,19 +474,72 @@ class Disk(AclBase, TimeStampedModel): ...@@ -466,19 +474,72 @@ class Disk(AclBase, TimeStampedModel):
local_tasks.restore.apply_async(args=[self, user], local_tasks.restore.apply_async(args=[self, user],
queue='localhost.man') queue='localhost.man')
def save_as_async(self, disk, task_uuid=None, timeout=300, user=None): def clone_async(self, new_disk=None, timeout=300, user=None):
return local_tasks.save_as.apply_async(args=[disk, timeout, user], """Clone a Disk to another Disk
:param new_disk: optional, the new Disk object to clone in
:type new_disk: storage.models.Disk
:param user: Creator of the disk.
:type user: django.contrib.auth.User
:return: AsyncResult
"""
return local_tasks.clone.apply_async(args=[self, new_disk,
timeout, user],
queue="localhost.man") queue="localhost.man")
def clone(self, disk=None, user=None, task_uuid=None, timeout=300):
"""Cloning Disk into another Disk.
The Disk.type can'T be snapshot.
:param new_disk: optional, the new Disk object to clone in
:type new_disk: storage.models.Disk
:param user: Creator of the disk.
:type user: django.contrib.auth.User
:return: the cloned Disk object.
"""
banned_types = ['qcow2-snap']
if self.type in banned_types:
raise self.WrongDiskTypeError(self.type)
if self.is_in_use:
raise self.DiskInUseError(self)
if not self.is_ready:
raise self.DiskIsNotReady(self)
if not disk:
base = None
if self.type == "iso":
base = self
disk = Disk.create(datastore=self.datastore,
name=self.name, size=self.size,
type=self.type, base=base)
with disk_activity(code_suffix="clone", disk=self,
user=user, task_uuid=task_uuid):
with disk_activity(code_suffix="deploy", disk=disk,
user=user, task_uuid=task_uuid):
queue_name = self.get_remote_queue_name('storage')
remote_tasks.merge.apply_async(args=[self.get_disk_desc(),
disk.get_disk_desc()],
queue=queue_name
).get() # Timeout
return disk
def save_as(self, user=None, task_uuid=None, timeout=300): def save_as(self, user=None, task_uuid=None, timeout=300):
"""Save VM as template. """Save VM as template.
Based on disk type:
qcow2-norm, qcow2-snap --> qcow2-norm
iso --> iso (with base)
VM must be in STOPPED state to perform this action. VM must be in STOPPED state to perform this action.
The timeout parameter is not used now. The timeout parameter is not used now.
""" """
mapping = { mapping = {
'qcow2-snap': ('qcow2-norm', self.base), 'qcow2-snap': ('qcow2-norm', None),
'qcow2-norm': ('qcow2-norm', self), 'qcow2-norm': ('qcow2-norm', None),
'iso': ("iso", self),
} }
if self.type not in mapping.keys(): if self.type not in mapping.keys():
raise self.WrongDiskTypeError(self.type) raise self.WrongDiskTypeError(self.type)
...@@ -486,7 +547,7 @@ class Disk(AclBase, TimeStampedModel): ...@@ -486,7 +547,7 @@ class Disk(AclBase, TimeStampedModel):
if self.is_in_use: if self.is_in_use:
raise self.DiskInUseError(self) raise self.DiskInUseError(self)
if not self.ready: if not self.is_ready:
raise self.DiskIsNotReady(self) raise self.DiskIsNotReady(self)
# from this point on, the caller has to guarantee that the disk is not # from this point on, the caller has to guarantee that the disk is not
...@@ -494,7 +555,8 @@ class Disk(AclBase, TimeStampedModel): ...@@ -494,7 +555,8 @@ class Disk(AclBase, TimeStampedModel):
new_type, new_base = mapping[self.type] new_type, new_base = mapping[self.type]
disk = Disk.create(base=new_base, datastore=self.datastore, disk = Disk.create(datastore=self.datastore,
base=new_base,
name=self.name, size=self.size, name=self.name, size=self.size,
type=new_type) type=new_type)
......
...@@ -10,16 +10,14 @@ def check_queue(storage, queue_id): ...@@ -10,16 +10,14 @@ def check_queue(storage, queue_id):
drivers = ['storage', 'download'] drivers = ['storage', 'download']
worker_list = [storage + "." + d for d in drivers] worker_list = [storage + "." + d for d in drivers]
queue_name = storage + "." + queue_id queue_name = storage + "." + queue_id
# v is List of List of queues dict
active_queues = celery.control.inspect(worker_list).active_queues() active_queues = celery.control.inspect(worker_list).active_queues()
if active_queues is not None: if active_queues is None:
node_workers = [v for k, v in active_queues.iteritems()]
for worker in node_workers:
for queue in worker:
if queue['name'] == queue_name:
return True
return False return False
queue_names = (queue['name'] for worker in active_queues.itervalues()
for queue in worker)
return queue_name in queue_names
@celery.task @celery.task
def save_as(disk, timeout, user): def save_as(disk, timeout, user):
...@@ -28,6 +26,12 @@ def save_as(disk, timeout, user): ...@@ -28,6 +26,12 @@ def save_as(disk, timeout, user):
@celery.task @celery.task
def clone(disk, new_disk, timeout, user):
disk.clone(task_uuid=save_as.request.id, user=user,
disk=new_disk, timeout=timeout)
@celery.task
def deploy(disk, user): def deploy(disk, user):
disk.deploy(task_uuid=deploy.request.id, user=user) disk.deploy(task_uuid=deploy.request.id, user=user)
...@@ -57,6 +61,7 @@ create_from_url = CreateFromURLTask() ...@@ -57,6 +61,7 @@ create_from_url = CreateFromURLTask()
@celery.task @celery.task
def create_empty(Disk, instance, params, user): def create_empty(Disk, instance, user, params):
Disk.create_empty(instance, params, user, Disk.create_empty(instance, user,
task_uuid=create_empty.request.id) task_uuid=create_empty.request.id,
**params)
...@@ -10,7 +10,6 @@ logger = logging.getLogger(__name__) ...@@ -10,7 +10,6 @@ logger = logging.getLogger(__name__)
def garbage_collector(timeout=15): def garbage_collector(timeout=15):
""" Garbage collector for disk images. """ Garbage collector for disk images.
Moves 1 day old deleted images to trash folder.
If there is not enough free space on datastore (default 10%) If there is not enough free space on datastore (default 10%)
deletes oldest images from trash. deletes oldest images from trash.
......
...@@ -78,7 +78,7 @@ class DiskTestCase(TestCase): ...@@ -78,7 +78,7 @@ class DiskTestCase(TestCase):
d.DiskIsNotReady = MockException d.DiskIsNotReady = MockException
d.type = "qcow2-norm" d.type = "qcow2-norm"
d.is_in_use = False d.is_in_use = False
d.ready = False d.is_ready = False
with self.assertRaises(MockException): with self.assertRaises(MockException):
Disk.save_as(d) Disk.save_as(d)
...@@ -89,4 +89,4 @@ class DiskTestCase(TestCase): ...@@ -89,4 +89,4 @@ class DiskTestCase(TestCase):
def test_undeployed_disk_ready(self): def test_undeployed_disk_ready(self):
d = self._disk() d = self._disk()
assert not d.ready assert not d.is_ready
# This import is responsible for running the operations' registration code.
from . import operations # noqa
...@@ -5,8 +5,12 @@ from .models import (Instance, InstanceActivity, InstanceTemplate, Interface, ...@@ -5,8 +5,12 @@ from .models import (Instance, InstanceActivity, InstanceTemplate, Interface,
NodeActivity, Trait) NodeActivity, Trait)
class InstanceActivityAdmin(admin.ModelAdmin):
exclude = ('parent', )
admin.site.register(Instance) admin.site.register(Instance)
admin.site.register(InstanceActivity) admin.site.register(InstanceActivity, InstanceActivityAdmin)
admin.site.register(InstanceTemplate) admin.site.register(InstanceTemplate)
admin.site.register(Interface) admin.site.register(Interface)
admin.site.register(InterfaceTemplate) admin.site.register(InterfaceTemplate)
......
...@@ -2,11 +2,16 @@ from __future__ import absolute_import, unicode_literals ...@@ -2,11 +2,16 @@ from __future__ import absolute_import, unicode_literals
from contextlib import contextmanager from contextlib import contextmanager
from logging import getLogger from logging import getLogger
from django.core.urlresolvers import reverse
from django.db.models import CharField, ForeignKey from django.db.models import CharField, ForeignKey
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.models import ActivityModel, activitycontextimpl from common.models import (
ActivityModel, activitycontextimpl, join_activity_code,
)
logger = getLogger(__name__) logger = getLogger(__name__)
...@@ -23,6 +28,7 @@ class ActivityInProgressError(Exception): ...@@ -23,6 +28,7 @@ class ActivityInProgressError(Exception):
class InstanceActivity(ActivityModel): class InstanceActivity(ActivityModel):
ACTIVITY_CODE_BASE = join_activity_code('vm', 'Instance')
instance = ForeignKey('Instance', related_name='activity_log', instance = ForeignKey('Instance', related_name='activity_log',
help_text=_('Instance this activity works on.'), help_text=_('Instance this activity works on.'),
verbose_name=_('instance')) verbose_name=_('instance'))
...@@ -42,9 +48,20 @@ class InstanceActivity(ActivityModel): ...@@ -42,9 +48,20 @@ class InstanceActivity(ActivityModel):
return '{}({})'.format(self.activity_code, return '{}({})'.format(self.activity_code,
self.instance) self.instance)
def get_absolute_url(self):
return reverse('dashboard.views.vm-activity', args=[self.pk])
def get_readable_name(self): def get_readable_name(self):
return self.activity_code.split('.')[-1].replace('_', ' ').capitalize() return self.activity_code.split('.')[-1].replace('_', ' ').capitalize()
def get_status_id(self):
if self.succeeded is None:
return 'wait'
elif self.succeeded:
return 'success'
else:
return 'failed'
@classmethod @classmethod
def create(cls, code_suffix, instance, task_uuid=None, user=None, def create(cls, code_suffix, instance, task_uuid=None, user=None,
concurrency_check=True): concurrency_check=True):
...@@ -53,9 +70,10 @@ class InstanceActivity(ActivityModel): ...@@ -53,9 +70,10 @@ class InstanceActivity(ActivityModel):
if concurrency_check and active_activities.exists(): if concurrency_check and active_activities.exists():
raise ActivityInProgressError(active_activities[0]) raise ActivityInProgressError(active_activities[0])
act = cls(activity_code='vm.Instance.' + code_suffix, activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
instance=instance, parent=None, resultant_state=None, act = cls(activity_code=activity_code, instance=instance, parent=None,
started=timezone.now(), task_uuid=task_uuid, user=user) resultant_state=None, started=timezone.now(),
task_uuid=task_uuid, user=user)
act.save() act.save()
return act return act
...@@ -66,7 +84,7 @@ class InstanceActivity(ActivityModel): ...@@ -66,7 +84,7 @@ class InstanceActivity(ActivityModel):
raise ActivityInProgressError(active_children[0]) raise ActivityInProgressError(active_children[0])
act = InstanceActivity( act = InstanceActivity(
activity_code=self.activity_code + '.' + code_suffix, activity_code=join_activity_code(self.activity_code, code_suffix),
instance=self.instance, parent=self, resultant_state=None, instance=self.instance, parent=self, resultant_state=None,
started=timezone.now(), task_uuid=task_uuid, user=self.user) started=timezone.now(), task_uuid=task_uuid, user=self.user)
act.save() act.save()
...@@ -97,6 +115,7 @@ def instance_activity(code_suffix, instance, on_abort=None, on_commit=None, ...@@ -97,6 +115,7 @@ def instance_activity(code_suffix, instance, on_abort=None, on_commit=None,
class NodeActivity(ActivityModel): class NodeActivity(ActivityModel):
ACTIVITY_CODE_BASE = join_activity_code('vm', 'Node')
node = ForeignKey('Node', related_name='activity_log', node = ForeignKey('Node', related_name='activity_log',
help_text=_('Node this activity works on.'), help_text=_('Node this activity works on.'),
verbose_name=_('node')) verbose_name=_('node'))
...@@ -119,15 +138,15 @@ class NodeActivity(ActivityModel): ...@@ -119,15 +138,15 @@ class NodeActivity(ActivityModel):
@classmethod @classmethod
def create(cls, code_suffix, node, task_uuid=None, user=None): def create(cls, code_suffix, node, task_uuid=None, user=None):
act = cls(activity_code='vm.Node.' + code_suffix, activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
node=node, parent=None, started=timezone.now(), act = cls(activity_code=activity_code, node=node, parent=None,
task_uuid=task_uuid, user=user) started=timezone.now(), task_uuid=task_uuid, user=user)
act.save() act.save()
return act return act
def create_sub(self, code_suffix, task_uuid=None): def create_sub(self, code_suffix, task_uuid=None):
act = NodeActivity( act = NodeActivity(
activity_code=self.activity_code + '.' + code_suffix, activity_code=join_activity_code(self.activity_code, code_suffix),
node=self.node, parent=self, started=timezone.now(), node=self.node, parent=self, started=timezone.now(),
task_uuid=task_uuid, user=self.user) task_uuid=task_uuid, user=self.user)
act.save() act.save()
......
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from datetime import timedelta from datetime import timedelta
from logging import getLogger
from importlib import import_module from importlib import import_module
from logging import getLogger
from string import ascii_lowercase
from warnings import warn from warnings import warn
import string
import django.conf import django.conf
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -16,14 +16,14 @@ from django.dispatch import Signal ...@@ -16,14 +16,14 @@ from django.dispatch import Signal
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from celery.exceptions import TimeLimitExceeded
from model_utils import Choices from model_utils import Choices
from model_utils.models import TimeStampedModel, StatusModel from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from acl.models import AclBase from acl.models import AclBase
from common.operations import OperatedMixin
from storage.models import Disk from storage.models import Disk
from ..tasks import local_tasks, vm_tasks, agent_tasks from ..tasks import vm_tasks, agent_tasks
from .activity import (ActivityInProgressError, instance_activity, from .activity import (ActivityInProgressError, instance_activity,
InstanceActivity) InstanceActivity)
from .common import BaseResourceConfigModel, Lease from .common import BaseResourceConfigModel, Lease
...@@ -151,6 +151,10 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -151,6 +151,10 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
else: else:
return 'linux' return 'linux'
@property
def is_ready(self):
return all(disk.is_ready for disk in self.disks)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
is_new = getattr(self, "pk", None) is None is_new = getattr(self, "pk", None) is None
super(InstanceTemplate, self).save(*args, **kwargs) super(InstanceTemplate, self).save(*args, **kwargs)
...@@ -162,7 +166,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): ...@@ -162,7 +166,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
return ('dashboard.views.template-detail', None, {'pk': self.pk}) return ('dashboard.views.template-detail', None, {'pk': self.pk})
class Instance(AclBase, VirtualMachineDescModel, StatusModel, class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
TimeStampedModel): TimeStampedModel):
"""Virtual machine instance. """Virtual machine instance.
...@@ -284,7 +288,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, ...@@ -284,7 +288,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel,
if old != self.status: if old != self.status:
logger.info('Status of Instance#%d changed to %s', logger.info('Status of Instance#%d changed to %s',
self.pk, self.status) self.pk, self.status)
self.save() self.save(update_fields=('status', ))
def _compute_status(self): def _compute_status(self):
"""Return the proper status of the instance based on activities. """Return the proper status of the instance based on activities.
...@@ -728,76 +732,10 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, ...@@ -728,76 +732,10 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel,
""" """
return scheduler.select_node(self, Node.objects.all()) return scheduler.select_node(self, Node.objects.all())
def __schedule_vm(self, act): def deploy_disks(self):
"""Schedule the virtual machine as part of a higher level activity. """Deploy all associated disks.
:param act: Parent activity.
"""
# Find unused port for VNC
if self.vnc_port is None:
self.vnc_port = find_unused_vnc_port()
# Schedule
if self.node is None:
self.node = self.select_node()
self.save()
def __deploy_vm(self, act, timeout=15):
"""Deploy the virtual machine.
:param self: The virtual machine.
:param act: Parent activity.
"""
queue_name = self.get_remote_queue_name('vm')
# Deploy VM on remote machine
with act.sub_activity('deploying_vm') as deploy_act:
deploy_act.result = vm_tasks.deploy.apply_async(
args=[self.get_vm_desc()],
queue=queue_name).get(timeout=timeout)
# Estabilish network connection (vmdriver)
with act.sub_activity('deploying_net'):
for net in self.interface_set.all():
net.deploy()
# Resume vm
with act.sub_activity('booting'):
vm_tasks.resume.apply_async(args=[self.vm_name],
queue=queue_name).get(timeout=timeout)
self.renew(which='both', base_activity=act)
def deploy(self, user=None, task_uuid=None):
"""Deploy new virtual machine with network
:param self: The virtual machine to deploy.
:type self: vm.models.Instance
:param user: The user who's issuing the command.
:type user: django.contrib.auth.models.User
:param task_uuid: The task's UUID, if the command is being executed
asynchronously.
:type task_uuid: str
""" """
if self.destroyed_at: devnums = list(ascii_lowercase) # a-z
raise self.InstanceDestroyedError(self)
def __on_commit(activity):
activity.resultant_state = 'RUNNING'
with instance_activity(code_suffix='deploy', instance=self,
on_commit=__on_commit, task_uuid=task_uuid,
user=user) as act:
self.__schedule_vm(act)
# Deploy virtual images
with act.sub_activity('deploying_disks'):
devnums = list(string.ascii_lowercase) # a-z
for disk in self.disks.all(): for disk in self.disks.all():
# assign device numbers # assign device numbers
if disk.dev_num in devnums: if disk.dev_num in devnums:
...@@ -808,33 +746,34 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, ...@@ -808,33 +746,34 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel,
# deploy disk # deploy disk
disk.deploy() disk.deploy()
self.__deploy_vm(act) def destroy_disks(self):
"""Destroy all associated disks.
def deploy_async(self, user=None):
"""Execute deploy asynchronously.
""" """
logger.debug('Calling async local_tasks.deploy(%s, %s)', for disk in self.disks.all():
unicode(self), unicode(user)) disk.destroy()
return local_tasks.deploy.apply_async(args=[self, user],
queue="localhost.man")
def __destroy_vm(self, act, timeout=15):
"""Destroy the virtual machine and its associated networks.
:param self: The virtual machine. def deploy_net(self):
"""Deploy all associated network interfaces.
"""
for net in self.interface_set.all():
net.deploy()
:param act: Parent activity. def destroy_net(self):
"""Destroy all associated network interfaces.
""" """
# Destroy networks
with act.sub_activity('destroying_net'):
for net in self.interface_set.all(): for net in self.interface_set.all():
net.destroy() net.destroy()
# Destroy virtual machine def shutdown_net(self):
with act.sub_activity('destroying_vm'): """Shutdown all associated network interfaces.
"""
for net in self.interface_set.all():
net.shutdown()
def delete_vm(self, timeout=15):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
try: try:
vm_tasks.destroy.apply_async(args=[self.vm_name], return vm_tasks.destroy.apply_async(args=[self.vm_name],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
except Exception as e: except Exception as e:
...@@ -844,363 +783,85 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, ...@@ -844,363 +783,85 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel,
else: else:
raise raise
def __cleanup_after_destroy_vm(self, act, timeout=15): def deploy_vm(self, timeout=15):
"""Clean up the virtual machine's data after destroy.
:param self: The virtual machine.
:param act: Parent activity.
"""
# Delete mem. dump if exists
try:
queue_name = self.mem_dump['datastore'].get_remote_queue_name(
'storage')
from storage.tasks.remote_tasks import delete_dump
delete_dump.apply_async(args=[self.mem_dump['path']],
queue=queue_name).get(timeout=timeout)
except:
pass
# Clear node and VNC port association
self.node = None
self.vnc_port = None
def redeploy(self, user=None, task_uuid=None):
"""Redeploy virtual machine with network
:param self: The virtual machine to redeploy.
:param user: The user who's issuing the command.
:type user: django.contrib.auth.models.User
:param task_uuid: The task's UUID, if the command is being executed
asynchronously.
:type task_uuid: str
"""
with instance_activity(code_suffix='redeploy', instance=self,
task_uuid=task_uuid, user=user) as act:
# Destroy VM
if self.node:
self.__destroy_vm(act)
self.__cleanup_after_destroy_vm(act)
# Deploy VM
self.__schedule_vm(act)
self.__deploy_vm(act)
def redeploy_async(self, user=None):
"""Execute redeploy asynchronously.
"""
return local_tasks.redeploy.apply_async(args=[self, user],
queue="localhost.man")
def shut_off(self, user=None, task_uuid=None):
"""Shut off VM. (plug-out)
"""
def __on_commit(activity):
activity.resultant_state = 'STOPPED'
with instance_activity(code_suffix='shut_off', instance=self,
task_uuid=task_uuid, user=user,
on_commit=__on_commit) as act:
# Destroy VM
if self.node:
self.__destroy_vm(act)
self.__cleanup_after_destroy_vm(act)
self.save()
def shut_off_async(self, user=None):
"""Shut off VM. (plug-out)
"""
return local_tasks.shut_off.apply_async(args=[self, user],
queue="localhost.man")
def destroy(self, user=None, task_uuid=None):
"""Remove virtual machine and its networks.
:param self: The virtual machine to destroy.
:type self: vm.models.Instance
:param user: The user who's issuing the command.
:type user: django.contrib.auth.models.User
:param task_uuid: The task's UUID, if the command is being executed
asynchronously.
:type task_uuid: str
"""
if self.destroyed_at:
return # already destroyed, nothing to do here
def __on_commit(activity):
activity.resultant_state = 'DESTROYED'
with instance_activity(code_suffix='destroy', instance=self,
on_commit=__on_commit, task_uuid=task_uuid,
user=user) as act:
if self.node:
self.__destroy_vm(act)
# Destroy disks
with act.sub_activity('destroying_disks'):
for disk in self.disks.all():
disk.destroy()
self.__cleanup_after_destroy_vm(act)
self.destroyed_at = timezone.now()
self.save()
def destroy_async(self, user=None):
"""Execute destroy asynchronously.
"""
return local_tasks.destroy.apply_async(args=[self, user],
queue="localhost.man")
def sleep(self, user=None, task_uuid=None, timeout=60):
"""Suspend virtual machine with memory dump.
"""
if self.status not in ['RUNNING']:
raise self.WrongStateError(self)
def __on_abort(activity, error):
if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None
else:
activity.resultant_state = 'ERROR'
def __on_commit(activity):
activity.resultant_state = 'SUSPENDED'
with instance_activity(code_suffix='sleep', instance=self,
on_abort=__on_abort, on_commit=__on_commit,
task_uuid=task_uuid, user=user) as act:
# Destroy networks
with act.sub_activity('destroying_net'):
for net in self.interface_set.all():
net.shutdown()
# Suspend vm
with act.sub_activity('suspending'):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.sleep.apply_async(args=[self.vm_name, return vm_tasks.deploy.apply_async(args=[self.get_vm_desc()],
self.mem_dump['path']],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
self.node = None
self.save()
def sleep_async(self, user=None):
"""Execute sleep asynchronously.
"""
return local_tasks.sleep.apply_async(args=[self, user],
queue="localhost.man")
def wake_up(self, user=None, task_uuid=None, timeout=60): def migrate_vm(self, to_node, timeout=120):
""" Wake up Virtual Machine from SUSPENDED state.
Power on Virtual Machine and load its memory from dump.
"""
if self.status not in ['SUSPENDED']:
raise self.WrongStateError(self)
def __on_abort(activity, error):
activity.resultant_state = 'ERROR'
def __on_commit(activity):
activity.resultant_state = 'RUNNING'
with instance_activity(code_suffix='wake_up', instance=self,
on_abort=__on_abort, on_commit=__on_commit,
task_uuid=task_uuid, user=user) as act:
# Schedule vm
self.__schedule_vm(act)
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
return vm_tasks.migrate.apply_async(args=[self.vm_name,
# Resume vm to_node.host.hostname],
with act.sub_activity('resuming'):
vm_tasks.wake_up.apply_async(args=[self.vm_name,
self.mem_dump['path']],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
# Estabilish network connection (vmdriver) def reboot_vm(self, timeout=5):
with act.sub_activity('deploying_net'):
for net in self.interface_set.all():
net.deploy()
# Renew vm
self.renew(which='both', base_activity=act)
def wake_up_async(self, user=None):
"""Execute wake_up asynchronously.
"""
return local_tasks.wake_up.apply_async(args=[self, user],
queue="localhost.man")
def shutdown(self, user=None, task_uuid=None, timeout=120):
"""Shutdown virtual machine with ACPI signal.
"""
def __on_abort(activity, error):
if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None
else:
activity.resultant_state = 'ERROR'
def __on_commit(activity):
activity.resultant_state = 'STOPPED'
with instance_activity(code_suffix='shutdown', instance=self,
on_abort=__on_abort, on_commit=__on_commit,
task_uuid=task_uuid, user=user):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
logger.debug("RPC Shutdown at queue: %s, for vm: %s.", return vm_tasks.reboot.apply_async(args=[self.vm_name],
self.vm_name, queue_name)
vm_tasks.shutdown.apply_async(kwargs={'name': self.vm_name},
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
self.node = None
self.vnc_port = None
self.save()
def shutdown_async(self, user=None):
"""Execute shutdown asynchronously.
"""
return local_tasks.shutdown.apply_async(args=[self, user],
queue="localhost.man")
def reset(self, user=None, task_uuid=None, timeout=5):
"""Reset virtual machine (reset button)
"""
with instance_activity(code_suffix='reset', instance=self,
task_uuid=task_uuid, user=user):
def reset_vm(self, timeout=5):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.reset.apply_async(args=[self.vm_name], return vm_tasks.reset.apply_async(args=[self.vm_name],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
def reset_async(self, user=None): def resume_vm(self, timeout=15):
"""Execute reset asynchronously. queue_name = self.get_remote_queue_name('vm')
""" return vm_tasks.resume.apply_async(args=[self.vm_name],
return local_tasks.reset.apply_async(args=[self, user], queue=queue_name
queue="localhost.man") ).get(timeout=timeout)
def reboot(self, user=None, task_uuid=None, timeout=5):
"""Reboot virtual machine with Ctrl+Alt+Del signal.
"""
with instance_activity(code_suffix='reboot', instance=self,
task_uuid=task_uuid, user=user):
def shutdown_vm(self, timeout=120):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.reboot.apply_async(args=[self.vm_name], logger.debug("RPC Shutdown at queue: %s, for vm: %s.", queue_name,
self.vm_name)
return vm_tasks.shutdown.apply_async(kwargs={'name': self.vm_name},
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
def reboot_async(self, user=None): def suspend_vm(self, timeout=60):
"""Execute reboot asynchronously. """ queue_name = self.get_remote_queue_name('vm')
return local_tasks.reboot.apply_async(args=[self, user], return vm_tasks.sleep.apply_async(args=[self.vm_name,
queue="localhost.man") self.mem_dump['path']],
queue=queue_name
def migrate_async(self, to_node, user=None): ).get(timeout=timeout)
"""Execute migrate asynchronously. """
return local_tasks.migrate.apply_async(args=[self, to_node, user],
queue="localhost.man")
def migrate(self, to_node=None, user=None, task_uuid=None, timeout=120):
"""Live migrate running vm to another node. """
with instance_activity(code_suffix='migrate', instance=self,
task_uuid=task_uuid, user=user) as act:
if not to_node:
with act.sub_activity('scheduling') as sa:
to_node = self.select_node()
sa.result = to_node
# Destroy networks
with act.sub_activity('destroying_net'):
for net in self.interface_set.all():
net.shutdown()
with act.sub_activity('migrate_vm'): def wake_up_vm(self, timeout=60):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.migrate.apply_async(args=[self.vm_name, return vm_tasks.wake_up.apply_async(args=[self.vm_name,
to_node.host.hostname], self.mem_dump['path']],
queue=queue_name queue=queue_name
).get(timeout=timeout) ).get(timeout=timeout)
# Refresh node information
self.node = to_node
self.save()
# Estabilish network connection (vmdriver)
with act.sub_activity('deploying_net'):
for net in self.interface_set.all():
net.deploy()
def save_as_template_async(self, name, user=None, **kwargs): def delete_mem_dump(self, timeout=15):
""" Save as template asynchronusly. queue_name = self.mem_dump['datastore'].get_remote_queue_name(
""" 'storage')
return local_tasks.save_as_template.apply_async( from storage.tasks.remote_tasks import delete_dump
args=[self, name, user, kwargs], queue="localhost.man") delete_dump.apply_async(args=[self.mem_dump['path']],
queue=queue_name).get(timeout=timeout)
def save_as_template(self, name, task_uuid=None, user=None, def allocate_node(self):
timeout=300, **kwargs): if self.node is None:
""" Save Virtual Machine as a Template. self.node = self.select_node()
self.save()
Template can be shared with groups and users. def yield_node(self):
Users can instantiate Virtual Machines from Templates. if self.node is not None:
""" self.node = None
with instance_activity(code_suffix="save_as_template", instance=self, self.save()
task_uuid=task_uuid, user=user) as act:
# prepare parameters
params = {
'access_method': self.access_method,
'arch': self.arch,
'boot_menu': self.boot_menu,
'description': self.description,
'lease': self.lease, # Can be problem in new VM
'max_ram_size': self.max_ram_size,
'name': name,
'num_cores': self.num_cores,
'owner': user,
'parent': self.template, # Can be problem
'priority': self.priority,
'ram_size': self.ram_size,
'raw_data': self.raw_data,
'system': self.system,
}
params.update(kwargs)
def __try_save_disk(disk): def allocate_vnc_port(self):
try: if self.vnc_port is None:
return disk.save_as() # can do in parallel self.vnc_port = find_unused_vnc_port()
except Disk.WrongDiskTypeError: self.save()
return disk
# create template and do additional setup
tmpl = InstanceTemplate(**params)
tmpl.full_clean() # Avoiding database errors.
tmpl.save()
try:
with act.sub_activity('saving_disks'):
tmpl.disks.add(*[__try_save_disk(disk)
for disk in self.disks.all()])
# create interface templates
for i in self.interface_set.all():
i.save_as_template(tmpl)
except:
tmpl.delete()
raise
else:
return tmpl
def shutdown_and_save_as_template(self, name, user=None, task_uuid=None, def yield_vnc_port(self):
**kwargs): if self.vnc_port is not None:
self.shutdown(user, task_uuid) self.vnc_port = None
self.save_as_template(name, **kwargs) self.save()
def get_status_icon(self): def get_status_icon(self):
return { return {
...@@ -1208,7 +869,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, ...@@ -1208,7 +869,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel,
'RUNNING': 'icon-play', 'RUNNING': 'icon-play',
'STOPPED': 'icon-stop', 'STOPPED': 'icon-stop',
'SUSPENDED': 'icon-pause', 'SUSPENDED': 'icon-pause',
'ERROR': 'icon-warning_sign', 'ERROR': 'icon-warning-sign',
'PENDING': 'icon-rocket', 'PENDING': 'icon-rocket',
'DESTROYED': 'icon-trash', 'DESTROYED': 'icon-trash',
'MIGRATING': 'icon-truck'}.get(self.status, 'icon-question-sign') 'MIGRATING': 'icon-truck'}.get(self.status, 'icon-question-sign')
...@@ -137,7 +137,7 @@ class Interface(Model): ...@@ -137,7 +137,7 @@ class Interface(Model):
iface.save() iface.save()
return iface return iface
def deploy(self, user=None, task_uuid=None): def deploy(self):
if self.destroyed: if self.destroyed:
from .instance import Instance from .instance import Instance
raise Instance.InstanceDestroyedError(self.instance, raise Instance.InstanceDestroyedError(self.instance,
...@@ -149,16 +149,23 @@ class Interface(Model): ...@@ -149,16 +149,23 @@ class Interface(Model):
args=[self.get_vmnetwork_desc()], args=[self.get_vmnetwork_desc()],
queue=self.instance.get_remote_queue_name('net')) queue=self.instance.get_remote_queue_name('net'))
def shutdown(self, user=None, task_uuid=None): def shutdown(self):
net_tasks.destroy.apply_async( if self.destroyed:
args=[self.get_vmnetwork_desc()], from .instance import Instance
queue=self.instance.get_remote_queue_name('net')) raise Instance.InstanceDestroyedError(self.instance,
"The associated instance "
"(%s) has already been "
"destroyed" % self.instance)
queue_name = self.instance.get_remote_queue_name('net')
net_tasks.destroy.apply_async(args=[self.get_vmnetwork_desc()],
queue=queue_name)
def destroy(self, user=None, task_uuid=None): def destroy(self):
if self.destroyed: if self.destroyed:
return return
self.shutdown(user, task_uuid) self.shutdown()
if self.host is not None: if self.host is not None:
self.host.delete() self.host.delete()
......
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from logging import getLogger from logging import getLogger
from warnings import warn
from django.db.models import ( from django.db.models import (
CharField, IntegerField, ForeignKey, BooleanField, ManyToManyField, CharField, IntegerField, ForeignKey, BooleanField, ManyToManyField,
FloatField, permalink, FloatField, permalink,
) )
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from warnings import warn
from celery.exceptions import TimeoutError from celery.exceptions import TimeoutError
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from common.models import method_cache, WorkerNotFound, HumanSortField from common.models import method_cache, WorkerNotFound, HumanSortField
from common.operations import OperatedMixin
from firewall.models import Host from firewall.models import Host
from ..tasks import vm_tasks, local_tasks
from .common import Trait
from .activity import node_activity, NodeActivity
from monitor.calvin.calvin import Query from monitor.calvin.calvin import Query
from monitor.calvin.calvin import GraphiteHandler from monitor.calvin.calvin import GraphiteHandler
from django.utils import timezone from ..tasks import vm_tasks
from .activity import node_activity, NodeActivity
from .common import Trait
logger = getLogger(__name__) logger = getLogger(__name__)
...@@ -38,7 +37,7 @@ def node_available(function): ...@@ -38,7 +37,7 @@ def node_available(function):
return decorate return decorate
class Node(TimeStampedModel): class Node(OperatedMixin, TimeStampedModel):
"""A VM host machine, a hypervisor. """A VM host machine, a hypervisor.
""" """
...@@ -131,22 +130,6 @@ class Node(TimeStampedModel): ...@@ -131,22 +130,6 @@ class Node(TimeStampedModel):
self.enabled = False self.enabled = False
self.save() self.save()
def flush(self, user=None, task_uuid=None):
"""Disable node and move all instances to other ones.
"""
with node_activity('flush', node=self, user=user,
task_uuid=task_uuid) as act:
self.disable(user, act)
for i in self.instance_set.all():
with act.sub_activity('migrate_instance_%d' % i.pk):
i.migrate()
def flush_async(self, user=None):
"""Execute flush asynchronously.
"""
return local_tasks.flush.apply_async(args=[self, user],
queue="localhost.man")
def enable(self, user=None): def enable(self, user=None):
''' Enable the node. ''' ''' Enable the node. '''
if self.enabled is not True: if self.enabled is not True:
...@@ -164,10 +147,10 @@ class Node(TimeStampedModel): ...@@ -164,10 +147,10 @@ class Node(TimeStampedModel):
@method_cache(30) @method_cache(30)
def get_remote_queue_name(self, queue_id): def get_remote_queue_name(self, queue_id):
"""Return the name of the remote celery queue for this node. """Returns the name of the remote celery queue for this node.
throws Exception if there is no worker on the queue. Throws Exception if there is no worker on the queue.
Until the cache provide reult there can be dead queues. The result may include dead queues because of caching.
""" """
if vm_tasks.check_queue(self.host.hostname, queue_id): if vm_tasks.check_queue(self.host.hostname, queue_id):
...@@ -189,7 +172,7 @@ class Node(TimeStampedModel): ...@@ -189,7 +172,7 @@ class Node(TimeStampedModel):
else: else:
logger.debug("The last activity was %s" % act) logger.debug("The last activity was %s" % act)
if act.activity_code.endswith("offline"): if act.activity_code.endswith("offline"):
act = NodeActivity.create(code_suffix='monitor_succes_online', act = NodeActivity.create(code_suffix='monitor_success_online',
node=self, user=None) node=self, user=None)
act.started = timezone.now() act.started = timezone.now()
act.finished = timezone.now() act.finished = timezone.now()
......
from __future__ import absolute_import, unicode_literals
from logging import getLogger
from django.core.exceptions import PermissionDenied
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from celery.exceptions import TimeLimitExceeded
from common.operations import Operation, register_operation
from .tasks.local_tasks import async_instance_operation, async_node_operation
from .models import (
Instance, InstanceActivity, InstanceTemplate, Node, NodeActivity,
)
logger = getLogger(__name__)
class InstanceOperation(Operation):
acl_level = 'owner'
async_operation = async_instance_operation
def __init__(self, instance):
super(InstanceOperation, self).__init__(subject=instance)
self.instance = instance
def check_precond(self):
if self.instance.destroyed_at:
raise self.instance.InstanceDestroyedError(self.instance)
def check_auth(self, user):
if not self.instance.has_level(user, self.acl_level):
raise PermissionDenied("%s doesn't have the required ACL level." %
user)
super(InstanceOperation, self).check_auth(user=user)
def create_activity(self, parent, user):
if parent:
if parent.instance != self.instance:
raise ValueError("The instance associated with the specified "
"parent activity does not match the instance "
"bound to the operation.")
if parent.user != user:
raise ValueError("The user associated with the specified "
"parent activity does not match the user "
"provided as parameter.")
return parent.create_sub(code_suffix=self.activity_code_suffix)
else:
return InstanceActivity.create(
code_suffix=self.activity_code_suffix, instance=self.instance,
user=user)
def register_instance_operation(op_cls, op_id=None):
return register_operation(Instance, op_cls, op_id)
class DeployOperation(InstanceOperation):
activity_code_suffix = 'deploy'
id = 'deploy'
name = _("deploy")
description = _("Deploy new virtual machine with network.")
def on_commit(self, activity):
activity.resultant_state = 'RUNNING'
def _operation(self, activity, user, system, timeout=15):
# Allocate VNC port and host node
self.instance.allocate_vnc_port()
self.instance.allocate_node()
# Deploy virtual images
with activity.sub_activity('deploying_disks'):
self.instance.deploy_disks()
# Deploy VM on remote machine
with activity.sub_activity('deploying_vm') as deploy_act:
deploy_act.result = self.instance.deploy_vm(timeout=timeout)
# Establish network connection (vmdriver)
with activity.sub_activity('deploying_net'):
self.instance.deploy_net()
# Resume vm
with activity.sub_activity('booting'):
self.instance.resume_vm(timeout=timeout)
self.instance.renew(which='both', base_activity=activity)
register_instance_operation(DeployOperation)
class DestroyOperation(InstanceOperation):
activity_code_suffix = 'destroy'
id = 'destroy'
name = _("destroy")
description = _("Destroy virtual machine and its networks.")
def on_commit(self, activity):
activity.resultant_state = 'DESTROYED'
def _operation(self, activity, user, system):
if self.instance.node:
# Destroy networks
with activity.sub_activity('destroying_net'):
self.instance.destroy_net()
# Delete virtual machine
with activity.sub_activity('destroying_vm'):
self.instance.delete_vm()
# Destroy disks
with activity.sub_activity('destroying_disks'):
self.instance.destroy_disks()
# Delete mem. dump if exists
try:
self.instance.delete_mem_dump()
except:
pass
# Clear node and VNC port association
self.instance.yield_node()
self.instance.yield_vnc_port()
self.instance.destroyed_at = timezone.now()
self.instance.save()
register_instance_operation(DestroyOperation)
class MigrateOperation(InstanceOperation):
activity_code_suffix = 'migrate'
id = 'migrate'
name = _("migrate")
description = _("Live migrate running VM to another node.")
def _operation(self, activity, user, system, to_node=None, timeout=120):
if not to_node:
with activity.sub_activity('scheduling') as sa:
to_node = self.instance.select_node()
sa.result = to_node
# Shutdown networks
with activity.sub_activity('shutdown_net'):
self.instance.shutdown_net()
with activity.sub_activity('migrate_vm'):
self.instance.migrate_vm(to_node=to_node, timeout=timeout)
# Refresh node information
self.instance.node = to_node
self.instance.save()
# Estabilish network connection (vmdriver)
with activity.sub_activity('deploying_net'):
self.instance.deploy_net()
register_instance_operation(MigrateOperation)
class RebootOperation(InstanceOperation):
activity_code_suffix = 'reboot'
id = 'reboot'
name = _("reboot")
description = _("Reboot virtual machine with Ctrl+Alt+Del signal.")
def _operation(self, activity, user, system, timeout=5):
self.instance.reboot_vm(timeout=timeout)
register_instance_operation(RebootOperation)
class ResetOperation(InstanceOperation):
activity_code_suffix = 'reset'
id = 'reset'
name = _("reset")
description = _("Reset virtual machine (reset button).")
def _operation(self, activity, user, system, timeout=5):
self.instance.reset_vm(timeout=timeout)
register_instance_operation(ResetOperation)
class SaveAsTemplateOperation(InstanceOperation):
activity_code_suffix = 'save_as_template'
id = 'save_as_template'
name = _("save as template")
description = _("""Save Virtual Machine as a Template.
Template can be shared with groups and users.
Users can instantiate Virtual Machines from Templates.
""")
def _operation(self, activity, name, user, system, timeout=300,
with_shutdown=True, **kwargs):
if with_shutdown:
try:
ShutdownOperation(self.instance).call(parent_activity=activity,
user=user)
except Instance.WrongStateError:
pass
# prepare parameters
params = {
'access_method': self.instance.access_method,
'arch': self.instance.arch,
'boot_menu': self.instance.boot_menu,
'description': self.instance.description,
'lease': self.instance.lease, # Can be problem in new VM
'max_ram_size': self.instance.max_ram_size,
'name': name,
'num_cores': self.instance.num_cores,
'owner': user,
'parent': self.instance.template, # Can be problem
'priority': self.instance.priority,
'ram_size': self.instance.ram_size,
'raw_data': self.instance.raw_data,
'system': self.instance.system,
}
params.update(kwargs)
from storage.models import Disk
def __try_save_disk(disk):
try:
return disk.save_as()
except Disk.WrongDiskTypeError:
return disk
with activity.sub_activity('saving_disks'):
disks = [__try_save_disk(disk)
for disk in self.instance.disks.all()]
# create template and do additional setup
tmpl = InstanceTemplate(**params)
tmpl.full_clean() # Avoiding database errors.
tmpl.save()
try:
tmpl.disks.add(*disks)
# create interface templates
for i in self.instance.interface_set.all():
i.save_as_template(tmpl)
except:
tmpl.delete()
raise
else:
return tmpl
register_instance_operation(SaveAsTemplateOperation)
class ShutdownOperation(InstanceOperation):
activity_code_suffix = 'shutdown'
id = 'shutdown'
name = _("shutdown")
description = _("Shutdown virtual machine with ACPI signal.")
def check_precond(self):
super(ShutdownOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error):
if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None
else:
activity.resultant_state = 'ERROR'
def on_commit(self, activity):
activity.resultant_state = 'STOPPED'
def _operation(self, activity, user, system, timeout=120):
self.instance.shutdown_vm(timeout=timeout)
self.instance.yield_node()
self.instance.yield_vnc_port()
register_instance_operation(ShutdownOperation)
class ShutOffOperation(InstanceOperation):
activity_code_suffix = 'shut_off'
id = 'shut_off'
name = _("shut off")
description = _("Shut off VM (plug-out).")
def on_commit(self, activity):
activity.resultant_state = 'STOPPED'
def _operation(self, activity, user, system):
# Shutdown networks
with activity.sub_activity('shutdown_net'):
self.instance.shutdown_net()
# Delete virtual machine
with activity.sub_activity('delete_vm'):
self.instance.delete_vm()
# Clear node and VNC port association
self.instance.yield_node()
self.instance.yield_vnc_port()
register_instance_operation(ShutOffOperation)
class SleepOperation(InstanceOperation):
activity_code_suffix = 'sleep'
id = 'sleep'
name = _("sleep")
description = _("Suspend virtual machine with memory dump.")
def check_precond(self):
super(SleepOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error):
if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None
else:
activity.resultant_state = 'ERROR'
def on_commit(self, activity):
activity.resultant_state = 'SUSPENDED'
def _operation(self, activity, user, system, timeout=60):
# Destroy networks
with activity.sub_activity('shutdown_net'):
self.instance.shutdown_net()
# Suspend vm
with activity.sub_activity('suspending'):
self.instance.suspend_vm(timeout=timeout)
self.instance.yield_node()
# VNC port needs to be kept
register_instance_operation(SleepOperation)
class WakeUpOperation(InstanceOperation):
activity_code_suffix = 'wake_up'
id = 'wake_up'
name = _("wake up")
description = _("""Wake up Virtual Machine from SUSPENDED state.
Power on Virtual Machine and load its memory from dump.
""")
def check_precond(self):
super(WakeUpOperation, self).check_precond()
if self.instance.status not in ['SUSPENDED']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error):
activity.resultant_state = 'ERROR'
def on_commit(self, activity):
activity.resultant_state = 'RUNNING'
def _operation(self, activity, user, system, timeout=60):
# Schedule vm
self.instance.allocate_vnc_port()
self.instance.allocate_node()
# Resume vm
with activity.sub_activity('resuming'):
self.instance.wake_up_vm(timeout=timeout)
# Estabilish network connection (vmdriver)
with activity.sub_activity('deploying_net'):
self.instance.deploy_net()
# Renew vm
self.instance.renew(which='both', base_activity=activity)
register_instance_operation(WakeUpOperation)
class NodeOperation(Operation):
async_operation = async_node_operation
def __init__(self, node):
super(NodeOperation, self).__init__(subject=node)
self.node = node
def create_activity(self, parent, user):
if parent:
if parent.node != self.node:
raise ValueError("The node associated with the specified "
"parent activity does not match the node "
"bound to the operation.")
if parent.user != user:
raise ValueError("The user associated with the specified "
"parent activity does not match the user "
"provided as parameter.")
return parent.create_sub(code_suffix=self.activity_code_suffix)
else:
return NodeActivity.create(code_suffix=self.activity_code_suffix,
node=self.node, user=user)
def register_node_operation(op_cls, op_id=None):
return register_operation(Node, op_cls, op_id)
class FlushOperation(NodeOperation):
activity_code_suffix = 'flush'
id = 'flush'
name = _("flush")
description = _("Disable node and move all instances to other ones.")
def _operation(self, activity, user, system):
self.node.disable(user, activity)
for i in self.node.instance_set.all():
with activity.sub_activity('migrate_instance_%d' % i.pk):
i.migrate()
register_node_operation(FlushOperation)
...@@ -27,7 +27,7 @@ def garbage_collector(timeout=15): ...@@ -27,7 +27,7 @@ def garbage_collector(timeout=15):
now = timezone.now() now = timezone.now()
for i in Instance.objects.filter(destroyed_at=None).all(): for i in Instance.objects.filter(destroyed_at=None).all():
if i.time_of_delete and now > i.time_of_delete: if i.time_of_delete and now > i.time_of_delete:
i.destroy_async() i.destroy.async()
logger.info("Expired instance %d destroyed.", i.pk) logger.info("Expired instance %d destroyed.", i.pk)
try: try:
i.owner.profile.notify( i.owner.profile.notify(
...@@ -39,7 +39,7 @@ def garbage_collector(timeout=15): ...@@ -39,7 +39,7 @@ def garbage_collector(timeout=15):
i.pk, unicode(e)) i.pk, unicode(e))
elif (i.time_of_suspend and now > i.time_of_suspend and elif (i.time_of_suspend and now > i.time_of_suspend and
i.state == 'RUNNING'): i.state == 'RUNNING'):
i.sleep_async() i.sleep.async()
logger.info("Expired instance %d suspended." % i.pk) logger.info("Expired instance %d suspended." % i.pk)
try: try:
i.owner.profile.notify( i.owner.profile.notify(
......
from manager.mancelery import celery from manager.mancelery import celery
# TODO: Keep synchronised with Instance funcs
@celery.task
def deploy(instance, user):
instance.deploy(task_uuid=deploy.request.id, user=user)
@celery.task
def redeploy(instance, user):
instance.redeploy(task_uuid=redeploy.request.id, user=user)
@celery.task @celery.task
def shut_off(instance, user): def async_instance_operation(operation_id, instance_pk, activity_pk, **kwargs):
instance.shut_off(task_uuid=shut_off.request.id, user=user) from vm.models import Instance, InstanceActivity
instance = Instance.objects.get(pk=instance_pk)
operation = getattr(instance, operation_id)
activity = InstanceActivity.objects.get(pk=activity_pk)
# save async task UUID to activity
activity.task_uuid = async_instance_operation.request.id
activity.save()
@celery.task return operation._exec_op(activity=activity, **kwargs)
def destroy(instance, user):
instance.destroy(task_uuid=destroy.request.id, user=user)
@celery.task
def save_as_template(instance, name, user, params):
instance.save_as_template(name, task_uuid=save_as_template.request.id,
user=user, **params)
@celery.task
def sleep(instance, user):
instance.sleep(task_uuid=sleep.request.id, user=user)
@celery.task @celery.task
def wake_up(instance, user): def async_node_operation(operation_id, node_pk, activity_pk, **kwargs):
instance.wake_up(task_uuid=wake_up.request.id, user=user) from vm.models import Node, NodeActivity
node = Node.objects.get(pk=node_pk)
operation = getattr(node, operation_id)
activity = NodeActivity.objects.get(pk=activity_pk)
# save async task UUID to activity
activity.task_uuid = async_node_operation.request.id
activity.save()
@celery.task return operation._exec_op(activity=activity, **kwargs)
def shutdown(instance, user):
instance.shutdown(task_uuid=shutdown.request.id, user=user)
@celery.task
def reset(instance, user):
instance.reset(task_uuid=reset.request.id, user=user)
@celery.task
def reboot(instance, user):
instance.reboot(task_uuid=reboot.request.id, user=user)
@celery.task
def migrate(instance, to_node, user):
instance.migrate(to_node, task_uuid=migrate.request.id, user=user)
@celery.task
def flush(node, user):
node.flush(task_uuid=flush.request.id, user=user)
...@@ -19,18 +19,16 @@ def check_queue(node_hostname, queue_id): ...@@ -19,18 +19,16 @@ def check_queue(node_hostname, queue_id):
active_queues = get_queues() active_queues = get_queues()
if active_queues is None: if active_queues is None:
return False return False
# v is List of List of queues dict queue_names = (queue['name'] for worker in active_queues.itervalues()
node_workers = [v for k, v in active_queues.iteritems()] for queue in worker)
for worker in node_workers: return queue_name in queue_names
for queue in worker:
if queue['name'] == queue_name:
return True
return False
def get_queues(): def get_queues():
"""Get active celery queues. """Get active celery queues.
Returns a dictionary whose entries are (worker name;list of queues) pairs,
where queues are represented by dictionaries.
Result is cached for 10 seconds! Result is cached for 10 seconds!
""" """
key = __name__ + u'queues' key = __name__ + u'queues'
......
from datetime import datetime from datetime import datetime
from mock import Mock, MagicMock, patch, call
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from mock import Mock, MagicMock, patch, call
from ..models import ( from ..models import (
Lease, Node, Interface, Instance, InstanceTemplate, InstanceActivity, Lease, Node, Interface, Instance, InstanceTemplate, InstanceActivity,
) )
from ..models.instance import find_unused_port, ActivityInProgressError from ..models.instance import find_unused_port, ActivityInProgressError
from ..operations import (
DeployOperation, DestroyOperation, FlushOperation, MigrateOperation,
)
class PortFinderTestCase(TestCase): class PortFinderTestCase(TestCase):
...@@ -52,50 +55,60 @@ class InstanceTestCase(TestCase): ...@@ -52,50 +55,60 @@ class InstanceTestCase(TestCase):
def test_deploy_destroyed(self): def test_deploy_destroyed(self):
inst = Mock(destroyed_at=datetime.now(), spec=Instance, inst = Mock(destroyed_at=datetime.now(), spec=Instance,
InstanceDestroyedError=Instance.InstanceDestroyedError) InstanceDestroyedError=Instance.InstanceDestroyedError)
deploy_op = DeployOperation(inst)
with patch.object(DeployOperation, 'create_activity'):
with self.assertRaises(Instance.InstanceDestroyedError): with self.assertRaises(Instance.InstanceDestroyedError):
Instance.deploy(inst) deploy_op(system=True)
def test_destroy_destroyed(self): def test_destroy_destroyed(self):
inst = Mock(destroyed_at=datetime.now(), spec=Instance) inst = Mock(destroyed_at=datetime.now(), spec=Instance,
Instance.destroy(inst) InstanceDestroyedError=Instance.InstanceDestroyedError)
destroy_op = DestroyOperation(inst)
with patch.object(DestroyOperation, 'create_activity'):
with self.assertRaises(Instance.InstanceDestroyedError):
destroy_op(system=True)
self.assertFalse(inst.save.called) self.assertFalse(inst.save.called)
def test_destroy_sets_destroyed(self): def test_destroy_sets_destroyed(self):
inst = MagicMock(destroyed_at=None, spec=Instance) inst = Mock(destroyed_at=None, spec=Instance,
InstanceDestroyedError=Instance.InstanceDestroyedError)
inst.node = MagicMock(spec=Node) inst.node = MagicMock(spec=Node)
inst.disks.all.return_value = [] inst.disks.all.return_value = []
with patch('vm.models.instance.instance_activity') as ia: destroy_op = DestroyOperation(inst)
ia.return_value = MagicMock() with patch.object(DestroyOperation, 'create_activity'):
Instance.destroy(inst) destroy_op(system=True)
self.assertTrue(inst.destroyed_at) self.assertTrue(inst.destroyed_at)
inst.save.assert_called() inst.save.assert_called()
def test_migrate_with_scheduling(self): def test_migrate_with_scheduling(self):
inst = MagicMock(spec=Instance) inst = Mock(destroyed_at=None, spec=Instance)
inst.interface_set.all.return_value = [] inst.interface_set.all.return_value = []
inst.node = MagicMock(spec=Node) inst.node = MagicMock(spec=Node)
with patch('vm.models.instance.instance_activity') as ia, \ migrate_op = MigrateOperation(inst)
patch('vm.models.instance.vm_tasks.migrate') as migr: with patch('vm.models.instance.vm_tasks.migrate') as migr:
Instance.migrate(inst) act = MagicMock()
with patch.object(MigrateOperation, 'create_activity',
return_value=act):
migrate_op(system=True)
migr.apply_async.assert_called() migr.apply_async.assert_called()
self.assertIn(call().__enter__().sub_activity(u'scheduling'), self.assertIn(call.sub_activity(u'scheduling'), act.mock_calls)
ia.mock_calls)
inst.select_node.assert_called() inst.select_node.assert_called()
def test_migrate_wo_scheduling(self): def test_migrate_wo_scheduling(self):
inst = MagicMock(spec=Instance) inst = MagicMock(destroyed_at=None, spec=Instance)
inst.interface_set.all.return_value = [] inst.interface_set.all.return_value = []
inst.node = MagicMock(spec=Node) inst.node = MagicMock(spec=Node)
with patch('vm.models.instance.instance_activity') as ia, \ migrate_op = MigrateOperation(inst)
patch('vm.models.instance.vm_tasks.migrate') as migr: with patch('vm.models.instance.vm_tasks.migrate') as migr:
inst.select_node.side_effect = AssertionError inst.select_node.side_effect = AssertionError
act = MagicMock()
Instance.migrate(inst, inst.node) with patch.object(MigrateOperation, 'create_activity',
return_value=act):
migrate_op(to_node=inst.node, system=True)
migr.apply_async.assert_called() migr.apply_async.assert_called()
self.assertNotIn(call().__enter__().sub_activity(u'scheduling'), self.assertNotIn(call.sub_activity(u'scheduling'), act.mock_calls)
ia.mock_calls)
def test_status_icon(self): def test_status_icon(self):
inst = MagicMock(spec=Instance) inst = MagicMock(spec=Instance)
...@@ -162,25 +175,19 @@ class InstanceActivityTestCase(TestCase): ...@@ -162,25 +175,19 @@ class InstanceActivityTestCase(TestCase):
instance.activity_log.filter.return_value.exists.return_value = True instance.activity_log.filter.return_value.exists.return_value = True
with self.assertRaises(ActivityInProgressError): with self.assertRaises(ActivityInProgressError):
InstanceActivity.create("test", instance, concurrency_check=True) InstanceActivity.create('test', instance, concurrency_check=True)
def test_create_no_concurrency_check(self): def test_create_no_concurrency_check(self):
instance = MagicMock(spec=Instance) instance = MagicMock(spec=Instance)
instance.activity_log.filter.return_value.exists.return_value = True instance.activity_log.filter.return_value.exists.return_value = True
original_method = InstanceActivity.create.__func__ with patch.object(InstanceActivity, '__new__'):
try:
with patch('vm.models.activity.InstanceActivity') as ia, \ InstanceActivity.create('test', instance,
patch('vm.models.activity.timezone.now'): concurrency_check=False)
# ia.__init__ = MagicMock() raises AttributeError except ActivityInProgressError:
raise AssertionError("'create' method checked for concurrent "
original_method(ia, "test", instance, concurrency_check=False) "activities.")
ia.save.assert_called()
# ia.__init__.assert_called_with(activity_code='vm.Instance.test',
# instance=instance, parent=None,
# resultant_state=None, started=now,
# task_uuid=None, user=None)
def test_create_sub_concurrency_check(self): def test_create_sub_concurrency_check(self):
iaobj = MagicMock(spec=InstanceActivity) iaobj = MagicMock(spec=InstanceActivity)
...@@ -194,12 +201,13 @@ class InstanceActivityTestCase(TestCase): ...@@ -194,12 +201,13 @@ class InstanceActivityTestCase(TestCase):
iaobj.activity_code = 'test' iaobj.activity_code = 'test'
iaobj.children.filter.return_value.exists.return_value = True iaobj.children.filter.return_value.exists.return_value = True
original_method = InstanceActivity.create_sub with patch.object(InstanceActivity, '__new__'):
try:
with patch('vm.models.activity.InstanceActivity') as ia, \ InstanceActivity.create_sub(iaobj, 'test',
patch('vm.models.activity.timezone.now'): concurrency_check=False)
original_method(iaobj, "test", concurrency_check=False) except ActivityInProgressError:
ia.save.assert_called() raise AssertionError("'create_sub' method checked for "
"concurrent activities.")
def test_disable_enabled(self): def test_disable_enabled(self):
node = MagicMock(spec=Node, enabled=True) node = MagicMock(spec=Node, enabled=True)
...@@ -231,33 +239,37 @@ class InstanceActivityTestCase(TestCase): ...@@ -231,33 +239,37 @@ class InstanceActivityTestCase(TestCase):
subact.__enter__.assert_called() subact.__enter__.assert_called()
def test_flush(self): def test_flush(self):
insts = [MagicMock(spec=Instance, migrate=MagicMock()),
MagicMock(spec=Instance, migrate=MagicMock())]
node = MagicMock(spec=Node, enabled=True) node = MagicMock(spec=Node, enabled=True)
node.instance_set.all.return_value = insts
user = MagicMock(spec=User) user = MagicMock(spec=User)
insts = [MagicMock(spec=Instance), MagicMock(spec=Instance)] flush_op = FlushOperation(node)
with patch('vm.models.node.node_activity') as na: with patch.object(FlushOperation, 'create_activity') as create_act:
act = na.return_value.__enter__.return_value = MagicMock() act = create_act.return_value = MagicMock()
node.instance_set.all.return_value = insts
Node.flush(node, user) flush_op(user=user)
na.__enter__.assert_called() create_act.assert_called()
node.disable.assert_called_with(user, act) node.disable.assert_called_with(user, act)
for i in insts: for i in insts:
i.migrate.assert_called() i.migrate.assert_called()
def test_flush_disabled_wo_user(self): def test_flush_disabled_wo_user(self):
insts = [MagicMock(spec=Instance, migrate=MagicMock()),
MagicMock(spec=Instance, migrate=MagicMock())]
node = MagicMock(spec=Node, enabled=False) node = MagicMock(spec=Node, enabled=False)
insts = [MagicMock(spec=Instance), MagicMock(spec=Instance)]
with patch('vm.models.node.node_activity') as na:
act = na.return_value.__enter__.return_value = MagicMock()
node.instance_set.all.return_value = insts node.instance_set.all.return_value = insts
flush_op = FlushOperation(node)
with patch.object(FlushOperation, 'create_activity') as create_act:
act = create_act.return_value = MagicMock()
Node.flush(node) flush_op(system=True)
create_act.assert_called()
node.disable.assert_called_with(None, act) node.disable.assert_called_with(None, act)
# ^ should be called, but real method no-ops if disabled # ^ should be called, but real method no-ops if disabled
na.__enter__.assert_called()
for i in insts: for i in insts:
i.migrate.assert_called() i.migrate.assert_called()
from django.test import TestCase
from common.operations import operation_registry_name as op_reg_name
from vm.models import Instance, Node
from vm.operations import (
DeployOperation, DestroyOperation, FlushOperation, MigrateOperation,
RebootOperation, ResetOperation, SaveAsTemplateOperation,
ShutdownOperation, ShutOffOperation, SleepOperation, WakeUpOperation,
)
class DeployOperationTestCase(TestCase):
def test_operation_registered(self):
assert DeployOperation.id in getattr(Instance, op_reg_name)
class DestroyOperationTestCase(TestCase):
def test_operation_registered(self):
assert DestroyOperation.id in getattr(Instance, op_reg_name)
class FlushOperationTestCase(TestCase):
def test_operation_registered(self):
assert FlushOperation.id in getattr(Node, op_reg_name)
class MigrateOperationTestCase(TestCase):
def test_operation_registered(self):
assert MigrateOperation.id in getattr(Instance, op_reg_name)
class RebootOperationTestCase(TestCase):
def test_operation_registered(self):
assert RebootOperation.id in getattr(Instance, op_reg_name)
class ResetOperationTestCase(TestCase):
def test_operation_registered(self):
assert ResetOperation.id in getattr(Instance, op_reg_name)
class SaveAsTemplateOperationTestCase(TestCase):
def test_operation_registered(self):
assert SaveAsTemplateOperation.id in getattr(Instance, op_reg_name)
class ShutdownOperationTestCase(TestCase):
def test_operation_registered(self):
assert ShutdownOperation.id in getattr(Instance, op_reg_name)
class ShutOffOperationTestCase(TestCase):
def test_operation_registered(self):
assert ShutOffOperation.id in getattr(Instance, op_reg_name)
class SleepOperationTestCase(TestCase):
def test_operation_registered(self):
assert SleepOperation.id in getattr(Instance, op_reg_name)
class WakeUpOperationTestCase(TestCase):
def test_operation_registered(self):
assert WakeUpOperation.id in getattr(Instance, op_reg_name)
...@@ -4,3 +4,4 @@ coverage==3.6 ...@@ -4,3 +4,4 @@ coverage==3.6
django-discover-runner==0.4 django-discover-runner==0.4
django-nose==1.2 django-nose==1.2
mock==1.0.1 mock==1.0.1
factory-boy==2.3.1
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