Commit dedcfc62 by Kálmán Viktor

Merge branch 'master' into issue-vm-detail-fixes

Conflicts:
	circle/dashboard/tests/test_views.py
parents 1a890736 5d753e30
......@@ -24,4 +24,14 @@ celerybeat-schedule
coverage.xml
# Gettext object file:
*.mo
\ No newline at end of file
*.mo
# saml
circle/attribute-maps
circle/remote_metadata.xml
circle/*.key
circle/*.pem
# collected static files:
circle/static
circle/static_collected
......@@ -125,11 +125,6 @@ STATIC_ROOT = normpath(join(SITE_ROOT, 'static_collected'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = get_env_variable('DJANGO_STATIC_URL', default='/static/')
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = (
normpath(join(SITE_ROOT, 'static')),
)
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
......
......@@ -13,6 +13,7 @@ class Operation(object):
"""
async_queue = 'localhost.man'
required_perms = ()
do_not_call_in_templates = True
def __call__(self, **kwargs):
return self.call(**kwargs)
......@@ -28,11 +29,11 @@ class Operation(object):
def __prelude(self, kwargs):
"""This method contains the shared prelude of call and async.
"""
skip_checks = kwargs.setdefault('system', False)
skip_auth_check = kwargs.setdefault('system', False)
user = kwargs.setdefault('user', None)
parent_activity = kwargs.pop('parent_activity', None)
if not skip_checks:
if not skip_auth_check:
self.check_auth(user)
self.check_precond()
return self.create_activity(parent=parent_activity, user=user)
......@@ -42,7 +43,7 @@ class Operation(object):
"""
with activity_context(activity, on_abort=self.on_abort,
on_commit=self.on_commit):
return self._operation(activity, user, **kwargs)
return self._operation(activity=activity, user=user, **kwargs)
def _operation(self, activity, user, system, **kwargs):
"""This method is the operation's particular implementation.
......@@ -127,15 +128,46 @@ class OperatedMixin(object):
raise AttributeError("%r object has no attribute %r" %
(self.__class__.__name__, name))
def register_operation(target_cls, op_cls, op_id=None):
def get_available_operations(self, user):
"""Yield Operations that match permissions of user and preconditions.
"""
for name in getattr(self, operation_registry_name, {}):
try:
op = getattr(self, name)
op.check_auth(user)
op.check_precond()
except:
pass # unavailable
else:
yield op
def register_operation(op_cls, op_id=None, target_cls=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
try:
op_id = op_cls.id
except AttributeError:
raise NotImplementedError("Operations should specify an 'id' "
"attribute designating the name the "
"operation can be called by on its "
"host. Alternatively, provide the name "
"in the 'op_id' parameter to this call.")
if target_cls is None:
try:
target_cls = op_cls.host_cls
except AttributeError:
raise NotImplementedError("Operations should specify a 'host_cls' "
"attribute designating the host class "
"the operation should be registered to. "
"Alternatively, provide the host class "
"in the 'target_cls' parameter to this "
"call.")
if not issubclass(target_cls, OperatedMixin):
raise TypeError("%r is not a subclass of %r" %
......
from __future__ import absolute_import
from datetime import timedelta
from django.contrib.auth.models import User
......
from __future__ import absolute_import
from itertools import chain
from logging import getLogger
......
......@@ -2,22 +2,21 @@
$(function() {
/* vm migrate */
$('.vm-migrate').click(function(e) {
var icon = $(this).children("i");
var vm = $(this).data("vm-pk");
icon.removeClass("icon-truck").addClass("icon-spinner icon-spin");
/* vm operations */
$('#ops').on('click', '.operation.btn', function(e) {
var icon = $(this).children("i").addClass('icon-spinner icon-spin');
$.ajax({
type: 'GET',
url: '/dashboard/vm/' + vm + '/migrate/',
url: $(this).attr('href'),
success: function(data) {
icon.addClass("icon-truck").removeClass("icon-spinner icon-spin");
icon.removeClass("icon-spinner icon-spin");
$('body').append(data);
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
$('#confirmation-modal').modal('show');
$('#confirmation-modal').on('hidden.bs.modal', function() {
$('#confirmation-modal').remove();
});
$('#vm-migrate-node-list li').click(function(e) {
var li = $(this).closest('li');
if (li.find('input').attr('disabled'))
......
......@@ -277,6 +277,7 @@ function checkNewActivity(only_status, runs) {
success: function(data) {
if(!only_status) {
$("#activity-timeline").html(data['activities']);
$("#ops").html(data['ops']);
$("[title]").tooltip();
}
......
from __future__ import absolute_import
from django.contrib.auth.models import Group, User
from django_tables2 import Table, A
from django_tables2.columns import (TemplateColumn, Column, BooleanColumn,
......
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div class="body-content">
<div class="panel panel-default" style="margin-top: 60px;">
<div class="panel-heading">
<h3 class="no-margin">
{% if title %}
{{ title }}
{% else %}
{% trans "Confirmation" %}
{% endif %}
</h3>
</div>
<div class="panel-body">
{{ body|safe|default:"(body missing from context.)" }}
</div>
</div>
{% endblock %}
{% load i18n %}
<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
{{ body|safe|default:"(body missing from context.)" }}
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
{% extends "dashboard/operate.html" %}
{% load i18n %}
{% load sizefieldtags %}
<form method="POST" action="{% url "dashboard.views.vm-migrate" pk=vm.pk %}">
{% csrf_token %}
<ul id="vm-migrate-node-list">
{% with current=vm.node.pk selected=vm.select_node.pk %}
{% block question %}
<p>
{% blocktrans with obj=object op=op.name %}
Choose a compute node to migrate {{obj}} to.
{% endblocktrans %}
</p>
<p class="text-info">{{op.name}}: {{op.description}}</p>
{% endblock %}
{% block formfields %}
<ul id="vm-migrate-node-list" class="list-unstyled">
{% with current=object.node.pk selected=object.select_node.pk %}
{% for n in nodes %}
<li class="panel panel-default"><div class="panel-body">
<label for="migrate-to-{{n.pk}}">
......@@ -22,5 +31,4 @@
{% endfor %}
{% endwith %}
</ul>
<button type="submit" class="btn btn-primary btn-sm"><i class="icon-truck"></i> Migrate</button>
</form>
{% endblock %}
......@@ -16,7 +16,7 @@
<div class="list-group" id="node-list-view">
{% for i in nodes %}
<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>
{% endfor %}
<div href="#" class="list-group-item list-group-footer">
......
{% load i18n %}
{% block question %}
<p>
{% blocktrans with obj=object op=op.name %}
Do you want to do the following operation on {{obj}}:
<strong>{{op}}</strong>?
{% endblocktrans %}
</p>
<p class="text-info">{{op.name}}: {{op.description}}</p>
{% endblock %}
<form method="POST" action="{{url}}">{% csrf_token %}
{% block formfields %}{% endblock %}
<div class="pull-right">
<a class="btn btn-default" href="{{object.get_absolute_url}}"
data-dismiss="modal">{% trans "Cancel" %}</a>
<button class="btn btn-danger" type="submit">{% if op.icon %}<i class="icon-{{op.icon}}"></i> {% endif %}{{ op|capfirst }}</button>
</div>
</form>
......@@ -6,54 +6,8 @@
{% block content %}
<div class="body-content">
<div class="page-header">
<div class="pull-right" style="padding-top: 15px;">
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="sleep" />
<button title="{% trans "Sleep" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-moon"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="deploy" />
<button title="{% trans "Deploy" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-play"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="wake_up" />
<button title="{% trans "Wake up" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-sun"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="shut_down" />
<button title="{% trans "Shut down" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-off"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="reboot" />
<button title="{% trans "Reboot (ctrl + alt + del)" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-refresh"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="reset" />
<button title="{% trans "Reset (power cycle)" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-bolt"></i></button>
</form>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="shut_off"/>
<button title="{% trans "Shut off" %}" class="btn btn-default btn-xs" type="submit">
<i class="icon-ban-circle"></i>
</button>
</form>
<a title="Migrate" data-vm-pk="{{ instance.pk }}" href="{% url "dashboard.views.vm-migrate" pk=instance.pk %}" class="btn btn-default btn-xs vm-migrate">
<i class="icon-truck"></i>
</a>
<form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}">
{% csrf_token %}
<input type="hidden" name="save_as" />
<button title="{% trans "Save as template" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-save"></i></button>
</form>
<a title="{% trans "Destroy" %}" href="{% url "dashboard.views.delete-vm" pk=instance.pk %}" class="btn btn-default btn-xs vm-delete" data-vm-pk="{{ instance.pk }}"><i class="icon-remove"></i></a>
<a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs vm-details-help-button"><i class="icon-question"></i></a>
<div class="pull-right" style="padding-top: 15px;" id="ops">
{% include "dashboard/vm-detail/_operations.html" %}
</div>
<h1>
<div id="vm-details-rename" class="vm-details-home-rename-form-div">
......@@ -72,46 +26,6 @@
</div>
<small>{{ instance.primary_host.get_fqdn }}</small>
</h1>
<div class="vm-details-help js-hidden">
<ul style="list-style: none;">
<li>
<strong>{% trans "Sleep" %}:</strong>
{% trans "Suspend virtual machine with memory dump." %}
</li>
<li>
<strong>{% trans "Wake up" %}:</strong>
{% trans "Wake up suspended machine." %}
</li>
<li>
<strong>{% trans "Shutdown" %}:</strong>
{% trans "Shutdown virtual machine with ACPI signal." %}
</li>
<li>
<strong>{% trans "Reboot (ctrl + alt + del)" %}:</strong>
{% trans "Reboot virtual machine with Ctrl+Alt+Del signal." %}
</li>
<li>
<strong>{% trans "Reset (power cycle)" %}:</strong>
{% trans "Reset virtual machine (reset button)" %}
</li>
<li>
<strong>{% trans "Shut off" %}:</strong>
{% trans "Shut off VM. (plug-out)" %}
</li>
<li>
<strong>{% trans "Migrate" %}:</strong>
{% trans "Live migrate running vm to another node." %}
</li>
<li>
<strong>{% trans "Save as template" %}:</strong>
{% trans "Shut down the virtual machine, and save it as a new template." %}
</li>
<li>
<strong>{% trans "Destroy" %}:</strong>
{% trans "Remove virtual machine and its networks." %}
</li>
</ul>
</div>
<div style="clear: both;"></div>
</div>
<div class="row">
......
{% load i18n %}
{% for op in ops %}
<a href="{{op.get_url}}" class="operation operation-{{op.op}} btn btn-default btn-xs"
title="{{op.name}}: {{op.description}}">
<i class="icon-{{op.icon}}"></i>
<span class="sr-only">{{op.name}}</span>
</a>
{% endfor %}
......@@ -3,10 +3,11 @@ 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.core.exceptions import PermissionDenied
from django.http import HttpRequest, Http404
from dashboard.views import InstanceActivityDetail, InstanceActivity
from ..views import InstanceActivityDetail, InstanceActivity
from ..views import vm_ops, Instance
class ViewUserTestCase(unittest.TestCase):
......@@ -36,6 +37,82 @@ class ViewUserTestCase(unittest.TestCase):
self.assertEquals(view(request, pk=1234).render().status_code, 200)
class VmOperationViewTestCase(unittest.TestCase):
def test_available(self):
request = FakeRequestFactory(superuser=True)
view = vm_ops['destroy']
with patch.object(view, 'get_object') as go:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.destroy = Instance._ops['destroy'](inst)
go.return_value = inst
self.assertEquals(
view.as_view()(request, pk=1234).render().status_code, 200)
def test_unpermitted(self):
request = FakeRequestFactory()
view = vm_ops['destroy']
with patch.object(view, 'get_object') as go:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.destroy = Instance._ops['destroy'](inst)
inst.has_level.return_value = False
go.return_value = inst
with self.assertRaises(PermissionDenied):
view.as_view()(request, pk=1234).render()
def test_migrate(self):
request = FakeRequestFactory(POST={'node': 1})
view = vm_ops['migrate']
with patch.object(view, 'get_object') as go, \
patch('dashboard.views.messages') as msg, \
patch('dashboard.views.get_object_or_404') as go4:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.migrate = Instance._ops['migrate'](inst)
inst.migrate.async = MagicMock()
inst.has_level.return_value = True
go.return_value = inst
go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234)['location']
assert not msg.error.called
def test_migrate_failed(self):
request = FakeRequestFactory(POST={'node': 1})
view = vm_ops['migrate']
with patch.object(view, 'get_object') as go, \
patch('dashboard.views.messages') as msg, \
patch('dashboard.views.get_object_or_404') as go4:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.migrate = Instance._ops['migrate'](inst)
inst.migrate.async = MagicMock()
inst.migrate.async.side_effect = Exception
inst.has_level.return_value = True
go.return_value = inst
go4.return_value = MagicMock()
assert view.as_view()(request, pk=1234)['location']
assert msg.error.called
def test_migrate_template(self):
request = FakeRequestFactory()
view = vm_ops['migrate']
with patch.object(view, 'get_object') as go:
inst = MagicMock(spec=Instance)
inst._meta.object_name = "Instance"
inst.migrate = Instance._ops['migrate'](inst)
inst.has_level.return_value = True
go.return_value = inst
self.assertEquals(
view.as_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.
......@@ -48,12 +125,12 @@ def FakeRequestFactory(*args, **kwargs):
request = HttpRequest()
request.user = user
request.session = kwargs.get('session', {})
if kwargs.get('POST'):
if kwargs.get('POST') is not None:
request.method = 'POST'
request.POST = kwargs.get('POST')
else:
request.method = 'GET'
request.POST = kwargs.get('GET', {})
request.GET = kwargs.get('GET', {})
return request
......
import json
from unittest import skip
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User, Group
......@@ -279,6 +280,7 @@ class VmDetailTest(LoginMixin, TestCase):
self.assertEqual(response.status_code, 403)
self.assertEqual(disks, inst.disks.count())
@skip("until fix merged")
def test_permitted_vm_disk_add(self):
c = Client()
self.login(c, "user1")
......@@ -495,9 +497,11 @@ class VmDetailTest(LoginMixin, TestCase):
mock_method.side_effect = inst.wake_up
inst.manual_state_change('RUNNING')
inst.set_level(self.u2, 'owner')
self.assertRaises(inst.WrongStateError, c.post,
"/dashboard/vm/1/", {'wake_up': True})
self.assertEqual(inst.status, 'RUNNING')
with patch('dashboard.views.messages') as msg:
c.post("/dashboard/vm/1/op/wake_up/")
assert msg.error.called
inst = Instance.objects.get(pk=1)
self.assertEqual(inst.status, 'RUNNING') # mocked anyway
assert mock_method.called
def test_permitted_wake_up(self):
......@@ -511,7 +515,9 @@ class VmDetailTest(LoginMixin, TestCase):
inst.get_remote_queue_name = Mock(return_value='test')
inst.manual_state_change('SUSPENDED')
inst.set_level(self.u2, 'owner')
response = c.post("/dashboard/vm/1/", {'wake_up': True})
with patch('dashboard.views.messages') as msg:
response = c.post("/dashboard/vm/1/op/wake_up/")
assert not msg.error.called
self.assertEqual(response.status_code, 302)
self.assertEqual(inst.status, 'RUNNING')
assert new_wake_up.called
......@@ -523,8 +529,11 @@ class VmDetailTest(LoginMixin, TestCase):
inst = Instance.objects.get(pk=1)
inst.manual_state_change('SUSPENDED')
inst.set_level(self.u2, 'user')
response = c.post("/dashboard/vm/1/", {'wake_up': True})
self.assertEqual(response.status_code, 403)
with patch('dashboard.views.messages') as msg:
response = c.post("/dashboard/vm/1/op/wake_up/")
assert msg.error.called
self.assertEqual(response.status_code, 302)
inst = Instance.objects.get(pk=1)
self.assertEqual(inst.status, 'SUSPENDED')
def test_non_existing_template_get(self):
......
from django.conf.urls import patterns, url
from __future__ import absolute_import
from django.conf.urls import patterns, url, include
from vm.models import Instance
from .views import (
......@@ -34,7 +35,7 @@ urlpatterns = patterns(
name="dashboard.views.template-list"),
url(r"^template/delete/(?P<pk>\d+)/$", TemplateDelete.as_view(),
name="dashboard.views.template-delete"),
url(r'^vm/(?P<pk>\d+)/op/', include('dashboard.vm.urls')),
url(r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$', PortDelete.as_view(),
name='dashboard.views.remove-port'),
url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(),
......
from django.conf.urls import patterns, url
from ..views import vm_ops
urlpatterns = patterns('',
*(url(r'^%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in vm_ops.iteritems()))
......@@ -2,7 +2,7 @@
from django.contrib import admin
from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall,
Domain, Record, Blacklist,
Domain, Record, BlacklistItem,
SwitchPort, EthernetDevice)
from django import contrib
......@@ -38,7 +38,7 @@ class HostAdmin(admin.ModelAdmin):
class HostInline(contrib.admin.TabularInline):
model = Host
fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip',
fields = ('hostname', 'ipv4', 'ipv6', 'external_ipv4', 'mac', 'shared_ip',
'owner', 'reverse')
......@@ -114,8 +114,8 @@ class RecordAdmin(admin.ModelAdmin):
list_display = ('name', 'type', 'address', 'ttl', 'host', 'owner')
class BlacklistAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at')
class BlacklistItemAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'type', 'reason', 'created_at', 'modified_at')
class SwitchPortAdmin(admin.ModelAdmin):
......@@ -133,6 +133,6 @@ admin.site.register(VlanGroup)
admin.site.register(Firewall, FirewallAdmin)
admin.site.register(Domain, DomainAdmin)
admin.site.register(Record, RecordAdmin)
admin.site.register(Blacklist, BlacklistAdmin)
admin.site.register(BlacklistItem, BlacklistItemAdmin)
admin.site.register(SwitchPort)
admin.site.register(EthernetDevice, EthernetDeviceAdmin)
......@@ -5,7 +5,8 @@ from netaddr import IPAddress, AddrFormatError
from datetime import datetime, timedelta
from itertools import product
from .models import (Host, Rule, Vlan, Domain, Record, Blacklist, SwitchPort)
from .models import (Host, Rule, Vlan, Domain, Record, BlacklistItem,
SwitchPort)
from .iptables import IptRule, IptChain
import django.conf
from django.db.models import Q
......@@ -136,7 +137,7 @@ def ipset():
week = datetime.now() - timedelta(days=2)
filter_ban = (Q(type='tempban', modified_at__gte=week) |
Q(type='permban'))
return Blacklist.objects.filter(filter_ban).values('ipv4', 'reason')
return BlacklistItem.objects.filter(filter_ban).values('ipv4', 'reason')
def ipv6_to_octal(ipv6):
......
......@@ -89,7 +89,7 @@ class IptChain(object):
self.rules.add(rule)
def sort(self):
return sorted(list(self.rules))
return sorted(list(self.rules), reverse=True)
def __len__(self):
return len(self.rules)
......
......@@ -32,8 +32,8 @@ class Migration(SchemaMigration):
if rule.nat:
# swap
tmp = rule.dport
# rule.dport = rule.nat_external_port
# rule.nat_external_port = tmp
rule.dport = rule.nat_external_port
rule.nat_external_port = tmp
if rule.direction == '0':
rule.direction = 'out'
elif rule.direction == '1':
......
......@@ -923,7 +923,7 @@ class EthernetDevice(models.Model):
return self.name
class Blacklist(models.Model):
class BlacklistItem(models.Model):
CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'),
('whitelist', 'whitelist'), ('tempwhite', 'tempwhite'))
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True)
......@@ -945,11 +945,15 @@ class Blacklist(models.Model):
def save(self, *args, **kwargs):
self.full_clean()
super(Blacklist, self).save(*args, **kwargs)
super(BlacklistItem, self).save(*args, **kwargs)
def __unicode__(self):
return self.ipv4
class Meta(object):
verbose_name = _('blacklist item')
verbose_name_plural = _('blacklist')
@models.permalink
def get_absolute_url(self):
return ('network.blacklist', None, {'pk': self.pk})
......@@ -959,7 +963,7 @@ def send_task(sender, instance, created=False, **kwargs):
reloadtask.apply_async(queue='localhost.man', args=[sender.__name__])
for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group, Blacklist,
SwitchPort, EthernetDevice]:
for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group,
BlacklistItem, SwitchPort, EthernetDevice]:
post_save.connect(send_task, sender=sender)
post_delete.connect(send_task, sender=sender)
......@@ -11,7 +11,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .tasks.local_tasks import reloadtask
from .models import Blacklist, Host
from .models import BlacklistItem, Host
def reload_firewall(request):
......@@ -38,7 +38,7 @@ def firewall_api(request):
raise Exception(_("Wrong password."))
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.snort_message = data["snort_message"]
if created:
......
......@@ -5,7 +5,7 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput
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)
......@@ -26,7 +26,7 @@ class LinkButton(BaseInput):
super(LinkButton, self).__init__(name, text, *args, **kwargs)
class BlacklistForm(ModelForm):
class BlacklistItemForm(ModelForm):