Add owner fields to applicable models

This commit is contained in:
Jeremy Stretch
2025-10-17 16:15:04 -04:00
parent 8fd88b357e
commit 27ddccbdf8
30 changed files with 926 additions and 109 deletions

View File

@@ -52,7 +52,7 @@ class ProviderForm(NetBoxModelForm):
class Meta:
model = Provider
fields = [
'name', 'slug', 'asns', 'description', 'comments', 'tags',
'name', 'slug', 'asns', 'description', 'owner', 'comments', 'tags',
]
@@ -68,7 +68,7 @@ class ProviderAccountForm(NetBoxModelForm):
class Meta:
model = ProviderAccount
fields = [
'provider', 'name', 'account', 'description', 'comments', 'tags',
'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
]
@@ -88,7 +88,7 @@ class ProviderNetworkForm(NetBoxModelForm):
class Meta:
model = ProviderNetwork
fields = [
'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
]
@@ -96,7 +96,7 @@ class CircuitTypeForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags'),
FieldSet('name', 'slug', 'color', 'description', 'owner', 'tags'),
)
class Meta:
@@ -147,7 +147,7 @@ class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
model = Circuit
fields = [
'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'comments', 'tags',
'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
]
widgets = {
'install_date': DatePicker(),
@@ -244,7 +244,7 @@ class CircuitGroupForm(TenancyForm, NetBoxModelForm):
class Meta:
model = CircuitGroup
fields = [
'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'tags',
]
@@ -317,7 +317,7 @@ class VirtualCircuitTypeForm(NetBoxModelForm):
class Meta:
model = VirtualCircuitType
fields = [
'name', 'slug', 'color', 'description', 'tags',
'name', 'slug', 'color', 'description', 'owner', 'tags',
]
@@ -350,7 +350,7 @@ class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
model = VirtualCircuit
fields = [
'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
'comments', 'tags',
'owner', 'comments', 'tags',
]

View File

@@ -0,0 +1,68 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0052_extend_circuit_abs_distance_upper_limit'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='circuit',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='circuitgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='circuittype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='provider',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='provideraccount',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='providernetwork',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualcircuit',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualcircuittype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -36,7 +36,8 @@ class DataSourceForm(NetBoxModelForm):
class Meta:
model = DataSource
fields = [
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'comments', 'tags',
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'owner',
'comments', 'tags',
]
widgets = {
'ignore_rules': forms.Textarea(

View File

@@ -0,0 +1,24 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0019_configrevision_active'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='datasource',
name='owner',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='+',
to='users.owner',
),
),
]

View File

