netbox-loadbalancer-plugin 0.1.0__py3-none-any.whl

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 (37) hide show
  1. netbox_loadbalancer/__init__.py +34 -0
  2. netbox_loadbalancer/api/__init__.py +0 -0
  3. netbox_loadbalancer/api/serializers.py +86 -0
  4. netbox_loadbalancer/api/urls.py +23 -0
  5. netbox_loadbalancer/api/views.py +72 -0
  6. netbox_loadbalancer/choices.py +185 -0
  7. netbox_loadbalancer/filtersets.py +127 -0
  8. netbox_loadbalancer/forms.py +352 -0
  9. netbox_loadbalancer/graphql/__init__.py +1 -0
  10. netbox_loadbalancer/graphql/filters.py +72 -0
  11. netbox_loadbalancer/graphql/schema.py +39 -0
  12. netbox_loadbalancer/graphql/types.py +70 -0
  13. netbox_loadbalancer/migrations/0001_initial.py +112 -0
  14. netbox_loadbalancer/migrations/0002_add_port_weight_validators.py +42 -0
  15. netbox_loadbalancer/migrations/0003_add_weight_min_validator.py +23 -0
  16. netbox_loadbalancer/migrations/__init__.py +0 -0
  17. netbox_loadbalancer/models.py +326 -0
  18. netbox_loadbalancer/navigation.py +42 -0
  19. netbox_loadbalancer/panels.py +82 -0
  20. netbox_loadbalancer/search.py +62 -0
  21. netbox_loadbalancer/tables.py +128 -0
  22. netbox_loadbalancer/templates/netbox_loadbalancer/loadbalancer.html +1 -0
  23. netbox_loadbalancer/templates/netbox_loadbalancer/pool.html +1 -0
  24. netbox_loadbalancer/templates/netbox_loadbalancer/poolmember.html +1 -0
  25. netbox_loadbalancer/templates/netbox_loadbalancer/virtualserver.html +1 -0
  26. netbox_loadbalancer/tests/__init__.py +0 -0
  27. netbox_loadbalancer/tests/test_api.py +135 -0
  28. netbox_loadbalancer/tests/test_filtersets.py +235 -0
  29. netbox_loadbalancer/tests/test_models.py +287 -0
  30. netbox_loadbalancer/tests/test_views.py +218 -0
  31. netbox_loadbalancer/urls.py +42 -0
  32. netbox_loadbalancer/views.py +320 -0
  33. netbox_loadbalancer_plugin-0.1.0.dist-info/METADATA +312 -0
  34. netbox_loadbalancer_plugin-0.1.0.dist-info/RECORD +37 -0
  35. netbox_loadbalancer_plugin-0.1.0.dist-info/WHEEL +5 -0
  36. netbox_loadbalancer_plugin-0.1.0.dist-info/licenses/LICENSE +191 -0
  37. netbox_loadbalancer_plugin-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,34 @@
