跳转到主要内容

嵌入式设备的配置管理器,作为可重用的django-app实现

项目描述

https://travis-ci.org/openwisp/django-netjsonconfig.svg https://coveralls.io/repos/openwisp/django-netjsonconfig/badge.svg Requirements Status https://badge.fury.io/py/django-netjsonconfig.svg

嵌入式设备的配置管理器,作为可重用的django-app实现。

基于NetJSON格式和netjsonconfig库。

adhoc interface
preview

当前功能

  • 支持不同固件的嵌入式设备的配置管理
  • 基于JSON-Schema编辑器配置编辑器

  • 高级编辑模式:编辑NetJSON DeviceConfiguration对象以获得最大灵活性

  • 配置模板:将重复性降到最低

  • 配置变量:在配置和模板中引用类似Ansible的变量

  • 模板标签:标记模板以自动化不同类型的自动配置(例如:网状,WDS,4G)

  • 简单的HTTP资源:允许设备自动下载配置更新

  • VPN管理:轻松创建VPN服务器和客户端

项目目标

  • 自动化嵌入式设备的配置管理

  • 通过使用模板来最小化重复

  • 提供由第三方应用程序扩展的基逻辑(请参阅扩展 django-netjsonconfig

  • 提供通过添加自定义后端来支持更多固件的方法

  • 尽可能保持核心简单

在生产环境中部署

自动安装器可在ansible-openwisp2中找到。

依赖项

  • Python >=3.6

  • OpenSSL

从pypi安装稳定版本

从pypi安装

pip install django-netjsonconfig

安装开发版本

安装tarball

pip install https://github.com/openwisp/django-netjsonconfig/tarball/master

或者,您可以通过pip使用git安装

pip install -e git+git://github.com/openwisp/django-netjsonconfig#egg=django-netjsonconfig

如果您想贡献,请安装您克隆的分支

git clone git@github.com:<your_fork>/django-netjsonconfig.git
cd django-netjsonconfig
python setup.py develop

设置(集成到现有django项目中)

django_netjsonconfigdjango.contrib.adminsortedm2mreversion按以下顺序添加到INSTALLED_APPS

INSTALLED_APPS = [
    # other apps
    'openwisp_utils.admin_theme',
    'django_netjsonconfig',
    # ensure the django admin comes after django-netjsonconfig
    'django.contrib.admin',
    'sortedm2m',
    'reversion'  # optional, can be removed if not needed
    # ...
]

将控制器URL添加到您的主urls.py

urlpatterns = [
    # ... other urls in your project ...

    # controller URLs
    # used by devices to download/update their configuration
    # keep the namespace argument unchanged
    url(r'^', include('django_netjsonconfig.controller.urls', namespace='controller')),
    # common URLs
    # shared among django-netjsonconfig components
    # keep the namespace argument unchanged
    url(r'^', include('django_netjsonconfig.urls', namespace='netjsonconfig')),
]

然后运行

./manage.py migrate

为开发安装

安装sqlite

sudo apt-get install sqlite3 libsqlite3-dev openssl libssl-dev

安装您的分支仓库

git clone git://github.com/<your_fork>/django-netjsonconfig
cd django-netjsonconfig/
python setup.py develop

安装测试需求

pip install -r requirements-test.txt

创建数据库

cd tests/
./manage.py migrate
./manage.py createsuperuser

启动开发服务器

./manage.py runserver

您可以在http://127.0.0.1:8000/admin/访问管理界面。

使用以下命令运行测试

./runtests.py

如何使用配置变量

有时配置在所有设备上并不完全相同,有些参数对每个设备都是唯一的或需要用户修改。

在这种情况下,可以使用配置变量与模板结合使用,此功能也称为配置上下文,将其视为传递给配置渲染函数的字典,以便它可以根据传递的上下文填充变量。

以下将描述变量定义的不同方式。

预定义设备变量

每个设备都会获得以下属性,作为配置变量传递

  • id

  • key

  • name

  • mac_address

用户定义设备变量

在设备配置部分,您可以通过单击“高级选项(显示)”来访问上下文字段。

advanced options (show)

然后您可以将变量定义为键值字典(JSON格式),如下所示。

context

模板默认值

可以在模板中指定变量的默认值。

这可以实现两个目标

  1. 无错误地通过架构验证(否则一开始就无法保存模板)

  2. 提供在大多数情况下都有效,但如需在设备上覆盖的默认值

这些默认值将被用户定义的设备变量覆盖。

要这样做,请点击编辑模板页面上的“高级选项(显示)”

advanced options (show)

然后您可以定义变量的默认值

default values

全局变量

也可以使用NETJSONCONFIG_CONTEXT设置全局定义变量。

变量示例用法

以下是一个典型用例,WiFi SSID和WiFi密码。您不希望为每个设备定义这些,但可能希望允许运营商轻松更改特定设备的SSID或WiFi密码,而无需重新定义整个WiFi接口以避免信息重复。

这将是模板

{
    "interfaces": [
        {
            "type": "wireless",
            "name": "wlan0",
            "wireless": {
                "mode": "access_point",
                "radio": "radio0",
                "ssid": "{{wlan0_ssid}}",
                "encryption": {
                    "protocol": "wpa2_personal",
                    "key": "{{wlan0_password}}",
                    "cipher": "auto"
                }
            }
        }
    ]
}

这些将是模板中的默认值

{
    "wlan0_ssid": "SnakeOil PublicWiFi",
    "wlan0_password": "Snakeoil_pwd!321654"
}

如果需要,可以在设备级别覆盖默认值,例如

{
    "wlan0_ssid": "Room 23 ACME Hotel",
    "wlan0_password": "room_23pwd!321654"
}

信号

config_modified

路径django_netjsonconfig.signals.config_modified

参数:

  • instance:其config被修改的Config实例

每次修改设备的配置时,都会发出此信号。

如果Config.status已经修改,这不会影响信号的发出,因为这个信号表示设备配置已更改。

当设备首次创建时,不会触发此信号。

当启用推送功能(需要设备凭据)时,此信号用于触发设备的配置更新。

config_status_changed

路径django_netjsonconfig.signals.config_status_changed

参数:

  • instance:其status被改变的Config实例

只有当设备的配置状态发生更改时,才会发出此信号。

请求校验和

路径django_netjsonconfig.signals.checksum_requested

参数:

  • instance:请求其配置校验和的Device实例

  • request:HTTP请求对象

当设备通过控制器视图请求校验和时,会发出此信号。

在成功响应返回之前发出信号,如果响应未成功,则不会发送信号。

请求配置下载

路径django_netjsonconfig.signals.config_download_requested

参数:

  • instance:请求下载其配置的Device实例

  • request:HTTP请求对象

当设备通过控制器视图请求下载其配置时,会发出此信号。

在成功响应返回之前发出信号,如果响应未成功,则不会发送信号。

设置

NETJSONCONFIG_BACKENDS

类型:

元组

默认值:

(
  ('netjsonconfig.OpenWrt', 'OpenWRT'),
  ('netjsonconfig.OpenWisp', 'OpenWISP'),
)

可用的配置后端。更多信息,请参阅netjsonconfig后端

NETJSONCONFIG_VPN_BACKENDS

类型:

元组

默认值:

(
  ('django_netjsonconfig.vpn_backends.OpenVpn', 'OpenVPN'),
)

用于VPN服务器对象的可用VPN后端。更多信息,请参阅OpenVPN netjsonconfig后端

VPN后端必须遵循一些基本规则才能与django-netjsonconfig兼容

  • 它必须至少允许一个VPN实例,最多一个VPN实例

  • NetJSON属性必须匹配类名的小写版本,例如:当使用OpenVpn后端时,系统将查看config['openvpn']

  • 它应专注于所使用的VPN软件的服务器功能

NETJSONCONFIG_DEFAULT_BACKEND

类型:

字符串

默认值:

NETJSONCONFIG_BACKENDS[0][0]

在管理界面中添加新的 ConfigTemplate 对象时,将用作初始值的首选后端。

此设置默认为 NETJSONCONFIG_BACKENDS 设置中第一个项目的原始值,即 netjsonconfig.OpenWrt

将其设置为 None 将强制用户显式选择。

NETJSONCONFIG_DEFAULT_VPN_BACKEND

类型:

字符串

默认值:

NETJSONCONFIG_VPN_BACKENDS[0][0]

在管理界面中添加新的 Vpn 对象时,将用作初始值的首选后端。

此设置默认为 NETJSONCONFIG_VPN_BACKENDS 设置中第一个项目的原始值,即 django_netjsonconfig.vpn_backends.OpenVpn

将其设置为 None 将强制用户显式选择。

NETJSONCONFIG_REGISTRATION_ENABLED

类型:

布尔值

默认值:

True

设备是否可以通过控制器自动注册。

此功能默认启用。

为了正常工作,设备必须支持自动注册,有关更多信息,请参阅openwisp-config自动注册

NETJSONCONFIG_CONSISTENT_REGISTRATION

类型:

布尔值

默认值:

True

在重新刷机或重置后是否识别已注册的设备,从而保留现有配置而无需创建新的配置。

此功能默认启用。

为了正常工作,设备上也必须启用自动注册,有关更多信息,请参阅openwisp-config一致密钥生成

NETJSONCONFIG_REGISTRATION_SELF_CREATION

类型:

布尔值

默认值:

True

是否允许尚未存在于系统中的设备进行注册。

如果您仍然想使用自动注册来避免手动设置设备的UUID和密钥在配置文件中,但又想避免未经明确许可就注册新设备,请关闭此选项。

NETJSONCONFIG_SHARED_SECRET

类型:

字符串

默认值:

""

设备执行自动注册必须使用的秘密密钥。

此密钥必须在生产环境中明确设置(如果 settings.DEBUG is False),否则启动时将引发 ImproperlyConfigured 异常。

NETJSONCONFIG_CONTEXT

类型:

字典

默认值:

{}

传递给每个设备对象默认上下文的附加上下文。

NETJSONCONFIG_CONTEXT 可以用来定义系统范围的配置变量。

有关在 OpenWISP 下一层处理变量的技术信息,请参阅 netjsonconfig 上下文:配置变量

NETJSONCONFIG_DEFAULT_AUTO_CERT

类型:

布尔值

默认值:

True

Template 对象的 auto_cert 字段的默认值。

auto_cert 字段仅对已将 type 设置为 VPN 的模板有效,表示是否应自动为使用该模板的每个配置创建新的 x509 证书。

当不再需要时(例如,当从配置对象中删除VPN模板时),自动创建的证书也将被删除。

NETJSONCONFIG_CERT_PATH

类型:

字符串

默认值:

/etc/x509

当使用 auto_cert(默认启用)并在路由器上下载时,x509 证书将被安装到的文件系统路径。

NETJSONCONFIG_COMMON_NAME_FORMAT

类型:

字符串

默认值:

{mac_address}-{name}

定义在使用将 auto_cert 设置为 True 的 VPN 模板自动创建 VPN 客户端证书时 common_name 属性的格式。

NETJSONCONFIG_MANAGEMENT_IP_DEVICE_LIST

类型:

布尔值

默认值:

True

在设备列表页面上,如果可用,则列 IP 将显示 management_ip,否则默认为 last_ip

如果此设置设置为False,即使在设备列表页面存在,management_ip也不会显示,它只会显示在设备详情页。

如果出于某种原因,大多数用户不关心管理IP地址,您可以将其设置为False

NETJSONCONFIG_BACKEND_DEVICE_LIST

类型:

布尔值

默认值:

True

在设备列表页面,默认显示列backend和后端过滤器。

如果此设置设置为False,这些项将从UI中删除。

如果您仅使用一个配置后端,并且此UI元素对用户没有增加任何价值,您可以将其设置为False

NETJSONCONFIG_HARDWARE_ID_ENABLED

类型:

布尔值

默认值:

False

字段hardware_id可用于存储唯一的硬件ID,例如序列号。

如果此设置设置为True,则此字段将首先在设备列表页面和添加/编辑设备页面显示。

此功能默认禁用。

NETJSONCONFIG_HARDWARE_ID_OPTIONS

类型:

字典

默认值:

{
    'blank': not NETJSONCONFIG_HARDWARE_ID_ENABLED,
    'null': True,
    'max_length': 32,
    'unique': True,
    'verbose_name': _('Serial number'),
    'help_text': _('Serial number of this device')
}

模型字段hardware_id的选项。

  • blank:字段是否允许为空

  • null:空值是否在数据库中以NULL存储

  • max_length:字段的最大长度

  • unique:字段值是否必须唯一

  • verbose_name:字段的用户可读标签的文本

  • help_text:与字段一起显示的帮助文本

NETJSONCONFIG_HARDWARE_ID_AS_NAME

类型:

布尔值

默认值:

True

当启用硬件ID功能时,设备将通过其硬件ID而不是其名称进行引用。

如果您仍然希望通过名称引用设备,请将其设置为False

扩展 django-netjsonconfig

django-netjsonconfig提供了一组模型、管理类和通用视图,可以被第三方应用程序导入、扩展和重用。

要扩展django-netjsonconfig您绝对不能将其添加到settings.INSTALLED_APPS,但您必须创建自己的应用程序(该应用程序包含在settings.INSTALLED_APPS中),从django-netjsonconfig导入基本类,并添加您的自定义设置。

为了帮助django找到django-netjsonconfig的静态文件和模板,您需要执行以下步骤。

1. 添加 EXTENDED_APPS

将以下内容添加到您的settings.py

EXTENDED_APPS = ('django_netjsonconfig', 'django_x509',)

2. 添加 openwisp_utils.staticfiles.DependencyFinder

openwisp_utils.staticfiles.DependencyFinder添加到您的settings.py中的STATICFILES_FINDERS

STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'openwisp_utils.staticfiles.DependencyFinder',
]

