7 Commits
9 changed files with 84 additions and 28 deletions
+4 -3
View File
@@ -9,6 +9,8 @@ Software Lifecycle Management
Installation Guide Installation Guide
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
WARNING: This plugin is only tested with Netbox 3.2 at this time.
When using the Docker version of Netbox, first follow the `netbox-docker When using the Docker version of Netbox, first follow the `netbox-docker
quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__ quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__
instructions to clone the netbox-docker repo and set up the instructions to clone the netbox-docker repo and set up the
@@ -20,13 +22,13 @@ to install the Netbox SLM plugin:
1. Add ``netbox_slm`` to the ``PLUGINS`` list in 1. Add ``netbox_slm`` to the ``PLUGINS`` list in
``configuration/extra.py``. ``configuration/extra.py``.
2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.94`` as 2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.98`` as
contents. contents.
3. Create a ``Dockerfile-SLM`` with contents: 3. Create a ``Dockerfile-SLM`` with contents:
.. code:: dockerfile .. code:: dockerfile
FROM netboxcommunity/netbox:v3.X.X FROM netboxcommunity/netbox:v3.2.X
COPY ./plugin_requirements.txt / COPY ./plugin_requirements.txt /
RUN /opt/netbox/venv/bin/pip install --no-warn-script-location -r /plugin_requirements.txt RUN /opt/netbox/venv/bin/pip install --no-warn-script-location -r /plugin_requirements.txt
@@ -96,7 +98,6 @@ Steps
$ git clone https://github.com/netbox-community/netbox $ git clone https://github.com/netbox-community/netbox
$ cd netbox $ cd netbox
$ git checkout f5356b8 (to pin 3.0.9)
install the virtual environment install the virtual environment
+8 -16
View File
@@ -9,24 +9,26 @@ Software Lifecycle Management
Installation Guide Installation Guide
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
WARNING: This plugin is only tested with Netbox 3.2 at this time.
When using the Docker version of Netbox, first follow the `netbox-docker When using the Docker version of Netbox, first follow the `netbox-docker
quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__ quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__
instructions to clone the netbox-docker repo and set up the instructions to clone the netbox-docker repo and set up the
``docker-compose.override.yml``. ``docker-compose.override.yml``.
Next, follow these instructions (based on the generic `Netbox plugin Next, follow these instructions (based on the `Netbox docker variant
instructions <https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins>`__) instructions <https://github.com/netbox-community/netbox-docker/wiki/Configuration#custom-configuration-files>`__)
to install the Netbox SLM plugin: to install the Netbox SLM plugin:
1. Add ``netbox_slm`` to the ``PLUGINS`` list in 1. Add ``netbox_slm`` to the ``PLUGINS`` list in
``configuration/configuration.py``. ``configuration/extra.py``.
2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.93`` as 2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.98`` as
contents. contents.
3. Create a ``Dockerfile-SLM`` with contents: 3. Create a ``Dockerfile-SLM`` with contents:
.. code:: dockerfile .. code:: dockerfile
FROM netboxcommunity/netbox:v3.0.9 # The Netbox SLM plugin currently only works with v3.0.9 of Netbox due to backwards incompatible changes in newer version of Netbox FROM netboxcommunity/netbox:v3.2.X
COPY ./plugin_requirements.txt / COPY ./plugin_requirements.txt /
RUN /opt/netbox/venv/bin/pip install --no-warn-script-location -r /plugin_requirements.txt RUN /opt/netbox/venv/bin/pip install --no-warn-script-location -r /plugin_requirements.txt
@@ -63,7 +65,7 @@ directory run
:: ::
# make sure to update the version in netbox_slm/__init__.py and setup.cfg # make sure to update the version in netbox_slm/__init__.py
$ python setup.py sdist $ python setup.py sdist
$ twine upload dist/* $ twine upload dist/*
@@ -87,15 +89,6 @@ Netbox Django copy with auto-reload to develop the plugin pointing to
the Dockerized postgres and redis instances, basically ignoring the the Dockerized postgres and redis instances, basically ignoring the
netbox docker runtime server. netbox docker runtime server.
Gotchas
~~~~~~~~
*Netboxs exception handler seems to supress initial Exceptions,
obfuscating missing imports or other Django related issues by re-raising
an opague error, e.g. netbox_slm plugin namespace doesnotexist. Its
recommended to often check the runtime server or patch Netboxs global
exception handler.*
Steps Steps
~~~~~ ~~~~~
@@ -105,7 +98,6 @@ Steps
$ git clone https://github.com/netbox-community/netbox $ git clone https://github.com/netbox-community/netbox
$ cd netbox $ cd netbox
$ git checkout f5356b8 (to pin 3.0.9)
install the virtual environment install the virtual environment
+1 -1
View File
@@ -5,7 +5,7 @@ class SLMConfig(PluginConfig):
name = 'netbox_slm' name = 'netbox_slm'
verbose_name = 'Software Lifecycle Management' verbose_name = 'Software Lifecycle Management'
description = 'Software Lifecycle Management' description = 'Software Lifecycle Management'
version = '0.95' version = '0.98'
author = 'Hedde van der Heide' author = 'Hedde van der Heide'
author_email = 'hedde.vanderheide@ictu.nl' author_email = 'hedde.vanderheide@ictu.nl'
base_url = 'slm' base_url = 'slm'
@@ -0,0 +1,46 @@
# Generated by Django 3.2.9 on 2022-04-15 07:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dcim', '0153_created_datetimefield'),
('virtualization', '0029_created_datetimefield'),
('netbox_slm', '0003_auto_20220407_1209'),
]
operations = [
migrations.AlterField(
model_name='softwareproduct',
name='manufacturer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.manufacturer'),
),
migrations.AlterField(
model_name='softwareproductinstallation',
name='device',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.device'),
),
migrations.AlterField(
model_name='softwareproductinstallation',
name='software_product',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='netbox_slm.softwareproduct'),
),
migrations.AlterField(
model_name='softwareproductinstallation',
name='version',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='netbox_slm.softwareproductversion'),
),
migrations.AlterField(
model_name='softwareproductinstallation',
name='virtualmachine',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='virtualization.virtualmachine'),
),
migrations.AlterField(
model_name='softwareproductversion',
name='software_product',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='netbox_slm.softwareproduct'),
),
]
-6
View File
@@ -17,7 +17,6 @@ class SoftwareProduct(NetBoxModel):
manufacturer = models.ForeignKey( manufacturer = models.ForeignKey(
to='dcim.Manufacturer', to='dcim.Manufacturer',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='software_products',
null=True, blank=True null=True, blank=True
) )
@@ -42,7 +41,6 @@ class SoftwareProductVersion(NetBoxModel):
software_product = models.ForeignKey( software_product = models.ForeignKey(
to='netbox_slm.SoftwareProduct', to='netbox_slm.SoftwareProduct',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='softwareproduct_versions'
) )
name = models.CharField(max_length=64) name = models.CharField(max_length=64)
@@ -67,26 +65,22 @@ class SoftwareProductInstallation(NetBoxModel):
device = models.ForeignKey( device = models.ForeignKey(
to='dcim.Device', to='dcim.Device',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='softwareproduct_installations',
null=True, null=True,
blank=True blank=True
) )
virtualmachine = models.ForeignKey( virtualmachine = models.ForeignKey(
to='virtualization.VirtualMachine', to='virtualization.VirtualMachine',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='softwareproduct_installations',
null = True, null = True,
blank = True blank = True
) )
software_product = models.ForeignKey( software_product = models.ForeignKey(
to='netbox_slm.SoftwareProduct', to='netbox_slm.SoftwareProduct',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='software_products'
) )
version = models.ForeignKey( version = models.ForeignKey(
to='netbox_slm.SoftwareProductVersion', to='netbox_slm.SoftwareProductVersion',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='softwareproduct_versions'
) )
objects = RestrictedQuerySet.as_manager() objects = RestrictedQuerySet.as_manager()
+21
View File
@@ -1,5 +1,6 @@
import django_tables2 as tables import django_tables2 as tables
from django.db.models import Count
from django_tables2.utils import Accessor from django_tables2.utils import Accessor
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
from netbox.tables import NetBoxTable, ChoiceFieldColumn, ToggleColumn, columns from netbox.tables import NetBoxTable, ChoiceFieldColumn, ToggleColumn, columns
@@ -45,6 +46,12 @@ class SoftwareProductTable(NetBoxTable):
"installations", "installations",
) )
def order_installations(self, queryset, is_descending):
queryset = queryset.annotate(
count=Count('softwareproductinstallation__id')
).order_by(("-" if is_descending else "") + "count")
return queryset, True
class SoftwareProductVersionTable(NetBoxTable): class SoftwareProductVersionTable(NetBoxTable):
"""Table for displaying SoftwareProductVersion objects.""" """Table for displaying SoftwareProductVersion objects."""
@@ -90,6 +97,12 @@ class SoftwareProductVersionTable(NetBoxTable):
"installations", "installations",
) )
def order_installations(self, queryset, is_descending):
queryset = queryset.annotate(
count=Count('softwareproductinstallation__id')
).order_by(("-" if is_descending else "") + "count")
return queryset, True
class SoftwareProductInstallationTable(NetBoxTable): class SoftwareProductInstallationTable(NetBoxTable):
"""Table for displaying SoftwareProductInstallation objects.""" """Table for displaying SoftwareProductInstallation objects."""
@@ -142,5 +155,13 @@ class SoftwareProductInstallationTable(NetBoxTable):
"tags", "tags",
) )
def order_platform(self, queryset, is_descending):
queryset = queryset.order_by(("device" if is_descending else "virtualmachine"))
return queryset, True
def order_type(self, queryset, is_descending):
queryset = queryset.order_by(("device" if is_descending else "virtualmachine"))
return queryset, True
def render_software_product(self, value, **kwargs): def render_software_product(self, value, **kwargs):
return f"{kwargs['record'].software_product.manufacturer.name} - {value}" return f"{kwargs['record'].software_product.manufacturer.name} - {value}"
@@ -24,6 +24,8 @@
<a href="{% url 'plugins:netbox_slm:softwareproductversion' pk=version.pk %}"> <a href="{% url 'plugins:netbox_slm:softwareproductversion' pk=version.pk %}">
<span class="badge" style="color: #ffffff; background-color: #9e9e9e">{{ version }}</span> <span class="badge" style="color: #ffffff; background-color: #9e9e9e">{{ version }}</span>
</a> </a>
{% empty %}
n/a
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>
+1 -1
View File
@@ -28,7 +28,7 @@ class SoftwareProductView(generic.ObjectView):
queryset = SoftwareProduct.objects.all() queryset = SoftwareProduct.objects.all()
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
versions = instance.softwareproduct_versions.all() versions = instance.softwareproductversion_set.all()
return {"versions": versions} return {"versions": versions}
+1 -1
View File
@@ -1,6 +1,6 @@
[metadata] [metadata]
name = netbox-slm name = netbox-slm
version = 0.95 version = 0.98
description = Software Lifecycle Management Netbox Plugin. description = Software Lifecycle Management Netbox Plugin.
# long_description = file: README.rst # long_description = file: README.rst
# long_description_content_type='text/x-rst' # long_description_content_type='text/x-rst'