Skip to content

Commit

Permalink
Integrated Device model changes from django-netjsonconfig openwisp#7
Browse files Browse the repository at this point in the history
Closes openwisp#7
  • Loading branch information
nemesifier committed May 15, 2017
1 parent 82b768e commit 714fd5f
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 101 deletions.
29 changes: 26 additions & 3 deletions openwisp_controller/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_repr(self, obj):

def get_queryset(self, request):
"""
if current user is not superuser, show only the
If current user is not superuser, show only the
objects associated to organizations she's associated with
"""
qs = super(MultitenantAdminMixin, self).get_queryset(request)
Expand All @@ -42,14 +42,15 @@ def get_queryset(self, request):
organizations = request.user.organizations_pk
return qs.filter(organization__in=organizations)

def get_form(self, request, obj=None, **kwargs):
def _edit_form(self, request, form):
"""
Modifies the form querysets as follows;
if current user is not superuser:
* show only relevant organizations
* show only relations associated to relevant organizations
or shared relations
else show everything
"""
form = super(MultitenantAdminMixin, self).get_form(request, obj, **kwargs)
fields = form.base_fields
if not request.user.is_superuser:
orgs_pk = request.user.organizations_pk
Expand All @@ -67,8 +68,17 @@ def get_form(self, request, obj=None, **kwargs):
continue
field = fields[field_name]
field.queryset = field.queryset.filter(q)

def get_form(self, request, obj=None, **kwargs):
form = super(MultitenantAdminMixin, self).get_form(request, obj, **kwargs)
self._edit_form(request, form)
return form

def get_formset(self, request, obj=None, **kwargs):
formset = super(MultitenantAdminMixin, self).get_formset(request, obj=None, **kwargs)
self._edit_form(request, formset.form)
return formset


class MultitenantOrgFilter(admin.RelatedFieldListFilter):
"""
Expand All @@ -91,3 +101,16 @@ class MultitenantTemplateFilter(MultitenantOrgFilter):
organizations the current user is associated with
"""
multitenant_lookup = 'organization__in'


class AlwaysHasChangedMixin(object):
def has_changed(self):
"""
This django-admin trick ensures the settings
are saved even if default values are unchanged
(without this trick new setting objects won't be
created unless users change the default values)
"""
if self.instance._state.adding:
return True
return super(AlwaysHasChangedMixin, self).has_changed()
58 changes: 31 additions & 27 deletions openwisp_controller/config/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,47 @@
from django.urls import reverse