3. 添加 openwisp_utils.loaders.DependencyLoader

openwisp_utils.loaders.DependencyLoader添加到您的settings.py中的TEMPLATES

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'loaders': [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
                'openwisp_utils.loaders.DependencyLoader',
            ],
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    }
]

扩展模型

此示例提供了一个示例,说明如何通过添加到名为Organization的另一个django模型的关系来扩展django-netjsonconfig的基本模型。

# models.py of your custom ``config`` app
from django.db import models
from sortedm2m.fields import SortedManyToManyField
from taggit.managers import TaggableManager

from django_netjsonconfig.base.config import AbstractConfig, TemplatesVpnMixin
from django_netjsonconfig.base.tag import AbstractTaggedTemplate, AbstractTemplateTag
from django_netjsonconfig.base.template import AbstractTemplate
from django_netjsonconfig.base.vpn import AbstractVpn, AbstractVpnClient

# the model ``organizations.Organization`` is omitted for brevity
# if you are curious to see a real implementation, check out django-organizations
# https://github.com/bennylope/django-organizations

class OrganizationMixin(models.Model):
    organization = models.ForeignKey('organizations.Organization')

    class Meta:
        abstract = True


class Config(OrganizationMixin, TemplatesVpnMixin, AbstractConfig):
    templates = SortedManyToManyField('config.Template',
                                      related_name='config_relations',
                                      blank=True)
    vpn = models.ManyToManyField('config.Vpn',
                                 through='config.VpnClient',
                                 related_name='vpn_relations',
                                 blank=True)

    def clean(self):
        # your own validation logic here...
        pass

    class Meta(AbstractConfig.Meta):
        abstract = False


