diff --git a/circle/firewall/models.py b/circle/firewall/models.py index b4d4299..ccca58d 100644 --- a/circle/firewall/models.py +++ b/circle/firewall/models.py @@ -970,6 +970,10 @@ class Firewall(models.Model): logger.exception("get_dhcp_clients failed") return {} + @models.permalink + def get_absolute_url(self): + return ('network.firewall', None, {'pk': self.pk}) + class Domain(models.Model): name = models.CharField(max_length=40, validators=[val_domain], diff --git a/circle/network/forms.py b/circle/network/forms.py index af1417c..f356713 100644 --- a/circle/network/forms.py +++ b/circle/network/forms.py @@ -23,8 +23,10 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput from crispy_forms.bootstrap import FormActions, FieldWithButtons, StrictButton -from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem, - Rule, VlanGroup, SwitchPort) +from firewall.models import ( + Host, Vlan, Domain, Group, Record, BlacklistItem, Rule, VlanGroup, + SwitchPort, Firewall +) class LinkButton(BaseInput): @@ -88,6 +90,21 @@ class DomainForm(ModelForm): model = Domain +class FirewallForm(ModelForm): + helper = FormHelper() + helper.layout = Layout( + Div(Fieldset('', 'name', ) ), + FormActions( + Submit('submit', _("Save")), + LinkButton('back', _("Back"), + reverse_lazy('network.firewall_list')) + ) + ) + + class Meta: + model = Firewall + + class GroupForm(ModelForm): helper = FormHelper() helper.layout = Layout( diff --git a/circle/network/static/network/network.less b/circle/network/static/network/network.less index 2d4551c..ff6d1c6 100644 --- a/circle/network/static/network/network.less +++ b/circle/network/static/network/network.less @@ -11,3 +11,20 @@ text-align: center; width: 60px; } + +body { + padding-top: 40px; +} + +/* note: this doesn't really work */ +a i:hover { + text-decoration: none; +} + +footer { + margin-top: 45px; +} + +.messagelist { + margin-top: 25px; +} diff --git a/circle/network/tables.py b/circle/network/tables.py index b966ab6..6b38a5b 100644 --- a/circle/network/tables.py +++ b/circle/network/tables.py @@ -233,3 +233,14 @@ class HostRecordsTable(Table): fields = ("type", "fqdn") order_by = ("name", ) empty_text = _("No records.") + + +class FirewallTable(Table): + pk = LinkColumn('network.firewall', args=[A('pk')], + verbose_name="ID") + + class Meta: + model = SwitchPort + attrs = {'class': 'table table-striped'} + fields = ('pk', 'name', ) + order_by = 'pk' diff --git a/circle/network/templates/network/base.html b/circle/network/templates/network/base.html index 3cdba7d..6df77d3 100644 --- a/circle/network/templates/network/base.html +++ b/circle/network/templates/network/base.html @@ -6,32 +6,6 @@ {% block title-site %}{% trans "Network" %} | CIRCLE{% endblock %} -{% block extra_link %} - <link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:200,400&subset=latin,latin-ext' rel='stylesheet' type='text/css'> - <link href="{% static "network/network.css" %}" rel="stylesheet"> -{% endblock %} - -{% block extra_css %} -<style type="text/css"> - body { - padding-top:40px; - } - - /* note: this doesn't really work */ - a i:hover { - text-decoration: none; - } - - footer { - margin-top: 45px; - } - - .messagelist { - margin-top: 25px; - } -</style> -{% endblock %} - {% block navbar-brand %} <a class="navbar-brand" href="{% url "network.index" %}">CIRCLE Network</a> {% endblock %} diff --git a/circle/network/templates/network/firewall-create.html b/circle/network/templates/network/firewall-create.html new file mode 100644 index 0000000..4fa1bd4 --- /dev/null +++ b/circle/network/templates/network/firewall-create.html @@ -0,0 +1,18 @@ +{% extends "network/base.html" %} +{% load i18n %} +{% load staticfiles %} +{% load crispy_forms_tags %} + +{% block title-page %}{% trans "Create" %} | {% trans "firewall" %}{% endblock %} + +{% block content %} +<div class="page-header"> + <h2>{% trans "Create a new firewall" %}</h2> +</div> + +<div class="row"> + <div class="col-sm-8"> + {% crispy form %} + </div> +</div> +{% endblock %} diff --git a/circle/network/templates/network/firewall-edit.html b/circle/network/templates/network/firewall-edit.html new file mode 100644 index 0000000..add9e55 --- /dev/null +++ b/circle/network/templates/network/firewall-edit.html @@ -0,0 +1,21 @@ +{% extends "network/base.html" %} +{% load i18n %} +{% load staticfiles %} +{% load crispy_forms_tags %} + +{% block title-page %}{{ switch_port_pk }} | {% trans "firewall" %}{% endblock %} + +{% block content %} +<div class="page-header"> + <a href="{% url "network.firewall_delete" pk=object.pk %}" class="btn btn-danger pull-right"> + <i class="fa fa-times-circle"></i> {% trans "Delete this firewall" %} + </a> + <h2>{{ object.name }}</h2> +</div> + +<div class="row"> + <div class="col-sm-5"> + {% crispy form %} + </div> +</div> +{% endblock %} diff --git a/circle/network/templates/network/firewall-list.html b/circle/network/templates/network/firewall-list.html new file mode 100644 index 0000000..eb47779 --- /dev/null +++ b/circle/network/templates/network/firewall-list.html @@ -0,0 +1,20 @@ +{% extends "network/base.html" %} +{% load render_table from django_tables2 %} +{% load i18n %} +{% load l10n %} +{% load staticfiles %} + +{% block title-page %}{% trans "Firewalls" %}{% endblock %} + +{% block content %} +<div class="page-header"> + <a href="{% url "network.firewall_create" %}" class="btn btn-success pull-right"> + <i class="fa fa-plus-circle"></i> {% trans "Create a new firewall" %} + </a> + <h1>{% trans "Firewalls" %}</h1> +</div> + +<div class="table-responsive"> + {% render_table table %} +</div> +{% endblock %} diff --git a/circle/network/templates/network/menu.html b/circle/network/templates/network/menu.html index e951e42..b3391f3 100644 --- a/circle/network/templates/network/menu.html +++ b/circle/network/templates/network/menu.html @@ -22,25 +22,19 @@ {% url "network.switch_port_list" as u %} {% trans "Switch ports" as t %} {% include "network/menu-item.html" with href=u text=t %} +{% url "network.firewall_list" as u %} +{% trans "Firewalls" as t %} +{% include "network/menu-item.html" with href=u text=t %} <li class="dropdown{% if "groups" in request.path %} active{% endif %}"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown">Groups <b class="caret"></b></a> - <ul class="dropdown-menu"> - {% url "network.vlan_group_list" as u %} - {% trans "Vlan groups" as t %} - {% include "network/menu-item.html" with href=u text=t %} + <a href="#" class="dropdown-toggle" data-toggle="dropdown">Groups <b class="caret"></b></a> + <ul class="dropdown-menu"> + {% url "network.vlan_group_list" as u %} + {% trans "Vlan groups" as t %} + {% include "network/menu-item.html" with href=u text=t %} - {% url "network.group_list" as u %} - {% trans "Host groups" as t %} - {% include "network/menu-item.html" with href=u text=t %} - </ul> + {% url "network.group_list" as u %} + {% trans "Host groups" as t %} + {% include "network/menu-item.html" with href=u text=t %} + </ul> </li> - -{# <li><a href="/vlans/">{% trans "Vlans" %}</a></li> #} -{# <li><a href="/vlangroups/">{% trans "Vlan groups" %}</a></li> #} -{# <li><a href="/hostgroups/">{% trans "Host groups" %}</a></li> #} -{# <li><a href="/hosts/">{% trans "Hosts" %}</a></li> #} -{# <li><a href="/firewalls/">{% trans "Firewalls" %}</a></li> #} -{# <li><a href="/domains/">{% trans "Domains" %}</a></li> #} -{# <li><a href="/records/">{% trans "DNS records" %}</a></li> #} -{# <li><a href="/blacklist/">{% trans "Blacklist" %}</a></li> #} diff --git a/circle/network/urls.py b/circle/network/urls.py index 7973248..dc13f31 100644 --- a/circle/network/urls.py +++ b/circle/network/urls.py @@ -16,26 +16,27 @@ # with CIRCLE. If not, see <http://www.gnu.org/licenses/>. from django.conf.urls import patterns, url -from .views import (IndexView, - HostList, HostDetail, HostCreate, HostDelete, - VlanList, VlanDetail, VlanDelete, VlanCreate, - DomainList, DomainDetail, DomainDelete, DomainCreate, - GroupList, GroupDetail, GroupDelete, GroupCreate, - RecordList, RecordDetail, RecordCreate, RecordDelete, - BlacklistList, BlacklistDetail, BlacklistDelete, - BlacklistCreate, - RuleList, RuleDetail, RuleDelete, RuleCreate, - SwitchPortList, SwitchPortDetail, SwitchPortCreate, - SwitchPortDelete, - VlanGroupList, VlanGroupDetail, VlanGroupDelete, - VlanGroupCreate, - remove_host_group, add_host_group, - remove_switch_port_device, add_switch_port_device, - VlanAclUpdateView) +from .views import ( + IndexView, + HostList, HostDetail, HostCreate, HostDelete, + VlanList, VlanDetail, VlanDelete, VlanCreate, + DomainList, DomainDetail, DomainDelete, DomainCreate, + GroupList, GroupDetail, GroupDelete, GroupCreate, + RecordList, RecordDetail, RecordCreate, RecordDelete, + BlacklistList, BlacklistDetail, BlacklistDelete, BlacklistCreate, + RuleList, RuleDetail, RuleDelete, RuleCreate, + SwitchPortList, SwitchPortDetail, SwitchPortCreate, SwitchPortDelete, + VlanGroupList, VlanGroupDetail, VlanGroupDelete, VlanGroupCreate, + FirewallList, FirewallDetail, FirewallCreate, FirewallDelete, + remove_host_group, add_host_group, + remove_switch_port_device, add_switch_port_device, + VlanAclUpdateView +) urlpatterns = patterns( '', url('^$', IndexView.as_view(), name='network.index'), + # blacklist url('^blacklist/$', BlacklistList.as_view(), name='network.blacklist_list'), url('^blacklist/create$', BlacklistCreate.as_view(), @@ -44,6 +45,8 @@ urlpatterns = patterns( name='network.blacklist'), url('^blacklist/delete/(?P<pk>\d+)/$', BlacklistDelete.as_view(), name="network.blacklist_delete"), + + # domain url('^domains/$', DomainList.as_view(), name='network.domain_list'), url('^domains/create$', DomainCreate.as_view(), name='network.domain_create'), @@ -51,17 +54,33 @@ urlpatterns = patterns( name='network.domain'), url('^domains/delete/(?P<pk>\d+)/$', DomainDelete.as_view(), name="network.domain_delete"), + + # firewall + url('^firewalls/$', FirewallList.as_view(), + name='network.firewall_list'), + url('^firewalls/create$', FirewallCreate.as_view(), + name='network.firewall_create'), + url('^firewalls/(?P<pk>\d+)/$', FirewallDetail.as_view(), + name='network.firewall'), + url('^firewalls/delete/(?P<pk>\d+)/$', FirewallDelete.as_view(), + name="network.firewall_delete"), + + # group (host) url('^groups/$', GroupList.as_view(), name='network.group_list'), url('^groups/create$', GroupCreate.as_view(), name='network.group_create'), url('^groups/(?P<pk>\d+)/$', GroupDetail.as_view(), name='network.group'), url('^groups/delete/(?P<pk>\d+)/$', GroupDelete.as_view(), name="network.group_delete"), + + # host url('^hosts/$', HostList.as_view(), name='network.host_list'), url('^hosts/create$', HostCreate.as_view(), name='network.host_create'), url('^hosts/(?P<pk>\d+)/$', HostDetail.as_view(), name='network.host'), url('^hosts/delete/(?P<pk>\d+)/$', HostDelete.as_view(), name="network.host_delete"), + + # record url('^records/$', RecordList.as_view(), name='network.record_list'), url('^records/create$', RecordCreate.as_view(), name='network.record_create'), @@ -69,10 +88,14 @@ urlpatterns = patterns( name='network.record'), url('^records/delete/(?P<pk>\d+)/$', RecordDelete.as_view(), name="network.record_delete"), + + # rule url('^rules/$', RuleList.as_view(), name='network.rule_list'), url('^rules/create$', RuleCreate.as_view(), name='network.rule_create'), url('^rules/(?P<pk>\d+)/$', RuleDetail.as_view(), name='network.rule'), + + # switchport url('^switchports/$', SwitchPortList.as_view(), name='network.switch_port_list'), url('^switchports/create$', SwitchPortCreate.as_view(), @@ -81,6 +104,8 @@ urlpatterns = patterns( name='network.switch_port'), url('^switchports/delete/(?P<pk>\d+)/$', SwitchPortDelete.as_view(), name="network.switch_port_delete"), + + # vlan url('^vlans/$', VlanList.as_view(), name='network.vlan_list'), url('^vlans/create$', VlanCreate.as_view(), name='network.vlan_create'), url('^vlans/(?P<vid>\d+)/$', VlanDetail.as_view(), name='network.vlan'), @@ -88,6 +113,8 @@ urlpatterns = patterns( name='network.vlan-acl'), url('^vlans/delete/(?P<vid>\d+)/$', VlanDelete.as_view(), name="network.vlan_delete"), + + # vlangroup url('^vlangroups/$', VlanGroupList.as_view(), name='network.vlan_group_list'), url('^vlangroups/create$', VlanGroupCreate.as_view(), diff --git a/circle/network/views.py b/circle/network/views.py index bcb1c7a..c964d72 100644 --- a/circle/network/views.py +++ b/circle/network/views.py @@ -25,15 +25,20 @@ from django.http import HttpResponse, Http404 from django_tables2 import SingleTableView -from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem, - Rule, VlanGroup, SwitchPort, EthernetDevice) +from firewall.models import ( + Host, Vlan, Domain, Group, Record, BlacklistItem, Rule, VlanGroup, + SwitchPort, EthernetDevice, Firewall) from vm.models import Interface -from .tables import (HostTable, VlanTable, SmallHostTable, DomainTable, - GroupTable, RecordTable, BlacklistItemTable, RuleTable, - VlanGroupTable, SmallRuleTable, SmallGroupRuleTable, - SmallRecordTable, SwitchPortTable, SmallDhcpTable, ) -from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm, - BlacklistItemForm, RuleForm, VlanGroupForm, SwitchPortForm) +from .tables import ( + HostTable, VlanTable, SmallHostTable, DomainTable, GroupTable, + RecordTable, BlacklistItemTable, RuleTable, VlanGroupTable, + SmallRuleTable, SmallGroupRuleTable, SmallRecordTable, SwitchPortTable, + SmallDhcpTable, FirewallTable +) +from .forms import ( + HostForm, VlanForm, DomainForm, GroupForm, RecordForm, BlacklistItemForm, + RuleForm, VlanGroupForm, SwitchPortForm, FirewallForm +) from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin @@ -284,6 +289,50 @@ class DomainDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): return context +class FirewallList(LoginRequiredMixin, SuperuserRequiredMixin, + SingleTableView): + model = Firewall + table_class = FirewallTable + template_name = "network/firewall-list.html" + table_pagination = False + + +class FirewallDetail(LoginRequiredMixin, SuperuserRequiredMixin, + SuccessMessageMixin, UpdateView): + model = Firewall + template_name = "network/firewall-edit.html" + form_class = FirewallForm + success_message = _(u'Succesfully modified firewall.') + + def get_success_url(self): + if 'pk' in self.kwargs: + return reverse_lazy('network.firewall', kwargs=self.kwargs) + + def get_context_data(self, **kwargs): + context = super(FirewallDetail, self).get_context_data(**kwargs) + return context + + +class FirewallCreate(LoginRequiredMixin, SuperuserRequiredMixin, + SuccessMessageMixin, CreateView): + model = Firewall + template_name = "network/firewall-create.html" + form_class = FirewallForm + success_message = _(u'Successfully created firewall.') + + +class FirewallDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): + model = Firewall + template_name = "network/confirm/base_delete.html" + + def get_success_url(self): + next = self.request.POST.get('next') + if next: + return next + else: + return reverse_lazy('network.firewall_list') + + class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): model = Group table_class = GroupTable