Commit c152076c by Czémán Arnold

network, dashboard: add privileged and non-privileged user support to Vxlan…

network, dashboard: add privileged and non-privileged user support to Vxlan views, and add access management
parent e4b766d1
......@@ -587,3 +587,7 @@ REQUEST_HOOK_URL = get_env_variable("REQUEST_HOOK_URL", "")
SSHKEY_EMAIL_ADD_KEY = False
TWO_FACTOR_ISSUER = get_env_variable("TWO_FACTOR_ISSUER", "CIRCLE")
DEFAULT_USERNET_VLAN_NAME = (
get_env_variable("DEFAULT_USERNET_VLAN_NAME", "usernet"))
USERNET_MAX = 2 ** 12
......@@ -55,7 +55,7 @@
</div>
{% endif %}
{% if perms.vxlan %}
{% if perms.network.create_vxlan %}
<div class="col-lg-4 col-sm-6">
{% include "dashboard/index-vxlans.html" %}
</div>
......
......@@ -104,8 +104,7 @@ class IndexView(LoginRequiredMixin, TemplateView):
# vxlan
if user.has_perm('network.create_vxlan'):
context['vxlans'] = Vxlan.get_objects_with_level(
'user', user, disregard_superuser=True).all()[:5]
context['vxlans'] = Vxlan.objects.all()[:5]
'user', user).all()[:5]
# toplist
if settings.STORE_URL:
......
......@@ -20,7 +20,7 @@ from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
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, Field
from crispy_forms.bootstrap import FormActions, FieldWithButtons, StrictButton
from firewall.models import (
......@@ -351,7 +351,7 @@ class VlanGroupForm(ModelForm):
fields = ("name", "vlans", "description", "owner", )
class VxlanForm(ModelForm):
class VxlanSuperUserForm(ModelForm):
helper = FormHelper()
helper.layout = Layout(
Div(
......@@ -366,12 +366,36 @@ class VxlanForm(ModelForm):
)
),
FormActions(
Submit('submit', _("Save")),
LinkButton('back', _("Back"), reverse_lazy(
Submit('submit', _('Save')),
LinkButton('back', _('Back'), reverse_lazy(
'network.vxlan-list'))
)
)
class Meta:
model = Vxlan
fields = ('name', 'vni', 'vlan', 'description', 'comment', 'owner', )
class VxlanForm(ModelForm):
helper = FormHelper()
helper.layout = Layout(
Div(
Fieldset(
'',
'name',
'description',
'comment',
Field('vni', type='hidden'),
)
),
FormActions(
Submit('submit', _('Save')),
LinkButton('back', _('Back'), reverse_lazy(
'network.vxlan-list'))
)
)
class Meta:
model = Vxlan
fields = ("name", "vni", "vlan", "description", "comment", "owner", )
fields = ('name', 'description', 'comment', 'vni', )
{% extends "network/base.html" %}
{% extends "dashboard/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
......
{% extends "network/base.html" %}
{% extends "dashboard/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
......
{% extends "network/base.html" %}
{% extends "dashboard/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
......
{% extends "network/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block title-page %}{% trans "Create" %} | {% trans "vxlan" %}{% endblock %}
{% block content %}
<div class="page-header">
<h2>{% trans "Create a new vxlan" %}</h2>
</div>
<div class="row">
<div class="col-sm-8">
{% crispy form %}
</div>
<div class="col-sm-4">
</div>
</div>
{% endblock %}
{% extends "network/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block title-page %}{{ form.name.value }} | {% trans "vxlan" %}{% endblock %}
{% block content %}
<div class="page-header">
<a href="{% url "network.vxlan-delete" vni=vxlan.vni %}" class="btn btn-danger pull-right"><i class="fa fa-times-circle"></i> {% trans "Delete this vxlan" %}</a>
<h2>{{ form.name.value }} <small>{% trans "details of vxlan" %}</small></h2>
</div>
<div class="row">
<div class="col-sm-6">
{% crispy form %}
</div>
<div class="col-sm-6">
<div class="page-header">
<h3>{% trans "Connected virtual machines" %}</h3>
</div>
{% render_table vm_list %}
<div class="page-header">
<h3>{% trans "Manage access" %}</h3>
</div>
{% include "dashboard/_manage_access.html" with table_id="vxlan-access-table" %}
</div>
</div>
{% endblock %}
{% extends "network/base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% load l10n %}
{% load staticfiles %}
{% block title-page %}{% trans "Vxlans" %}{% endblock %}
{% block content %}
<div class="page-header">
<a href="{% url "network.vxlan-create" %}" class="btn btn-success pull-right"><i class="fa fa-plus-circle"></i> {% trans "Create a new vxlan" %}</a>
<h1>Vxlans <small>{% trans "list of all vxlans" %}</small></h1>
</div>
<div class="table-responsive">
{% render_table table %}
</div>
{% endblock %}
......@@ -15,16 +15,22 @@
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import logging
import random
from collections import OrderedDict
from netaddr import IPNetwork
from django.views.generic import (TemplateView, UpdateView, DeleteView,
CreateView)
from django.core.exceptions import ValidationError
from django.views.generic import (
TemplateView, UpdateView, DeleteView, CreateView
)
from django.core.exceptions import (
ValidationError, PermissionDenied, ImproperlyConfigured
)
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, Http404
from django.db.models import Q
from django.conf import settings
from django_tables2 import SingleTableView
......@@ -34,6 +40,8 @@ from firewall.models import (
)
from network.models import Vxlan
from vm.models import Interface
from common.views import CreateLimitedResourceMixin
from acl.views import CheckedObjectMixin
from .tables import (
HostTable, VlanTable, SmallHostTable, DomainTable, GroupTable,
RecordTable, BlacklistItemTable, RuleTable, VlanGroupTable,
......@@ -42,7 +50,8 @@ from .tables import (
)
from .forms import (
HostForm, VlanForm, DomainForm, GroupForm, RecordForm, BlacklistItemForm,
RuleForm, VlanGroupForm, SwitchPortForm, FirewallForm, VxlanForm
RuleForm, VlanGroupForm, SwitchPortForm, FirewallForm,
VxlanForm, VxlanSuperUserForm,
)
from django.contrib import messages
......@@ -73,6 +82,9 @@ except ImportError:
content_type=content_type)
logger = logging.getLogger(__name__)
class MagicMixin(object):
def get(self, *args, **kwargs):
......@@ -509,7 +521,7 @@ class HostDetail(HostMagicMixin, LoginRequiredMixin, SuperuserRequiredMixin,
class HostCreate(HostMagicMixin, LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, InitialOwnerMixin, CreateView):
SuccessMessageMixin, CreateView):
model = Host
template_name = "network/host-create.html"
form_class = HostForm
......@@ -916,20 +928,27 @@ class VlanGroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
return reverse_lazy('network.vlan_group_list')
class VxlanList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
class VxlanList(LoginRequiredMixin, SingleTableView):
model = Vxlan
table_class = VxlanTable
template_name = "network/vxlan-list.html"
table_pagination = False
def get_template_names(self):
if self.request.user.is_superuser:
return ["network/vxlan-superuser-list.html"]
else:
return ["network/vxlan-list.html"]
def get_queryset(self):
return Vxlan.get_objects_with_level('user', self.request.user)
def get(self, *args, **kwargs):
if self.request.is_ajax():
return self._create_ajax_request()
return super(VxlanList, self).get(*args, **kwargs)
def _create_ajax_request(self):
vxlans = Vxlan.get_objects_with_level(
'user', self.request.user)
vxlans = self.get_queryset()
vxlans = [{
'pk': i.pk,
'url': reverse_lazy('network.vxlan', args=[i.pk]),
......@@ -944,16 +963,25 @@ class VxlanAclUpdateView(AclUpdateView):
model = Vxlan
class VxlanDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class VxlanDetail(LoginRequiredMixin, CheckedObjectMixin,
SuccessMessageMixin, UpdateView):
model = Vxlan
template_name = "network/vxlan-edit.html"
form_class = VxlanForm
slug_field = 'vni'
slug_url_kwarg = 'vni'
success_message = _(u'Succesfully modified vlan %(name)s.')
success_url = reverse_lazy('network.vxlan-list')
def get_template_names(self):
if self.request.user.is_superuser:
return ["network/vxlan-superuser-edit.html"]
else:
return ["network/vxlan-edit.html"]
def get_form_class(self, is_post=False):
if self.request.user.is_superuser:
return VxlanSuperUserForm
return VxlanForm
def get_context_data(self, **kwargs):
context = super(VxlanDetail, self).get_context_data(**kwargs)
context['vm_list'] = SmallVmTable(self.object.vm_interface.all())
......@@ -962,18 +990,93 @@ class VxlanDetail(LoginRequiredMixin, SuperuserRequiredMixin,
context['aclform'] = AclUserOrGroupAddForm()
return context
def post(self, *args, **kwargs):
if not self.object.has_level(self.request.user, 'owner'):
raise PermissionDenied()
return super(VxlanDetail, self).post(*args, **kwargs)
class VxlanCreate(LoginRequiredMixin, SuccessMessageMixin,
InitialOwnerMixin, CreateView):
class VxlanCreate(LoginRequiredMixin, CreateLimitedResourceMixin,
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Vxlan
template_name = "network/vxlan-create.html"
form_class = VxlanForm
profile_attribute = 'network_limit'
resource_name = _('Virtual network')
success_message = _(u'Successfully created vxlan %(name)s.')
def get_template_names(self):
if self.request.user.is_superuser:
return ["network/vxlan-superuser-create.html"]
else:
return ["network/vxlan-create.html"]
def get_form_class(self, is_post=False):
if self.request.user.is_superuser:
return VxlanSuperUserForm
return VxlanForm
class VxlanDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
def get_initial(self):
initial = super(VxlanCreate, self).get_initial()
initial['vni'] = self._generate_vni()
return initial
def get_default_vlan(self):
vlan = Vlan.objects.filter(
name=settings.DEFAULT_USERNET_VLAN_NAME).first()
if vlan is None:
msg = (_('Cannot find server vlan: %s') %
settings.DEFAULT_USERNET_VLAN_NAME)
if self.request.user.is_superuser:
messages.error(self.request, msg)
logger.error(msg)
raise ImproperlyConfigured()
return vlan
def form_valid(self, form):
obj = form.save(commit=False)
obj.owner = self.request.user
obj.vlan = self.get_default_vlan()
try:
obj.full_clean()
obj.save()
obj.set_level(obj.owner, 'owner')
self.object = obj
except Exception as e:
msg = _('Unexpected error occured. '
'Please try again or contact administrator!')
messages.error(self.request, msg)
logger.exception(e)
return redirect(self.get_success_url())
def form_invalid(self, form):
# When multiple client get same VNI value
if 'vni' in form.errors.as_data():
messages.error(self.request, _('Cannot create virtual network.'
' Please try again.'))
return redirect('network.vxlan-create')
return super(VxlanCreate, self).form_invalid(form)
def _generate_vni(self):
if Vxlan.objects.count() == settings.USERNET_MAX:
msg = _('Cannot find unused VNI value. '
'Please contact administrator!')
messages.error(self.request, msg)
logger.error(msg)
else:
full_range = set(range(0, settings.USERNET_MAX))
used_values = {vni[0] for vni in Vxlan.objects.values_list('vni')}
free_values = full_range - used_values
return random.choice(list(free_values))
class VxlanDelete(LoginRequiredMixin, CheckedObjectMixin, DeleteView):
model = Vlan
template_name = "network/confirm/base_delete.html"
read_level = 'owner'
def get_template_names(self):
if self.request.user.is_superuser:
return ["network/confirm/base_delete.html"]
else:
return ["dashboard/confirm/base-delete.html"]
def get_success_url(self):
next = self.request.POST.get('next')
......@@ -987,10 +1090,11 @@ class VxlanDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
return Vxlan.objects.get(vni=self.kwargs['vni'])
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
if unicode(self.object) != request.POST.get('confirm'):
messages.error(request, _(u"Object name does not match."))
return self.get(request, *args, **kwargs)
if self.request.user.is_superuser:
self.object = self.get_object()
if unicode(self.object) != request.POST.get('confirm'):
messages.error(request, _(u"Object name does not match."))
return self.get(request, *args, **kwargs)
response = super(VxlanDelete, self).delete(request, *args, **kwargs)
messages.success(request, _(u"Vxlan successfully deleted."))
......@@ -998,7 +1102,8 @@ class VxlanDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
def get_context_data(self, **kwargs):
context = super(VxlanDelete, self).get_context_data(**kwargs)
context['confirmation'] = True
if self.request.user.is_superuser:
context['confirmation'] = True
return context
......
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