class TemplateTag(AbstractTemplateTag):
    class Meta(AbstractTemplateTag.Meta):
        abstract = False


class TaggedTemplate(AbstractTaggedTemplate):
    tag = models.ForeignKey('config.TemplateTag',
                            related_name='%(app_label)s_%(class)s_items',
                            on_delete=models.CASCADE)

    class Meta(AbstractTaggedTemplate.Meta):
        abstract = False


class Template(OrganizationMixin, AbstractTemplate):
    tags = TaggableManager(through='config.TaggedTemplate', blank=True)
    vpn = models.ForeignKey('config.Vpn', blank=True, null=True)

    def clean(self):
        # your own validation logic here...
        pass

    class Meta(AbstractTemplate.Meta):
        abstract = False


class Vpn(OrganizationMixin, AbstractVpn):
    class Meta(AbstractVpn.Meta):
        abstract = False


class VpnClient(AbstractVpnClient):
    config = models.ForeignKey('config.Config', on_delete=models.CASCADE)
    vpn = models.ForeignKey('config.Vpn', on_delete=models.CASCADE)
    cert = models.OneToOneField('django_x509.Cert',
                                on_delete=models.CASCADE,
                                blank=True,
                                null=True)

    class Meta(AbstractVpnClient.Meta):
        abstract = False

