diff --git a/.devcontainer/Dockerfile-plugin_dev b/.devcontainer/Dockerfile-plugin_dev
index 55fd2cb..ea10664 100644
--- a/.devcontainer/Dockerfile-plugin_dev
+++ b/.devcontainer/Dockerfile-plugin_dev
@@ -1,8 +1,8 @@
-ARG NETBOX_VARIANT=v3.3
+ARG NETBOX_VARIANT=v3.4
FROM netboxcommunity/netbox:${NETBOX_VARIANT}
-ARG NETBOX_INITIALIZERS_VARIANT=3.3.*
+ARG NETBOX_INITIALIZERS_VARIANT=3.4.*
ARG DEBIAN_FRONTEND=noninteractive
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index a213cbe..b008240 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -10,37 +10,53 @@ services:
#- netbox-worker
env_file: env/netbox.env
user: 'unit:root'
+ healthcheck:
+ start_period: 60s
+ timeout: 3s
+ interval: 15s
+ test: "curl -f http://localhost:8080/api/ || exit 1"
volumes:
- - ./initializers:/opt/netbox/initializers:z,ro
- ./configuration:/etc/netbox/config:z,ro
+ #- ./reports:/etc/netbox/reports:z,ro
+ #- ./scripts:/etc/netbox/scripts:z,ro
#- netbox-media-files:/opt/netbox/netbox/media:z
#netbox-worker:
# <<: *netbox
# depends_on:
- # - redis
- # - postgres
+ # netbox:
+ # condition: service_healthy
# command:
# - /opt/netbox/venv/bin/python
# - /opt/netbox/netbox/manage.py
# - rqworker
+ # healthcheck:
+ # start_period: 20s
+ # timeout: 3s
+ # interval: 15s
+ # test: "ps -aux | grep -v grep | grep -q rqworker || exit 1"
#netbox-housekeeping:
# <<: *netbox
# depends_on:
- # - redis
- # - postgres
+ # netbox:
+ # condition: service_healthy
# command:
# - /opt/netbox/housekeeping.sh
+ # healthcheck:
+ # start_period: 20s
+ # timeout: 3s
+ # interval: 15s
+ # test: "ps -aux | grep -v grep | grep -q housekeeping || exit 1"
# postgres
postgres:
- image: postgres:14-alpine
+ image: postgres:15-alpine
env_file: env/postgres.env
volumes:
- netbox-postgres-data:/var/lib/postgresql/data
# redis
redis:
- image: redis:6-alpine
+ image: redis:7-alpine
command:
- sh
- -c # this is to evaluate the $REDIS_PASSWORD from the env
@@ -49,12 +65,14 @@ services:
#volumes:
# - netbox-redis-data:/data
#redis-cache:
- # image: redis:6-alpine
+ # image: redis:7-alpine
# command:
- # - sh
- # - -c # this is to evaluate the $REDIS_PASSWORD from the env
- # - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
+ # - sh
+ # - -c # this is to evaluate the $REDIS_PASSWORD from the env
+ # - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
# env_file: env/redis-cache.env
+ # volumes:
+ # - netbox-redis-cache-data:/data
volumes:
#netbox-media-files:
@@ -63,3 +81,5 @@ volumes:
driver: local
#netbox-redis-data:
# driver: local
+ #netbox-redis-cache-data:
+ # driver: local
diff --git a/.devcontainer/initializers/aggregates.yml b/.devcontainer/initializers/aggregates.yml
deleted file mode 100644
index d41539d..0000000
--- a/.devcontainer/initializers/aggregates.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- prefix: 10.0.0.0/16
- rir: RFC1918
- tenant: tenant1
-- prefix: fd00:ccdd::/32
- rir: RFC4193 ULA
-- prefix: 2001:db8::/32
- rir: RFC3849
diff --git a/.devcontainer/initializers/asns.yml b/.devcontainer/initializers/asns.yml
deleted file mode 100644
index b65197c..0000000
--- a/.devcontainer/initializers/asns.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- asn: 1
- rir: RFC1918
- tenant: tenant1
-- asn: 2
- rir: RFC4193 ULA
-- asn: 3
- rir: RFC3849
diff --git a/.devcontainer/initializers/cables.yml b/.devcontainer/initializers/cables.yml
deleted file mode 100644
index 0e9b83d..0000000
--- a/.devcontainer/initializers/cables.yml
+++ /dev/null
@@ -1,72 +0,0 @@
----
-# Required parameters for termination X ('a' or 'b'):
-#
-# ```
-# termination_x_name -> name of interface
-# termination_x_device -> name of the device interface belongs to
-# termination_x_class -> required if different from 'Interface' which is the default
-# ```
-#
-# Supported termination classes: Interface, ConsolePort, ConsoleServerPort, FrontPort, RearPort, PowerPort, PowerOutlet
-#
-#
-# If a termination is a circuit then the required parameter is termination_x_circuit.
-# Required parameters for a circuit termination:
-#
-# ```
-# termination_x_circuit:
-# term_side -> termination side of a circuit. Must be A or B
-# cid -> circuit ID value
-# site OR provider_network -> name of Site or ProviderNetwork respectively. If both provided, Site takes precedence
-# ```
-#
-# If a termination is a power feed then the required parameter is termination_x_feed.
-#
-# ```
-# termination_x_feed:
-# name -> name of the PowerFeed object
-# power_panel:
-# name -> name of the PowerPanel the PowerFeed is attached to
-# site -> name of the Site in which the PowerPanel is present
-# ```
-#
-# Any other Cable parameters supported by Netbox are supported as the top level keys, e.g. 'type', 'status', etc.
-#
-# - termination_a_name: console
-# termination_a_device: spine
-# termination_a_class: ConsolePort
-# termination_b_name: tty9
-# termination_b_device: console-server
-# termination_b_class: ConsoleServerPort
-# type: cat6
-#
-- termination_a_name: to-server02
- termination_a_device: server01
- termination_b_name: to-server01
- termination_b_device: server02
- status: planned
- type: mmf
-
-- termination_a_name: eth0
- termination_a_device: server02
- termination_b_circuit:
- term_side: A
- cid: Circuit_ID-1
- site: AMS 1
- type: cat6
-
-- termination_a_name: psu0
- termination_a_device: server04
- termination_a_class: PowerPort
- termination_b_feed:
- name: power feed 1
- power_panel:
- name: power panel AMS 1
- site: AMS 1
-
-- termination_a_name: outlet1
- termination_a_device: server04
- termination_a_class: PowerOutlet
- termination_b_name: psu1
- termination_b_device: server04
- termination_b_class: PowerPort
diff --git a/.devcontainer/initializers/circuit_types.yml b/.devcontainer/initializers/circuit_types.yml
deleted file mode 100644
index 5bd53e4..0000000
--- a/.devcontainer/initializers/circuit_types.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: VPLS
- slug: vpls
-- name: MPLS
- slug: mpls
-- name: Internet
- slug: internet
diff --git a/.devcontainer/initializers/circuits.yml b/.devcontainer/initializers/circuits.yml
deleted file mode 100644
index b69efb3..0000000
--- a/.devcontainer/initializers/circuits.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- cid: Circuit_ID-1
- provider: Provider1
- type: Internet
- tenant: tenant1
-- cid: Circuit_ID-2
- provider: Provider2
- type: MPLS
diff --git a/.devcontainer/initializers/cluster_groups.yml b/.devcontainer/initializers/cluster_groups.yml
deleted file mode 100644
index 503fd7e..0000000
--- a/.devcontainer/initializers/cluster_groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Group 1
- slug: group-1
-- name: Group 2
- slug: group-2
diff --git a/.devcontainer/initializers/cluster_types.yml b/.devcontainer/initializers/cluster_types.yml
deleted file mode 100644
index c2342ac..0000000
--- a/.devcontainer/initializers/cluster_types.yml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-- name: Hyper-V
- slug: hyper-v
diff --git a/.devcontainer/initializers/clusters.yml b/.devcontainer/initializers/clusters.yml
deleted file mode 100644
index c47ca5f..0000000
--- a/.devcontainer/initializers/clusters.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- name: cluster1
- type: Hyper-V
- group: Group 1
- tenant: tenant1
-- name: cluster2
- type: Hyper-V
- site: SING 1
diff --git a/.devcontainer/initializers/contact_groups.yml b/.devcontainer/initializers/contact_groups.yml
deleted file mode 100644
index db171af..0000000
--- a/.devcontainer/initializers/contact_groups.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- name: Network-Team
- slug: network-team
- description: This is a new contact group for the Network-Team
-- name: New Contact Group
- slug: new-contact-group
- description: This is a new contact group sub under of Network-Team
- parent: Network-Team
diff --git a/.devcontainer/initializers/contact_roles.yml b/.devcontainer/initializers/contact_roles.yml
deleted file mode 100644
index 858a225..0000000
--- a/.devcontainer/initializers/contact_roles.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-- name: New Contact Role
- slug: new-contact-role
- description: This is a new contact role description
diff --git a/.devcontainer/initializers/contacts.yml b/.devcontainer/initializers/contacts.yml
deleted file mode 100644
index e48a81e..0000000
--- a/.devcontainer/initializers/contacts.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-- name: Lee Widget
- title: CEO of Widget Corp
- phone: 221-555-1212
- email: widgetCEO@widgetcorp.com
- address: 1200 Nowhere Blvd, Scranton NJ, 555111
- comments: This is a very important contact
-- name: Ali Gator
- group: Network-Team
- title: Consultant for Widget Corp
- phone: 221-555-1213
- email: Consultant@widgetcorp.com
- address: 1200 Nowhere Blvd, Scranton NJ, 555111
- comments: This is a very important contact
-- name: Karlchen Maier
- group: New Contact Group
- title: COO of Widget Corp
- phone: 221-555-1214
- email: Karlchen@widgetcorp.com
- address: 1200 Nowhere Blvd, Scranton NJ, 555111
- comments: This is a very important contact
diff --git a/.devcontainer/initializers/custom_fields.yml b/.devcontainer/initializers/custom_fields.yml
deleted file mode 100644
index 0687a2b..0000000
--- a/.devcontainer/initializers/custom_fields.yml
+++ /dev/null
@@ -1,118 +0,0 @@
----
-## Possible Choices:
-## type:
-## - text
-## - integer
-## - boolean
-## - date
-## - url
-## - select
-## - multiselect
-## - object
-## - multiobject
-## filter_logic:
-## - disabled
-## - loose
-## - exact
-##
-## Examples:
-
-text_field:
- type: text
- label: Custom Text
- description: Enter text in a text field.
- required: false
- weight: 0
- on_objects:
- - dcim.models.Device
- - dcim.models.Rack
- - dcim.models.Site
- - dcim.models.DeviceType
- - ipam.models.IPAddress
- - ipam.models.Prefix
- - tenancy.models.Tenant
- - virtualization.models.VirtualMachine
-integer_field:
- type: integer
- label: Custom Number
- description: Enter numbers into an integer field.
- required: true
- filter_logic: loose
- validation_minimum: 0
- validation_maximum: 255
- weight: 10
- on_objects:
- - tenancy.models.Tenant
-select_field:
- type: select
- label: Choose between items
- required: false
- filter_logic: exact
- weight: 30
- default: First Item
- on_objects:
- - dcim.models.Device
- choices:
- - First Item
- - Second Item
- - Third Item
- - Fifth Item
- - Fourth Item
-select_field_legacy_format:
- type: select
- label: Choose between items
- required: false
- filter_logic: loose
- weight: 30
- on_objects:
- - dcim.models.Device
- choices:
- - value: A # this is the deprecated format.
- - value: B # we only use it for the tests.
- - value: C # please see above for the new format.
- - value: "D like deprecated"
- weight: 999
- - value: E
-boolean_field:
- type: boolean
- label: Yes Or No?
- required: true
- filter_logic: loose
- default: "false" # important: put "false" in quotes!
- weight: 90
- on_objects:
- - dcim.models.Device
-url_field:
- type: url
- label: Hyperlink
- description: Link to something nice.
- required: true
- filter_logic: disabled
- validation_regex: ^https://
- on_objects:
- - tenancy.models.Tenant
-date_field:
- type: date
- label: Important Date
- required: false
- filter_logic: disabled
- on_objects:
- - dcim.models.Device
-multiobject_field:
- type: multiobject
- label: Related Objects
- description: IP addresses that belong to this location
- required: true
- filter_logic: loose
- on_objects:
- - dcim.models.Location
- object_type: ipam.models.IPAddress
-object_field:
- type: object
- label: ASN
- description: This device has an ASN now
- required: false
- filter_logic: loose
- on_objects:
- - dcim.models.Device
- object_type: ipam.models.ASN
diff --git a/.devcontainer/initializers/custom_links.yml b/.devcontainer/initializers/custom_links.yml
deleted file mode 100644
index c7348bc..0000000
--- a/.devcontainer/initializers/custom_links.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-## Possible Choices:
-## new_window:
-## - True
-## - False
-## content_type:
-## - device
-## - site
-## - any-other-content-type
-##
-## Examples:
-
-- name: link_to_repo
- link_text: 'Link to Netbox Docker'
- link_url: 'https://github.com/netbox-community/netbox-docker'
- new_window: False
- content_type: device
-- name: link_to_localhost
- link_text: 'Link to localhost'
- link_url: 'http://localhost'
- new_window: True
- content_type: device
diff --git a/.devcontainer/initializers/device_roles.yml b/.devcontainer/initializers/device_roles.yml
deleted file mode 100644
index aa829c9..0000000
--- a/.devcontainer/initializers/device_roles.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-- name: switch
- slug: switch
- color: Grey
-- name: router
- slug: router
- color: Cyan
-- name: load-balancer
- slug: load-balancer
- color: Red
-- name: server
- slug: server
- color: Blue
-- name: patchpanel
- slug: patchpanel
- color: Black
diff --git a/.devcontainer/initializers/device_types.yml b/.devcontainer/initializers/device_types.yml
deleted file mode 100644
index 08c472f..0000000
--- a/.devcontainer/initializers/device_types.yml
+++ /dev/null
@@ -1,58 +0,0 @@
----
-- model: Model 1
- manufacturer: Manufacturer 1
- slug: model-1
- u_height: 2
- custom_field_data:
- text_field: Description
-- model: Model 2
- manufacturer: Manufacturer 1
- slug: model-2
- custom_field_data:
- text_field: Description
-- model: Model 3
- manufacturer: Manufacturer 1
- slug: model-3
- is_full_depth: false
- u_height: 0
- custom_field_data:
- text_field: Description
-- model: Other
- manufacturer: No Name
- slug: other
- custom_field_data:
- text_field: Description
- interfaces:
- - name: eth0
- type: 1000base-t
- mgmt_only: True
- - name: eth1
- type: 1000base-t
- console_server_ports:
- - name_template: ttyS[1-48]
- type: rj-45
- power_ports:
- - name_template: psu[0,1]
- type: iec-60320-c14
- maximum_draw: 35
- allocated_draw: 35
- front_ports:
- - name_template: front[1,2]
- type: 8p8c
- rear_port_template: rear[0,1]
- rear_port_position_template: "[1,2]"
- rear_ports:
- - name_template: rear[0,1]
- type: 8p8c
- positions_template: "[3,2]"
- device_bays:
- - name: bay0 # both non-template and template field specified; non-template field takes precedence
- name_template: bay[0-9]
- label: test0
- label_template: test[0-5,9,6-8]
- description: Test description
- power_outlets:
- - name_template: outlet[0,1]
- type: iec-60320-c5
- power_port: psu0
- feed_leg: B
diff --git a/.devcontainer/initializers/devices.yml b/.devcontainer/initializers/devices.yml
deleted file mode 100644
index 06a0fbf..0000000
--- a/.devcontainer/initializers/devices.yml
+++ /dev/null
@@ -1,54 +0,0 @@
----
-## Possible Choices:
-## face:
-## - front
-## - rear
-## status:
-## - offline
-## - active
-## - planned
-## - staged
-## - failed
-## - inventory
-## - decommissioning
-##
-## Examples:
-
-- name: server01
- device_role: server
- device_type: Other
- site: AMS 1
- rack: rack-01
- face: front
- position: 1
- custom_field_data:
- text_field: Description
-- name: server02
- device_role: server
- device_type: Other
- site: AMS 2
- rack: rack-02
- face: front
- position: 2
- primary_ip4: 10.1.1.2/24
- primary_ip6: 2001:db8:a000:1::2/64
- custom_field_data:
- text_field: Description
-- name: server03
- device_role: server
- device_type: Other
- site: SING 1
- rack: rack-03
- face: front
- position: 3
- custom_field_data:
- text_field: Description
-- name: server04
- device_role: server
- device_type: Other
- site: SING 1
- location: cage 101
- face: front
- position: 3
- custom_field_data:
- text_field: Description
diff --git a/.devcontainer/initializers/groups.yml b/.devcontainer/initializers/groups.yml
deleted file mode 100644
index c49242f..0000000
--- a/.devcontainer/initializers/groups.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-applications:
- users:
- - technical_user
-readers:
- users:
- - reader
-writers:
- users:
- - writer
diff --git a/.devcontainer/initializers/interfaces.yml b/.devcontainer/initializers/interfaces.yml
deleted file mode 100644
index f3c8309..0000000
--- a/.devcontainer/initializers/interfaces.yml
+++ /dev/null
@@ -1,36 +0,0 @@
----
-## Possible Choices:
-## type:
-## - virtual
-## - lag
-## - 1000base-t
-## - ... and many more. See for yourself:
-## https://github.com/netbox-community/netbox/blob/295d4f0394b431351c0cb2c3ecc791df68c6c2fb/netbox/dcim/choices.py#L510
-##
-## Examples:
-
-- device: server01
- name: ath0
- type: 1000base-t
- lag: ae0
- bridge: br0
-- device: server01
- name: ath1
- type: 1000base-t
- parent: ath0
-- device: server01
- enabled: true
- type: 1000base-x-sfp
- name: to-server02
-- device: server02
- enabled: true
- type: 1000base-x-sfp
- name: to-server01
-- device: server02
- enabled: true
- type: 1000base-t
- name: eth0
-- device: server02
- enabled: true
- type: virtual
- name: loopback
diff --git a/.devcontainer/initializers/ip_addresses.yml b/.devcontainer/initializers/ip_addresses.yml
deleted file mode 100644
index a81cb3a..0000000
--- a/.devcontainer/initializers/ip_addresses.yml
+++ /dev/null
@@ -1,45 +0,0 @@
----
-## Possible Choices:
-## status:
-## - active
-## - reserved
-## - deprecated
-## - dhcp
-## role:
-## - loopback
-## - secondary
-## - anycast
-## - vip
-## - vrrp
-## - hsrp
-## - glbp
-## - carp
-##
-## Examples:
-
-- address: 10.1.1.1/24
- device: server01
- interface: to-server02
- status: active
- vrf: vrf1
-- address: 2001:db8:a000:1::1/64
- device: server01
- interface: to-server02
- status: active
- vrf: vrf1
-- address: 10.1.1.2/24
- device: server02
- interface: to-server01
- status: active
-- address: 2001:db8:a000:1::2/64
- device: server02
- interface: to-server01
- status: active
-- address: 10.1.1.10/24
- description: reserved IP
- status: reserved
- tenant: tenant1
-- address: 2001:db8:a000:1::10/64
- description: reserved IP
- status: reserved
- tenant: tenant1
diff --git a/.devcontainer/initializers/locations.yml b/.devcontainer/initializers/locations.yml
deleted file mode 100644
index 3595f7d..0000000
--- a/.devcontainer/initializers/locations.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-- name: cage 101
- slug: cage-101
- site: SING 1
diff --git a/.devcontainer/initializers/manufacturers.yml b/.devcontainer/initializers/manufacturers.yml
deleted file mode 100644
index 8463d76..0000000
--- a/.devcontainer/initializers/manufacturers.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Manufacturer 1
- slug: manufacturer-1
-- name: Manufacturer 2
- slug: manufacturer-2
-- name: No Name
- slug: no-name
diff --git a/.devcontainer/initializers/object_permissions.yml b/.devcontainer/initializers/object_permissions.yml
deleted file mode 100644
index 4914bdc..0000000
--- a/.devcontainer/initializers/object_permissions.yml
+++ /dev/null
@@ -1,61 +0,0 @@
----
-all.ro:
- actions:
- - view
- description: 'Read Only for All Objects'
- enabled: true
- groups:
- - applications
- - readers
- object_types: all
- users:
- - jdoe
-all.rw:
- actions:
- - add
- - change
- - delete
- - view
- description: 'Read/Write for All Objects'
- enabled: true
- groups:
- - writers
- object_types: all
-network_team.rw:
- actions:
- - add
- - change
- - delete
- - view
- description: "Network Team Permissions"
- enabled: true
- object_types:
- circuits:
- - circuit
- - circuittermination
- - circuittype
- - provider
- dcim: all
- ipam:
- - aggregate
- - ipaddress
- - prefix
- - rir
- - role
- - routetarget
- - service
- - vlan
- - vlangroup
- - vrf
-vips.change:
- actions:
- - change
- description: "Update VIP object permission"
- enabled: true
- object_types:
- ipam:
- - ipaddress
- groups:
- - devops
- constraints:
- role: vip
diff --git a/.devcontainer/initializers/platforms.yml b/.devcontainer/initializers/platforms.yml
deleted file mode 100644
index 510ab04..0000000
--- a/.devcontainer/initializers/platforms.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-- name: Platform 1
- slug: platform-1
- manufacturer: Manufacturer 1
- napalm_driver: driver1
- napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
-- name: Platform 2
- slug: platform-2
- manufacturer: Manufacturer 2
- napalm_driver: driver2
- napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
-- name: Platform 3
- slug: platform-3
- manufacturer: No Name
- napalm_driver: driver3
- napalm_args: "{'arg1': 'value1', 'arg2': 'value2'}"
diff --git a/.devcontainer/initializers/power_feeds.yml b/.devcontainer/initializers/power_feeds.yml
deleted file mode 100644
index a32e3dd..0000000
--- a/.devcontainer/initializers/power_feeds.yml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-- name: power feed 1
- power_panel: power panel AMS 1
- voltage: 208
- amperage: 50
- max_utilization: 80
- phase: Single phase
- rack: rack-01
-- name: power feed 2
- power_panel: power panel SING 1
- voltage: 208
- amperage: 50
- max_utilization: 80
- phase: Three-phase
- rack: rack-03
diff --git a/.devcontainer/initializers/power_panels.yml b/.devcontainer/initializers/power_panels.yml
deleted file mode 100644
index 757b4b3..0000000
--- a/.devcontainer/initializers/power_panels.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-- name: power panel AMS 1
- site: AMS 1
-- name: power panel SING 1
- site: SING 1
- location: cage 101
diff --git a/.devcontainer/initializers/prefix_vlan_roles.yml b/.devcontainer/initializers/prefix_vlan_roles.yml
deleted file mode 100644
index 4f269c7..0000000
--- a/.devcontainer/initializers/prefix_vlan_roles.yml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-- name: Main Management
- slug: main-management
diff --git a/.devcontainer/initializers/prefixes.yml b/.devcontainer/initializers/prefixes.yml
deleted file mode 100644
index 78ae23c..0000000
--- a/.devcontainer/initializers/prefixes.yml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-## Possible Choices:
-## status:
-## - container
-## - active
-## - reserved
-## - deprecated
-##
-## Examples:
-
-- description: prefix1
- prefix: 10.1.1.0/24
- site: AMS 1
- status: active
- tenant: tenant1
- vlan: vlan1
-- description: prefix2
- prefix: 10.1.2.0/24
- site: AMS 2
- status: active
- tenant: tenant2
- vlan: vlan2
- is_pool: true
- vrf: vrf2
-- description: ipv6 prefix1
- prefix: 2001:db8:a000:1::/64
- site: AMS 2
- status: active
- tenant: tenant2
- vlan: vlan2
diff --git a/.devcontainer/initializers/providers.yml b/.devcontainer/initializers/providers.yml
deleted file mode 100644
index 6465b82..0000000
--- a/.devcontainer/initializers/providers.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Provider1
- slug: provider1
- asn: 121
-- name: Provider2
- slug: provider2
- asn: 122
diff --git a/.devcontainer/initializers/rack_roles.yml b/.devcontainer/initializers/rack_roles.yml
deleted file mode 100644
index 54e95ed..0000000
--- a/.devcontainer/initializers/rack_roles.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- name: Role 1
- slug: role-1
- color: Pink
-- name: Role 2
- slug: role-2
- color: Cyan
-- name: Role 3
- slug: role-3
- color: Grey
-- name: Role 4
- slug: role-4
- color: Teal
diff --git a/.devcontainer/initializers/racks.yml b/.devcontainer/initializers/racks.yml
deleted file mode 100644
index 05d6b22..0000000
--- a/.devcontainer/initializers/racks.yml
+++ /dev/null
@@ -1,42 +0,0 @@
----
-## Possible Choices:
-## width:
-## - 19
-## - 23
-## types:
-## - 2-post-frame
-## - 4-post-frame
-## - 4-post-cabinet
-## - wall-frame
-## - wall-cabinet
-## outer_unit:
-## - mm
-## - in
-##
-## Examples:
-
-- site: AMS 1
- name: rack-01
- role: Role 1
- type: 4-post-cabinet
- width: 19
- u_height: 47
- custom_field_data:
- text_field: Description
-- site: AMS 2
- name: rack-02
- role: Role 2
- type: 4-post-cabinet
- width: 19
- u_height: 47
- custom_field_data:
- text_field: Description
-- site: SING 1
- name: rack-03
- location: cage 101
- role: Role 3
- type: 4-post-cabinet
- width: 19
- u_height: 47
- custom_field_data:
- text_field: Description
diff --git a/.devcontainer/initializers/regions.yml b/.devcontainer/initializers/regions.yml
deleted file mode 100644
index a818c6f..0000000
--- a/.devcontainer/initializers/regions.yml
+++ /dev/null
@@ -1,11 +0,0 @@
----
-- name: Singapore
- slug: singapore
-- name: Amsterdam
- slug: amsterdam
-- name: Downtown
- slug: downtown
- parent: Amsterdam
-- name: Suburbs
- slug: suburbs
- parent: Amsterdam
diff --git a/.devcontainer/initializers/rirs.yml b/.devcontainer/initializers/rirs.yml
deleted file mode 100644
index 34777f9..0000000
--- a/.devcontainer/initializers/rirs.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-- is_private: true
- name: RFC1918
- slug: rfc1918
-- is_private: true
- name: RFC4193 ULA
- slug: rfc4193-ula
-- is_private: true
- name: RFC3849
- slug: rfc3849
diff --git a/.devcontainer/initializers/route_targets.yml b/.devcontainer/initializers/route_targets.yml
deleted file mode 100644
index 2fa188e..0000000
--- a/.devcontainer/initializers/route_targets.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-- name: 65000:1001
- tenant: tenant1
-- name: 65000:1002
diff --git a/.devcontainer/initializers/services.yml b/.devcontainer/initializers/services.yml
deleted file mode 100644
index 88cd473..0000000
--- a/.devcontainer/initializers/services.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-- name: DNS
- protocol: TCP
- ports:
- - 53
- virtual_machine: virtual machine 1
-- name: DNS
- protocol: UDP
- ports:
- - 53
- virtual_machine: virtual machine 1
-- name: MISC
- protocol: UDP
- ports:
- - 4000
- device: server01
diff --git a/.devcontainer/initializers/sites.yml b/.devcontainer/initializers/sites.yml
deleted file mode 100644
index cbc20b8..0000000
--- a/.devcontainer/initializers/sites.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-- name: AMS 1
- slug: ams1
- region: Downtown
- status: active
- facility: Amsterdam 1
- custom_field_data:
- text_field: Description for AMS1
-- name: AMS 2
- slug: ams2
- region: Downtown
- status: active
- facility: Amsterdam 2
- custom_field_data:
- text_field: Description for AMS2
-- name: AMS 3
- slug: ams3
- region: Suburbs
- status: active
- facility: Amsterdam 3
- tenant: tenant1
- custom_field_data:
- text_field: Description for AMS3
-- name: SING 1
- slug: sing1
- region: Singapore
- status: active
- facility: Singapore 1
- tenant: tenant2
- custom_field_data:
- text_field: Description for SING1
diff --git a/.devcontainer/initializers/tags.yml b/.devcontainer/initializers/tags.yml
deleted file mode 100644
index 2697237..0000000
--- a/.devcontainer/initializers/tags.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- name: Tag 1
- slug: tag-1
- color: Pink
-- name: Tag 2
- slug: tag-2
- color: Cyan
-- name: Tag 3
- slug: tag-3
- color: Grey
-- name: Tag 4
- slug: tag-4
- color: Teal
diff --git a/.devcontainer/initializers/tenant_groups.yml b/.devcontainer/initializers/tenant_groups.yml
deleted file mode 100644
index 79fa5cb..0000000
--- a/.devcontainer/initializers/tenant_groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Tenant Group 1
- slug: tenant-group-1
-- name: Tenant Group 2
- slug: tenant-group-2
diff --git a/.devcontainer/initializers/tenants.yml b/.devcontainer/initializers/tenants.yml
deleted file mode 100644
index d3a983e..0000000
--- a/.devcontainer/initializers/tenants.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-- name: tenant1
- slug: tenant1
-- name: tenant2
- slug: tenant2
- group: Tenant Group 2
diff --git a/.devcontainer/initializers/users.yml b/.devcontainer/initializers/users.yml
deleted file mode 100644
index d6ebd18..0000000
--- a/.devcontainer/initializers/users.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-technical_user:
- api_token: "" # a token is generated automatically unless the value is explicity set to empty
-reader:
- api_token: "" # a token is generated automatically unless the value is explicity set to empty
-writer:
- api_token: "" # a token is generated automatically unless the value is explicity set to empty
-jdoe:
- first_name: John
- last_name: Doe
- is_active: True
- is_superuser: False
- is_staff: False
diff --git a/.devcontainer/initializers/virtual_machines.yml b/.devcontainer/initializers/virtual_machines.yml
deleted file mode 100644
index bc2c4ee..0000000
--- a/.devcontainer/initializers/virtual_machines.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-## Possible Choices:
-## status:
-## - active
-## - offline
-## - staged
-##
-## Examples:
-
-- cluster: cluster1
- comments: VM1
- disk: 200
- memory: 4096
- name: virtual machine 1
- platform: Platform 2
- status: active
- tenant: tenant1
- vcpus: 8
-- cluster: cluster1
- comments: VM2
- disk: 100
- memory: 2048
- name: virtual machine 2
- platform: Platform 2
- primary_ip4: 10.1.1.10/24
- primary_ip6: 2001:db8:a000:1::10/64
- status: active
- tenant: tenant1
- vcpus: 8
diff --git a/.devcontainer/initializers/virtualization_interfaces.yml b/.devcontainer/initializers/virtualization_interfaces.yml
deleted file mode 100644
index 396a8c8..0000000
--- a/.devcontainer/initializers/virtualization_interfaces.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- description: Network Interface 1
- enabled: true
- mac_address: 00:77:77:77:77:77
- mtu: 1500
- name: Network Interface 1
- virtual_machine: virtual machine 1
-- description: Network Interface 2
- enabled: true
- mac_address: 00:55:55:55:55:55
- mtu: 1500
- name: Network Interface 2
- virtual_machine: virtual machine 1
diff --git a/.devcontainer/initializers/vlan_groups.yml b/.devcontainer/initializers/vlan_groups.yml
deleted file mode 100644
index 6aae0d8..0000000
--- a/.devcontainer/initializers/vlan_groups.yml
+++ /dev/null
@@ -1,25 +0,0 @@
----
-- name: VLAN group 1
- scope_type: dcim.region
- scope: Amsterdam
- slug: vlan-group-1
-- name: VLAN group 2
- scope_type: dcim.site
- scope: AMS 1
- slug: vlan-group-2
-- name: VLAN group 3
- scope_type: dcim.location
- scope: cage 101
- slug: vlan-group-3
-- name: VLAN group 4
- scope_type: dcim.rack
- scope: rack-01
- slug: vlan-group-4
-- name: VLAN group 5
- scope_type: virtualization.cluster
- scope: cluster1
- slug: vlan-group-5
-- name: VLAN group 6
- scope_type: virtualization.clustergroup
- scope: Group 1
- slug: vlan-group-6
diff --git a/.devcontainer/initializers/vlans.yml b/.devcontainer/initializers/vlans.yml
deleted file mode 100644
index d2c8543..0000000
--- a/.devcontainer/initializers/vlans.yml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-## Possible Choices:
-## status:
-## - active
-## - reserved
-## - deprecated
-##
-## Examples:
-
-- name: vlan1
- site: AMS 1
- status: active
- vid: 5
- role: Main Management
- description: VLAN 5 for MGMT
-- group: VLAN group 2
- name: vlan2
- site: AMS 1
- status: active
- vid: 1300
diff --git a/.devcontainer/initializers/vrfs.yml b/.devcontainer/initializers/vrfs.yml
deleted file mode 100644
index accb990..0000000
--- a/.devcontainer/initializers/vrfs.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-- enforce_unique: true
- name: vrf1
- tenant: tenant1
- description: main VRF
-- enforce_unique: true
- name: vrf2
- rd: "6500:6500"
- tenant: tenant2
diff --git a/.devcontainer/initializers/webhooks.yml b/.devcontainer/initializers/webhooks.yml
deleted file mode 100644
index f25260c..0000000
--- a/.devcontainer/initializers/webhooks.yml
+++ /dev/null
@@ -1,28 +0,0 @@
----
-## Possible Choices:
-## object_types:
-## - device
-## - site
-## - any-other-content-type
-## types:
-## - type_create
-## - type_update
-## - type_delete
-## Examples:
-
-- name: device_creation
- payload_url: 'http://localhost:8080'
- object_types:
- - device
- - cable
- type_create: True
-- name: device_update
- payload_url: 'http://localhost:8080'
- object_types:
- - device
- type_update: True
-- name: device_delete
- payload_url: 'http://localhost:8080'
- object_types:
- - device
- type_delete: True
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index a88d335..ac362e9 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -23,14 +23,14 @@ body:
attributes:
label: NetBox access-list plugin version
description: What version of the NetBox access-list plugin are you currently running?
- placeholder: v0.1
+ placeholder: v1.2.0
validations:
required: true
- type: input
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
- placeholder: v3.2.7
+ placeholder: v3.4.3
validations:
required: true
- type: textarea
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index a388992..ca31eaf 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -15,7 +15,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
- placeholder: v3.2.5
+ placeholder: v3.4.3
validations:
required: true
- type: dropdown
diff --git a/Makefile b/Makefile
index 44e8234..aa6c2f9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
PLUGIN_NAME=netbox_acls
REPO_PATH=/opt/netbox/netbox/netbox-acls
VENV_PY_PATH=/opt/netbox/venv/bin/python3
-NETBOX_MANAGE_PATH=/opt/netbox/netbox/manage.py
+NETBOX_MANAGE_PATH=/opt/netbox/netbox
+NETBOX_INITIALIZER_PATH=${NETBOX_MANAGE_PATH}/netbox_initializers/
VERFILE=./version.py
.PHONY: help ## Display help message
@@ -26,7 +27,7 @@ help:
.PHONY: nbshell ## Run nbshell
nbshell:
- ${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} nbshell
+ ${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py nbshell
from netbox_acls.models import *
.PHONY: setup ## Copy plugin settings. Setup NetBox plugin.
@@ -36,27 +37,31 @@ setup:
.PHONY: example_initializers ## Run initializers
example_initializers:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} copy_initializers_examples --path /opt/netbox/netbox/netbox-acls/.devcontainer/initializers
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py copy_initializers_examples --path /opt/netbox/netbox/netbox-acls/.devcontainer/initializers
.PHONY: load_initializers ## Run initializers
load_initializers:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} load_initializer_data --path /opt/netbox/netbox/netbox-acls/.devcontainer/initializers
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py load_initializer_data --path /opt/netbox/netbox/netbox-acls/.devcontainer/initializers
.PHONY: makemigrations ## Run makemigrations
makemigrations:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} makemigrations --name ${PLUGIN_NAME}
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py makemigrations --name ${PLUGIN_NAME}
.PHONY: migrate ## Run migrate
migrate:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} migrate
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py migrate
.PHONY: collectstatic
collectstatic:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} collectstatic --no-input
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py collectstatic --no-input
.PHONY: initializers
initializers:
- -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH} load_initializer_data --path /opt/netbox/netbox/netbox-acls/.devcontainer/initializers
+ -rm -rf ${NETBOX_INITIALIZER_PATH}
+ -mkdir ${NETBOX_INITIALIZER_PATH}
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py copy_initializers_examples --path ${NETBOX_INITIALIZER_PATH}
+ -for file in ${NETBOX_INITIALIZER_PATH}/*.yml; do sed -i "s/^# //g" "$$file"; done
+ -${VENV_PY_PATH} ${NETBOX_MANAGE_PATH}/manage.py load_initializer_data --path ${NETBOX_INITIALIZER_PATH}
.PHONY: start ## Start NetBox
start:
diff --git a/README.md b/README.md
index 5f864ad..ed83796 100644
--- a/README.md
+++ b/README.md
@@ -37,10 +37,10 @@ See the [CONTRIBUTING](CONTRIBUTING.md) for more information.
Each Plugin Version listed below has been tested with its corresponding NetBox Version.
| NetBox Version | Plugin Version |
-|----------------|----------------|
-| 3.2 | 1.0.1 |
-| 3.3 | 1.1.1 |
-| 3.4 | 1.2.0(coming) |
+|:--------------:|:--------------:|
+| 3.2 | 1.0.1 |
+| 3.3 | 1.1.0 |
+| 3.4 | 1.2.0 |
## Installing
@@ -81,8 +81,8 @@ To develop this plugin further one can use the included .devcontainer configurat
1. In the WSL terminal, enter `code` to run Visual studio code.
2. Install the devcontainer extension "ms-vscode-remote.remote-containers"
3. Press Ctrl+Shift+P and use the "Dev Container: Clone Repository in Container Volume" function to clone this repository. This will take a while depending on your computer
-4. If you'd like the netbox instance to be prepopulated run `make Makefile example_initializers` and `make Makefile load_initializers`
-5. Start the netbox instance using `make Makefile all`
+4. If you'd like the netbox instance to be prepopulated with example data from [netbox-initializers](https://github.com/tobiasge/netbox-initializers) run `make initializers`
+5. Start the netbox instance using `make all`
Your netbox instance will be served under 0.0.0.0:8000, so it should now be available under localhost:8000.
diff --git a/docs/img/access_list_type_extended.png b/docs/img/access_list_type_extended.png
index e0e3f8b..f540d16 100644
Binary files a/docs/img/access_list_type_extended.png and b/docs/img/access_list_type_extended.png differ
diff --git a/docs/img/access_list_type_standard.png b/docs/img/access_list_type_standard.png
index 2fddca1..0794aeb 100644
Binary files a/docs/img/access_list_type_standard.png and b/docs/img/access_list_type_standard.png differ
diff --git a/docs/img/access_lists.png b/docs/img/access_lists.png
index 2420b11..98ecf16 100644
Binary files a/docs/img/access_lists.png and b/docs/img/access_lists.png differ
diff --git a/docs/img/acl_extended_rules.png b/docs/img/acl_extended_rules.png
index 987371c..c21a56b 100644
Binary files a/docs/img/acl_extended_rules.png and b/docs/img/acl_extended_rules.png differ
diff --git a/docs/img/acl_host_view.png b/docs/img/acl_host_view.png
index 18c559e..15127e6 100644
Binary files a/docs/img/acl_host_view.png and b/docs/img/acl_host_view.png differ
diff --git a/docs/img/acl_interface_assignments.png b/docs/img/acl_interface_assignments.png
index ce88f82..34031ca 100644
Binary files a/docs/img/acl_interface_assignments.png and b/docs/img/acl_interface_assignments.png differ
diff --git a/docs/img/acl_interface_view.png b/docs/img/acl_interface_view.png
index 0728749..39431d7 100644
Binary files a/docs/img/acl_interface_view.png and b/docs/img/acl_interface_view.png differ
diff --git a/docs/img/acl_standard_rules.png b/docs/img/acl_standard_rules.png
index 024f745..6e8efe5 100644
Binary files a/docs/img/acl_standard_rules.png and b/docs/img/acl_standard_rules.png differ
diff --git a/netbox_acls/__init__.py b/netbox_acls/__init__.py
index 4709253..fa5e548 100644
--- a/netbox_acls/__init__.py
+++ b/netbox_acls/__init__.py
@@ -17,9 +17,8 @@ class NetBoxACLsConfig(PluginConfig):
version = __version__
description = "Manage simple ACLs in NetBox"
base_url = "access-lists"
- min_version = "3.3.0"
- max_version = "3.3.99"
- # default_settings = {}
+ min_version = "3.4.0"
+ max_version = "3.4.99"
config = NetBoxACLsConfig
diff --git a/netbox_acls/api/views.py b/netbox_acls/api/views.py
index 2db85ba..cdff48d 100644
--- a/netbox_acls/api/views.py
+++ b/netbox_acls/api/views.py
@@ -28,8 +28,12 @@ class AccessListViewSet(NetBoxModelViewSet):
Defines the view set for the django AccessList model & associates it to a view.
"""
- queryset = models.AccessList.objects.prefetch_related("tags").annotate(
- rule_count=Count("aclextendedrules") + Count("aclstandardrules"),
+ queryset = (
+ models.AccessList.objects.prefetch_related("tags")
+ .annotate(
+ rule_count=Count("aclextendedrules") + Count("aclstandardrules"),
+ )
+ .prefetch_related("tags")
)
serializer_class = AccessListSerializer
filterset_class = filtersets.AccessListFilterSet
diff --git a/netbox_acls/navigation.py b/netbox_acls/navigation.py
index bb96b8f..6d784f5 100644
--- a/netbox_acls/navigation.py
+++ b/netbox_acls/navigation.py
@@ -2,82 +2,77 @@
Define the plugin menu buttons & the plugin navigation bar enteries.
"""
-from extras.plugins import PluginMenuButton, PluginMenuItem
+from extras.plugins import PluginMenu, PluginMenuButton, PluginMenuItem
from utilities.choices import ButtonColorChoices
#
# Define plugin menu buttons
#
-accesslist_buttons = [
- PluginMenuButton(
- link="plugins:netbox_acls:accesslist_add",
- title="Add",
- icon_class="mdi mdi-plus-thick",
- color=ButtonColorChoices.GREEN,
- permissions=["netbox_acls.add_accesslist"],
- ),
-]
-
-aclstandardrule_butons = [
- PluginMenuButton(
- link="plugins:netbox_acls:aclstandardrule_add",
- title="Add",
- icon_class="mdi mdi-plus-thick",
- color=ButtonColorChoices.GREEN,
- permissions=["netbox_acls.add_aclstandardrule"],
- ),
-]
-
-aclextendedrule_butons = [
- PluginMenuButton(
- link="plugins:netbox_acls:aclextendedrule_add",
- title="Add",
- icon_class="mdi mdi-plus-thick",
- color=ButtonColorChoices.GREEN,
- permissions=["netbox_acls.add_aclextendedrule"],
- ),
-]
-
-accesslistassignment_buttons = [
- PluginMenuButton(
- link="plugins:netbox_acls:aclinterfaceassignment_add",
- title="Add",
- icon_class="mdi mdi-plus-thick",
- color=ButtonColorChoices.GREEN,
- permissions=["netbox_acls.add_aclinterfaceassignment"],
- ),
-]
-
-#
-# Define navigation bar links including the above buttons defined.
-#
-
-menu_items = (
- PluginMenuItem(
- link="plugins:netbox_acls:accesslist_list",
- link_text="Access Lists",
- buttons=accesslist_buttons,
- permissions=["netbox_acls.view_accesslist"],
- ),
- # Comment out Standard Access List rule to force creation in the ACL view
- PluginMenuItem(
- link="plugins:netbox_acls:aclstandardrule_list",
- link_text="ACL Standard Rules",
- buttons=aclstandardrule_butons,
- permissions=["netbox_acls.view_aclstandardrule"],
- ),
- # Comment out Extended Access List rule to force creation in the ACL view
- PluginMenuItem(
- link="plugins:netbox_acls:aclextendedrule_list",
- link_text="ACL Extended Rules",
- buttons=aclextendedrule_butons,
- permissions=["netbox_acls.view_aclextendedrule"],
- ),
- PluginMenuItem(
- link="plugins:netbox_acls:aclinterfaceassignment_list",
- link_text="ACL Interface Assignments",
- buttons=accesslistassignment_buttons,
- permissions=["netbox_acls.view_aclinterfaceassignment"],
+menu = PluginMenu(
+ label="Access Lists",
+ groups=(
+ (
+ "ACLs",
+ (
+ PluginMenuItem(
+ link="plugins:netbox_acls:accesslist_list",
+ link_text="Access Lists",
+ permissions=["netbox_acls.view_accesslist"],
+ buttons=(
+ PluginMenuButton(
+ link="plugins:netbox_acls:accesslist_add",
+ title="Add",
+ icon_class="mdi mdi-plus-thick",
+ color=ButtonColorChoices.GREEN,
+ permissions=["netbox_acls.add_accesslist"],
+ ),
+ ),
+ ),
+ PluginMenuItem(
+ link="plugins:netbox_acls:aclstandardrule_list",
+ link_text="Standard Rules",
+ permissions=["netbox_acls.view_aclstandardrule"],
+ buttons=(
+ PluginMenuButton(
+ link="plugins:netbox_acls:aclstandardrule_add",
+ title="Add",
+ icon_class="mdi mdi-plus-thick",
+ color=ButtonColorChoices.GREEN,
+ permissions=["netbox_acls.add_aclstandardrule"],
+ ),
+ ),
+ ),
+ PluginMenuItem(
+ link="plugins:netbox_acls:aclextendedrule_list",
+ link_text="Extended Rules",
+ permissions=["netbox_acls.view_aclextendedrule"],
+ buttons=(
+ PluginMenuButton(
+ link="plugins:netbox_acls:aclextendedrule_add",
+ title="Add",
+ icon_class="mdi mdi-plus-thick",
+ color=ButtonColorChoices.GREEN,
+ permissions=["netbox_acls.add_aclextendedrule"],
+ ),
+ ),
+ ),
+ PluginMenuItem(
+ link="plugins:netbox_acls:aclinterfaceassignment_list",
+ link_text="Interface Assignments",
+ permissions=["netbox_acls.view_aclinterfaceassignment"],
+ buttons=(
+ PluginMenuButton(
+ link="plugins:netbox_acls:aclinterfaceassignment_add",
+ title="Add",
+ icon_class="mdi mdi-plus-thick",
+ color=ButtonColorChoices.GREEN,
+ permissions=["netbox_acls.add_aclinterfaceassignment"],
+ ),
+ ),
+ ),
+ ),
+ ),
),
+ icon_class="mdi mdi-lock",
)
diff --git a/netbox_acls/template_content.py b/netbox_acls/template_content.py
deleted file mode 100644
index 598e71d..0000000
--- a/netbox_acls/template_content.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from django.contrib.contenttypes.models import ContentType
-from extras.plugins import PluginTemplateExtension
-
-from .models import AccessList, ACLInterfaceAssignment
-
-__all__ = (
- "AccessLists",
- "ACLInterfaceAssignments",
- "DeviceAccessLists",
- "VirtualChassisAccessLists",
- "VMAccessLists",
- "DeviceACLInterfaceAssignments",
- "VMAACLInterfaceAssignments",
-)
-
-
-class ACLInterfaceAssignments(PluginTemplateExtension):
- def right_page(self):
- obj = self.context["object"]
-
- acl_interface_assignments = None
- ctype = ContentType.objects.get_for_model(obj)
- if ctype.model in ["interface", "vminterface"]:
- acl_interface_assignments = ACLInterfaceAssignment.objects.filter(
- assigned_object_id=obj.pk,
- assigned_object_type=ctype,
- )
- if ctype.model == "interface":
- parent_type = "device"
- parent_id = obj.device.pk
- elif ctype.model == "vminterface":
- parent_type = "virtual_machine"
- parent_id = obj.virtual_machine.pk
- else:
- parent_type = None
- parent_id = None
-
- return self.render(
- "inc/assigned_interface/access_lists.html",
- extra_context={
- "acl_interface_assignments": acl_interface_assignments,
- "type": ctype.model,
- "parent_type": parent_type,
- "parent_id": parent_id,
- },
- )
-
-
-class AccessLists(PluginTemplateExtension):
- def right_page(self):
- obj = self.context["object"]
-
- access_lists = None
- ctype = ContentType.objects.get_for_model(obj)
- if ctype.model in ["device", "virtualchassis", "virtualmachine"]:
- access_lists = AccessList.objects.filter(
- assigned_object_id=obj.pk,
- assigned_object_type=ctype,
- )
-
- return self.render(
- "inc/assigned_host/access_lists.html",
- extra_context={
- "access_lists": access_lists,
- "type": ctype.model
- if ctype.model == "device"
- else ctype.name.replace(" ", "_"),
- },
- )
-
-
-class DeviceAccessLists(AccessLists):
- model = "dcim.device"
-
-
-class VirtualChassisAccessLists(AccessLists):
- model = "dcim.virtualchassis"
-
-
-class VMAccessLists(AccessLists):
- model = "virtualization.virtualmachine"
-
-
-class DeviceACLInterfaceAssignments(ACLInterfaceAssignments):
- model = "dcim.interface"
-
-
-class VMAACLInterfaceAssignments(ACLInterfaceAssignments):
- model = "virtualization.vminterface"
-
-
-template_extensions = [
- DeviceAccessLists,
- VirtualChassisAccessLists,
- VMAccessLists,
- DeviceACLInterfaceAssignments,
- VMAACLInterfaceAssignments,
-]
diff --git a/netbox_acls/templates/inc/assigned_host/access_lists.html b/netbox_acls/templates/inc/assigned_host/access_lists.html
deleted file mode 100644
index 9738fa9..0000000
--- a/netbox_acls/templates/inc/assigned_host/access_lists.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- {% include 'inc/assigned_host/assigned_access_lists.html' %}
-
-
-
diff --git a/netbox_acls/templates/inc/assigned_host/assigned_access_lists.html b/netbox_acls/templates/inc/assigned_host/assigned_access_lists.html
deleted file mode 100644
index f7ce8a3..0000000
--- a/netbox_acls/templates/inc/assigned_host/assigned_access_lists.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% if access_lists %}
-
-
-
- | Name |
- Type |
- Default Action |
- Rule Count |
-
- {% for object in access_lists %}
-
- | {{ object|linkify }} |
- {{ object.type|title }} |
- {{ object.default_action|title }} |
- {% if object.type == 'standard' %}
- {{ object.aclstandardrules.count|placeholder }} |
- {% elif object.type == 'extended' %}
- {{ object.aclextendedrules.count|placeholder }} |
- {% endif %}
-
- {% endfor %}
-
-{% else %}
-
- None found
-
-{% endif %}
diff --git a/netbox_acls/templates/inc/assigned_interface/access_lists.html b/netbox_acls/templates/inc/assigned_interface/access_lists.html
deleted file mode 100644
index 165b0bf..0000000
--- a/netbox_acls/templates/inc/assigned_interface/access_lists.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- {% include 'inc/assigned_interface/assigned_access_lists.html' %}
-
-
-
diff --git a/netbox_acls/templates/inc/assigned_interface/assigned_access_lists.html b/netbox_acls/templates/inc/assigned_interface/assigned_access_lists.html
deleted file mode 100644
index 26bb009..0000000
--- a/netbox_acls/templates/inc/assigned_interface/assigned_access_lists.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% if acl_interface_assignments %}
-
-
-
- | Name |
- Type |
- Default Action |
- Rule Count |
- Direction |
-
- {% for object in acl_interface_assignments %}
-
- | {{ object.access_list|linkify }} |
- {{ object.access_list.type|title }} |
- {{ object.access_list.default_action|title }} |
- {% if object.access_list.type == 'standard' %}
- {{ object.access_list.aclstandardrules.count|placeholder }} |
- {% elif object.access_list.type == 'extended' %}
- {{ object.access_list.aclextendedrules.count|placeholder }} |
- {% endif %}
- {{ object.direction|title }} |
-
- {% endfor %}
-
-{% else %}
-
- None
-
-{% endif %}
diff --git a/netbox_acls/templates/inc/view_tab.html b/netbox_acls/templates/inc/view_tab.html
new file mode 100644
index 0000000..2109252
--- /dev/null
+++ b/netbox_acls/templates/inc/view_tab.html
@@ -0,0 +1,31 @@
+{% extends 'generic/object.html' %}
+{% load buttons %}
+{% load helpers %}
+{% load plugins %}
+{% load render_table from django_tables2 %}
+
+{% block extra_controls %}
+ {% if perms.netbox_todo.add_accesslist %}
+
+ Add Access List
+
+ {% endif %}
+{% endblock extra_controls %}
+
+{% block content %}
+ {% include 'inc/table_controls_htmx.html' with table_modal=table_config %}
+
+{% endblock content %}
+
+{% block modals %}
+ {{ block.super }}
+ {% table_config_form table %}
+{% endblock modals %}
diff --git a/netbox_acls/urls.py b/netbox_acls/urls.py
index f5ed0db..c5f07a1 100644
--- a/netbox_acls/urls.py
+++ b/netbox_acls/urls.py
@@ -2,8 +2,8 @@
Map Views to URLs.
"""
-from django.urls import path
-from netbox.views.generic import ObjectChangeLogView
+from django.urls import include, path
+from utilities.urls import get_model_urls
from . import models, views
@@ -33,12 +33,8 @@ urlpatterns = (
name="accesslist_delete",
),
path(
- "access-lists//changelog/",
- ObjectChangeLogView.as_view(),
- name="accesslist_changelog",
- kwargs={
- "model": models.AccessList,
- },
+ "access-lists//",
+ include(get_model_urls("netbox_acls", "accesslist")),
),
# Access List Interface Assignments
path(
@@ -73,12 +69,8 @@ urlpatterns = (
name="aclinterfaceassignment_delete",
),
path(
- "interface-assignments//changelog/",
- ObjectChangeLogView.as_view(),
- name="aclinterfaceassignment_changelog",
- kwargs={
- "model": models.ACLInterfaceAssignment,
- },
+ "interface-assignments//",
+ include(get_model_urls("netbox_acls", "aclinterfaceassignment")),
),
# Standard Access List Rules
path(
@@ -112,12 +104,8 @@ urlpatterns = (
name="aclstandardrule_delete",
),
path(
- "standard-rules//changelog/",
- ObjectChangeLogView.as_view(),
- name="aclstandardrule_changelog",
- kwargs={
- "model": models.ACLStandardRule,
- },
+ "standard-rules//",
+ include(get_model_urls("netbox_acls", "aclstandardrule")),
),
# Extended Access List Rules
path(
@@ -151,11 +139,7 @@ urlpatterns = (
name="aclextendedrule_delete",
),
path(
- "extended-rules//changelog/",
- ObjectChangeLogView.as_view(),
- name="aclextendedrule_changelog",
- kwargs={
- "model": models.ACLExtendedRule,
- },
+ "extended-rules//",
+ include(get_model_urls("netbox_acls", "aclextendedrule")),
),
)
diff --git a/netbox_acls/version.py b/netbox_acls/version.py
index a82b376..c68196d 100644
--- a/netbox_acls/version.py
+++ b/netbox_acls/version.py
@@ -1 +1 @@
-__version__ = "1.1.1"
+__version__ = "1.2.0"
diff --git a/netbox_acls/views.py b/netbox_acls/views.py
index 853f9b9..d986945 100644
--- a/netbox_acls/views.py
+++ b/netbox_acls/views.py
@@ -3,8 +3,11 @@ Defines the business logic for the plugin.
Specifically, all the various interactions with a client.
"""
+from dcim.models import Device, Interface, VirtualChassis
from django.db.models import Count
from netbox.views import generic
+from utilities.views import ViewTab, register_model_view
+from virtualization.models import VirtualMachine, VMInterface
from . import choices, filtersets, forms, models, tables
@@ -37,12 +40,13 @@ __all__ = (
#
+@register_model_view(models.AccessList)
class AccessListView(generic.ObjectView):
"""
Defines the view for the AccessLists django model.
"""
- queryset = models.AccessList.objects.all()
+ queryset = models.AccessList.objects.prefetch_related("tags")
def get_extra_context(self, request, instance):
"""
@@ -73,47 +77,121 @@ class AccessListListView(generic.ObjectListView):
queryset = models.AccessList.objects.annotate(
rule_count=Count("aclextendedrules") + Count("aclstandardrules"),
- )
+ ).prefetch_related("tags")
table = tables.AccessListTable
filterset = filtersets.AccessListFilterSet
filterset_form = forms.AccessListFilterForm
+@register_model_view(models.AccessList, "edit")
class AccessListEditView(generic.ObjectEditView):
"""
Defines the edit view for the AccessLists django model.
"""
- queryset = models.AccessList.objects.all()
+ queryset = models.AccessList.objects.prefetch_related("tags")
form = forms.AccessListForm
template_name = "netbox_acls/accesslist_edit.html"
+@register_model_view(models.AccessList, "delete")
class AccessListDeleteView(generic.ObjectDeleteView):
"""
Defines delete view for the AccessLists django model.
"""
- queryset = models.AccessList.objects.all()
+ queryset = models.AccessList.objects.prefetch_related("tags")
class AccessListBulkDeleteView(generic.BulkDeleteView):
- queryset = models.AccessList.objects.all()
+ queryset = models.AccessList.objects.prefetch_related("tags")
filterset = filtersets.AccessListFilterSet
table = tables.AccessListTable
+class AccessListChildView(generic.ObjectChildrenView):
+ """
+ Defines the child view for the AccessLists model.
+ """
+
+ child_model = models.AccessList
+ table = tables.AccessListTable
+ filterset = filtersets.AccessListFilterSet
+ template_name = "inc/view_tab.html"
+
+ def get_extra_context(self, request, instance):
+ return {
+ "table_config": self.table.__name__,
+ "model_type": self.queryset.model._meta.verbose_name.replace(" ", "_"),
+ "add_url": "plugins:netbox_acls:accesslist_add",
+ }
+
+ def prep_table_data(self, request, queryset, parent):
+ return queryset.annotate(
+ rule_count=Count("aclextendedrules") + Count("aclstandardrules"),
+ )
+
+
+@register_model_view(Device, "access_lists")
+class DeviceAccessListView(AccessListChildView):
+ queryset = Device.objects.prefetch_related("tags")
+ tab = ViewTab(
+ label="Access Lists",
+ badge=lambda obj: models.AccessList.objects.filter(device=obj).count(),
+ permission="netbox_acls.view_accesslist",
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.restrict(request.user, "view").filter(
+ device=parent,
+ )
+
+
+@register_model_view(VirtualChassis, "access_lists")
+class VirtualChassisAccessListView(AccessListChildView):
+ queryset = VirtualChassis.objects.prefetch_related("tags")
+ tab = ViewTab(
+ label="Access Lists",
+ badge=lambda obj: models.AccessList.objects.filter(virtual_chassis=obj).count(),
+ permission="netbox_acls.view_accesslist",
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.restrict(request.user, "view").filter(
+ virtual_chassis=parent,
+ )
+
+
+@register_model_view(VirtualMachine, "access_lists")
+class VirtualMachineAccessListView(AccessListChildView):
+ queryset = VirtualMachine.objects.prefetch_related("tags")
+ tab = ViewTab(
+ label="Access Lists",
+ badge=lambda obj: models.AccessList.objects.filter(virtual_machine=obj).count(),
+ permission="netbox_acls.view_accesslist",
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.restrict(request.user, "view").filter(
+ virtual_machine=parent,
+ )
+
+
#
# ACLInterfaceAssignment views
#
+@register_model_view(models.ACLInterfaceAssignment)
class ACLInterfaceAssignmentView(generic.ObjectView):
"""
Defines the view for the ACLInterfaceAssignments django model.
"""
- queryset = models.ACLInterfaceAssignment.objects.all()
+ queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
+ "access_list",
+ "tags",
+ )
class ACLInterfaceAssignmentListView(generic.ObjectListView):
@@ -121,18 +199,25 @@ class ACLInterfaceAssignmentListView(generic.ObjectListView):
Defines the list view for the ACLInterfaceAssignments django model.
"""
- queryset = models.ACLInterfaceAssignment.objects.all()
+ queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
+ "access_list",
+ "tags",
+ )
table = tables.ACLInterfaceAssignmentTable
filterset = filtersets.ACLInterfaceAssignmentFilterSet
filterset_form = forms.ACLInterfaceAssignmentFilterForm
+@register_model_view(models.ACLInterfaceAssignment, "edit")
class ACLInterfaceAssignmentEditView(generic.ObjectEditView):
"""
Defines the edit view for the ACLInterfaceAssignments django model.
"""
- queryset = models.ACLInterfaceAssignment.objects.all()
+ queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
+ "access_list",
+ "tags",
+ )
form = forms.ACLInterfaceAssignmentForm
template_name = "netbox_acls/aclinterfaceassignment_edit.html"
@@ -148,31 +233,97 @@ class ACLInterfaceAssignmentEditView(generic.ObjectEditView):
}
+@register_model_view(models.ACLInterfaceAssignment, "delete")
class ACLInterfaceAssignmentDeleteView(generic.ObjectDeleteView):
"""
Defines delete view for the ACLInterfaceAssignments django model.
"""
- queryset = models.ACLInterfaceAssignment.objects.all()
+ queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
+ "access_list",
+ "tags",
+ )
class ACLInterfaceAssignmentBulkDeleteView(generic.BulkDeleteView):
- queryset = models.ACLInterfaceAssignment.objects.all()
+ queryset = models.ACLInterfaceAssignment.objects.prefetch_related(
+ "access_list",
+ "tags",
+ )
filterset = filtersets.ACLInterfaceAssignmentFilterSet
table = tables.ACLInterfaceAssignmentTable
+class ACLInterfaceAssignmentChildView(generic.ObjectChildrenView):
+ """
+ Defines the child view for the ACLInterfaceAssignments model.
+ """
+
+ child_model = models.ACLInterfaceAssignment
+ table = tables.ACLInterfaceAssignmentTable
+ filterset = filtersets.ACLInterfaceAssignmentFilterSet
+ template_name = "inc/view_tab.html"
+
+ def get_extra_context(self, request, instance):
+ return {
+ "table_config": self.table.__name__,
+ "model_type": self.queryset.model._meta.verbose_name.replace(" ", "_"),
+ "add_url": "plugins:netbox_acls:aclinterfaceassignment_add",
+ }
+
+
+@register_model_view(Interface, "acl_interface_assignments")
+class InterfaceACLInterfaceAssignmentView(ACLInterfaceAssignmentChildView):
+ queryset = Interface.objects.prefetch_related("device", "tags")
+ tab = ViewTab(
+ label="ACL Interface Assignments",
+ badge=lambda obj: models.ACLInterfaceAssignment.objects.filter(
+ interface=obj,
+ ).count(),
+ permission="netbox_acls.view_aclinterfaceassignment",
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.restrict(request.user, "view").filter(
+ interface=parent,
+ )
+
+
+@register_model_view(VMInterface, "acl_interface_assignments")
+class VirtualMachineInterfaceACLInterfaceAssignmentView(
+ ACLInterfaceAssignmentChildView,
+):
+ queryset = VMInterface.objects.prefetch_related("virtual_machine", "tags")
+ tab = ViewTab(
+ label="ACL Interface Assignments",
+ badge=lambda obj: models.ACLInterfaceAssignment.objects.filter(
+ vminterface=obj,
+ ).count(),
+ permission="netbox_acls.view_aclinterfaceassignment",
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.restrict(request.user, "view").filter(
+ vminterface=parent,
+ )
+
+
#
# ACLStandardRule views
#
+@register_model_view(models.ACLStandardRule)
class ACLStandardRuleView(generic.ObjectView):
"""
Defines the view for the ACLStandardRule django model.
"""
- queryset = models.ACLStandardRule.objects.all()
+ queryset = models.ACLStandardRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ )
class ACLStandardRuleListView(generic.ObjectListView):
@@ -180,18 +331,27 @@ class ACLStandardRuleListView(generic.ObjectListView):
Defines the list view for the ACLStandardRule django model.
"""
- queryset = models.ACLStandardRule.objects.all()
+ queryset = models.ACLStandardRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ )
table = tables.ACLStandardRuleTable
filterset = filtersets.ACLStandardRuleFilterSet
filterset_form = forms.ACLStandardRuleFilterForm
+@register_model_view(models.ACLStandardRule, "edit")
class ACLStandardRuleEditView(generic.ObjectEditView):
"""
Defines the edit view for the ACLStandardRule django model.
"""
- queryset = models.ACLStandardRule.objects.all()
+ queryset = models.ACLStandardRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ )
form = forms.ACLStandardRuleForm
def get_extra_addanother_params(self, request):
@@ -205,16 +365,25 @@ class ACLStandardRuleEditView(generic.ObjectEditView):
}
+@register_model_view(models.ACLStandardRule, "delete")
class ACLStandardRuleDeleteView(generic.ObjectDeleteView):
"""
Defines delete view for the ACLStandardRules django model.
"""
- queryset = models.ACLStandardRule.objects.all()
+ queryset = models.ACLStandardRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ )
class ACLStandardRuleBulkDeleteView(generic.BulkDeleteView):
- queryset = models.ACLStandardRule.objects.all()
+ queryset = models.ACLStandardRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ )
filterset = filtersets.ACLStandardRuleFilterSet
table = tables.ACLStandardRuleTable
@@ -224,12 +393,18 @@ class ACLStandardRuleBulkDeleteView(generic.BulkDeleteView):
#
+@register_model_view(models.ACLExtendedRule)
class ACLExtendedRuleView(generic.ObjectView):
"""
Defines the view for the ACLExtendedRule django model.
"""
- queryset = models.ACLExtendedRule.objects.all()
+ queryset = models.ACLExtendedRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ "destination_prefix",
+ )
class ACLExtendedRuleListView(generic.ObjectListView):
@@ -237,18 +412,29 @@ class ACLExtendedRuleListView(generic.ObjectListView):
Defines the list view for the ACLExtendedRule django model.
"""
- queryset = models.ACLExtendedRule.objects.all()
+ queryset = models.ACLExtendedRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ "destination_prefix",
+ )
table = tables.ACLExtendedRuleTable
filterset = filtersets.ACLExtendedRuleFilterSet
filterset_form = forms.ACLExtendedRuleFilterForm
+@register_model_view(models.ACLExtendedRule, "edit")
class ACLExtendedRuleEditView(generic.ObjectEditView):
"""
Defines the edit view for the ACLExtendedRule django model.
"""
- queryset = models.ACLExtendedRule.objects.all()
+ queryset = models.ACLExtendedRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ "destination_prefix",
+ )
form = forms.ACLExtendedRuleForm
def get_extra_addanother_params(self, request):
@@ -262,15 +448,26 @@ class ACLExtendedRuleEditView(generic.ObjectEditView):
}
+@register_model_view(models.ACLExtendedRule, "delete")
class ACLExtendedRuleDeleteView(generic.ObjectDeleteView):
"""
Defines delete view for the ACLExtendedRules django model.
"""
- queryset = models.ACLExtendedRule.objects.all()
+ queryset = models.ACLExtendedRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ "destination_prefix",
+ )
class ACLExtendedRuleBulkDeleteView(generic.BulkDeleteView):
- queryset = models.ACLExtendedRule.objects.all()
+ queryset = models.ACLExtendedRule.objects.prefetch_related(
+ "access_list",
+ "tags",
+ "source_prefix",
+ "destination_prefix",
+ )
filterset = filtersets.ACLExtendedRuleFilterSet
table = tables.ACLExtendedRuleTable