1
+ """NetBox plugin for managing load balancers, virtual servers, pools, and pool members.
2
+
3
+ This is the plugin entry point. NetBox discovers plugins by looking for a module-level
4
+ ``config`` variable that points to a PluginConfig subclass. The PluginConfig class
5
+ declares metadata (name, version, author) and settings (base_url, min_version) that
6
+ NetBox uses when loading the plugin.
7
+
8
+ When NetBox starts, it imports this module, reads the ``config`` variable, and uses it
9
+ to register the plugin's models, views, API endpoints, navigation menu, and search
10
+ indexes — all of which are defined in sibling modules within this package.
11
+ """
12
+
13
+ from netbox.plugins import PluginConfig
14
+
15
+
16
+ class NetBoxLoadBalancerConfig(PluginConfig):
17
+ """Declares plugin metadata and compatibility requirements.
18
+
19
+ NetBox reads these attributes at startup to register the plugin. The ``name``
20
+ must match the Python package name (``netbox_loadbalancer``). The ``base_url``
21
+ determines the URL prefix for all plugin views (e.g. ``/plugins/loadbalancer/``).
22
+ The ``min_version`` prevents the plugin from loading on incompatible NetBox releases.
23
+ """
24
+ name = 'netbox_loadbalancer'
25
+ verbose_name = 'Load Balancer'
26
+ description = 'Manage load balancers, virtual servers, pools, and pool members'
27
+ version = '0.1.0'
28
+ author = 'David Johnnes'
29
+ author_email = 'david.johnnes@gmail.com'
30
+ base_url = 'loadbalancer'
31
+ min_version = '4.0.0'
32
+
33
+
34
+ config = NetBoxLoadBalancerConfig
File without changes
@@ -0,0 +1,86 @@
1
+ """REST API serializers for the netbox_loadbalancer plugin.
2
+
3
+ Each serializer extends ``NetBoxModelSerializer``, which is a DRF ModelSerializer
4
+ pre-configured with NetBox's conventions for nested object representation, tag
5
+ handling, and custom field support.
6
+
7
+ Key concepts:
8
+ - ``Meta.fields``: the complete list of fields included in the full (detail) API
9
+ response. This includes computed fields like ``url`` and ``display`` (provided
10
+ by NetBoxModelSerializer), standard model fields, and audit fields (``created``,
11
+ ``last_updated``).
12
+ - ``Meta.brief_fields``: the subset of fields returned when the ``?brief=true``
13
+ query parameter is used on the API. Brief mode returns a minimal representation
14
+ (typically just id, url, display, and name) and is used when the object appears
15
+ as a nested related object inside another serializer's response, to avoid deeply
16
+ nested JSON.
17
+ """
18
+
19
+ from netbox.api.serializers import NetBoxModelSerializer
20
+
21
+ from ..models import LoadBalancer, VirtualServer, Pool, PoolMember
22
+
23
+
24
+ class LoadBalancerSerializer(NetBoxModelSerializer):
25
+ """Serializer for load balancer API responses.
26
+
27
+ Related objects (device, site, tenant, management_ip) are automatically serialized
28
+ as nested objects in full mode, or as brief references when this serializer is
29
+ used as a nested serializer inside another response.
30
+ """
31
+ class Meta:
32
+ model = LoadBalancer
33
+ fields = (
34
+ 'id', 'url', 'display', 'name', 'platform', 'status',
35
+ 'device', 'site', 'tenant', 'management_ip', 'description',
36
+ 'tags', 'custom_fields', 'created', 'last_updated',
37
+ )
38
+ brief_fields = ('id', 'url', 'display', 'name')
39
+
40
+
41
+ class PoolSerializer(NetBoxModelSerializer):
42
+ """Serializer for pool API responses.
43
+
44
+ The loadbalancer field is serialized as a nested object in full mode, showing
45
+ the parent load balancer's details inline within the pool response.
46
+ """
47
+ class Meta:
48
+ model = Pool
49
+ fields = (
50
+ 'id', 'url', 'display', 'name', 'loadbalancer', 'method',
51
+ 'protocol', 'description',
52
+ 'tags', 'custom_fields', 'created', 'last_updated',
53
+ )
54
+ brief_fields = ('id', 'url', 'display', 'name')
55
+
56
+
57
+ class VirtualServerSerializer(NetBoxModelSerializer):
58
+ """Serializer for virtual server API responses.
59
+
60
+ Includes nested representations for loadbalancer, ip_address, pool, and tenant.
61
+ The port and protocol fields are serialized as simple values.
62
+ """
63
+ class Meta:
64
+ model = VirtualServer
65
+ fields = (
66
+ 'id', 'url', 'display', 'name', 'loadbalancer', 'ip_address',
67
+ 'port', 'protocol', 'status', 'pool', 'tenant', 'description',
68
+ 'tags', 'custom_fields', 'created', 'last_updated',
69
+ )
70
+ brief_fields = ('id', 'url', 'display', 'name')
71
+
72
+
73
+ class PoolMemberSerializer(NetBoxModelSerializer):
74
+ """Serializer for pool member API responses.
75
+
76
+ Includes nested representations for pool, ip_address, and device. Numeric fields
77
+ (port, weight, priority) are serialized as plain integers.
78
+ """
79
+ class Meta:
80
+ model = PoolMember
81
+ fields = (
82
+ 'id', 'url', 'display', 'name', 'pool', 'ip_address', 'device',
83
+ 'port', 'weight', 'priority', 'status', 'description',
84
+ 'tags', 'custom_fields', 'created', 'last_updated',
85
+ )
86
+ brief_fields = ('id', 'url', 'display', 'name')
@@ -0,0 +1,23 @@
1
+ """API URL routing for the netbox_loadbalancer plugin.
2
+
3
+ Uses ``NetBoxRouter`` (a subclass of DRF's DefaultRouter) to automatically generate
4
+ REST API URL patterns for all four model viewsets. The router creates standard
5
+ endpoints like ``/api/plugins/loadbalancer/loadbalancers/`` and
6
+ ``/api/plugins/loadbalancer/loadbalancers/<id>/``.
7
+
8
+ NetBox discovers this module automatically because the plugin package contains an
9
+ ``api/`` sub-package with a ``urls.py`` module. The ``urlpatterns`` variable is
10
+ included under the plugin's API namespace by NetBox's plugin loading machinery.
11
+ """
12
+
13
+ from netbox.api.routers import NetBoxRouter
14
+
15
+ from . import views
16
+
17
+ router = NetBoxRouter()
18
+ router.register('loadbalancers', views.LoadBalancerViewSet)
19
+ router.register('pools', views.PoolViewSet)
20
+ router.register('virtual-servers', views.VirtualServerViewSet)
21
+ router.register('pool-members', views.PoolMemberViewSet)
22
+
23
+ urlpatterns = router.urls
@@ -0,0 +1,72 @@
1
+ """REST API viewsets for the netbox_loadbalancer plugin.
2
+
3
+ Each viewset extends ``NetBoxModelViewSet``, which is a DRF (Django REST Framework)
4
+ ModelViewSet pre-configured with NetBox's authentication, permission checking,
5
+ pagination, and nested serializer support. A single viewset provides all standard
6
+ REST actions: list, retrieve, create, update, partial_update, and destroy.
7
+
8
+ The three class attributes on each viewset control its behaviour:
9
+ - ``queryset``: the base database query (can include annotations like Count).
10
+ - ``serializer_class``: the serializer used to convert model instances to/from JSON.
11
+ - ``filterset_class``: the FilterSet that enables URL query parameter filtering
12
+ (e.g. ``GET /api/plugins/loadbalancer/pools/?method=round-robin``).
13
+
14
+ These viewsets are registered with the ``NetBoxRouter`` in ``api/urls.py``, which
15
+ generates the standard REST URL patterns (list, detail, etc.) automatically.
16
+ """
17
+
18
+ from django.db.models import Count
19
+ from netbox.api.viewsets import NetBoxModelViewSet
20
+
21
+ from ..models import LoadBalancer, VirtualServer, Pool, PoolMember
22
+ from ..filtersets import (
23
+ LoadBalancerFilterSet, VirtualServerFilterSet, PoolFilterSet, PoolMemberFilterSet,
24
+ )
25
+ from .serializers import (
26
+ LoadBalancerSerializer, VirtualServerSerializer, PoolSerializer, PoolMemberSerializer,
27
+ )
28
+
29
+
30
+ class LoadBalancerViewSet(NetBoxModelViewSet):
31
+ """API endpoint for managing load balancers.
32
+
33
+ Supports ``GET /``, ``POST /``, ``GET /<id>/``, ``PUT /<id>/``, ``PATCH /<id>/``,
34
+ and ``DELETE /<id>/`` under the ``/api/plugins/loadbalancer/loadbalancers/`` path.
35
+ """
36
+ queryset = LoadBalancer.objects.all()
37
+ serializer_class = LoadBalancerSerializer
38
+ filterset_class = LoadBalancerFilterSet
39
+
40
+
41
+ class PoolViewSet(NetBoxModelViewSet):
42
+ """API endpoint for managing pools.
43
+
44
+ The queryset includes ``.annotate(member_count=Count('members'))`` so that the
45
+ pool member count is available in the serialized response without requiring a
46
+ separate database query for each pool.
47
+ """
48
+ queryset = Pool.objects.annotate(member_count=Count('members'))
49
+ serializer_class = PoolSerializer
50
+ filterset_class = PoolFilterSet
51
+
52
+
53
+ class VirtualServerViewSet(NetBoxModelViewSet):
54
+ """API endpoint for managing virtual servers.
55
+
56
+ Supports full CRUD and filtering by load balancer, status, protocol, pool, and
57
+ tenant via the ``VirtualServerFilterSet``.
58
+ """
59
+ queryset = VirtualServer.objects.all()
60
+ serializer_class = VirtualServerSerializer
61
+ filterset_class = VirtualServerFilterSet
62
+
63
+
64
+ class PoolMemberViewSet(NetBoxModelViewSet):
65
+ """API endpoint for managing pool members.
66
+
67
+ Supports full CRUD and filtering by pool, status, device, IP address, port,
68
+ weight, and priority via the ``PoolMemberFilterSet``.
69
+ """
70
+ queryset = PoolMember.objects.all()
71
+ serializer_class = PoolMemberSerializer
72
+ filterset_class = PoolMemberFilterSet
@@ -0,0 +1,185 @@
1
+ """Choice sets for the netbox_loadbalancer plugin.
2
+
3
+ Each class in this module extends NetBox's ``ChoiceSet`` base class to define the
4
+ valid options for a CharField on one of the plugin's models. A ChoiceSet works like
5
+ a Django choices tuple but adds colour-coded badges in the UI (the optional third
6
+ element in each CHOICES entry).
7
+
8
+ The ``key`` attribute (e.g. ``'LoadBalancer.platform'``) allows NetBox administrators
9
+ to override or extend the default choices at runtime through the FIELD_CHOICES
10
+ configuration setting, without modifying the plugin source code.
11
+
12
+ Convention: class constants (e.g. ``STATUS_ACTIVE = 'active'``) are used throughout
13
+ the codebase to reference choices by name instead of hardcoding string values.
14
+ """
15
+
16
+ from utilities.choices import ChoiceSet
17
+
18
+
19
+ class LoadBalancerPlatformChoices(ChoiceSet):
20
+ """Platform types for load balancer appliances.
21
+
22
+ Identifies the vendor or technology stack running the load balancer. This is a
23
+ required field on the LoadBalancer model. Common platforms include hardware
24
+ appliances (F5 BIG-IP, Citrix ADC), software-based solutions (HAProxy, NGINX),
25
+ and cloud-native services (AWS ELB/ALB, Azure LB). Use 'Other' for platforms
26
+ not listed here.
27
+ """
28
+ key = 'LoadBalancer.platform'
29
+
30
+ PLATFORM_F5 = 'f5'
31
+ PLATFORM_HAPROXY = 'haproxy'
32
+ PLATFORM_CITRIX = 'citrix'
33
+ PLATFORM_NGINX = 'nginx'
34
+ PLATFORM_AWS = 'aws'
35
+ PLATFORM_AZURE = 'azure'
36
+ PLATFORM_OTHER = 'other'
37
+
38
+ CHOICES = [
39
+ (PLATFORM_F5, 'F5 BIG-IP'),
40
+ (PLATFORM_HAPROXY, 'HAProxy'),
41
+ (PLATFORM_CITRIX, 'Citrix ADC'),
42
+ (PLATFORM_NGINX, 'NGINX'),
43
+ (PLATFORM_AWS, 'AWS ELB/ALB'),
44
+ (PLATFORM_AZURE, 'Azure LB'),
45
+ (PLATFORM_OTHER, 'Other'),
46
+ ]
47
+
48
+
49
+ class LoadBalancerStatusChoices(ChoiceSet):
50
+ """Operational lifecycle status for load balancers.
51
+
52
+ Tracks where a load balancer is in its lifecycle: planned (not yet deployed),
53
+ active (in production), maintenance (temporarily offline for work), or
54
+ decommissioned (retired). Each status has an associated colour that is displayed
55
+ as a badge in the NetBox UI.
56
+ """
57
+ key = 'LoadBalancer.status'
58
+
59
+ STATUS_ACTIVE = 'active'
60
+ STATUS_PLANNED = 'planned'
61
+ STATUS_MAINTENANCE = 'maintenance'
62
+ STATUS_DECOMMISSIONED = 'decommissioned'
63
+
64
+ CHOICES = [
65
+ (STATUS_ACTIVE, 'Active', 'green'),
66
+ (STATUS_PLANNED, 'Planned', 'cyan'),
67
+ (STATUS_MAINTENANCE, 'Maintenance', 'yellow'),
68
+ (STATUS_DECOMMISSIONED, 'Decommissioned', 'gray'),
69
+ ]
70
+
71
+
72
+ class VirtualServerStatusChoices(ChoiceSet):
73
+ """Operational status for virtual servers.
74
+
75
+ Indicates whether a virtual server is actively accepting traffic (active),
76
+ not yet deployed (planned), or intentionally taken offline (disabled).
77
+ """
78
+ key = 'VirtualServer.status'
79
+
80
+ STATUS_ACTIVE = 'active'
81
+ STATUS_PLANNED = 'planned'
82
+ STATUS_DISABLED = 'disabled'
83
+
84
+ CHOICES = [
85
+ (STATUS_ACTIVE, 'Active', 'green'),
86
+ (STATUS_PLANNED, 'Planned', 'cyan'),
87
+ (STATUS_DISABLED, 'Disabled', 'red'),
88
+ ]
89
+
90
+
91
+ class VirtualServerProtocolChoices(ChoiceSet):
92
+ """Network protocol that a virtual server listener accepts.
93
+
94
+ Determines what kind of traffic the virtual server handles. TCP and UDP are
95
+ layer-4 protocols, while HTTP and HTTPS are layer-7 (application-level).
96
+ The choice of protocol affects how the load balancer inspects and routes traffic.
97
+ """
98
+ key = 'VirtualServer.protocol'
99
+
100
+ PROTOCOL_TCP = 'tcp'
101
+ PROTOCOL_UDP = 'udp'
102
+ PROTOCOL_HTTP = 'http'
103
+ PROTOCOL_HTTPS = 'https'
104
+ PROTOCOL_OTHER = 'other'
105
+
106
+ CHOICES = [
107
+ (PROTOCOL_TCP, 'TCP'),
108
+ (PROTOCOL_UDP, 'UDP'),
109
+ (PROTOCOL_HTTP, 'HTTP'),
110
+ (PROTOCOL_HTTPS, 'HTTPS'),
111
+ (PROTOCOL_OTHER, 'Other'),
112
+ ]
113
+
114
+
115
+ class PoolMethodChoices(ChoiceSet):
116
+ """Load balancing algorithm used to distribute traffic among pool members.
117
+
118
+ - Round Robin: requests are sent to each member in turn, cycling through the list.
119
+ - Least Connections: the member with the fewest active connections gets the next
120
+ request, which is useful when request processing times vary.
121
+ - IP Hash: the client's source IP determines which member receives the request,
122
+ providing session persistence (sticky sessions).
123
+ - Weighted: traffic is distributed proportionally based on each member's weight
124
+ value, allowing more powerful servers to handle more traffic.
125
+ """
126
+ key = 'Pool.method'
127
+
128
+ METHOD_ROUND_ROBIN = 'round-robin'
129
+ METHOD_LEAST_CONNECTIONS = 'least-connections'
130
+ METHOD_IP_HASH = 'ip-hash'
131
+ METHOD_WEIGHTED = 'weighted'
132
+ METHOD_OTHER = 'other'
133
+
134
+ CHOICES = [
135
+ (METHOD_ROUND_ROBIN, 'Round Robin'),
136
+ (METHOD_LEAST_CONNECTIONS, 'Least Connections'),
137
+ (METHOD_IP_HASH, 'IP Hash'),
138
+ (METHOD_WEIGHTED, 'Weighted'),
139
+ (METHOD_OTHER, 'Other'),
140
+ ]
141
+
142
+
143
+ class PoolProtocolChoices(ChoiceSet):
144
+ """Network protocol used for communication between the load balancer and pool members.
145
+
146
+ This may differ from the virtual server's protocol. For example, a virtual server
147
+ might accept HTTPS traffic from clients while the pool communicates with backend
148
+ members over plain HTTP (SSL offloading).
149
+ """
150
+ key = 'Pool.protocol'
151
+
152
+ PROTOCOL_TCP = 'tcp'
153
+ PROTOCOL_UDP = 'udp'
154
+ PROTOCOL_HTTP = 'http'
155
+ PROTOCOL_HTTPS = 'https'
156
+ PROTOCOL_OTHER = 'other'
157
+
158
+ CHOICES = [
159
+ (PROTOCOL_TCP, 'TCP'),
160
+ (PROTOCOL_UDP, 'UDP'),
161
+ (PROTOCOL_HTTP, 'HTTP'),
162
+ (PROTOCOL_HTTPS, 'HTTPS'),
163
+ (PROTOCOL_OTHER, 'Other'),
164
+ ]
165
+
166
+
167
+ class PoolMemberStatusChoices(ChoiceSet):
168
+ """Operational status for individual pool members.
169
+
170
+ - Active: the member is healthy and receiving new traffic.
171
+ - Drain: the member stops accepting new connections but finishes processing
172
+ existing ones. This is used for graceful removal during maintenance.
173
+ - Disabled: the member is completely offline and receives no traffic at all.
174
+ """
175
+ key = 'PoolMember.status'
176
+
177
+ STATUS_ACTIVE = 'active'
178
+ STATUS_DRAIN = 'drain'
179
+ STATUS_DISABLED = 'disabled'
180
+
181
+ CHOICES = [
182
+ (STATUS_ACTIVE, 'Active', 'green'),
183
+ (STATUS_DRAIN, 'Drain', 'yellow'),
184
+ (STATUS_DISABLED, 'Disabled', 'red'),
185
+ ]
@@ -0,0 +1,127 @@
1
+ """Filter sets for querying and filtering load balancer objects.
2
+
3
+ Each FilterSet class defines the fields that can be used to filter a model's queryset
4
+ in both the web UI list views and the REST API. They extend NetBox's
5
+ ``NetBoxModelFilterSet``, which provides built-in support for tags, custom fields,
6
+ and the ``q`` search parameter.
7
+
8
+ Filter types used:
9
+ - ``MultipleChoiceFilter``: allows filtering by one or more choice values
10
+ (e.g. ``?status=active&status=planned``).
11
+ - ``ModelMultipleChoiceFilter``: allows filtering by one or more related object IDs
12
+ (e.g. ``?loadbalancer_id=1&loadbalancer_id=2``).
13
+
14
+ The ``search()`` method on each FilterSet handles the ``q`` parameter from the search
15
+ box on list views. It defines which model fields are searched when a user types a
16
+ free-text query.
17
+ """
18
+
19
+ import django_filters
20
+ from netbox.filtersets import NetBoxModelFilterSet
21
+
22
+ from .models import LoadBalancer, VirtualServer, Pool, PoolMember
23
+ from .choices import (
24
+ LoadBalancerPlatformChoices,
25
+ LoadBalancerStatusChoices,
26
+ VirtualServerStatusChoices,
27
+ VirtualServerProtocolChoices,
28
+ PoolMethodChoices,
29
+ PoolProtocolChoices,
30
+ PoolMemberStatusChoices,
31
+ )
32
+
33
+
34
+ class LoadBalancerFilterSet(NetBoxModelFilterSet):
35
+ """Filters load balancers by platform, status, device, site, and tenant.
36
+
37
+ The Meta.fields tuple lists all fields that can be filtered via URL query
38
+ parameters. Fields like ``device_id``, ``site_id``, and ``tenant_id`` are
39
+ automatically handled by NetBoxModelFilterSet because they are standard
40
+ ForeignKey fields on the model.
41
+ """
42
+
43
+ platform = django_filters.MultipleChoiceFilter(choices=LoadBalancerPlatformChoices)
44
+ status = django_filters.MultipleChoiceFilter(choices=LoadBalancerStatusChoices)
45
+
46
+ class Meta:
47
+ model = LoadBalancer
48
+ fields = ('id', 'name', 'platform', 'status', 'device_id', 'site_id', 'tenant_id')
49
+
50
+ def search(self, queryset, name, value):
51
+ """Handle the ``q`` search parameter from the list view search box.
52
+
53
+ Performs a case-insensitive substring match on the load balancer's name field.
54
+ For example, searching for "prod" would match "Production-LB-01".
55
+ """
56
+ return queryset.filter(name__icontains=value)
57
+
58
+
59
+ class PoolFilterSet(NetBoxModelFilterSet):
60
+ """Filters pools by load balancer, method, and protocol.
61
+
62
+ The ``loadbalancer_id`` filter uses ``ModelMultipleChoiceFilter`` which accepts
63
+ one or more load balancer IDs and returns only pools belonging to those load
64
+ balancers.
65
+ """
66
+
67
+ loadbalancer_id = django_filters.ModelMultipleChoiceFilter(
68
+ queryset=LoadBalancer.objects.all(),
69
+ )
70
+ method = django_filters.MultipleChoiceFilter(choices=PoolMethodChoices)
71
+ protocol = django_filters.MultipleChoiceFilter(choices=PoolProtocolChoices)
72
+
73
+ class Meta:
74
+ model = Pool
75
+ fields = ('id', 'name', 'loadbalancer_id', 'method', 'protocol')
76
+
77
+ def search(self, queryset, name, value):
78
+ """Handle the ``q`` search parameter by matching against the pool name."""
79
+ return queryset.filter(name__icontains=value)
80
+
81
+
82
+ class VirtualServerFilterSet(NetBoxModelFilterSet):
83
+ """Filters virtual servers by load balancer, status, protocol, port, pool, and tenant.
84
+
85
+ This is the most feature-rich FilterSet in the plugin, reflecting the number of
86
+ filterable attributes on the VirtualServer model. The ``pool_id`` filter allows
87
+ finding all virtual servers that point to a specific pool.
88
+ """
89
+
90
+ loadbalancer_id = django_filters.ModelMultipleChoiceFilter(
91
+ queryset=LoadBalancer.objects.all(),
92
+ )
93
+ status = django_filters.MultipleChoiceFilter(choices=VirtualServerStatusChoices)
94
+ protocol = django_filters.MultipleChoiceFilter(choices=VirtualServerProtocolChoices)
95
+ pool_id = django_filters.ModelMultipleChoiceFilter(
96
+ queryset=Pool.objects.all(),
97
+ )
98
+
99
+ class Meta:
100
+ model = VirtualServer
101
+ fields = ('id', 'name', 'loadbalancer_id', 'status', 'protocol', 'port', 'pool_id', 'tenant_id')
102
+
103
+ def search(self, queryset, name, value):
104
+ """Handle the ``q`` search parameter by matching against the virtual server name."""
105
+ return queryset.filter(name__icontains=value)
106
+
107
+
108
+ class PoolMemberFilterSet(NetBoxModelFilterSet):
109
+ """Filters pool members by pool, status, device, IP address, port, weight, and priority.
110
+
111
+ This FilterSet exposes the widest range of filterable fields, allowing operators
112
+ to find members by their parent pool, operational status, associated device or
113
+ IP, specific port numbers, or weight/priority values.
114
+ """
115
+
116
+ pool_id = django_filters.ModelMultipleChoiceFilter(
117
+ queryset=Pool.objects.all(),
118
+ )
119
+ status = django_filters.MultipleChoiceFilter(choices=PoolMemberStatusChoices)
120
+
121
+ class Meta:
122
+ model = PoolMember
123
+ fields = ('id', 'name', 'pool_id', 'ip_address_id', 'device_id', 'port', 'weight', 'priority', 'status')
124
+
125
+ def search(self, queryset, name, value):
126
+ """Handle the ``q`` search parameter by matching against the pool member name."""
127
+ return queryset.filter(name__icontains=value)