扩展管理界面

在先前的Organization示例中,您可以通过导入基本管理类并使用它们来注册模型来避免重复管理代码。

# admin.py of your app
# these are your custom models, they must be imported before the abstract admin classes
from .models import Config, Template, Vpn

from django.contrib import admin
from django_netjsonconfig.base.admin import (AbstractConfigAdmin,
                                             AbstractConfigForm,
                                             AbstractTemplateAdmin,
                                             AbstractVpnAdmin,
                                             AbstractVpnForm,
                                             BaseForm)


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


class ConfigAdmin(AbstractConfigAdmin):
    form = ConfigForm


class TemplateForm(BaseForm):
    class Meta(BaseForm.Meta):
        model = Template


class TemplateAdmin(AbstractTemplateAdmin):
    form = TemplateForm


class VpnForm(AbstractVpnForm):
    class Meta(AbstractVpnForm.Meta):
        model = Vpn


class VpnAdmin(AbstractVpnAdmin):
    form = VpnForm


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

扩展控制器视图

如果您的用例与基本用例没有很大差异,您也可能想尝试重用控制器视图

# your_config_app.controller.views
from ..models import Device, Vpn
from django_netjsonconfig.controller.generics import (BaseDeviceChecksumView, BaseDeviceDownloadConfigView,
                                                      BaseDeviceRegisterView, BaseDeviceReportStatusView,
                                                      BaseVpnChecksumView, BaseVpnDownloadConfigView)

