Package: openwisp-firmware-upgrader
Firmware upgrade module of OpenWISP.Features:Stores information of each upgrade operation which can be seen from the device pageAutomatic retries for recoverable failures
(eg: firmware image upload issues because of intermittent internet connection)Performs a final check to find out if the upgrade completed successfully or notPrevents accidental multiple upgrades using the same firmware imageSingle device upgradeMass upgradesPossibility to divide firmware images in categoriesREST APIPossibility of writing custom upgradersfor other
firmware OSes or for custom OpenWRT based firmwaresConfigurable timeoutsExtensibleFor a more complete overview of the OpenWISP modules and architecture,
see theOpenWISP Architecture Overview.Want to help OpenWISP?Find out how to help us grow here.Table of Contents:Installation instructionsRequirementsInstall DependenciesSetup (integrate in an existing Django project)Installing for developmentQuickstart Guide1. Create a category2. Create the build object3. Upload images to the build4. Perform a firmware upgrade to a specific device5. Performing mass upgradesAutomatic device firmware detectionWriting Custom Firmware Upgrader ClassesREST APILive documentationBrowsable web interfaceAuthenticationPaginationFiltering by organization slugList of endpointsList mass upgrade operationsGet mass upgrade operation detailList firmware buildsCreate firmware buildGet firmware build detailsChange details of firmware buildPatch details of firmware buildDelete firmware buildGet list of images of a firmware buildUpload new firmware image to the buildGet firmware image detailsDelete firmware imageDownload firmware imagePerform batch upgradeDry-run batch upgradeList firmware categoriesCreate new firmware categoryGet firmware category detailsChange the details of a firmware categoryPatch the details of a firmware categoryDelete a firmware categorySettingsOPENWISP_FIRMWARE_UPGRADER_RETRY_OPTIONSOPENWISP_FIRMWARE_UPGRADER_TASK_TIMEOUTOPENWISP_CUSTOM_OPENWRT_IMAGESOPENWISP_FIRMWARE_UPGRADER_MAX_FILE_SIZEOPENWISP_FIRMWARE_UPGRADER_APIOPENWISP_FIRMWARE_UPGRADER_OPENWRT_SETTINGSOPENWISP_FIRMWARE_API_BASEURLOPENWISP_FIRMWARE_UPGRADERS_MAPOPENWISP_FIRMWARE_PRIVATE_STORAGE_INSTANCEExtending openwisp-firmware-upgrader1. Initialize your custom module2. Installopenwisp-firmware-upgrader3. AddEXTENDED_APPS4. Addopenwisp_utils.staticfiles.DependencyFinder5. Addopenwisp_utils.loaders.DependencyLoader6. Inherit the AppConfig class7. Create your custom models8. Add swapper configurations9. Create database migrations10. Create the admin1. Monkey patching2. Inheriting admin classes11. Create root URL configuration12. Create celery.py13. Import the automated testsOther base classes that can be inherited and extendedFirmwareImageDownloadViewAPI viewsContributingSupportChangelogLicenseInstallation instructionsRequirementsPython >= 3.7openwisp-controller (and its dependencies) >= 1.0.0Install DependenciesInstall spatialite and sqlite:sudoapt-getinstallsqlite3libsqlite3-devopenssllibssl-devsudoapt-getinstallgdal-binlibproj-devlibgeos-devlibspatialite-devSetup (integrate in an existing Django project)Follow thesetup instructions of openwisp-controller, then add the settings described below.INSTALLED_APPS=[# django apps# all-auth'django.contrib.sites','openwisp_users.accounts','allauth','allauth.account','django_extensions','private_storage',# openwisp2 modules'openwisp_controller.pki','openwisp_controller.config','openwisp_controller.connection','openwisp_controller.geo','openwisp_firmware_upgrader','openwisp_users','openwisp_notifications','openwisp_ipam',# openwisp2 admin theme (must be loaded here)'openwisp_utils.admin_theme',# admin'django.contrib.admin','django.forms',# other dependencies'sortedm2m','reversion','leaflet','flat_json_widget',# rest framework'rest_framework','rest_framework.authtoken','rest_framework_gis','django_filters','drf_yasg',# channels'channels',]MEDIA_ROOT=os.path.join(BASE_DIR,'media')PRIVATE_STORAGE_ROOT=os.path.join(MEDIA_ROOT,'firmware')The root URLconf (urls.py) should look like the following example:fromdjango.confimportsettingsfromdjango.contribimportadminfromdjango.conf.urlsimportinclude,urlfromdjango.contrib.staticfiles.urlsimportstaticfiles_urlpatternsurlpatterns=[path('admin/',admin.site.urls),path('',include('openwisp_controller.urls')),path('',redirect_view,name='index'),path('',include('openwisp_firmware_upgrader.urls')),path('api/v1/',include((get_api_urls(),'users'),namespace='users')),path('api/v1/',include('openwisp_utils.api.urls')),]urlpatterns+=staticfiles_urlpatterns()Installing for developmentInstall your forked repo:gitclonegit://github.com/<your_fork>/openwisp-firmware-upgradercdopenwisp-firmware-upgrader/pythonsetup.pydevelopInstall test requirements:pipinstall-rrequirements-test.txtCreate database:cdtests/./manage.pymigrate./manage.pycreatesuperuserLaunch development server:./manage.pyrunserver0.0.0.0:8000You can access the admin interface athttp://127.0.0.1:8000/admin/.Run celery and celery-beat with the following commands
(separate terminal windows are needed):# (cd tests)celery-Aopenwisp2worker-linfocelery-Aopenwisp2beat-linfoRun tests with:# run qa checks./run-qa-checks# standard tests./runtests.py# tests for the sample appSAMPLE_APP=1./runtests.py--keepdb--failfastWhen running the last line of the previous example, the environment variableSAMPLE_APPactivates the app in/tests/openwisp2/sample_firmware_upgrader/which is a simple django app that extendsopenwisp-firmware-upgraderwith
the sole purpose of testing its extensibility, for more information regarding
this concept, read the following section.Quickstart GuideRequirements:Devices running at least OpenWRT 12.09 Attitude Adjustment, older versions
of OpenWRT have not worked at all in our testsDevices must have enough free RAM to be able to upload the
new image to/tmp1. Create a categoryCreate a category for your firmware images
by going toFirmware management > Firmware categories > Add firmware category,
if you use only one firmware type in your network, you could simply
name the category “default” or “standard”.If you use multiple firmware images with different features, create one category
for each firmware type, eg:WiFiSDN routerLoRa GatewayThis is necessary in order to perform mass upgrades only on specific
firmware categories when, for example, a newLoRa Gatewayfirmware becomes available.2. Create the build objectCreate a build a build object by going toFirmware management > Firmware builds > Add firmware build,
the build object is related to a firmware category and is the collection of the
different firmware images which have been compiled for the different hardware models
supported by the system.The version field indicates the firmware version, the change log field is optional but
we recommend filling it to help operators know the differences between each version.An important but optional field of the build model isOS identifier, this field
should match the value of theOperating Systemfield which gets automatically filled
during device registration, eg:OpenWrt19.07-SNAPSHOTr11061-6ffd4d8a4d.
It is used by the firmware-upgrader module to automatically
createDeviceFirmwareobjects for existing devices or when new devices register.
ADeviceFirmwareobject represent the relationship between a device and a firmware image,
it basically tells us which firmware image is installed on the device.To find out the exact value to use, you should either do a
test flash on a device and register it to the system or you should inspect the firmware image
by decompressing it and find the generated value in the firmware image.If you’re not sure about whatOS identifierto use, just leave it empty, you can fill
it later on when you find out.Now save the build object to create it.3. Upload images to the buildNow is time to add images to the build, we suggest adding one image at time.
Alternatively theREST APIcan be used to automate this step.If you use a hardware model which is not listed in the image types, if the
hardware model is officially supported by OpenWRT, you can send us a pull-request to add it,
otherwise you can usethe setting OPENWISP_CUSTOM_OPENWRT_IMAGESto add it.4. Perform a firmware upgrade to a specific deviceOnce a new build is ready, has been created in the system and its image have been uploaded,
it will be the time to finally upgrade our devices.To perform the upgrade of a single device, navigate to the device details,
then go to the “Firmware” tab.If you correctly filledOS identifierin step 2, you should have a situation
similar to the one above: in this example, the device is using version1.0and we want to upgrade it to version2.0, once the new firmware image
is selected we just have to hit save, then a new tab will appear in the device page
which allows us to see what’s going on during the upgrade.Right now, the update of the upgrade information is not asynchronous yet, so you will
have to reload the page periodically to find new information. This will be addressed
in a future release.5. Performing mass upgradesFirst of all, please ensure the following preconditions are met:the system is configured correctlythe new firmware images are working as expectedyou already tried the upgrade of single devices several times.At this stage you can try a mass upgrade by doing the following:go to the build list pageselect the build which contains the latest firmware images you
want the devices to be upgraded withclick on “Mass-upgrade devices related to the selected build”.At this point you should see a summary page which will inform you of which devices
are going to be upgraded, you can either confirm the operation or cancel.Once the operation is confirmed you will be redirected to a page in which you
can monitor the progress of the upgrade operations.Right now, the update of the upgrade information is not asynchronous yet, so you will
have to reload the page periodically to find new information. This will be addressed
in a future release.Automatic device firmware detectionOpenWISP Firmware Upgradermaintains a data structure for mapping
the firmware image files to board names calledOPENWRT_FIRMWARE_IMAGE_MAP.Here is an example firmware image item fromOPENWRT_FIRMWARE_IMAGE_MAP{# Firmware image file name.'ar71xx-generic-cf-e320n-v2-squashfs-sysupgrade.bin':{# Human readable name of the model which is displayed on# the UI'label':'COMFAST CF-E320N v2 (OpenWRT 19.07 and earlier)',# Tupe of board names with which the different versions# of the hardware are identified on OpenWrt'boards':('COMFAST CF-E320N v2',),}}When a device registers on OpenWISP, theopenwisp-config agentread the device board name from/tmp/sysinfo/modeland sends it to OpenWISP.
This value is then saved in theDevice.modelfield.OpenWISP Firmware Upgraderuses this field to automatically detect
the correct firmware image for the device.Use theOPENWISP_CUSTOM_OPENWRT_IMAGESsetting to add additional firmware image in your project.Writing Custom Firmware Upgrader ClassesYou can write custom upgraders for other firmware OSes or for
custom OpenWrt based firmwares.Here is an example custom OpenWrt firmware upgrader class:fromopenwisp_firmware_upgrader.upgraders.openwrtimportOpenWrtclassCustomOpenWrtBasedFirmware(OpenWrt):# this firmware uses a custom upgrade commandUPGRADE_COMMAND='upgrade_firmware.sh --keep-config'# it takes somewhat more time to boot so it needs more timeRECONNECT_DELAY=150RECONNECT_RETRY_DELAY=5RECONNECT_MAX_RETRIES=20defget_remote_path(self,image):return'/tmp/firmware.img'defget_upgrade_command(self,path):returnself.UPGRADE_COMMANDYou will need to place your custom upgrader class on the python path
of your application and then add this path to theOPENWISP_FIRMWARE_UPGRADERS_MAPsetting.REST APITo enable the API the settingOPENWISP_FIRMWARE_UPGRADER_APImust be set toTrue.Live documentationA general live API documentation (following the OpenAPI specification) at/api/v1/docs/.Browsable web interfaceAdditionally, opening any of the endpointslisted belowdirectly in the browser will show thebrowsable API interface of Django-REST-Framework,
which makes it even easier to find out the details of each endpoint.AuthenticationSee openwisp-users:authenticating with the user token.When browsing the API via theLive documentationor theBrowsable web page, you can also use
the session authentication by logging in the django admin.PaginationAlllistendpoints support thepage_sizeparameter that allows paginating
the results in conjunction with thepageparameter.GET /api/v1/firmware-upgrader/build/?page_size=10
GET /api/v1/firmware-upgrader/build/?page_size=10&page=2Filtering by organization slugMost endpoints allow to filter by organization slug, eg:GET /api/v1/firmware-upgrader/build/?organization=org-slugList of endpointsSince the detailed explanation is contained in theLive documentationand in theBrowsable web pageof each point,
here we’ll provide just a list of the available endpoints,
for further information please open the URL of the endpoint in your browser.List mass upgrade operationsGET /api/v1/firmware-upgrader/batch-upgrade-operation/Get mass upgrade operation detailGET /api/v1/firmware-upgrader/batch-upgrade-operation/{id}/List firmware buildsGET /api/v1/firmware-upgrader/build/Create firmware buildPOST /api/v1/firmware-upgrader/build/Get firmware build detailsGET /api/v1/firmware-upgrader/build/{id}/Change details of firmware buildPUT /api/v1/firmware-upgrader/build/{id}/Patch details of firmware buildPATCH /api/v1/firmware-upgrader/build/{id}/Delete firmware buildDELETE /api/v1/firmware-upgrader/build/{id}/Get list of images of a firmware buildGET /api/v1/firmware-upgrader/build/{id}/image/Upload new firmware image to the buildPOST /api/v1/firmware-upgrader/build/{id}/image/Get firmware image detailsGET /api/v1/firmware-upgrader/build/{build_pk}/image/{id}/Delete firmware imageDELETE /api/v1/firmware-upgrader/build/{build_pk}/image/{id}/Download firmware imageGET /api/v1/firmware-upgrader/build/{build_pk}/image/{id}/download/Perform batch upgradeUpgrades all the devices related to the specified build ID.POST /api/v1/firmware-upgrader/build/{id}/upgrade/Dry-run batch upgradeReturns a list representing theDeviceFirmwareandDeviceinstances that would be upgraded if POST is used.Deviceobjects are indicated only when noDeviceFirmwareobject exists for a device which would be upgraded.GET /api/v1/firmware-upgrader/build/{id}/upgrade/List firmware categoriesGET /api/v1/firmware-upgrader/category/Create new firmware categoryPOST /api/v1/firmware-upgrader/category/Get firmware category detailsGET /api/v1/firmware-upgrader/category/{id}/Change the details of a firmware categoryPUT /api/v1/firmware-upgrader/category/{id}/Patch the details of a firmware categoryPATCH /api/v1/firmware-upgrader/category/{id}/Delete a firmware categoryDELETE /api/v1/firmware-upgrader/category/{id}/SettingsOPENWISP_FIRMWARE_UPGRADER_RETRY_OPTIONStype:dictdefault:see below# default value of OPENWISP_FIRMWARE_UPGRADER_RETRY_OPTIONS:dict(max_retries=4,retry_backoff=60,retry_backoff_max=600,retry_jitter=True,)Retry settings for recoverable failures during firmware upgrades.By default if an upgrade operation fails before the firmware is flashed
(eg: because of a network issue during the upload of the image),
the upgrade operation will be retried 4 more times with an exponential
random backoff and a maximum delay of 10 minutes.For more information regarding these settings, consult thecelery documentation
regarding automatic retries for known errors.OPENWISP_FIRMWARE_UPGRADER_TASK_TIMEOUTtype:intdefault:600Timeout for the background tasks which perform firmware upgrades.If for some unexpected reason an upgrade remains stuck for more than 10 minutes,
the upgrade operation will be flagged as failed and the task will be killed.This should not happen, but a global task time out is a best practice when
using background tasks because it prevents the situation in which an unexpected
bug causes a specific task to hang, which will quickly fill all the available
slots in a background queue and prevent other tasks from being executed, which
will end up affecting negatively the rest of the application.OPENWISP_CUSTOM_OPENWRT_IMAGEStype:tupledefault:NoneThis setting can be used to extend the list of firmware image types
included inOpenWISP Firmware Upgrader. This setting is suited to
add support for custom OpenWrt images.OPENWISP_CUSTOM_OPENWRT_IMAGES=((# Firmware image file name.'customimage-squashfs-sysupgrade.bin',{# Human readable name of the model which is displayed on# the UI'label':'Custom WAP-1200',# Tuple of board names with which the different versions of# the hardware are identified on OpenWrt'boards':('CWAP1200',)}),)Kindly read“Automatic detection of firmware of device”section of this documentation to know howOpenWISP Firmware Upgraderuses this setting in upgrades.OPENWISP_FIRMWARE_UPGRADER_MAX_FILE_SIZEtype:intdefault:30 * 1024 * 1024(30 MB)This setting can be used to set the maximum size limit for firmware images, eg:OPENWISP_FIRMWARE_UPGRADER_MAX_FILE_SIZE=40*1024*1024# 40MBNotes:Value must be specified in bytes.Nonemeans unlimited.OPENWISP_FIRMWARE_UPGRADER_APItype:booldefault:TrueIndicates whether the API for Firmware Upgrader is enabled or not.OPENWISP_FIRMWARE_UPGRADER_OPENWRT_SETTINGStype:dictdefault:{}Allows changing the default OpenWRT upgrader settings, eg:OPENWISP_FIRMWARE_UPGRADER_OPENWRT_SETTINGS={'reconnect_delay':120,'reconnect_retry_delay':20,'reconnect_max_retries':15,'upgrade_timeout':90,}reconnect_delay: amount of seconds to wait before trying to connect
again to the device after the upgrade command has been launched;
the re-connection step is necessary to verify the upgrade has completed successfully;
defaults to120secondsreconnect_retry_delay: amount of seconds to wait after a
re-connection attempt has failed;
defaults to20secondsreconnect_max_retries: maximum re-connection attempts
defaults to15attemptsupgrade_timeout: amount of seconds before the shell session
is closed after the upgrade command is launched on the device,
useful in case  the upgrade command hangs (it happens on older OpenWRT versions);
defaults to90secondsOPENWISP_FIRMWARE_API_BASEURLtype:dictdefault:/(points to same server)If you have a seperate instance of openwisp-firmware-upgrader API on a
different domain, you can use this option to change the base of the image
download url, this will enable you to point to your API server’s domain,
example value:https://myfirmware.myapp.com.OPENWISP_FIRMWARE_UPGRADERS_MAPtype:dictdefault:{'openwisp_controller.connection.connectors.openwrt.ssh.OpenWrt':'openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt',}A dictionary that maps update strategies to upgraders.If you want to use a custom update strategy you will need to use this setting
to provide an entry with the class path of your update strategy as the key.If you need to use acustom upgrader classyou will need to use this setting to provide an entry with the class path of your upgrader
as the value.OPENWISP_FIRMWARE_PRIVATE_STORAGE_INSTANCEtype:strdefault:openwisp_firmware_upgrader.private_storage.storage.file_system_private_storageDotted path to an instance of any one of the storage classes inprivate_storage.
This instance is used to store firmware image files.By default, an instance ofprivate_storage.storage.files.PrivateFileSystemStorageis used.Extending openwisp-firmware-upgraderOne of the core values of the OpenWISP project isSoftware Reusability,
for this reasonOpenWISP Firmware Upgraderprovides a set of base classes
which can be imported, extended and reused to create derivative apps.In order to implement your custom version ofOpenWISP Firmware Upgrader,
you need to perform the steps described in this section.When in doubt, the code in thetest projectand thesample appwill serve you as source of truth:
just replicate and adapt that code to get a basic derivative ofOpenWISP Firmware Upgraderworking.Premise: if you plan on using a customized version of this module,
we suggest to start with it since the beginning, because migrating your data
from the default module to your extended version may be time consuming.1. Initialize your custom moduleThe first thing you need to do is to create a new django app which will
contain your custom version ofOpenWISP Firmware Upgrader.A django app is nothing more than apython package(a directory of python scripts), in the following examples we’ll call this django appmyupgrader, but you can name it how you want:django-admin startapp myupgraderKeep in mind that the command mentioned above must be called from a directory
which is available in yourPYTHON_PATHso that you can then import the result into your project.Now you need to addmyupgradertoINSTALLED_APPSin yoursettings.py,
ensuring also thatopenwisp_firmware_upgraderhas been removed:INSTALLED_APPS=[# ... other apps ...# 'openwisp_firmware_upgrader'  <-- comment out or delete this line'myupgrader']For more information about how to work with django projects and django apps,
please refer to thedjango documentation.2. Installopenwisp-firmware-upgraderInstall (and add to the requirement of your project)openwisp-firmware-upgrader:pip install openwisp-firmware-upgrader3. AddEXTENDED_APPSAdd the following to yoursettings.py:EXTENDED_APPS=['openwisp_firmware_upgrader']4. Addopenwisp_utils.staticfiles.DependencyFinderAddopenwisp_utils.staticfiles.DependencyFindertoSTATICFILES_FINDERSin yoursettings.py:STATICFILES_FINDERS=['django.contrib.staticfiles.finders.FileSystemFinder','django.contrib.staticfiles.finders.AppDirectoriesFinder','openwisp_utils.staticfiles.DependencyFinder',]5. Addopenwisp_utils.loaders.DependencyLoaderAddopenwisp_utils.loaders.DependencyLoadertoTEMPLATESin yoursettings.py: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',],},}]6. Inherit the AppConfig classPlease refer to the following files in the sample app of the test project:sample_firmware_upgrader/__init__.py.sample_firmware_upgrader/apps.py.You have to replicate and adapt that code in your project.For more information regarding the concept ofAppConfigplease refer to
the“Applications” section in the django documentation.7. Create your custom modelsFor the purpose of showing an example, we added a simple “details” field to themodels of the sample app in the test project.You can add fields in a similar way in yourmodels.pyfile.Note: for doubts regarding how to use, extend or develop models please refer to
the“Models” section in the django documentation.8. Add swapper configurationsOnce you have created the models, add the following to yoursettings.py:# Setting models for swapper moduleFIRMWARE_UPGRADER_CATEGORY_MODEL='myupgrader.Category'FIRMWARE_UPGRADER_BUILD_MODEL='myupgrader.Build'FIRMWARE_UPGRADER_FIRMWAREIMAGE_MODEL='myupgrader.FirmwareImage'FIRMWARE_UPGRADER_DEVICEFIRMWARE_MODEL='myupgrader.DeviceFirmware'FIRMWARE_UPGRADER_BATCHUPGRADEOPERATION_MODEL='myupgrader.BatchUpgradeOperation'FIRMWARE_UPGRADER_UPGRADEOPERATION_MODEL='myupgrader.UpgradeOperation'Substitutemyupgraderwith the name you chose in step 1.9. Create database migrationsCreate and apply database migrations:./manage.py makemigrations
./manage.py migrateFor more information, refer to the“Migrations” section in the django documentation.10. Create the adminRefer to theadmin.py file of the sample app.To introduce changes to the admin, you can do it in two main ways which are described below.Note: for more information regarding how the django admin works, or how it can be customized,
please refer to“The django admin site” section in the django documentation.1. Monkey patchingIf the changes you need to add are relatively small, you can resort to monkey patching.For example:fromopenwisp_firmware_upgrader.adminimport(# noqaBatchUpgradeOperationAdmin,BuildAdmin,CategoryAdmin,)BuildAdmin.list_display.insert(1,'my_custom_field')BuildAdmin.ordering=['-my_custom_field']2. Inheriting admin classesIf you need to introduce significant changes and/or you don’t want to resort to
monkey patching, you can proceed as follows:fromdjango.contribimportadminfromopenwisp_firmware_upgrader.adminimport(BatchUpgradeOperationAdminasBaseBatchUpgradeOperationAdmin,BuildAdminasBaseBuildAdmin,CategoryAdminasBaseCategoryAdmin,)fromopenwisp_firmware_upgrader.swapperimportload_modelBatchUpgradeOperation=load_model('BatchUpgradeOperation')Build=load_model('Build')Category=load_model('Category')DeviceFirmware=load_model('DeviceFirmware')FirmwareImage=load_model('FirmwareImage')UpgradeOperation=load_model('UpgradeOperation')admin.site.unregister(BatchUpgradeOperation)admin.site.unregister(Build)admin.site.unregister(Category)classBatchUpgradeOperationAdmin(BaseBatchUpgradeOperationAdmin):# add your changes hereclassBuildAdmin(BaseBuildAdmin):# add your changes hereclassCategoryAdmin(BaseCategoryAdmin):# add your changes here11. Create root URL configurationPlease refer to theurls.pyfile in the test project.For more information about URL configuration in django, please refer to the“URL dispatcher” section in the django documentation.12. Create celery.pyPlease refer to thecelery.pyfile in the test project.For more information about the usage of celery in django, please refer to the“First steps with Django” section in the celery documentation.13. Import the automated testsWhen developing a custom application based on this module, it’s a good
idea to import and run the base tests too, so that you can be sure the changes
you’re introducing are not breaking some of the existing features ofOpenWISP Firmware Upgrader.In case you need to add breaking changes, you can overwrite the tests defined
in the base classes to test your own behavior.See thetests of the sample appto find out how to do this.You can then run tests with:# the --parallel flag is optional
./manage.py test --parallel myupgraderSubstitutemyupgraderwith the name you chose in step 1.For more information about automated tests in django, please refer to“Testing in Django”.Other base classes that can be inherited and extendedThe following steps are not required and are intended for more advanced customization.FirmwareImageDownloadViewThis view controls how the firmware images are stored and who has permission to download them.The full python path is:openwisp_firmware_upgrader.private_storage.FirmwareImageDownloadView.If you want to extend this view, you will have to perform the additional steps below.Step 1. import and extend view:# myupgrader/views.pyfromopenwisp_firmware_upgrader.private_storageimport(FirmwareImageDownloadViewasBaseFirmwareImageDownloadView)classFirmwareImageDownloadView(BaseFirmwareImageDownloadView):# add your customizations here ...passStep 2: remove the following line from your rooturls.pyfile:path('firmware/',include('openwisp_firmware_upgrader.private_storage.urls')),Step 3: add an URL route pointing to your custom view inurls.pyfile:# urls.pyfrommyupgrader.viewsimportFirmwareImageDownloadViewurlpatterns=[# ... other URLspath('<your-custom-path>',FirmwareImageDownloadView.as_view(),name='serve_private_file',),]For more information regarding django views, please refer to the“Class based views” section in the django documentation.API viewsIf you need to customize the behavior of the API views, the procedure to follow
is similar to the one described inFirmwareImageDownloadView,
with the difference that you may also want to create your ownserializersif needed.The API code is stored inopenwisp_firmware_upgrader.apiand is built usingdjango-rest-frameworkFor more information regarding Django REST Framework API views, please refer to the“Generic views” section in the Django REST Framework documentation.ContributingPlease refer to theOpenWISP contributing guidelines.SupportSeeOpenWISP Support Channels.ChangelogSeeCHANGES.LicenseSeeLICENSE.