@@ -91,7 +91,7 @@ class RegionForm(NetBoxModelForm):
class Meta:
model = Region
fields = (
'parent', 'name', 'slug', 'description', 'tags', 'comments',
'parent', 'name', 'slug', 'description', 'owner', 'tags', 'comments',
)
@@ -111,7 +111,7 @@ class SiteGroupForm(NetBoxModelForm):
class Meta:
model = SiteGroup
fields = (
'parent', 'name', 'slug', 'description', 'comments', 'tags',
'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
)
@@ -154,7 +154,7 @@ class SiteForm(TenancyForm, NetBoxModelForm):
model = Site
fields = (
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner', 'comments', 'tags',
)
widgets = {
'physical_address': forms.Textarea(
@@ -195,8 +195,8 @@ class LocationForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Location
fields = (
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
'facility', 'tags', 'comments',
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'owner',
'comments', 'tags',
)
@@ -210,7 +210,7 @@ class RackRoleForm(NetBoxModelForm):
class Meta:
model = RackRole
fields = [
'name', 'slug', 'color', 'description', 'tags',
'name', 'slug', 'color', 'description', 'owner', 'tags',
]
@@ -242,7 +242,7 @@ class RackTypeForm(NetBoxModelForm):
fields = [
'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
'weight_unit', 'description', 'comments', 'tags',
'weight_unit', 'description', 'owner', 'comments', 'tags',
]
@@ -288,7 +288,7 @@ class RackForm(TenancyForm, NetBoxModelForm):
'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight',
'weight_unit', 'description', 'comments', 'tags',
'weight_unit', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -343,7 +343,7 @@ class RackReservationForm(TenancyForm, NetBoxModelForm):
class Meta:
model = RackReservation
fields = [
'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
@@ -357,7 +357,7 @@ class ManufacturerForm(NetBoxModelForm):
class Meta:
model = Manufacturer
fields = [
'name', 'slug', 'description', 'tags',
'name', 'slug', 'description', 'owner', 'tags',
]
@@ -396,7 +396,7 @@ class DeviceTypeForm(NetBoxModelForm):
fields = [
'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization',
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
'description', 'comments', 'tags',
'description', 'owner', 'comments', 'tags',
]
widgets = {
'front_image': ClearableFileInput(attrs={
@@ -423,7 +423,7 @@ class ModuleTypeProfileForm(NetBoxModelForm):
class Meta:
model = ModuleTypeProfile
fields = [
'name', 'description', 'schema', 'comments', 'tags',
'name', 'description', 'schema', 'owner', 'comments', 'tags',
]
@@ -452,7 +452,7 @@ class ModuleTypeForm(NetBoxModelForm):
model = ModuleType
fields = [
'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
'comments', 'tags',
'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -531,7 +531,7 @@ class DeviceRoleForm(NetBoxModelForm):
class Meta:
model = DeviceRole
fields = [
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags',
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'owner', 'comments', 'tags',
]
@@ -567,7 +567,7 @@ class PlatformForm(NetBoxModelForm):
class Meta:
model = Platform
fields = [
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'comments', 'tags',
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'owner', 'comments', 'tags',
]
@@ -677,7 +677,7 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
'name', 'role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'location', 'position', 'face',
'latitude', 'longitude', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster',
'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', 'description', 'config_template',
'comments', 'tags', 'local_context_data',
'owner', 'comments', 'tags', 'local_context_data',
]
def __init__(self, *args, **kwargs):
@@ -788,7 +788,7 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm):
model = Module
fields = [
'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'tags', 'replicate_components',
'adopt_components', 'description', 'comments',
'adopt_components', 'description', 'owner', 'comments',
]
def __init__(self, *args, **kwargs):
@@ -828,7 +828,7 @@ class CableForm(TenancyForm, NetBoxModelForm):
model = Cable
fields = [
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
'length', 'length_unit', 'description', 'comments', 'tags',
'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
]
@@ -855,7 +855,7 @@ class PowerPanelForm(NetBoxModelForm):
class Meta:
model = PowerPanel
fields = [
'site', 'location', 'name', 'description', 'comments', 'tags',
'site', 'location', 'name', 'description', 'owner', 'comments', 'tags',
]
@@ -887,7 +887,7 @@ class PowerFeedForm(TenancyForm, NetBoxModelForm):
model = PowerFeed
fields = [
'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
'max_utilization', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags'
]
@@ -906,7 +906,7 @@ class VirtualChassisForm(NetBoxModelForm):
class Meta:
model = VirtualChassis
fields = [
'name', 'domain', 'master', 'description', 'comments', 'tags',
'name', 'domain', 'master', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'master': SelectWithPK(),
@@ -1396,7 +1396,7 @@ class ConsolePortForm(ModularDeviceComponentForm):
class Meta:
model = ConsolePort
fields = [
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
]
@@ -1410,7 +1410,7 @@ class ConsoleServerPortForm(ModularDeviceComponentForm):
class Meta:
model = ConsoleServerPort
fields = [
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
]
@@ -1426,7 +1426,7 @@ class PowerPortForm(ModularDeviceComponentForm):
model = PowerPort
fields = [
'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected',
'description', 'tags',
'description', 'owner', 'tags',
]
@@ -1443,7 +1443,7 @@ class PowerOutletForm(ModularDeviceComponentForm):
fieldsets = (
FieldSet(
'device', 'module', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'mark_connected',
'description', 'tags',
'description', 'owner', 'tags',
),
)
@@ -1587,7 +1587,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
'lag', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address',
'tags',
'owner', 'tags',
]
widgets = {
'speed': NumberWithOptions(
@@ -1619,7 +1619,7 @@ class FrontPortForm(ModularDeviceComponentForm):
model = FrontPort
fields = [
'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected',
'description', 'tags',
'description', 'owner', 'tags',
]
@@ -1633,7 +1633,8 @@ class RearPortForm(ModularDeviceComponentForm):
class Meta:
model = RearPort
fields = [
'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags',
'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner',
'tags',
]
@@ -1645,7 +1646,7 @@ class ModuleBayForm(ModularDeviceComponentForm):
class Meta:
model = ModuleBay
fields = [
'device', 'module', 'name', 'label', 'position', 'description', 'tags',
'device', 'module', 'name', 'label', 'position', 'description', 'owner', 'tags',
]
@@ -1657,7 +1658,7 @@ class DeviceBayForm(DeviceComponentForm):
class Meta:
model = DeviceBay
fields = [
'device', 'name', 'label', 'description', 'tags',
'device', 'name', 'label', 'description', 'owner', 'tags',
]
@@ -1782,7 +1783,7 @@ class InventoryItemForm(DeviceComponentForm):
model = InventoryItem
fields = [
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
'status', 'description', 'tags',
'status', 'description', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -1828,9 +1829,6 @@ class InventoryItemForm(DeviceComponentForm):
self.instance.component = None
# Device component roles
#
class InventoryItemRoleForm(NetBoxModelForm):
slug = SlugField()
@@ -1841,7 +1839,7 @@ class InventoryItemRoleForm(NetBoxModelForm):
class Meta:
model = InventoryItemRole
fields = [
'name', 'slug', 'color', 'description', 'tags',
'name', 'slug', 'color', 'description', 'owner', 'tags',
]
@@ -1881,7 +1879,7 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
class Meta:
model = VirtualDeviceContext
fields = [
'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant',
'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'owner',
'comments', 'tags'
]
@@ -1929,7 +1927,7 @@ class MACAddressForm(NetBoxModelForm):
class Meta:
model = MACAddress
fields = [
'mac_address', 'interface', 'vminterface', 'description', 'tags',
'mac_address', 'interface', 'vminterface', 'description', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):

View File

@@ -0,0 +1,243 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0215_rackreservation_status'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='cable',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='consoleport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='consoleserverport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='device',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='devicebay',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='devicerole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='devicetype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='frontport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='interface',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='inventoryitem',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='inventoryitemrole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='location',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='macaddress',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='manufacturer',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='module',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='modulebay',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='moduletype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='moduletypeprofile',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='platform',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='powerfeed',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='poweroutlet',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='powerpanel',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='powerport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='rack',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='rackreservation',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='rackrole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='racktype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='rearport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='region',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='site',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='sitegroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualchassis',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualdevicecontext',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -14,6 +14,7 @@ from dcim.fields import WWNField
from dcim.models.mixins import InterfaceValidationMixin
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, NetBoxModel
from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
from utilities.ordering import naturalize_interface
@@ -40,7 +41,7 @@ __all__ = (
)
class ComponentModel(NetBoxModel):
class ComponentModel(OwnerMixin, NetBoxModel):
"""
An abstract model inherited by any model which has a parent Device.
"""

View File

@@ -179,7 +179,7 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
class Meta:
model = CustomFieldChoiceSet
fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner')
def __init__(self, *args, initial=None, **kwargs):
super().__init__(*args, initial=initial, **kwargs)
@@ -480,7 +480,7 @@ class EventRuleForm(NetBoxModelForm):
model = EventRule
fields = (
'object_types', 'name', 'description', 'enabled', 'event_types', 'conditions', 'action_type',
'action_object_type', 'action_object_id', 'action_data', 'comments', 'tags'
'action_object_type', 'action_object_id', 'action_data', 'owner', 'comments', 'tags'
)
widgets = {
'conditions': forms.Textarea(attrs={'class': 'font-monospace'}),
@@ -582,7 +582,7 @@ class TagForm(ChangelogMessageMixin, forms.ModelForm):
class Meta:
model = Tag
fields = [
'name', 'slug', 'color', 'weight', 'description', 'object_types',
'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
]
@@ -606,7 +606,8 @@ class ConfigContextProfileForm(SyncedDataMixin, NetBoxModelForm):
class Meta:
model = ConfigContextProfile
fields = (
'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'comments', 'tags',
'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'owner', 'comments',
'tags',
)
@@ -701,7 +702,7 @@ class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm)
fields = (
'name', 'weight', 'profile', 'description', 'data', 'is_active', 'regions', 'site_groups', 'sites',
'locations', 'roles', 'device_types', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
'tenant_groups', 'tenants', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
'tenant_groups', 'tenants', 'owner', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
)
def __init__(self, *args, initial=None, **kwargs):

View File

@@ -0,0 +1,89 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('extras', '0133_make_cf_minmax_decimal'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='configcontext',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='configcontextprofile',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='configtemplate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='customfield',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='customfieldchoiceset',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='customlink',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='eventrule',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='exporttemplate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='savedfilter',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='tag',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='webhook',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -13,6 +13,7 @@ from extras.models.mixins import RenderTemplateMixin
from extras.querysets import ConfigContextQuerySet
from netbox.models import ChangeLoggedModel, PrimaryModel
from netbox.models.features import CloningMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin
from netbox.models.mixins import OwnerMixin
from utilities.data import deepmerge
from utilities.jsonschema import validate_schema
@@ -68,7 +69,7 @@ class ConfigContextProfile(SyncedDataMixin, PrimaryModel):
sync_data.alters_data = True
class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, ChangeLoggedModel):
class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, OwnerMixin, ChangeLoggedModel):
"""
A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned
qualifiers (region, site, etc.). For example, the data stored in a ConfigContext assigned to site A and tenant B
@@ -266,7 +267,13 @@ class ConfigContextModel(models.Model):
#
class ConfigTemplate(
RenderTemplateMixin, SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel
RenderTemplateMixin,
SyncedDataMixin,
CustomLinksMixin,
ExportTemplatesMixin,
OwnerMixin,
TagsMixin,
ChangeLoggedModel,
):
name = models.CharField(
verbose_name=_('name'),

View File

@@ -21,6 +21,7 @@ from extras.choices import *
from extras.data import CHOICE_SETS
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
from netbox.models.mixins import OwnerMixin
from netbox.search import FieldTypes
from utilities import filters
from utilities.datetime import datetime_from_timestamp
@@ -70,7 +71,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
}
class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='custom_fields',
@@ -773,7 +774,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
raise ValidationError(_("Required field cannot be empty."))
class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
Represents a set of choices available for choice and multi-choice custom fields.
"""

View File

@@ -25,6 +25,7 @@ from netbox.models import ChangeLoggedModel
from netbox.models.features import (
CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin, has_feature
)
from netbox.models.mixins import OwnerMixin
from utilities.html import clean_html
from utilities.jinja2 import render_jinja2
from utilities.querydict import dict_to_querydict
@@ -44,7 +45,7 @@ __all__ = (
)
class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
class EventRule(CustomFieldsMixin, ExportTemplatesMixin, OwnerMixin, TagsMixin, ChangeLoggedModel):
"""
An EventRule defines an action to be taken automatically in response to a specific set of events, such as when a
specific type of object is created, modified, or deleted. The action to be taken might entail transmitting a
@@ -155,7 +156,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
return False
class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, OwnerMixin, ChangeLoggedModel):
"""
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
delete in NetBox. The request will contain a representation of the object, which the remote application can act on.
@@ -294,7 +295,7 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
return render_jinja2(self.payload_url, context)
class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
class CustomLink(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
code to be rendered with an object as context.
@@ -394,7 +395,14 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
}
class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin):
class ExportTemplate(
SyncedDataMixin,
CloningMixin,
ExportTemplatesMixin,
OwnerMixin,
ChangeLoggedModel,
RenderTemplateMixin,
):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='export_templates',
@@ -456,7 +464,7 @@ class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, Change
return _context
class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
class SavedFilter(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
"""
A set of predefined keyword parameters that can be reused to filter for specific objects.
"""

View File

@@ -8,6 +8,7 @@ from taggit.models import TagBase, GenericTaggedItemBase
from netbox.choices import ColorChoices
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField
from utilities.querysets import RestrictedQuerySet
@@ -21,7 +22,7 @@ __all__ = (
# Tags
#
class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
class Tag(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel, TagBase):
id = models.BigAutoField(
primary_key=True
)

View File

@@ -72,7 +72,7 @@ class VRFForm(TenancyForm, NetBoxModelForm):
model = VRF
fields = [
'name', 'rd', 'enforce_unique', 'import_targets', 'export_targets', 'tenant_group', 'tenant', 'description',
'comments', 'tags',
'owner', 'comments', 'tags',
]
labels = {
'rd': "RD",
@@ -89,7 +89,7 @@ class RouteTargetForm(TenancyForm, NetBoxModelForm):
class Meta:
model = RouteTarget
fields = [
'name', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
'name', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
@@ -103,7 +103,7 @@ class RIRForm(NetBoxModelForm):
class Meta:
model = RIR
fields = [
'name', 'slug', 'is_private', 'description', 'tags',
'name', 'slug', 'is_private', 'description', 'owner', 'tags',
]
@@ -123,7 +123,7 @@ class AggregateForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Aggregate
fields = [
'prefix', 'rir', 'date_added', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
'prefix', 'rir', 'date_added', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'date_added': DatePicker(),
@@ -145,7 +145,7 @@ class ASNRangeForm(TenancyForm, NetBoxModelForm):
class Meta:
model = ASNRange
fields = [
'name', 'slug', 'rir', 'start', 'end', 'tenant_group', 'tenant', 'description', 'tags'
'name', 'slug', 'rir', 'start', 'end', 'tenant_group', 'tenant', 'owner', 'description', 'tags'
]
@@ -170,7 +170,7 @@ class ASNForm(TenancyForm, NetBoxModelForm):
class Meta:
model = ASN
fields = [
'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags'
]
widgets = {
'date_added': DatePicker(),
@@ -198,7 +198,7 @@ class RoleForm(NetBoxModelForm):
class Meta:
model = Role
fields = [
'name', 'slug', 'weight', 'description', 'tags',
'name', 'slug', 'weight', 'description', 'owner', 'tags',
]
@@ -238,7 +238,7 @@ class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm):
model = Prefix
fields = [
'prefix', 'vrf', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'scope_type', 'tenant_group',
'tenant', 'description', 'comments', 'tags',
'tenant', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -276,7 +276,7 @@ class IPRangeForm(TenancyForm, NetBoxModelForm):
model = IPRange
fields = [
'vrf', 'start_address', 'end_address', 'status', 'role', 'tenant_group', 'tenant', 'mark_populated',
'mark_utilized', 'description', 'comments', 'tags',
'mark_utilized', 'description', 'owner', 'comments', 'tags',
]
@@ -344,7 +344,7 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
model = IPAddress
fields = [
'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'oob_for_parent', 'nat_inside',
'tenant_group', 'tenant', 'description', 'comments', 'tags',
'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -523,7 +523,7 @@ class FHRPGroupForm(NetBoxModelForm):
model = FHRPGroup
fields = (
'protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'ip_vrf', 'ip_address', 'ip_status', 'description',
'comments', 'tags',
'owner', 'comments', 'tags',
)
def save(self, *args, **kwargs):
@@ -628,7 +628,7 @@ class VLANGroupForm(TenancyForm, NetBoxModelForm):
class Meta:
model = VLANGroup
fields = [
'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'tags',
'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'owner', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -704,7 +704,7 @@ class VLANForm(TenancyForm, NetBoxModelForm):
model = VLAN
fields = [
'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'qinq_role', 'qinq_svlan',
'description', 'comments', 'tags',
'description', 'owner', 'comments', 'tags',
]
@@ -717,7 +717,7 @@ class VLANTranslationPolicyForm(NetBoxModelForm):
class Meta:
model = VLANTranslationPolicy
fields = [
'name', 'description', 'tags',
'name', 'description', 'owner', 'tags',
]
@@ -756,7 +756,7 @@ class ServiceTemplateForm(NetBoxModelForm):
class Meta:
model = ServiceTemplate
fields = ('name', 'protocol', 'ports', 'description', 'comments', 'tags')
fields = ('name', 'protocol', 'ports', 'description', 'owner', 'comments', 'tags')
class ServiceForm(NetBoxModelForm):
@@ -799,7 +799,7 @@ class ServiceForm(NetBoxModelForm):
class Meta:
model = Service
fields = [
'name', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags',
'name', 'protocol', 'ports', 'ipaddresses', 'description', 'owner', 'comments', 'tags',
'parent_object_type',
]

View File

@@ -0,0 +1,124 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ipam', '0082_add_prefix_network_containment_indexes'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='aggregate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='asn',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='asnrange',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='fhrpgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='ipaddress',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='iprange',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='prefix',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='rir',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='role',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='routetarget',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='service',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='servicetemplate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='vlan',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='vlangroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='vlantranslationpolicy',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='vrf',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -11,7 +11,7 @@ from extras.models import CustomField, Tag
from utilities.forms import BulkEditForm, CSVModelForm
from utilities.forms.fields import CSVModelMultipleChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.mixins import CheckLastUpdatedMixin
from .mixins import ChangelogMessageMixin, CustomFieldsMixin, SavedFiltersMixin, TagsMixin
from .mixins import ChangelogMessageMixin, CustomFieldsMixin, OwnerMixin, SavedFiltersMixin, TagsMixin
__all__ = (
'NetBoxModelForm',
@@ -21,7 +21,14 @@ __all__ = (
)
class NetBoxModelForm(ChangelogMessageMixin, CheckLastUpdatedMixin, CustomFieldsMixin, TagsMixin, forms.ModelForm):
class NetBoxModelForm(
ChangelogMessageMixin,
CheckLastUpdatedMixin,
CustomFieldsMixin,
OwnerMixin,
TagsMixin,
forms.ModelForm
):
"""
Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields.
@@ -100,7 +107,7 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
return customfield.to_form_field(for_csv_import=True)
class NetBoxModelBulkEditForm(ChangelogMessageMixin, CustomFieldsMixin, BulkEditForm):
class NetBoxModelBulkEditForm(ChangelogMessageMixin, CustomFieldsMixin, OwnerMixin, BulkEditForm):
"""
Base form for modifying multiple NetBox objects (of the same type) in bulk via the UI. Adds support for custom
fields and adding/removing tags.

View File

@@ -4,11 +4,13 @@ from django.utils.translation import gettext as _
from core.models import ObjectType
from extras.choices import *
from extras.models import *
from utilities.forms.fields import DynamicModelMultipleChoiceField
from users.models import Owner
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
__all__ = (
'ChangelogMessageMixin',
'CustomFieldsMixin',
'OwnerMixin',
'SavedFiltersMixin',
'TagsMixin',
)
@@ -118,3 +120,14 @@ class TagsMixin(forms.Form):
object_type = ObjectType.objects.get_for_model(self._meta.model)
if object_type and hasattr(self.fields['tags'].widget, 'add_query_param'):
self.fields['tags'].widget.add_query_param('for_object_type_id', object_type.pk)
class OwnerMixin(forms.Form):
"""
Add an `owner` field to forms for models which support Owner assignment.
"""
owner = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)

View File

@@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
from netbox.models.features import *
from netbox.models.mixins import OwnerMixin
from utilities.mptt import TreeManager
from utilities.querysets import RestrictedQuerySet
from utilities.views import get_viewname
@@ -120,7 +121,7 @@ class NetBoxModel(NetBoxFeatureSet, BaseModel):
# NetBox internal base models
#
class PrimaryModel(NetBoxModel):
class PrimaryModel(OwnerMixin, NetBoxModel):
"""
Primary models represent real objects within the infrastructure being modeled.
"""
@@ -138,7 +139,7 @@ class PrimaryModel(NetBoxModel):
abstract = True
class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
class NestedGroupModel(NetBoxFeatureSet, OwnerMixin, MPTTModel):
"""
Base model for objects which are used to form a hierarchy (regions, locations, etc.). These models nest
recursively using MPTT. Within each parent, each child instance must have a unique name.
@@ -190,7 +191,7 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
})
class OrganizationalModel(NetBoxModel):
class OrganizationalModel(OwnerMixin, NetBoxModel):
"""
Organizational models are those which are used solely to categorize and qualify other objects, and do not convey
any real information about the infrastructure being modeled (for example, functional device roles). Organizational

View File

@@ -641,6 +641,7 @@ register_model_feature('image_attachments', lambda model: issubclass(model, Imag
register_model_feature('jobs', lambda model: issubclass(model, JobsMixin))
register_model_feature('journaling', lambda model: issubclass(model, JournalingMixin))
register_model_feature('notifications', lambda model: issubclass(model, NotificationsMixin))
register_model_feature('owner', lambda model: issubclass(model, OwnerMixin))
register_model_feature('synced_data', lambda model: issubclass(model, SyncedDataMixin))
register_model_feature('tags', lambda model: issubclass(model, TagsMixin))

View File

@@ -7,10 +7,27 @@ from utilities.conversion import to_grams, to_meters
__all__ = (
'DistanceMixin',
'OwnerMixin',
'WeightMixin',
)
class OwnerMixin(models.Model):
"""
Adds a ForeignKey to users.Owner to indicate an object's owner.
"""
owner = models.ForeignKey(
to='users.Owner',
on_delete=models.PROTECT,
related_name='+',
blank=True,
null=True
)
class Meta:
abstract = True
class WeightMixin(models.Model):
weight = models.DecimalField(
verbose_name=_('weight'),

View File

@@ -22,6 +22,15 @@
</div>
{% endif %}
{% if form.owner %}
<div class="field-group mb-5">
<div class="row">
<h2 class="col-9 offset-3">{% trans "Ownership" %}</h2>
</div>
{% render_field form.owner %}
</div>
{% endif %}
{% if form.comments %}
<div class="field-group mb-5">
{% render_field form.comments %}

View File

@@ -36,7 +36,7 @@ class TenantGroupForm(NetBoxModelForm):
class Meta:
model = TenantGroup
fields = [
'parent', 'name', 'slug', 'description', 'tags', 'comments'
'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
]
@@ -56,7 +56,7 @@ class TenantForm(NetBoxModelForm):
class Meta:
model = Tenant
fields = (
'name', 'slug', 'group', 'description', 'comments', 'tags',
'name', 'slug', 'group', 'description', 'owner', 'comments', 'tags',
)
@@ -79,7 +79,7 @@ class ContactGroupForm(NetBoxModelForm):
class Meta:
model = ContactGroup
fields = ('parent', 'name', 'slug', 'description', 'tags', 'comments')
fields = ('parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags')
class ContactRoleForm(NetBoxModelForm):
@@ -91,7 +91,7 @@ class ContactRoleForm(NetBoxModelForm):
class Meta:
model = ContactRole
fields = ('name', 'slug', 'description', 'tags')
fields = ('name', 'slug', 'description', 'owner', 'tags')
class ContactForm(NetBoxModelForm):
@@ -117,7 +117,7 @@ class ContactForm(NetBoxModelForm):
class Meta:
model = Contact
fields = (
'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags',
'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'owner', 'comments', 'tags',
)
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),

View File

@@ -0,0 +1,47 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tenancy', '0020_remove_contactgroupmembership'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='contact',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='contactgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='contactrole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='tenant',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='tenantgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -42,7 +42,7 @@ class ClusterTypeForm(NetBoxModelForm):
class Meta:
model = ClusterType
fields = (
'name', 'slug', 'description', 'tags',
'name', 'slug', 'description', 'owner', 'tags',
)
@@ -56,7 +56,7 @@ class ClusterGroupForm(NetBoxModelForm):
class Meta:
model = ClusterGroup
fields = (
'name', 'slug', 'description', 'tags',
'name', 'slug', 'description', 'owner', 'tags',
)
@@ -83,7 +83,7 @@ class ClusterForm(TenancyForm, ScopedForm, NetBoxModelForm):
class Meta:
model = Cluster
fields = (
'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'description', 'comments', 'tags',
'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'description', 'owner', 'comments', 'tags',
)
@@ -236,7 +236,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
model = VirtualMachine
fields = [
'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'comments', 'tags',
'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'owner', 'comments', 'tags',
'local_context_data', 'config_template',
]
@@ -387,7 +387,7 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
fields = [
'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode', 'vlan_group',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address',
'tags',
'owner', 'tags',
]
labels = {
'mode': _('802.1Q Mode'),
@@ -406,5 +406,5 @@ class VirtualDiskForm(VMComponentForm):
class Meta:
model = VirtualDisk
fields = [
'virtual_machine', 'name', 'size', 'description', 'tags',
'virtual_machine', 'name', 'size', 'description', 'owner', 'tags',
]

View File

@@ -0,0 +1,54 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0015_owner'),
('virtualization', '0048_populate_mac_addresses'),
]
operations = [
migrations.AddField(
model_name='cluster',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='clustergroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='clustertype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualdisk',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='virtualmachine',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='vminterface',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -15,6 +15,7 @@ from extras.querysets import ConfigContextModelQuerySet
from netbox.config import get_config
from netbox.models import NetBoxModel, PrimaryModel
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
from netbox.models.mixins import OwnerMixin
from utilities.fields import CounterCacheField, NaturalOrderingField
from utilities.ordering import naturalize_interface
from utilities.query_functions import CollateAsChar
@@ -263,7 +264,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co
#
class ComponentModel(NetBoxModel):
class ComponentModel(OwnerMixin, NetBoxModel):
"""
An abstract model inherited by any model which has a parent VirtualMachine.
"""

View File

@@ -39,7 +39,7 @@ class TunnelGroupForm(NetBoxModelForm):
class Meta:
model = TunnelGroup
fields = [
'name', 'slug', 'description', 'tags',
'name', 'slug', 'description', 'owner', 'tags',
]
@@ -67,7 +67,7 @@ class TunnelForm(TenancyForm, NetBoxModelForm):
model = Tunnel
fields = [
'name', 'status', 'group', 'encapsulation', 'description', 'tunnel_id', 'ipsec_profile', 'tenant_group',
'tenant', 'comments', 'tags',
'tenant', 'owner', 'comments', 'tags',
]
@@ -307,7 +307,7 @@ class IKEProposalForm(NetBoxModelForm):
model = IKEProposal
fields = [
'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group',
'sa_lifetime', 'comments', 'tags',
'sa_lifetime', 'owner', 'comments', 'tags',
]
@@ -326,7 +326,7 @@ class IKEPolicyForm(NetBoxModelForm):
class Meta:
model = IKEPolicy
fields = [
'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'comments', 'tags',
'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'owner', 'comments', 'tags',
]
@@ -344,7 +344,7 @@ class IPSecProposalForm(NetBoxModelForm):
model = IPSecProposal
fields = [
'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
'sa_lifetime_data', 'comments', 'tags',
'sa_lifetime_data', 'owner', 'comments', 'tags',
]
@@ -363,7 +363,7 @@ class IPSecPolicyForm(NetBoxModelForm):
class Meta:
model = IPSecPolicy
fields = [
'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
'name', 'description', 'proposals', 'pfs_group', 'owner', 'comments', 'tags',
]
@@ -386,7 +386,7 @@ class IPSecProfileForm(NetBoxModelForm):
class Meta:
model = IPSecProfile
fields = [
'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags',
'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'owner', 'comments', 'tags',
]
@@ -417,8 +417,8 @@ class L2VPNForm(TenancyForm, NetBoxModelForm):
class Meta:
model = L2VPN
fields = (
'name', 'slug', 'type', 'status', 'identifier', 'import_targets', 'export_targets', 'tenant',
'description', 'comments', 'tags'
'name', 'slug', 'type', 'status', 'identifier', 'import_targets', 'export_targets', 'tenant', 'description',
'owner', 'comments', 'tags'
)

View File

@@ -0,0 +1,68 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0015_owner'),
('vpn', '0009_remove_redundant_indexes'),
]
operations = [
migrations.AddField(
model_name='ikepolicy',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='ikeproposal',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='ipsecpolicy',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='ipsecprofile',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='ipsecproposal',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='l2vpn',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='tunnel',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='tunnelgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]

View File

@@ -34,7 +34,7 @@ class WirelessLANGroupForm(NetBoxModelForm):
class Meta:
model = WirelessLANGroup
fields = [
'parent', 'name', 'slug', 'description', 'tags', 'comments',
'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
]
@@ -64,7 +64,7 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
model = WirelessLAN
fields = [
'ssid', 'group', 'status', 'vlan', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
'scope_type', 'description', 'comments', 'tags',
'scope_type', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(
@@ -181,7 +181,7 @@ class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
fields = [
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
'status', 'ssid', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
'distance', 'distance_unit', 'description', 'comments', 'tags',
'distance', 'distance_unit', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(

View File

@@ -0,0 +1,33 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0015_owner'),
('wireless', '0015_extend_wireless_link_abs_distance_upper_limit'),
]
operations = [
migrations.AddField(
model_name='wirelesslan',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='wirelesslangroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
migrations.AddField(
model_name='wirelesslink',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='users.owner'
),
),
]