class DeviceChecksumView(BaseDeviceChecksumView):
    model = Device


class DeviceDownloadConfigView(BaseDeviceDownloadConfigView):
    model = Device


class DeviceReportStatusView(BaseDeviceReportStatusView):
    model = Device


class DeviceRegisterView(BaseDeviceRegisterView):
    model = Device


class VpnChecksumView(BaseVpnChecksumView):
    model = Vpn


class VpnDownloadConfigView(BaseVpnDownloadConfigView):
    model = Vpn


device_checksum = DeviceChecksumView.as_view()
device_download_config = DeviceDownloadConfigView.as_view()
device_report_status = DeviceReportStatusView.as_view()
device_register = DeviceRegisterView.as_view()
vpn_checksum = VpnChecksumView.as_view()
vpn_download_config = VpnDownloadConfigView.as_view()

控制器URL

如果您没有对控制器视图进行重大更改,您可以通过使用get_controller_urls函数来避免重复URL逻辑。在您的控制器urls.py中放置此内容

# your_config_app.controller.urls
from django_netjsonconfig.utils import get_controller_urls
from . import views

urlpatterns = get_controller_urls(views)

扩展 AppConfig

您可能还想重用django-netjsonconfigAppConfig

from django_netjsonconfig.apps import DjangoNetjsonconfigApp


class MyOwnConfig(DjangoNetjsonconfigApp):
    name = 'yourapp.config'
    label = 'config'

    def __setmodels__(self):
        from .models import Config, VpnClient  # these are your custom models
        self.config_model = Config
        self.vpnclient_model = VpnClient

django-netjsonconfig的真实世界扩展

有关扩展django-netjsonconfig的django项目的完整工作示例,请参阅

截图

configuration item
bridge
radio
wpa enterprise
preview
adhoc interface

贡献

  1. OpenWISP 邮件列表中宣布您的意图

  2. 分叉此存储库并安装它

  3. 遵循PEP8,Python 代码风格指南

  4. 编写代码

  5. 为您的代码编写测试

  6. 确保所有测试都通过

  7. 确保测试覆盖率不降低

  8. 记录您的更改

  9. 发送拉取请求

变更日志

查看变更记录

许可证

查看许可证

支持

查看OpenWISP 支持渠道

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解有关安装软件包的更多信息。

源分布

django-netjsonconfig-0.12.tar.gz (390.5 kB 查看哈希值)

上传时间:

构建分布

django_netjsonconfig-0.12-py2.py3-none-any.whl (412.8 kB 查看哈希值)

上传时间: Python 2 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面