netbox-services 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. netbox_services-0.0.1/PKG-INFO +77 -0
  2. netbox_services-0.0.1/README.md +57 -0
  3. netbox_services-0.0.1/netbox_services/__init__.py +19 -0
  4. netbox_services-0.0.1/netbox_services/api/__init__.py +1 -0
  5. netbox_services-0.0.1/netbox_services/api/serializers.py +20 -0
  6. netbox_services-0.0.1/netbox_services/api/urls.py +7 -0
  7. netbox_services-0.0.1/netbox_services/api/views.py +8 -0
  8. netbox_services-0.0.1/netbox_services/filtersets.py +8 -0
  9. netbox_services-0.0.1/netbox_services/forms.py +148 -0
  10. netbox_services-0.0.1/netbox_services/migrations/0001_initial.py +43 -0
  11. netbox_services-0.0.1/netbox_services/migrations/0002_alter_service_asns_alter_service_cables_and_more.py +72 -0
  12. netbox_services-0.0.1/netbox_services/migrations/0003_alter_service_options_alter_service_asns_and_more.py +71 -0
  13. netbox_services-0.0.1/netbox_services/migrations/0004_rename_vrf_service_vrfs_alter_service_service_id.py +23 -0
  14. netbox_services-0.0.1/netbox_services/migrations/__init__.py +0 -0
  15. netbox_services-0.0.1/netbox_services/models.py +105 -0
  16. netbox_services-0.0.1/netbox_services/navigation.py +24 -0
  17. netbox_services-0.0.1/netbox_services/tables.py +46 -0
  18. netbox_services-0.0.1/netbox_services/urls.py +73 -0
  19. netbox_services-0.0.1/netbox_services/views.py +112 -0
  20. netbox_services-0.0.1/netbox_services.egg-info/PKG-INFO +77 -0
  21. netbox_services-0.0.1/netbox_services.egg-info/SOURCES.txt +23 -0
  22. netbox_services-0.0.1/netbox_services.egg-info/dependency_links.txt +1 -0
  23. netbox_services-0.0.1/netbox_services.egg-info/top_level.txt +2 -0
  24. netbox_services-0.0.1/pyproject.toml +41 -0
  25. netbox_services-0.0.1/setup.cfg +4 -0
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: netbox-services
3
+ Version: 0.0.1
4
+ Summary: A NetBox services Plugin for Service Providers
5
+ Author-email: Arturo Baldo <baldoarturo@gmail.com>
6
+ Maintainer-email: Arturo Baldo <baldoarturo@gmail.com>
7
+ Project-URL: Documentation, https://github.com/baldoarturo/netbox-services/blob/main/README.md
8
+ Project-URL: Source, https://github.com/baldoarturo/netbox-services
9
+ Project-URL: Tracker, https://github.com/baldoarturo/netbox-services/issues
10
+ Keywords: netbox-plugin
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+
21
+ # NetBox Services Plugin
22
+
23
+ This plugin extends NetBox to manage and relate business services to network resources. It introduces a `Service` model that allows you to track various service types (such as L2VPN, L3VPN, DIA, Transit, CDN, Voice) and associate them with devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, and virtual machines.
24
+
25
+ ## Features
26
+ - Add, edit, and delete business services.
27
+ - Relate services to network objects (devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, virtual machines).
28
+ - Filter and view services in a table with all relevant fields.
29
+ - Custom forms and views for relating specific network objects to a service.
30
+ - Integrated navigation and changelog support.
31
+
32
+ ## Usage
33
+ - Access the plugin from the NetBox navigation menu under "Business Services".
34
+ - Create new services and relate them to network resources.
35
+ - Use the detail view to see all associations for a service.
36
+
37
+ ![alt text](02-main.png)
38
+
39
+ ## Why
40
+ Because you might be looking for such a thing.
41
+
42
+ Tagging is great for simple categorization, but the NetBox Services plugin goes far beyond that by letting you model real business services and their relationships to network resources.
43
+
44
+ Instead of just tagging a device or prefix as "DIA," you can create a full DIA service—like "NW-123456" from Cogent—and link it to all relevant devices, interfaces, prefixes, VRFs, ASNs, and more.
45
+
46
+ For example, with a DIA service from Cogent (Service ID: NW-123456), you can:
47
+
48
+ - See all devices and interfaces delivering that service.
49
+ - Track the exact IP prefixes, VRFs, and ASNs involved.
50
+ - Relate cables, tunnels, and even virtual machines to the service.
51
+ - View and manage all these relationships in one place, with history and forms tailored to each resource.
52
+
53
+ This gives you a true service-centric view of your network, making troubleshooting, reporting, and change management much more powerful and organized than simple tagging ever could.
54
+
55
+ ## Requirements
56
+ - NetBox 4.x or later
57
+ - Django 4.x or later
58
+
59
+ ## Installation
60
+ 1. Clone this repository into your NetBox `plugins` directory. You can also install it on your system / venv / coffee maker with
61
+ ```bash
62
+ pip install netbox_services
63
+ ```
64
+
65
+ 2. Add `'netbox_services'` to the `PLUGINS` list in your NetBox configuration.
66
+
67
+ ```python
68
+ PLUGINS = [
69
+ 'netbox_services'
70
+ ],
71
+ ```
72
+
73
+ 3. Run migrations: `python manage.py migrate netbox_services`
74
+ 4. Restart NetBox.
75
+
76
+ ## License
77
+ MIT
@@ -0,0 +1,57 @@
1
+ # NetBox Services Plugin
2
+
3
+ This plugin extends NetBox to manage and relate business services to network resources. It introduces a `Service` model that allows you to track various service types (such as L2VPN, L3VPN, DIA, Transit, CDN, Voice) and associate them with devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, and virtual machines.
4
+
5
+ ## Features
6
+ - Add, edit, and delete business services.
7
+ - Relate services to network objects (devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, virtual machines).
8
+ - Filter and view services in a table with all relevant fields.
9
+ - Custom forms and views for relating specific network objects to a service.
10
+ - Integrated navigation and changelog support.
11
+
12
+ ## Usage
13
+ - Access the plugin from the NetBox navigation menu under "Business Services".
14
+ - Create new services and relate them to network resources.
15
+ - Use the detail view to see all associations for a service.
16
+
17
+ ![alt text](02-main.png)
18
+
19
+ ## Why
20
+ Because you might be looking for such a thing.
21
+
22
+ Tagging is great for simple categorization, but the NetBox Services plugin goes far beyond that by letting you model real business services and their relationships to network resources.
23
+
24
+ Instead of just tagging a device or prefix as "DIA," you can create a full DIA service—like "NW-123456" from Cogent—and link it to all relevant devices, interfaces, prefixes, VRFs, ASNs, and more.
25
+
26
+ For example, with a DIA service from Cogent (Service ID: NW-123456), you can:
27
+
28
+ - See all devices and interfaces delivering that service.
29
+ - Track the exact IP prefixes, VRFs, and ASNs involved.
30
+ - Relate cables, tunnels, and even virtual machines to the service.
31
+ - View and manage all these relationships in one place, with history and forms tailored to each resource.
32
+
33
+ This gives you a true service-centric view of your network, making troubleshooting, reporting, and change management much more powerful and organized than simple tagging ever could.
34
+
35
+ ## Requirements
36
+ - NetBox 4.x or later
37
+ - Django 4.x or later
38
+
39
+ ## Installation
40
+ 1. Clone this repository into your NetBox `plugins` directory. You can also install it on your system / venv / coffee maker with
41
+ ```bash
42
+ pip install netbox_services
43
+ ```
44
+
45
+ 2. Add `'netbox_services'` to the `PLUGINS` list in your NetBox configuration.
46
+
47
+ ```python
48
+ PLUGINS = [
49
+ 'netbox_services'
50
+ ],
51
+ ```
52
+
53
+ 3. Run migrations: `python manage.py migrate netbox_services`
54
+ 4. Restart NetBox.
55
+
56
+ ## License
57
+ MIT
@@ -0,0 +1,19 @@
1
+ from importlib.metadata import metadata
2
+
3
+ from netbox.plugins import PluginConfig
4
+
5
+
6
+ plugin = metadata('netbox_services')
7
+
8
+
9
+ class Services(PluginConfig):
10
+ name = plugin.get('Name').replace('-', '_')
11
+ verbose_name = plugin.get('Name').replace('-', ' ').title()
12
+ description = plugin.get('Summary')
13
+ version = plugin.get('Version')
14
+ author = 'Arturo Baldo'
15
+ author_email = 'baldoarturo@gmail.com'
16
+ base_url = 'services'
17
+ min_version = '4.4.0'
18
+
19
+ config = Services
@@ -0,0 +1 @@
1
+ from .serializers import *
@@ -0,0 +1,20 @@
1
+ from rest_framework import serializers
2
+
3
+ from netbox.api.serializers import NetBoxModelSerializer
4
+ from netbox_services.models import Service
5
+
6
+ class ServiceSerializer(NetBoxModelSerializer):
7
+
8
+ url = serializers.HyperlinkedIdentityField(
9
+ view_name='plugins-api:netbox_services-api:service-detail'
10
+ )
11
+ class Meta:
12
+ model = Service
13
+ fields = ('type',
14
+ 'service_id',
15
+ 'tenant',
16
+ 'tags',
17
+ 'custom_fields',
18
+ 'created',
19
+ 'last_updated',
20
+ )
@@ -0,0 +1,7 @@
1
+ from netbox.api.routers import NetBoxRouter
2
+ from .views import ServiceViewSet
3
+ app_name = 'netbox_services'
4
+ router = NetBoxRouter()
5
+ router.register('services', ServiceViewSet)
6
+
7
+ urlpatterns = router.urls
@@ -0,0 +1,8 @@
1
+ from netbox.api.viewsets import NetBoxModelViewSet
2
+
3
+ from .. import models
4
+ from .serializers import ServiceSerializer
5
+
6
+ class ServiceViewSet(NetBoxModelViewSet):
7
+ queryset = models.Service.objects.prefetch_related('tags')
8
+ serializer_class = ServiceSerializer
@@ -0,0 +1,8 @@
1
+ from netbox.filtersets import NetBoxModelFilterSet
2
+ from .models import Service
3
+
4
+ class ServiceFilterSet(NetBoxModelFilterSet):
5
+
6
+ class Meta:
7
+ model = Service
8
+ fields = '__all__'
@@ -0,0 +1,148 @@
1
+ from django import forms
2
+ from utilities.forms.fields import CommentField
3
+
4
+ from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm
5
+ from .models import Service, ServiceTypeChoices
6
+
7
+ from tenancy.models import Tenant
8
+ from dcim.models import Device, Interface, Cable
9
+ from ipam.models import VRF, Prefix, VLAN, ASN, RouteTarget
10
+ from vpn.models import L2VPN, Tunnel
11
+ from virtualization.models import VirtualMachine
12
+
13
+
14
+ class NewServiceForm(NetBoxModelForm):
15
+ comments = CommentField()
16
+
17
+ class Meta:
18
+ model = Service
19
+ fields = ('type', 'service_id', 'tenant')
20
+
21
+
22
+ class ServiceRelatedDevicesForm(forms.ModelForm):
23
+
24
+ class Meta:
25
+ model = Service
26
+ fields = ('devices',)
27
+
28
+ class ServiceRelatedInterfacesForm(forms.ModelForm):
29
+
30
+ class Meta:
31
+ model = Service
32
+ fields = ('interfaces',)
33
+
34
+ class ServiceRelatedCablesForm(forms.ModelForm):
35
+
36
+ class Meta:
37
+ model = Service
38
+ fields = ('cables',)
39
+
40
+
41
+ class ServiceRelatedVLANsForm(forms.ModelForm):
42
+
43
+ class Meta:
44
+ model = Service
45
+ fields = ('vlans',)
46
+
47
+ class ServiceRelatedPrefixesForm(forms.ModelForm):
48
+
49
+ class Meta:
50
+ model = Service
51
+ fields = ('prefixes',)
52
+
53
+ class ServiceRelatedVRFsForm(forms.ModelForm):
54
+
55
+ class Meta:
56
+ model = Service
57
+ fields = ('vrfs',)
58
+
59
+ class ServiceRelatedASNsForm(forms.ModelForm):
60
+
61
+ class Meta:
62
+ model = Service
63
+ fields = ('asns',)
64
+
65
+
66
+ class ServiceRelatedRouteTargetsForm(forms.ModelForm):
67
+
68
+ class Meta:
69
+ model = Service
70
+ fields = ('route_targets',)
71
+
72
+ class ServiceRelatedL2VPNsForm(forms.ModelForm):
73
+
74
+ class Meta:
75
+ model = Service
76
+ fields = ('l2vpns',)
77
+
78
+ class ServiceRelatedTunnelsForm(forms.ModelForm):
79
+
80
+ class Meta:
81
+ model = Service
82
+ fields = ('tunnels',)
83
+
84
+ class ServiceRelatedVirtualMachinesForm(forms.ModelForm):
85
+
86
+ class Meta:
87
+ model = Service
88
+ fields = ('virtual_machines',)
89
+
90
+
91
+ class ServiceFilterForm(NetBoxModelFilterSetForm):
92
+ comments = CommentField()
93
+ model = Service
94
+ type = forms.MultipleChoiceField(
95
+ choices=ServiceTypeChoices,
96
+ required=False
97
+ )
98
+ service_id = forms.CharField(
99
+ required=False
100
+ )
101
+ tenant = forms.ModelMultipleChoiceField(
102
+ queryset=Tenant.objects.all(),
103
+ required=False
104
+ )
105
+ devices = forms.ModelMultipleChoiceField(
106
+ queryset=Device.objects.all(),
107
+ required=False
108
+ )
109
+ interfaces = forms.ModelMultipleChoiceField(
110
+ queryset=Interface.objects.all(),
111
+ required=False
112
+ )
113
+ cables = forms.ModelMultipleChoiceField(
114
+ queryset=Cable.objects.all(),
115
+ required=False
116
+ )
117
+ vlans = forms.ModelMultipleChoiceField(
118
+ queryset=VLAN.objects.all(),
119
+ required=False
120
+ )
121
+ prefixes = forms.ModelMultipleChoiceField(
122
+ queryset=Prefix.objects.all(),
123
+ required=False
124
+ )
125
+ vrf = forms.ModelMultipleChoiceField(
126
+ queryset=VRF.objects.all(),
127
+ required=False
128
+ )
129
+ asns = forms.ModelMultipleChoiceField(
130
+ queryset=ASN.objects.all(),
131
+ required=False
132
+ )
133
+ route_targets = forms.ModelMultipleChoiceField(
134
+ queryset=RouteTarget.objects.all(),
135
+ required=False
136
+ )
137
+ l2vpns = forms.ModelMultipleChoiceField(
138
+ queryset=L2VPN.objects.all(),
139
+ required=False
140
+ )
141
+ tunnels = forms.ModelMultipleChoiceField(
142
+ queryset=Tenant.objects.all(),
143
+ required=False
144
+ )
145
+ virtual_machines = forms.ModelMultipleChoiceField(
146
+ queryset=VirtualMachine.objects.all(),
147
+ required=False
148
+ )
@@ -0,0 +1,43 @@
1
+ # Generated by Django 5.2.6 on 2025-09-27 20:31
2
+
3
+ import django.db.models.deletion
4
+ import netbox.models.deletion
5
+ import taggit.managers
6
+ import utilities.json
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+
12
+ initial = True
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Service',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
19
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
20
+ ('last_updated', models.DateTimeField(auto_now=True, null=True)),
21
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
22
+ ('type', models.CharField()),
23
+ ('service_id', models.CharField()),
24
+ ('asns', models.ManyToManyField(to='ipam.asn')),
25
+ ('cables', models.ManyToManyField(to='dcim.cable')),
26
+ ('devices', models.ManyToManyField(to='dcim.device')),
27
+ ('interfaces', models.ManyToManyField(to='dcim.interface')),
28
+ ('l2vpns', models.ManyToManyField(to='vpn.l2vpn')),
29
+ ('prefixes', models.ManyToManyField(to='ipam.prefix')),
30
+ ('route_targets', models.ManyToManyField(to='ipam.routetarget')),
31
+ ('tags', taggit.managers.TaggableManager(through='taggit.TaggedItem', to='taggit.Tag')),
32
+ ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='tenancy.tenant')),
33
+ ('tunnels', models.ManyToManyField(to='vpn.tunnel')),
34
+ ('virtual_machines', models.ManyToManyField(to='virtualization.virtualmachine')),
35
+ ('vlans', models.ManyToManyField(to='ipam.vlan')),
36
+ ('vrf', models.ManyToManyField(to='ipam.vrf')),
37
+ ],
38
+ options={
39
+ 'abstract': False,
40
+ },
41
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
42
+ ),
43
+ ]
@@ -0,0 +1,72 @@
1
+ # Generated by Django 5.2.6 on 2025-09-27 20:54
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('dcim', '0215_rackreservation_status'),
10
+ ('ipam', '0082_add_prefix_network_containment_indexes'),
11
+ ('netbox_services', '0001_initial'),
12
+ ('virtualization', '0048_populate_mac_addresses'),
13
+ ('vpn', '0009_remove_redundant_indexes'),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AlterField(
18
+ model_name='service',
19
+ name='asns',
20
+ field=models.ManyToManyField(blank=True, null=True, to='ipam.asn'),
21
+ ),
22
+ migrations.AlterField(
23
+ model_name='service',
24
+ name='cables',
25
+ field=models.ManyToManyField(blank=True, to='dcim.cable'),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name='service',
29
+ name='devices',
30
+ field=models.ManyToManyField(blank=True, null=True, to='dcim.device'),
31
+ ),
32
+ migrations.AlterField(
33
+ model_name='service',
34
+ name='interfaces',
35
+ field=models.ManyToManyField(blank=True, null=True, to='dcim.interface'),
36
+ ),
37
+ migrations.AlterField(
38
+ model_name='service',
39
+ name='l2vpns',
40
+ field=models.ManyToManyField(blank=True, null=True, to='vpn.l2vpn'),
41
+ ),
42
+ migrations.AlterField(
43
+ model_name='service',
44
+ name='prefixes',
45
+ field=models.ManyToManyField(blank=True, null=True, to='ipam.prefix'),
46
+ ),
47
+ migrations.AlterField(
48
+ model_name='service',
49
+ name='route_targets',
50
+ field=models.ManyToManyField(blank=True, null=True, to='ipam.routetarget'),
51
+ ),
52
+ migrations.AlterField(
53
+ model_name='service',
54
+ name='tunnels',
55
+ field=models.ManyToManyField(blank=True, null=True, to='vpn.tunnel'),
56
+ ),
57
+ migrations.AlterField(
58
+ model_name='service',
59
+ name='virtual_machines',
60
+ field=models.ManyToManyField(blank=True, null=True, to='virtualization.virtualmachine'),
61
+ ),
62
+ migrations.AlterField(
63
+ model_name='service',
64
+ name='vlans',
65
+ field=models.ManyToManyField(blank=True, null=True, to='ipam.vlan'),
66
+ ),
67
+ migrations.AlterField(
68
+ model_name='service',
69
+ name='vrf',
70
+ field=models.ManyToManyField(blank=True, to='ipam.vrf'),
71
+ ),
72
+ ]
@@ -0,0 +1,71 @@
1
+ # Generated by Django 5.2.6 on 2025-10-01 19:09
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('dcim', '0215_rackreservation_status'),
10
+ ('ipam', '0082_add_prefix_network_containment_indexes'),
11
+ ('netbox_services', '0002_alter_service_asns_alter_service_cables_and_more'),
12
+ ('virtualization', '0048_populate_mac_addresses'),
13
+ ('vpn', '0009_remove_redundant_indexes'),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AlterModelOptions(
18
+ name='service',
19
+ options={'ordering': ['service_id']},
20
+ ),
21
+ migrations.AlterField(
22
+ model_name='service',
23
+ name='asns',
24
+ field=models.ManyToManyField(to='ipam.asn'),
25
+ ),
26
+ migrations.AlterField(
27
+ model_name='service',
28
+ name='cables',
29
+ field=models.ManyToManyField(to='dcim.cable'),
30
+ ),
31
+ migrations.AlterField(
32
+ model_name='service',
33
+ name='devices',
34
+ field=models.ManyToManyField(to='dcim.device'),
35
+ ),
36
+ migrations.AlterField(
37
+ model_name='service',
38
+ name='interfaces',
39
+ field=models.ManyToManyField(to='dcim.interface'),
40
+ ),
41
+ migrations.AlterField(
42
+ model_name='service',
43
+ name='l2vpns',
44
+ field=models.ManyToManyField(to='vpn.l2vpn'),
45
+ ),
46
+ migrations.AlterField(
47
+ model_name='service',
48
+ name='prefixes',
49
+ field=models.ManyToManyField(to='ipam.prefix'),
50
+ ),
51
+ migrations.AlterField(
52
+ model_name='service',
53
+ name='route_targets',
54
+ field=models.ManyToManyField(to='ipam.routetarget'),
55
+ ),
56
+ migrations.AlterField(
57
+ model_name='service',
58
+ name='tunnels',
59
+ field=models.ManyToManyField(to='vpn.tunnel'),
60
+ ),
61
+ migrations.AlterField(
62
+ model_name='service',
63
+ name='virtual_machines',
64
+ field=models.ManyToManyField(to='virtualization.virtualmachine'),
65
+ ),
66
+ migrations.AlterField(
67
+ model_name='service',
68
+ name='vlans',
69
+ field=models.ManyToManyField(to='ipam.vlan'),
70
+ ),
71
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 5.2.6 on 2025-10-02 19:48
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_services', '0003_alter_service_options_alter_service_asns_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RenameField(
14
+ model_name='service',
15
+ old_name='vrf',
16
+ new_name='vrfs',
17
+ ),
18
+ migrations.AlterField(
19
+ model_name='service',
20
+ name='service_id',
21
+ field=models.CharField(unique=True),
22
+ ),
23
+ ]
@@ -0,0 +1,105 @@
1
+ from django.db import models
2
+ from django.urls import reverse
3
+
4
+ from netbox.models import NetBoxModel
5
+
6
+ from tenancy.models import Tenant
7
+ from dcim.models import Device, Interface, Cable
8
+ from ipam.models import VRF, Prefix, VLAN, ASN, RouteTarget
9
+ from vpn.models import L2VPN, Tunnel
10
+ from virtualization.models import VirtualMachine
11
+
12
+ from utilities.choices import ChoiceSet
13
+ from taggit.managers import TaggableManager
14
+
15
+
16
+ class ServiceTypeChoices(ChoiceSet):
17
+ CHOICES = [
18
+ ('l2vpn', 'L2VPN'),
19
+ ('l3vpn', 'L3VPN'),
20
+ ('dia', 'DIA'),
21
+ ('transit', 'IP Transit'),
22
+ ('cdn', 'CDN'),
23
+ ('voice', 'Voice')
24
+ ]
25
+
26
+
27
+ class Service(NetBoxModel):
28
+ type = models.CharField(
29
+ choices=ServiceTypeChoices,
30
+ verbose_name='Service Type',
31
+ null=False,
32
+ blank=False
33
+ )
34
+ service_id = models.CharField(
35
+ verbose_name='Service ID',
36
+ unique=True,
37
+ null=False,
38
+ blank=False
39
+ )
40
+ tenant = models.ForeignKey(
41
+ Tenant,
42
+ on_delete=models.PROTECT,
43
+ verbose_name='Service Tenant',
44
+ null=True,
45
+ blank=True
46
+ )
47
+ devices = models.ManyToManyField(
48
+ Device,
49
+ verbose_name='Related Devices',
50
+ )
51
+ interfaces = models.ManyToManyField(
52
+ Interface,
53
+ verbose_name='Related interfaces',
54
+ )
55
+ cables = models.ManyToManyField(
56
+ Cable,
57
+ verbose_name='Related Cables/XConnects',
58
+ )
59
+ vlans = models.ManyToManyField(
60
+ VLAN,
61
+ verbose_name='Related VLANs',
62
+ )
63
+ prefixes = models.ManyToManyField(
64
+ Prefix,
65
+ verbose_name='Related IP Prefixes',
66
+ )
67
+ vrfs = models.ManyToManyField(
68
+ VRF,
69
+ verbose_name='Related VRF',
70
+ blank=True
71
+ )
72
+ asns = models.ManyToManyField(
73
+ ASN,
74
+ verbose_name='Related Autonomous Systems',
75
+ )
76
+ route_targets = models.ManyToManyField(
77
+ RouteTarget,
78
+ verbose_name='Related Route Targets',
79
+ )
80
+ l2vpns = models.ManyToManyField(
81
+ L2VPN,
82
+ verbose_name='Related Route Targets',
83
+ )
84
+ tunnels = models.ManyToManyField(
85
+ Tunnel,
86
+ verbose_name='Related Tunnels',
87
+ )
88
+ virtual_machines = models.ManyToManyField(
89
+ VirtualMachine,
90
+ verbose_name='Related Virtual Machines',
91
+ )
92
+ tags = TaggableManager(
93
+ related_name='netbox_services_service_set',
94
+ )
95
+
96
+ class Meta:
97
+ verbose_name = 'Service'
98
+ verbose_name_plural = 'Services'
99
+ ordering = ['service_id']
100
+
101
+ def __str__(self):
102
+ return f"{self.service_id}"
103
+
104
+ def get_absolute_url(self):
105
+ return f"/plugins/services/{self.pk}/"
@@ -0,0 +1,24 @@
1
+ from netbox.plugins import PluginMenu
2
+ from netbox.plugins import PluginMenuItem, PluginMenuButton
3
+
4
+ services = PluginMenuItem(
5
+ auth_required=True,
6
+ link='plugins:netbox_services:service_list',
7
+ link_text='Business Services',
8
+ buttons=(
9
+ PluginMenuButton(
10
+ link='plugins:netbox_services:service_add',
11
+ title='Add',
12
+ icon_class='mdi mdi-plus',
13
+ ),
14
+ )
15
+ )
16
+
17
+
18
+ menu = PluginMenu(
19
+ label='Business Services',
20
+ groups=(
21
+ ('Services', (services,)),
22
+ ),
23
+ icon_class='mdi mdi-receipt-text-arrow-right'
24
+ )
@@ -0,0 +1,46 @@
1
+ import django_tables2 as tables
2
+
3
+ from netbox.tables import NetBoxTable
4
+
5
+ from .models import Service
6
+
7
+
8
+ class ServiceListTable(NetBoxTable):
9
+ service_id = tables.Column(
10
+ linkify=True,
11
+ verbose_name='Service ID',
12
+
13
+ )
14
+
15
+ class Meta(NetBoxTable.Meta):
16
+ model = Service
17
+ fields = ('pk', 'service_id', 'type', 'tenant')
18
+ default_columns = ('pk', 'service_id', 'type', 'tenant')
19
+
20
+
21
+ class ServiceTable(NetBoxTable):
22
+ service_id = tables.Column(
23
+ linkify=True,
24
+ verbose_name='Service ID',
25
+
26
+ )
27
+
28
+ class Meta(NetBoxTable.Meta):
29
+ model = Service
30
+ fields = (
31
+ 'type',
32
+ 'service_id',
33
+ 'tenant',
34
+ 'devices',
35
+ 'interfaces',
36
+ 'cables',
37
+ 'vlans',
38
+ 'prefixes',
39
+ 'vrf',
40
+ 'asns',
41
+ 'route_targets',
42
+ 'l2vpns',
43
+ 'tunnels',
44
+ 'virtual_machines',
45
+ )
46
+ default_columns = ('pk', 'service_id', 'type', 'tenant')
@@ -0,0 +1,73 @@
1
+ from django.urls import path, include
2
+ from utilities.urls import get_model_urls
3
+ from .views import (
4
+ ServiceView,
5
+ ServiceListView,
6
+ ServiceAddView,
7
+ ServiceEditView,
8
+ ServiceDeleteView,
9
+ ServiceRelatedDevicesView,
10
+ ServiceRelatedInterfacesView,
11
+ ServiceRelatedCablesView,
12
+ ServiceRelatedVLANsView,
13
+ ServiceRelatedPrefixesView,
14
+ ServiceRelatedVRFsView,
15
+ ServiceRelatedASNsView,
16
+ ServiceRelatedRouteTargetsView,
17
+ ServiceRelatedL2VPNsView,
18
+ ServiceRelatedTunnelsView,
19
+ ServiceRelatedVirtualMachinesView,
20
+ )
21
+
22
+ urlpatterns = [
23
+ path('',
24
+ include(get_model_urls('netbox_services', 'service', detail=False))
25
+ ),
26
+ path('<int:pk>/',
27
+ include(get_model_urls('netbox_services', 'service', detail=True))
28
+ ),
29
+ path('<int:pk>/relate/devices/',
30
+ ServiceRelatedDevicesView.as_view(),
31
+ name='relate_devices',
32
+ ),
33
+ path('<int:pk>/relate/interfaces/',
34
+ ServiceRelatedInterfacesView.as_view(),
35
+ name='relate_interfaces',
36
+ ),
37
+ path('<int:pk>/relate/cables/',
38
+ ServiceRelatedCablesView.as_view(),
39
+ name='relate_cables',
40
+ ),
41
+ path('<int:pk>/relate/vlans/',
42
+ ServiceRelatedVLANsView.as_view(),
43
+ name='relate_vlans',
44
+ ),
45
+ path('<int:pk>/relate/prefixes/',
46
+ ServiceRelatedPrefixesView.as_view(),
47
+ name='relate_prefixes',
48
+ ),
49
+ path('<int:pk>/relate/vrfs/',
50
+ ServiceRelatedVRFsView.as_view(),
51
+ name='relate_vrfs',
52
+ ),
53
+ path('<int:pk>/relate/asns/',
54
+ ServiceRelatedASNsView.as_view(),
55
+ name='relate_asns',
56
+ ),
57
+ path('<int:pk>/relate/route_targets/',
58
+ ServiceRelatedRouteTargetsView.as_view(),
59
+ name='relate_route_targets',
60
+ ),
61
+ path('<int:pk>/relate/l2vpns/',
62
+ ServiceRelatedL2VPNsView.as_view(),
63
+ name='relate_l2vpns',
64
+ ),
65
+ path('<int:pk>/relate/tunnels/',
66
+ ServiceRelatedTunnelsView.as_view(),
67
+ name='relate_tunnels',
68
+ ),
69
+ path('<int:pk>/relate/virtual_machines/',
70
+ ServiceRelatedVirtualMachinesView.as_view(),
71
+ name='relate_virtual_machines',
72
+ ),
73
+ ]
@@ -0,0 +1,112 @@
1
+ from utilities.views import register_model_view
2
+ from netbox.views.generic import ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView
3
+
4
+ from .models import Service
5
+ from .tables import ServiceListTable
6
+ from .forms import NewServiceForm
7
+ from .forms import (
8
+ ServiceRelatedDevicesForm,
9
+ ServiceRelatedInterfacesForm,
10
+ ServiceRelatedCablesForm,
11
+ ServiceRelatedVLANsForm,
12
+ ServiceRelatedPrefixesForm,
13
+ ServiceRelatedVRFsForm,
14
+ ServiceRelatedASNsForm,
15
+ ServiceRelatedRouteTargetsForm,
16
+ ServiceRelatedL2VPNsForm,
17
+ ServiceRelatedTunnelsForm,
18
+ ServiceRelatedVirtualMachinesForm
19
+ )
20
+
21
+ #
22
+ # GENERIC VIEWS
23
+ #
24
+
25
+
26
+ @register_model_view(Service, name='view', path='', detail=True)
27
+ class ServiceView(ObjectView):
28
+ queryset = Service.objects.all()
29
+ template_name = 'netbox_services/service.html'
30
+
31
+
32
+ @register_model_view(Service, name='list', path='', detail=False)
33
+ class ServiceListView(ObjectListView):
34
+ queryset = Service.objects.all()
35
+ table = ServiceListTable
36
+ template_name = 'generic/object_list.html'
37
+
38
+
39
+ @register_model_view(Service, name='add', path='add', detail=False)
40
+ class ServiceAddView(ObjectEditView):
41
+ queryset = Service.objects.all()
42
+ form = NewServiceForm
43
+
44
+
45
+ @register_model_view(Service, name='edit', detail=True)
46
+ class ServiceEditView(ObjectEditView):
47
+ queryset = Service.objects.all()
48
+ form = NewServiceForm
49
+
50
+
51
+ @register_model_view(Service, name='delete', detail=True)
52
+ class ServiceDeleteView(ObjectDeleteView):
53
+ queryset = Service.objects.all()
54
+
55
+ #
56
+ # RELATION VIEWS
57
+ #
58
+
59
+
60
+ class ServiceRelatedDevicesView(ObjectEditView):
61
+ queryset = Service.objects.all()
62
+ form = ServiceRelatedDevicesForm
63
+
64
+
65
+ class ServiceRelatedInterfacesView(ObjectEditView):
66
+ queryset = Service.objects.all()
67
+ form = ServiceRelatedInterfacesForm
68
+
69
+
70
+ class ServiceRelatedCablesView(ObjectEditView):
71
+ queryset = Service.objects.all()
72
+ form = ServiceRelatedCablesForm
73
+
74
+
75
+ class ServiceRelatedVLANsView(ObjectEditView):
76
+ queryset = Service.objects.all()
77
+ form = ServiceRelatedVLANsForm
78
+
79
+
80
+ class ServiceRelatedPrefixesView(ObjectEditView):
81
+ queryset = Service.objects.all()
82
+ form = ServiceRelatedPrefixesForm
83
+
84
+
85
+ class ServiceRelatedVRFsView(ObjectEditView):
86
+ queryset = Service.objects.all()
87
+ form = ServiceRelatedVRFsForm
88
+
89
+
90
+ class ServiceRelatedASNsView(ObjectEditView):
91
+ queryset = Service.objects.all()
92
+ form = ServiceRelatedASNsForm
93
+
94
+
95
+ class ServiceRelatedRouteTargetsView(ObjectEditView):
96
+ queryset = Service.objects.all()
97
+ form = ServiceRelatedRouteTargetsForm
98
+
99
+
100
+ class ServiceRelatedL2VPNsView(ObjectEditView):
101
+ queryset = Service.objects.all()
102
+ form = ServiceRelatedL2VPNsForm
103
+
104
+
105
+ class ServiceRelatedTunnelsView(ObjectEditView):
106
+ queryset = Service.objects.all()
107
+ form = ServiceRelatedTunnelsForm
108
+
109
+
110
+ class ServiceRelatedVirtualMachinesView(ObjectEditView):
111
+ queryset = Service.objects.all()
112
+ form = ServiceRelatedVirtualMachinesForm
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: netbox-services
3
+ Version: 0.0.1
4
+ Summary: A NetBox services Plugin for Service Providers
5
+ Author-email: Arturo Baldo <baldoarturo@gmail.com>
6
+ Maintainer-email: Arturo Baldo <baldoarturo@gmail.com>
7
+ Project-URL: Documentation, https://github.com/baldoarturo/netbox-services/blob/main/README.md
8
+ Project-URL: Source, https://github.com/baldoarturo/netbox-services
9
+ Project-URL: Tracker, https://github.com/baldoarturo/netbox-services/issues
10
+ Keywords: netbox-plugin
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+
21
+ # NetBox Services Plugin
22
+
23
+ This plugin extends NetBox to manage and relate business services to network resources. It introduces a `Service` model that allows you to track various service types (such as L2VPN, L3VPN, DIA, Transit, CDN, Voice) and associate them with devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, and virtual machines.
24
+
25
+ ## Features
26
+ - Add, edit, and delete business services.
27
+ - Relate services to network objects (devices, interfaces, cables, VLANs, prefixes, VRFs, ASNs, route targets, L2VPNs, tunnels, virtual machines).
28
+ - Filter and view services in a table with all relevant fields.
29
+ - Custom forms and views for relating specific network objects to a service.
30
+ - Integrated navigation and changelog support.
31
+
32
+ ## Usage
33
+ - Access the plugin from the NetBox navigation menu under "Business Services".
34
+ - Create new services and relate them to network resources.
35
+ - Use the detail view to see all associations for a service.
36
+
37
+ ![alt text](02-main.png)
38
+
39
+ ## Why
40
+ Because you might be looking for such a thing.
41
+
42
+ Tagging is great for simple categorization, but the NetBox Services plugin goes far beyond that by letting you model real business services and their relationships to network resources.
43
+
44
+ Instead of just tagging a device or prefix as "DIA," you can create a full DIA service—like "NW-123456" from Cogent—and link it to all relevant devices, interfaces, prefixes, VRFs, ASNs, and more.
45
+
46
+ For example, with a DIA service from Cogent (Service ID: NW-123456), you can:
47
+
48
+ - See all devices and interfaces delivering that service.
49
+ - Track the exact IP prefixes, VRFs, and ASNs involved.
50
+ - Relate cables, tunnels, and even virtual machines to the service.
51
+ - View and manage all these relationships in one place, with history and forms tailored to each resource.
52
+
53
+ This gives you a true service-centric view of your network, making troubleshooting, reporting, and change management much more powerful and organized than simple tagging ever could.
54
+
55
+ ## Requirements
56
+ - NetBox 4.x or later
57
+ - Django 4.x or later
58
+
59
+ ## Installation
60
+ 1. Clone this repository into your NetBox `plugins` directory. You can also install it on your system / venv / coffee maker with
61
+ ```bash
62
+ pip install netbox_services
63
+ ```
64
+
65
+ 2. Add `'netbox_services'` to the `PLUGINS` list in your NetBox configuration.
66
+
67
+ ```python
68
+ PLUGINS = [
69
+ 'netbox_services'
70
+ ],
71
+ ```
72
+
73
+ 3. Run migrations: `python manage.py migrate netbox_services`
74
+ 4. Restart NetBox.
75
+
76
+ ## License
77
+ MIT
@@ -0,0 +1,23 @@
1
+ README.md
2
+ pyproject.toml
3
+ netbox_services/__init__.py
4
+ netbox_services/filtersets.py
5
+ netbox_services/forms.py
6
+ netbox_services/models.py
7
+ netbox_services/navigation.py
8
+ netbox_services/tables.py
9
+ netbox_services/urls.py
10
+ netbox_services/views.py
11
+ netbox_services.egg-info/PKG-INFO
12
+ netbox_services.egg-info/SOURCES.txt
13
+ netbox_services.egg-info/dependency_links.txt
14
+ netbox_services.egg-info/top_level.txt
15
+ netbox_services/api/__init__.py
16
+ netbox_services/api/serializers.py
17
+ netbox_services/api/urls.py
18
+ netbox_services/api/views.py
19
+ netbox_services/migrations/0001_initial.py
20
+ netbox_services/migrations/0002_alter_service_asns_alter_service_cables_and_more.py
21
+ netbox_services/migrations/0003_alter_service_options_alter_service_asns_and_more.py
22
+ netbox_services/migrations/0004_rename_vrf_service_vrfs_alter_service_service_id.py
23
+ netbox_services/migrations/__init__.py
@@ -0,0 +1,2 @@
1
+ dist
2
+ netbox_services
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools",
4
+ "wheel"
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "netbox-services"
10
+ version = "0.0.1"
11
+ description = "A NetBox services Plugin for Service Providers"
12
+ requires-python = ">=3.10"
13
+
14
+ authors = [
15
+ {name = "Arturo Baldo", email = "baldoarturo@gmail.com"}
16
+ ]
17
+ maintainers = [
18
+ {name = "Arturo Baldo", email = "baldoarturo@gmail.com"},
19
+ ]
20
+
21
+ readme = "README.md"
22
+ keywords = ["netbox-plugin", ]
23
+
24
+ license = {file = "LICENSE.md"}
25
+ classifiers=[
26
+ 'Development Status :: 3 - Alpha',
27
+ 'Intended Audience :: Developers',
28
+ 'Natural Language :: English',
29
+ "Programming Language :: Python :: 3 :: Only",
30
+ 'Programming Language :: Python :: 3.10',
31
+ 'Programming Language :: Python :: 3.11',
32
+ 'Programming Language :: Python :: 3.12',
33
+ ]
34
+
35
+ [project.urls]
36
+ Documentation = "https://github.com/baldoarturo/netbox-services/blob/main/README.md"
37
+ Source = "https://github.com/baldoarturo/netbox-services"
38
+ Tracker = "https://github.com/baldoarturo/netbox-services/issues"
39
+
40
+ [tool.setuptools.packages.find]
41
+ exclude=["netbox_services.tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+