from django_netjsonconfig import settings as django_netjsonconfig_settings
from django_netjsonconfig.base.admin import (AbstractConfigAdmin,
AbstractConfigForm,
from django_netjsonconfig.base.admin import (AbstractConfigForm,
AbstractConfigInline,
AbstractDeviceAdmin,
AbstractTemplateAdmin,
AbstractVpnAdmin, AbstractVpnForm,
BaseConfigAdmin, BaseForm)
from openwisp_controller.admin import (MultitenantAdminMixin,
BaseForm)
from openwisp_controller.admin import (AlwaysHasChangedMixin,
MultitenantAdminMixin,
MultitenantOrgFilter,
MultitenantTemplateFilter)
from openwisp_users.admin import OrganizationAdmin as BaseOrganizationAdmin
from openwisp_users.models import Organization

from .models import Config, OrganizationConfigSettings, Template, Vpn
from .models import Config, Device, OrganizationConfigSettings, Template, Vpn


class ConfigForm(AbstractConfigForm):
class ConfigForm(AlwaysHasChangedMixin, AbstractConfigForm):
class Meta(AbstractConfigForm.Meta):
model = Config

def clean_templates(self):
org = Organization.objects.get(pk=self.data['organization'])
self.cleaned_data['organization'] = org
return super(ConfigForm, self).clean_templates()

class ConfigAdmin(MultitenantAdminMixin, AbstractConfigAdmin):
form = ConfigForm

class ConfigInline(MultitenantAdminMixin, AbstractConfigInline):
model = Config
form = ConfigForm
extra = 0
multitenant_shared_relations = ('templates',)


class DeviceAdmin(MultitenantAdminMixin, AbstractDeviceAdmin):
inlines = [ConfigInline]
list_filter = [('organization', MultitenantOrgFilter),
'backend',
('templates', MultitenantTemplateFilter),
'status',
'config__backend',
('config__templates', MultitenantTemplateFilter),
'config__status',
'created']
list_select_related = ('config', 'organization')

def _get_default_template_urls(self):
"""
Expand All @@ -46,17 +59,17 @@ def _get_default_template_urls(self):
return json.dumps(urls)

def get_extra_context(self, pk=None):
ctx = super(ConfigAdmin, self).get_extra_context(pk)
ctx = super(DeviceAdmin, self).get_extra_context(pk)
ctx.update({'default_template_urls': self._get_default_template_urls()})
return ctx

def add_view(self, request, form_url='', extra_context=None):
extra_context = self.get_extra_context()
return super(BaseConfigAdmin, self).add_view(request, form_url, extra_context)
return super(DeviceAdmin, self).add_view(request, form_url, extra_context)


ConfigAdmin.list_display.insert(1, 'organization')
ConfigAdmin.fields.insert(1, 'organization')
DeviceAdmin.list_display.insert(1, 'organization')
DeviceAdmin.fields.insert(1, 'organization')


class TemplateForm(BaseForm):
Expand Down Expand Up @@ -90,17 +103,8 @@ class VpnAdmin(MultitenantAdminMixin, AbstractVpnAdmin):
VpnAdmin.fields.insert(2, 'organization')


class ConfigSettingsForm(forms.ModelForm):
def has_changed(self):
"""
This django-admin trick ensures the settings
are saved even if default values are unchanged
(without this trick new setting objects won't be
created unless users change the default values)
"""
if self.instance._state.adding:
return True
return super(ConfigSettingsForm, self).has_changed()
class ConfigSettingsForm(AlwaysHasChangedMixin, forms.ModelForm):
pass


class ConfigSettingsInline(admin.StackedInline):
Expand All @@ -113,7 +117,7 @@ class OrganizationAdmin(BaseOrganizationAdmin):
inlines = [ConfigSettingsInline] + BaseOrganizationAdmin.inlines


admin.site.register(Config, ConfigAdmin)
admin.site.register(Device, DeviceAdmin)
admin.site.register(Template, TemplateAdmin)
admin.site.register(Vpn, VpnAdmin)

Expand Down
16 changes: 9 additions & 7 deletions openwisp_controller/config/controller/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
BaseReportStatusView)
from django_netjsonconfig.utils import invalid_response

from ..models import Config, OrganizationConfigSettings
from ..models import Device, OrganizationConfigSettings


class ActiveOrgMixin(object):
Expand All @@ -19,19 +19,19 @@ def get_object(self, *args, **kwargs):


class ChecksumView(ActiveOrgMixin, BaseChecksumView):
model = Config
model = Device


class DownloadConfigView(ActiveOrgMixin, BaseDownloadConfigView):
model = Config
model = Device


class ReportStatusView(ActiveOrgMixin, BaseReportStatusView):
model = Config
model = Device


class RegisterView(BaseRegisterView):
model = Config
model = Device

def forbidden(self, request):
"""
Expand All @@ -54,8 +54,10 @@ def forbidden(self, request):
self.organization = org_settings.organization

def init_object(self, **kwargs):
kwargs['organization'] = self.organization
return super(RegisterView, self).init_object(**kwargs)
config = super(RegisterView, self).init_object(**kwargs)
config.organization = self.organization
config.device.organization = self.organization
return config

def get_template_queryset(self, config):
queryset = super(RegisterView, self).get_template_queryset(config)
Expand Down
46 changes: 46 additions & 0 deletions openwisp_controller/config/migrations/0004_add_device_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-11 18:29
from __future__ import unicode_literals

import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import django_netjsonconfig.utils
import model_utils.fields
import re
import uuid


class Migration(migrations.Migration):

dependencies = [
('openwisp_users', '0001_initial'),
('config', '0003_template_tags'),
]

operations = [
migrations.CreateModel(
name='Device',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64, unique=True)),
('mac_address', models.CharField(help_text='primary mac address', max_length=17, unique=True, validators=[django.core.validators.RegexValidator(re.compile('^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})', 32), code='invalid', message='Must be a valid mac address.')])),
('key', models.CharField(db_index=True, default=django_netjsonconfig.utils.get_random_key, help_text='unique device key', max_length=64, unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[^\\s/\\.]+$', 32), code='invalid', message='Key must not contain spaces, dots or slashes.')])),
('model', models.CharField(blank=True, help_text='device model and manufacturer', max_length=64)),
('os', models.CharField(blank=True, help_text='operating system identifier', max_length=128, verbose_name='operating system')),
('notes', models.TextField(blank=True)),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='openwisp_users.Organization', verbose_name='organization')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='config',
name='device',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='config.Device'),
),
]
38 changes: 38 additions & 0 deletions openwisp_controller/config/migrations/0005_populate_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


def forward(apps, schema_editor):
"""
Creates a Device record for each existing Config
TODO: delete this migration in future releases
"""
if not schema_editor.connection.alias == 'default':
return
Device = apps.get_model('config', 'Device')
Config = apps.get_model('config', 'Config')

for config in Config.objects.all():
device = Device(id=config.id,
organization=config.organization,
name=config.name,
mac_address=config.mac_address,
key=config.key,
created=config.created,
modified=config.modified)
device.full_clean()
device.save()
config.device = device
config.save()


class Migration(migrations.Migration):
dependencies = [
('config', '0004_add_device_model'),
]

operations = [
migrations.RunPython(forward, reverse_code=migrations.RunPython.noop),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-11 18:35
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('config', '0005_populate_device'),
]

operations = [
migrations.AlterField(
model_name='config',
name='device',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='config.Device'),
),
]
27 changes: 27 additions & 0 deletions openwisp_controller/config/migrations/0007_simplify_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-11 18:41
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('config', '0006_config_device_not_null'),
]

operations = [
migrations.RemoveField(
model_name='config',
name='name',
),
migrations.RemoveField(
model_name='config',
name='key',
),
migrations.RemoveField(
model_name='config',
name='mac_address',
),
]
Loading

0 comments on commit 714fd5f

Please sign in to comment.