mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Code formatting cleanup
This commit is contained in:
@ -7,8 +7,7 @@ from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from dcim.constants import STATUS_CLASSES
|
||||
from dcim.fields import ASNField
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomFieldModel
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
|
||||
|
||||
@ -19,15 +18,43 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
|
||||
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
|
||||
stores information pertinent to the user's relationship with the Provider.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
asn = ASNField(blank=True, null=True, verbose_name='ASN')
|
||||
account = models.CharField(max_length=30, blank=True, verbose_name='Account number')
|
||||
portal_url = models.URLField(blank=True, verbose_name='Portal')
|
||||
noc_contact = models.TextField(blank=True, verbose_name='NOC contact')
|
||||
admin_contact = models.TextField(blank=True, verbose_name='Admin contact')
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
asn = ASNField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='ASN'
|
||||
)
|
||||
account = models.CharField(
|
||||
max_length=30,
|
||||
blank=True,
|
||||
verbose_name='Account number'
|
||||
)
|
||||
portal_url = models.URLField(
|
||||
blank=True,
|
||||
verbose_name='Portal'
|
||||
)
|
||||
noc_contact = models.TextField(
|
||||
blank=True,
|
||||
verbose_name='NOC contact'
|
||||
)
|
||||
admin_contact = models.TextField(
|
||||
blank=True,
|
||||
verbose_name='Admin contact'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
||||
|
||||
@ -59,8 +86,13 @@ class CircuitType(models.Model):
|
||||
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
|
||||
"Long Haul," "Metro," or "Out-of-Band".
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
|
||||
@ -87,16 +119,52 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
||||
circuits. Each circuit is also assigned a CircuitType and a Site. A Circuit may be terminated to a specific device
|
||||
interface, but this is not required. Circuit port speed and commit rate are measured in Kbps.
|
||||
"""
|
||||
cid = models.CharField(max_length=50, verbose_name='Circuit ID')
|
||||
provider = models.ForeignKey('Provider', related_name='circuits', on_delete=models.PROTECT)
|
||||
type = models.ForeignKey('CircuitType', related_name='circuits', on_delete=models.PROTECT)
|
||||
status = models.PositiveSmallIntegerField(choices=CIRCUIT_STATUS_CHOICES, default=CIRCUIT_STATUS_ACTIVE)
|
||||
tenant = models.ForeignKey(Tenant, related_name='circuits', blank=True, null=True, on_delete=models.PROTECT)
|
||||
install_date = models.DateField(blank=True, null=True, verbose_name='Date installed')
|
||||
commit_rate = models.PositiveIntegerField(blank=True, null=True, verbose_name='Commit rate (Kbps)')
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
cid = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Circuit ID'
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
to='circuits.Provider',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuits'
|
||||
)
|
||||
type = models.ForeignKey(
|
||||
to='CircuitType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuits'
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
default=CIRCUIT_STATUS_ACTIVE
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuits',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
install_date = models.DateField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Date installed'
|
||||
)
|
||||
commit_rate = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Commit rate (Kbps)')
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = [
|
||||
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
||||
@ -145,19 +213,47 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CircuitTermination(models.Model):
|
||||
circuit = models.ForeignKey('Circuit', related_name='terminations', on_delete=models.CASCADE)
|
||||
term_side = models.CharField(max_length=1, choices=TERM_SIDE_CHOICES, verbose_name='Termination')
|
||||
site = models.ForeignKey('dcim.Site', related_name='circuit_terminations', on_delete=models.PROTECT)
|
||||
interface = models.OneToOneField(
|
||||
'dcim.Interface', related_name='circuit_termination', blank=True, null=True, on_delete=models.PROTECT
|
||||
circuit = models.ForeignKey(
|
||||
to='circuits.Circuit',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='terminations'
|
||||
)
|
||||
term_side = models.CharField(
|
||||
max_length=1,
|
||||
choices=TERM_SIDE_CHOICES,
|
||||
verbose_name='Termination'
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuit_terminations'
|
||||
)
|
||||
interface = models.OneToOneField(
|
||||
to='dcim.Interface',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='circuit_termination',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
port_speed = models.PositiveIntegerField(
|
||||
verbose_name='Port speed (Kbps)'
|
||||
)
|
||||
port_speed = models.PositiveIntegerField(verbose_name='Port speed (Kbps)')
|
||||
upstream_speed = models.PositiveIntegerField(
|
||||
blank=True, null=True, verbose_name='Upstream speed (Kbps)',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Upstream speed (Kbps)',
|
||||
help_text='Upstream speed, if different from port speed'
|
||||
)
|
||||
xconnect_id = models.CharField(max_length=50, blank=True, verbose_name='Cross-connect ID')
|
||||
pp_info = models.CharField(max_length=100, blank=True, verbose_name='Patch panel/port(s)')
|
||||
xconnect_id = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
verbose_name='Cross-connect ID'
|
||||
)
|
||||
pp_info = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
verbose_name='Patch panel/port(s)'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['circuit', 'term_side']
|
||||
|
@ -17,9 +17,8 @@ from mptt.models import MPTTModel, TreeForeignKey
|
||||
from timezone_field import TimeZoneField
|
||||
|
||||
from circuits.models import Circuit
|
||||
from extras.models import CustomFieldModel, CustomFieldValue, ImageAttachment
|
||||
from extras.models import CustomFieldModel
|
||||
from extras.rpc import RPC_CLIENTS
|
||||
from tenancy.models import Tenant
|
||||
from utilities.fields import ColorField, NullableCharField
|
||||
from utilities.managers import NaturalOrderByManager
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
@ -38,10 +37,20 @@ class Region(MPTTModel):
|
||||
Sites can be grouped within geographic Regions.
|
||||
"""
|
||||
parent = TreeForeignKey(
|
||||
'self', null=True, blank=True, related_name='children', db_index=True, on_delete=models.CASCADE
|
||||
to='self',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='children',
|
||||
blank=True,
|
||||
null=True,
|
||||
db_index=True
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
|
||||
csv_headers = ['name', 'slug', 'parent']
|
||||
|
||||
@ -78,23 +87,78 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
A Site represents a geographic location within a network; typically a building or campus. The optional facility
|
||||
field can be used to include an external designation, such as a data center name (e.g. Equinix SV6).
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
status = models.PositiveSmallIntegerField(choices=SITE_STATUS_CHOICES, default=SITE_STATUS_ACTIVE)
|
||||
region = models.ForeignKey('Region', related_name='sites', blank=True, null=True, on_delete=models.SET_NULL)
|
||||
tenant = models.ForeignKey(Tenant, related_name='sites', blank=True, null=True, on_delete=models.PROTECT)
|
||||
facility = models.CharField(max_length=50, blank=True)
|
||||
asn = ASNField(blank=True, null=True, verbose_name='ASN')
|
||||
time_zone = TimeZoneField(blank=True)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
physical_address = models.CharField(max_length=200, blank=True)
|
||||
shipping_address = models.CharField(max_length=200, blank=True)
|
||||
contact_name = models.CharField(max_length=50, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True)
|
||||
contact_email = models.EmailField(blank=True, verbose_name="Contact E-mail")
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
images = GenericRelation(ImageAttachment)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=SITE_STATUS_CHOICES,
|
||||
default=SITE_STATUS_ACTIVE
|
||||
)
|
||||
region = models.ForeignKey(
|
||||
to='dcim.Region',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='sites',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='sites',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
facility = models.CharField(
|
||||
max_length=50,
|
||||
blank=True
|
||||
)
|
||||
asn = ASNField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='ASN'
|
||||
)
|
||||
time_zone = TimeZoneField(
|
||||
blank=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
physical_address = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
shipping_address = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
contact_name = models.CharField(
|
||||
max_length=50,
|
||||
blank=True
|
||||
)
|
||||
contact_phone = models.CharField(
|
||||
max_length=20,
|
||||
blank=True
|
||||
)
|
||||
contact_email = models.EmailField(
|
||||
blank=True,
|
||||
verbose_name='Contact E-mail'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
images = GenericRelation(
|
||||
to='extras.ImageAttachment'
|
||||
)
|
||||
|
||||
objects = SiteManager()
|
||||
|
||||
@ -171,9 +235,15 @@ class RackGroup(models.Model):
|
||||
example, if a Site spans a corporate campus, a RackGroup might be defined to represent each building within that
|
||||
campus. If a Site instead represents a single building, a RackGroup might represent a single room or floor.
|
||||
"""
|
||||
name = models.CharField(max_length=50)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
slug = models.SlugField()
|
||||
site = models.ForeignKey('Site', related_name='rack_groups', on_delete=models.CASCADE)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='rack_groups'
|
||||
)
|
||||
|
||||
csv_headers = ['site', 'name', 'slug']
|
||||
|
||||
@ -203,8 +273,13 @@ class RackRole(models.Model):
|
||||
"""
|
||||
Racks can be organized by functional role, similar to Devices.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
color = ColorField()
|
||||
|
||||
csv_headers = ['name', 'slug', 'color']
|
||||
@ -238,23 +313,79 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
|
||||
Each Rack is assigned to a Site and (optionally) a RackGroup.
|
||||
"""
|
||||
name = models.CharField(max_length=50)
|
||||
facility_id = NullableCharField(max_length=50, blank=True, null=True, verbose_name='Facility ID')
|
||||
site = models.ForeignKey('Site', related_name='racks', on_delete=models.PROTECT)
|
||||
group = models.ForeignKey('RackGroup', related_name='racks', blank=True, null=True, on_delete=models.SET_NULL)
|
||||
tenant = models.ForeignKey(Tenant, blank=True, null=True, related_name='racks', on_delete=models.PROTECT)
|
||||
role = models.ForeignKey('RackRole', related_name='racks', blank=True, null=True, on_delete=models.PROTECT)
|
||||
serial = models.CharField(max_length=50, blank=True, verbose_name='Serial number')
|
||||
type = models.PositiveSmallIntegerField(choices=RACK_TYPE_CHOICES, blank=True, null=True, verbose_name='Type')
|
||||
width = models.PositiveSmallIntegerField(choices=RACK_WIDTH_CHOICES, default=RACK_WIDTH_19IN, verbose_name='Width',
|
||||
help_text='Rail-to-rail width')
|
||||
u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)',
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)])
|
||||
desc_units = models.BooleanField(default=False, verbose_name='Descending units',
|
||||
help_text='Units are numbered top-to-bottom')
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
images = GenericRelation(ImageAttachment)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
facility_id = NullableCharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Facility ID'
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='racks'
|
||||
)
|
||||
group = models.ForeignKey(
|
||||
to='dcim.RackGroup',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='racks',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='racks',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='dcim.RackRole',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='racks',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
serial = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
verbose_name='Serial number'
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=RACK_TYPE_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Type'
|
||||
)
|
||||
width = models.PositiveSmallIntegerField(
|
||||
choices=RACK_WIDTH_CHOICES,
|
||||
default=RACK_WIDTH_19IN,
|
||||
verbose_name='Width',
|
||||
help_text='Rail-to-rail width'
|
||||
)
|
||||
u_height = models.PositiveSmallIntegerField(
|
||||
default=42,
|
||||
verbose_name='Height (U)',
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)]
|
||||
)
|
||||
desc_units = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Descending units',
|
||||
help_text='Units are numbered top-to-bottom'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
images = GenericRelation(
|
||||
to='extras.ImageAttachment'
|
||||
)
|
||||
|
||||
objects = RackManager()
|
||||
|
||||
@ -438,12 +569,31 @@ class RackReservation(models.Model):
|
||||
"""
|
||||
One or more reserved units within a Rack.
|
||||
"""
|
||||
rack = models.ForeignKey('Rack', related_name='reservations', on_delete=models.CASCADE)
|
||||
units = ArrayField(models.PositiveSmallIntegerField())
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
tenant = models.ForeignKey(Tenant, blank=True, null=True, related_name='rackreservations', on_delete=models.PROTECT)
|
||||
user = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
description = models.CharField(max_length=100)
|
||||
rack = models.ForeignKey(
|
||||
to='dcim.Rack',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='reservations'
|
||||
)
|
||||
units = ArrayField(
|
||||
base_field=models.PositiveSmallIntegerField()
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='rackreservations',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
to=User,
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['created']
|
||||
@ -496,8 +646,13 @@ class Manufacturer(models.Model):
|
||||
"""
|
||||
A Manufacturer represents a company which produces hardware devices; for example, Juniper or Dell.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
|
||||
@ -533,27 +688,63 @@ class DeviceType(models.Model, CustomFieldModel):
|
||||
When a new Device of this type is created, the appropriate console, power, and interface objects (as defined by the
|
||||
DeviceType) are automatically created as well.
|
||||
"""
|
||||
manufacturer = models.ForeignKey('Manufacturer', related_name='device_types', on_delete=models.PROTECT)
|
||||
model = models.CharField(max_length=50)
|
||||
manufacturer = models.ForeignKey(
|
||||
to='dcim.Manufacturer',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='device_types'
|
||||
)
|
||||
model = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
slug = models.SlugField()
|
||||
part_number = models.CharField(max_length=50, blank=True, help_text="Discrete part number (optional)")
|
||||
u_height = models.PositiveSmallIntegerField(verbose_name='Height (U)', default=1)
|
||||
is_full_depth = models.BooleanField(default=True, verbose_name="Is full depth",
|
||||
help_text="Device consumes both front and rear rack faces")
|
||||
interface_ordering = models.PositiveSmallIntegerField(choices=IFACE_ORDERING_CHOICES,
|
||||
default=IFACE_ORDERING_POSITION)
|
||||
is_console_server = models.BooleanField(default=False, verbose_name='Is a console server',
|
||||
help_text="This type of device has console server ports")
|
||||
is_pdu = models.BooleanField(default=False, verbose_name='Is a PDU',
|
||||
help_text="This type of device has power outlets")
|
||||
is_network_device = models.BooleanField(default=True, verbose_name='Is a network device',
|
||||
help_text="This type of device has network interfaces")
|
||||
subdevice_role = models.NullBooleanField(default=None, verbose_name='Parent/child status',
|
||||
part_number = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
help_text='Discrete part number (optional)'
|
||||
)
|
||||
u_height = models.PositiveSmallIntegerField(
|
||||
default=1,
|
||||
verbose_name='Height (U)'
|
||||
)
|
||||
is_full_depth = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name='Is full depth',
|
||||
help_text='Device consumes both front and rear rack faces'
|
||||
)
|
||||
interface_ordering = models.PositiveSmallIntegerField(
|
||||
choices=IFACE_ORDERING_CHOICES,
|
||||
default=IFACE_ORDERING_POSITION
|
||||
)
|
||||
is_console_server = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Is a console server',
|
||||
help_text='This type of device has console server ports'
|
||||
)
|
||||
is_pdu = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Is a PDU',
|
||||
help_text='This type of device has power outlets'
|
||||
)
|
||||
is_network_device = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name='Is a network device',
|
||||
help_text='This type of device has network interfaces'
|
||||
)
|
||||
subdevice_role = models.NullBooleanField(
|
||||
default=None,
|
||||
verbose_name='Parent/child status',
|
||||
choices=SUBDEVICE_ROLE_CHOICES,
|
||||
help_text="Parent devices house child devices in device bays. Select "
|
||||
"\"None\" if this device type is neither a parent nor a child.")
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
help_text='Parent devices house child devices in device bays. Select '
|
||||
'"None" if this device type is neither a parent nor a child.'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = [
|
||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server',
|
||||
@ -658,8 +849,14 @@ class ConsolePortTemplate(models.Model):
|
||||
"""
|
||||
A template for a ConsolePort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='console_port_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='console_port_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
@ -674,8 +871,14 @@ class ConsoleServerPortTemplate(models.Model):
|
||||
"""
|
||||
A template for a ConsoleServerPort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='cs_port_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='cs_port_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
@ -690,8 +893,14 @@ class PowerPortTemplate(models.Model):
|
||||
"""
|
||||
A template for a PowerPort to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='power_port_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='power_port_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
@ -706,8 +915,14 @@ class PowerOutletTemplate(models.Model):
|
||||
"""
|
||||
A template for a PowerOutlet to be created for a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='power_outlet_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='power_outlet_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
@ -722,10 +937,22 @@ class InterfaceTemplate(models.Model):
|
||||
"""
|
||||
A template for a physical data interface on a new Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='interface_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=64)
|
||||
form_factor = models.PositiveSmallIntegerField(choices=IFACE_FF_CHOICES, default=IFACE_FF_10GE_SFP_PLUS)
|
||||
mgmt_only = models.BooleanField(default=False, verbose_name='Management only')
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='interface_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
form_factor = models.PositiveSmallIntegerField(
|
||||
choices=IFACE_FF_CHOICES,
|
||||
default=IFACE_FF_10GE_SFP_PLUS
|
||||
)
|
||||
mgmt_only = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Management only'
|
||||
)
|
||||
|
||||
objects = InterfaceQuerySet.as_manager()
|
||||
|
||||
@ -742,8 +969,14 @@ class DeviceBayTemplate(models.Model):
|
||||
"""
|
||||
A template for a DeviceBay to be created for a new parent Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='device_bay_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='device_bay_templates'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
@ -764,13 +997,18 @@ class DeviceRole(models.Model):
|
||||
color to be used when displaying rack elevations. The vm_role field determines whether the role is applicable to
|
||||
virtual machines as well.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
color = ColorField()
|
||||
vm_role = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name="VM Role",
|
||||
help_text="Virtual machines may be assigned to this role"
|
||||
verbose_name='VM Role',
|
||||
help_text='Virtual machines may be assigned to this role'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'color', 'vm_role']
|
||||
@ -800,27 +1038,32 @@ class Platform(models.Model):
|
||||
NetBox uses Platforms to determine how to interact with devices when pulling inventory data or other information by
|
||||
specifying a NAPALM driver.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
manufacturer = models.ForeignKey(
|
||||
to='Manufacturer',
|
||||
to='dcim.Manufacturer',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='platforms',
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text="Optionally limit this platform to devices of a certain manufacturer"
|
||||
help_text='Optionally limit this platform to devices of a certain manufacturer'
|
||||
)
|
||||
napalm_driver = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
verbose_name='NAPALM driver',
|
||||
help_text="The name of the NAPALM driver to use when interacting with devices"
|
||||
help_text='The name of the NAPALM driver to use when interacting with devices'
|
||||
)
|
||||
rpc_client = models.CharField(
|
||||
max_length=30,
|
||||
choices=RPC_CLIENT_CHOICES,
|
||||
blank=True,
|
||||
verbose_name="Legacy RPC client"
|
||||
verbose_name='Legacy RPC client'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'manufacturer', 'napalm_driver']
|
||||
@ -862,30 +1105,93 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
by the component templates assigned to its DeviceType. Components can also be added, modified, or deleted after the
|
||||
creation of a Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='instances', on_delete=models.PROTECT)
|
||||
device_role = models.ForeignKey('DeviceRole', related_name='devices', on_delete=models.PROTECT)
|
||||
tenant = models.ForeignKey(Tenant, blank=True, null=True, related_name='devices', on_delete=models.PROTECT)
|
||||
platform = models.ForeignKey('Platform', related_name='devices', blank=True, null=True, on_delete=models.SET_NULL)
|
||||
name = NullableCharField(max_length=64, blank=True, null=True, unique=True)
|
||||
serial = models.CharField(max_length=50, blank=True, verbose_name='Serial number')
|
||||
device_type = models.ForeignKey(
|
||||
to='dcim.DeviceType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='instances'
|
||||
)
|
||||
device_role = models.ForeignKey(
|
||||
to='dcim.DeviceRole',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='devices'
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='devices',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
platform = models.ForeignKey(
|
||||
to='dcim.Platform',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='devices',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
name = NullableCharField(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
null=True,
|
||||
unique=True
|
||||
)
|
||||
serial = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
verbose_name='Serial number'
|
||||
)
|
||||
asset_tag = NullableCharField(
|
||||
max_length=50, blank=True, null=True, unique=True, verbose_name='Asset tag',
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
unique=True,
|
||||
verbose_name='Asset tag',
|
||||
help_text='A unique tag used to identify this device'
|
||||
)
|
||||
site = models.ForeignKey('Site', related_name='devices', on_delete=models.PROTECT)
|
||||
rack = models.ForeignKey('Rack', related_name='devices', blank=True, null=True, on_delete=models.PROTECT)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='devices'
|
||||
)
|
||||
rack = models.ForeignKey(
|
||||
to='dcim.Rack',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='devices',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
position = models.PositiveSmallIntegerField(
|
||||
blank=True, null=True, validators=[MinValueValidator(1)], verbose_name='Position (U)',
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinValueValidator(1)],
|
||||
verbose_name='Position (U)',
|
||||
help_text='The lowest-numbered unit occupied by the device'
|
||||
)
|
||||
face = models.PositiveSmallIntegerField(blank=True, null=True, choices=RACK_FACE_CHOICES, verbose_name='Rack face')
|
||||
status = models.PositiveSmallIntegerField(choices=DEVICE_STATUS_CHOICES, default=DEVICE_STATUS_ACTIVE, verbose_name='Status')
|
||||
face = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
choices=RACK_FACE_CHOICES,
|
||||
verbose_name='Rack face'
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=DEVICE_STATUS_CHOICES,
|
||||
default=DEVICE_STATUS_ACTIVE,
|
||||
verbose_name='Status'
|
||||
)
|
||||
primary_ip4 = models.OneToOneField(
|
||||
'ipam.IPAddress', related_name='primary_ip4_for', on_delete=models.SET_NULL, blank=True, null=True,
|
||||
to='ipam.IPAddress',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='primary_ip4_for',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Primary IPv4'
|
||||
)
|
||||
primary_ip6 = models.OneToOneField(
|
||||
'ipam.IPAddress', related_name='primary_ip6_for', on_delete=models.SET_NULL, blank=True, null=True,
|
||||
to='ipam.IPAddress',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='primary_ip6_for',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Primary IPv6'
|
||||
)
|
||||
cluster = models.ForeignKey(
|
||||
@ -912,9 +1218,17 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
null=True,
|
||||
validators=[MaxValueValidator(255)]
|
||||
)
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
images = GenericRelation(ImageAttachment)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
images = GenericRelation(
|
||||
to='extras.ImageAttachment'
|
||||
)
|
||||
|
||||
objects = DeviceManager()
|
||||
|
||||
@ -1169,11 +1483,26 @@ class ConsolePort(models.Model):
|
||||
"""
|
||||
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='console_ports', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
cs_port = models.OneToOneField('ConsoleServerPort', related_name='connected_console', on_delete=models.SET_NULL,
|
||||
verbose_name='Console server port', blank=True, null=True)
|
||||
connection_status = models.NullBooleanField(choices=CONNECTION_STATUS_CHOICES, default=CONNECTION_STATUS_CONNECTED)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='console_ports'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
cs_port = models.OneToOneField(
|
||||
to='dcim.ConsoleServerPort',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='connected_console',
|
||||
verbose_name='Console server port',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
connection_status = models.NullBooleanField(
|
||||
choices=CONNECTION_STATUS_CHOICES,
|
||||
default=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
csv_headers = ['console_server', 'cs_port', 'device', 'console_port', 'connection_status']
|
||||
|
||||
@ -1216,8 +1545,14 @@ class ConsoleServerPort(models.Model):
|
||||
"""
|
||||
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='cs_ports', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='cs_ports'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
objects = ConsoleServerPortManager()
|
||||
|
||||
@ -1251,11 +1586,25 @@ class PowerPort(models.Model):
|
||||
"""
|
||||
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='power_ports', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
power_outlet = models.OneToOneField('PowerOutlet', related_name='connected_port', on_delete=models.SET_NULL,
|
||||
blank=True, null=True)
|
||||
connection_status = models.NullBooleanField(choices=CONNECTION_STATUS_CHOICES, default=CONNECTION_STATUS_CONNECTED)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='power_ports'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
power_outlet = models.OneToOneField(
|
||||
to='dcim.PowerOutlet',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='connected_port',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
connection_status = models.NullBooleanField(
|
||||
choices=CONNECTION_STATUS_CHOICES,
|
||||
default=CONNECTION_STATUS_CONNECTED
|
||||
)
|
||||
|
||||
csv_headers = ['pdu', 'power_outlet', 'device', 'power_port', 'connection_status']
|
||||
|
||||
@ -1298,8 +1647,14 @@ class PowerOutlet(models.Model):
|
||||
"""
|
||||
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='power_outlets', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='power_outlets'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
objects = PowerOutletManager()
|
||||
|
||||
@ -1356,17 +1711,35 @@ class Interface(models.Model):
|
||||
blank=True,
|
||||
verbose_name='Parent LAG'
|
||||
)
|
||||
name = models.CharField(max_length=64)
|
||||
form_factor = models.PositiveSmallIntegerField(choices=IFACE_FF_CHOICES, default=IFACE_FF_10GE_SFP_PLUS)
|
||||
enabled = models.BooleanField(default=True)
|
||||
mac_address = MACAddressField(null=True, blank=True, verbose_name='MAC Address')
|
||||
mtu = models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='MTU')
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
form_factor = models.PositiveSmallIntegerField(
|
||||
choices=IFACE_FF_CHOICES,
|
||||
default=IFACE_FF_10GE_SFP_PLUS
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
default=True
|
||||
)
|
||||
mac_address = MACAddressField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name='MAC Address'
|
||||
)
|
||||
mtu = models.PositiveSmallIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='MTU'
|
||||
)
|
||||
mgmt_only = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='OOB Management',
|
||||
help_text="This interface is used only for out-of-band management"
|
||||
help_text='This interface is used only for out-of-band management'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
mode = models.PositiveSmallIntegerField(
|
||||
choices=IFACE_MODE_CHOICES,
|
||||
blank=True,
|
||||
@ -1375,16 +1748,16 @@ class Interface(models.Model):
|
||||
untagged_vlan = models.ForeignKey(
|
||||
to='ipam.VLAN',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='interfaces_as_untagged',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name='Untagged VLAN',
|
||||
related_name='interfaces_as_untagged'
|
||||
verbose_name='Untagged VLAN'
|
||||
)
|
||||
tagged_vlans = models.ManyToManyField(
|
||||
to='ipam.VLAN',
|
||||
related_name='interfaces_as_tagged',
|
||||
blank=True,
|
||||
verbose_name='Tagged VLANs',
|
||||
related_name='interfaces_as_tagged'
|
||||
verbose_name='Tagged VLANs'
|
||||
)
|
||||
|
||||
objects = InterfaceQuerySet.as_manager()
|
||||
@ -1525,10 +1898,21 @@ class InterfaceConnection(models.Model):
|
||||
An InterfaceConnection represents a symmetrical, one-to-one connection between two Interfaces. There is no
|
||||
significant difference between the interface_a and interface_b fields.
|
||||
"""
|
||||
interface_a = models.OneToOneField('Interface', related_name='connected_as_a', on_delete=models.CASCADE)
|
||||
interface_b = models.OneToOneField('Interface', related_name='connected_as_b', on_delete=models.CASCADE)
|
||||
connection_status = models.BooleanField(choices=CONNECTION_STATUS_CHOICES, default=CONNECTION_STATUS_CONNECTED,
|
||||
verbose_name='Status')
|
||||
interface_a = models.OneToOneField(
|
||||
to='dcim.Interface',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='connected_as_a'
|
||||
)
|
||||
interface_b = models.OneToOneField(
|
||||
to='dcim.Interface',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='connected_as_b'
|
||||
)
|
||||
connection_status = models.BooleanField(
|
||||
choices=CONNECTION_STATUS_CHOICES,
|
||||
default=CONNECTION_STATUS_CONNECTED,
|
||||
verbose_name='Status'
|
||||
)
|
||||
|
||||
csv_headers = ['device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status']
|
||||
|
||||
@ -1560,10 +1944,22 @@ class DeviceBay(models.Model):
|
||||
"""
|
||||
An empty space within a Device which can house a child device
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='device_bays', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50, verbose_name='Name')
|
||||
installed_device = models.OneToOneField('Device', related_name='parent_bay', on_delete=models.SET_NULL, blank=True,
|
||||
null=True)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='device_bays'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Name'
|
||||
)
|
||||
installed_device = models.OneToOneField(
|
||||
to='dcim.Device',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='parent_bay',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
@ -1598,20 +1994,55 @@ class InventoryItem(models.Model):
|
||||
An InventoryItem represents a serialized piece of hardware within a Device, such as a line card or power supply.
|
||||
InventoryItems are used only for inventory purposes.
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='inventory_items', on_delete=models.CASCADE)
|
||||
parent = models.ForeignKey('self', related_name='child_items', blank=True, null=True, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50, verbose_name='Name')
|
||||
manufacturer = models.ForeignKey(
|
||||
'Manufacturer', models.PROTECT, related_name='inventory_items', blank=True, null=True
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='inventory_items'
|
||||
)
|
||||
parent = models.ForeignKey(
|
||||
to='self',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='child_items',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Name'
|
||||
)
|
||||
manufacturer = models.ForeignKey(
|
||||
to='dcim.Manufacturer',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='inventory_items',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
part_id = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Part ID',
|
||||
blank=True
|
||||
)
|
||||
serial = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Serial number',
|
||||
blank=True
|
||||
)
|
||||
part_id = models.CharField(max_length=50, verbose_name='Part ID', blank=True)
|
||||
serial = models.CharField(max_length=50, verbose_name='Serial number', blank=True)
|
||||
asset_tag = NullableCharField(
|
||||
max_length=50, blank=True, null=True, unique=True, verbose_name='Asset tag',
|
||||
max_length=50,
|
||||
unique=True,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='Asset tag',
|
||||
help_text='A unique tag used to identify this item'
|
||||
)
|
||||
discovered = models.BooleanField(default=False, verbose_name='Discovered')
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
discovered = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Discovered'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
|
||||
csv_headers = [
|
||||
'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
||||
|
@ -73,7 +73,8 @@ class CustomField(models.Model):
|
||||
label = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
help_text='Name of the field as displayed to users (if not provided, the field\'s name will be used)'
|
||||
help_text='Name of the field as displayed to users (if not provided, '
|
||||
'the field\'s name will be used)'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
@ -81,17 +82,20 @@ class CustomField(models.Model):
|
||||
)
|
||||
required = models.BooleanField(
|
||||
default=False,
|
||||
help_text='If true, this field is required when creating new objects or editing an existing object.'
|
||||
help_text='If true, this field is required when creating new objects '
|
||||
'or editing an existing object.'
|
||||
)
|
||||
filter_logic = models.PositiveSmallIntegerField(
|
||||
choices=CF_FILTER_CHOICES,
|
||||
default=CF_FILTER_LOOSE,
|
||||
help_text="Loose matches any instance of a given string; exact matches the entire field."
|
||||
help_text='Loose matches any instance of a given string; exact '
|
||||
'matches the entire field.'
|
||||
)
|
||||
default = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
help_text='Default value for the field. Use "true" or "false" for booleans. N/A for selection fields.'
|
||||
help_text='Default value for the field. Use "true" or "false" for '
|
||||
'booleans. N/A for selection fields.'
|
||||
)
|
||||
weight = models.PositiveSmallIntegerField(
|
||||
default=100,
|
||||
@ -143,11 +147,24 @@ class CustomField(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomFieldValue(models.Model):
|
||||
field = models.ForeignKey('CustomField', related_name='values', on_delete=models.CASCADE)
|
||||
obj_type = models.ForeignKey(ContentType, related_name='+', on_delete=models.PROTECT)
|
||||
field = models.ForeignKey(
|
||||
to='extras.CustomField',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='values'
|
||||
)
|
||||
obj_type = models.ForeignKey(
|
||||
to=ContentType,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+'
|
||||
)
|
||||
obj_id = models.PositiveIntegerField()
|
||||
obj = GenericForeignKey('obj_type', 'obj_id')
|
||||
serialized_value = models.CharField(max_length=255)
|
||||
obj = GenericForeignKey(
|
||||
ct_field='obj_type',
|
||||
fk_field='obj_id'
|
||||
)
|
||||
serialized_value = models.CharField(
|
||||
max_length=255
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['obj_type', 'obj_id']
|
||||
@ -174,10 +191,19 @@ class CustomFieldValue(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomFieldChoice(models.Model):
|
||||
field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT},
|
||||
on_delete=models.CASCADE)
|
||||
value = models.CharField(max_length=100)
|
||||
weight = models.PositiveSmallIntegerField(default=100, help_text="Higher weights appear lower in the list")
|
||||
field = models.ForeignKey(
|
||||
to='extras.CustomField',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='choices',
|
||||
limit_choices_to={'type': CF_TYPE_SELECT}
|
||||
)
|
||||
value = models.CharField(
|
||||
max_length=100
|
||||
)
|
||||
weight = models.PositiveSmallIntegerField(
|
||||
default=100,
|
||||
help_text='Higher weights appear lower in the list'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['field', 'weight', 'value']
|
||||
@ -203,11 +229,24 @@ class CustomFieldChoice(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Graph(models.Model):
|
||||
type = models.PositiveSmallIntegerField(choices=GRAPH_TYPE_CHOICES)
|
||||
weight = models.PositiveSmallIntegerField(default=1000)
|
||||
name = models.CharField(max_length=100, verbose_name='Name')
|
||||
source = models.CharField(max_length=500, verbose_name='Source URL')
|
||||
link = models.URLField(verbose_name='Link URL', blank=True)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=GRAPH_TYPE_CHOICES
|
||||
)
|
||||
weight = models.PositiveSmallIntegerField(
|
||||
default=1000
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name='Name'
|
||||
)
|
||||
source = models.CharField(
|
||||
max_length=500,
|
||||
verbose_name='Source URL'
|
||||
)
|
||||
link = models.URLField(
|
||||
blank=True,
|
||||
verbose_name='Link URL'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['type', 'weight', 'name']
|
||||
@ -233,13 +272,26 @@ class Graph(models.Model):
|
||||
@python_2_unicode_compatible
|
||||
class ExportTemplate(models.Model):
|
||||
content_type = models.ForeignKey(
|
||||
ContentType, limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS}, on_delete=models.CASCADE
|
||||
to=ContentType,
|
||||
on_delete=models.CASCADE,
|
||||
limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS}
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=100
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
name = models.CharField(max_length=100)
|
||||
description = models.CharField(max_length=200, blank=True)
|
||||
template_code = models.TextField()
|
||||
mime_type = models.CharField(max_length=15, blank=True)
|
||||
file_extension = models.CharField(max_length=15, blank=True)
|
||||
mime_type = models.CharField(
|
||||
max_length=15,
|
||||
blank=True
|
||||
)
|
||||
file_extension = models.CharField(
|
||||
max_length=15,
|
||||
blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['content_type', 'name']
|
||||
@ -278,25 +330,35 @@ class ExportTemplate(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class TopologyMap(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=TOPOLOGYMAP_TYPE_CHOICES,
|
||||
default=TOPOLOGYMAP_TYPE_NETWORK
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='topology_maps',
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.CASCADE
|
||||
null=True
|
||||
)
|
||||
device_patterns = models.TextField(
|
||||
help_text="Identify devices to include in the diagram using regular expressions, one per line. Each line will "
|
||||
"result in a new tier of the drawing. Separate multiple regexes within a line using semicolons. "
|
||||
"Devices will be rendered in the order they are defined."
|
||||
help_text='Identify devices to include in the diagram using regular '
|
||||
'expressions, one per line. Each line will result in a new '
|
||||
'tier of the drawing. Separate multiple regexes within a '
|
||||
'line using semicolons. Devices will be rendered in the '
|
||||
'order they are defined.'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
@ -432,14 +494,29 @@ class ImageAttachment(models.Model):
|
||||
"""
|
||||
An uploaded image which is associated with an object.
|
||||
"""
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
content_type = models.ForeignKey(
|
||||
to=ContentType,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
object_id = models.PositiveIntegerField()
|
||||
parent = GenericForeignKey('content_type', 'object_id')
|
||||
image = models.ImageField(upload_to=image_upload, height_field='image_height', width_field='image_width')
|
||||
parent = GenericForeignKey(
|
||||
ct_field='content_type',
|
||||
fk_field='object_id'
|
||||
)
|
||||
image = models.ImageField(
|
||||
upload_to=image_upload,
|
||||
height_field='image_height',
|
||||
width_field='image_width'
|
||||
)
|
||||
image_height = models.PositiveSmallIntegerField()
|
||||
image_width = models.PositiveSmallIntegerField()
|
||||
name = models.CharField(max_length=50, blank=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
blank=True
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
@ -482,9 +559,20 @@ class ReportResult(models.Model):
|
||||
"""
|
||||
This model stores the results from running a user-defined report.
|
||||
"""
|
||||
report = models.CharField(max_length=255, unique=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='+', blank=True, null=True)
|
||||
report = models.CharField(
|
||||
max_length=255,
|
||||
unique=True
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
to=User,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='+',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
failed = models.BooleanField()
|
||||
data = JSONField()
|
||||
|
||||
@ -544,12 +632,29 @@ class UserAction(models.Model):
|
||||
"""
|
||||
A record of an action (add, edit, or delete) performed on an object by a User.
|
||||
"""
|
||||
time = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
user = models.ForeignKey(User, related_name='actions', on_delete=models.CASCADE)
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.PositiveIntegerField(blank=True, null=True)
|
||||
action = models.PositiveSmallIntegerField(choices=ACTION_CHOICES)
|
||||
message = models.TextField(blank=True)
|
||||
time = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
editable=False
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
to=User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='actions'
|
||||
)
|
||||
content_type = models.ForeignKey(
|
||||
to=ContentType,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
object_id = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
action = models.PositiveSmallIntegerField(
|
||||
choices=ACTION_CHOICES
|
||||
)
|
||||
message = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
|
||||
objects = UserActionManager()
|
||||
|
||||
|
@ -12,8 +12,7 @@ from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from dcim.models import Interface
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
from tenancy.models import Tenant
|
||||
from extras.models import CustomFieldModel
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
from .constants import *
|
||||
from .fields import IPNetworkField, IPAddressField
|
||||
@ -27,13 +26,35 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
|
||||
table). Prefixes and IPAddresses can optionally be assigned to VRFs. (Prefixes and IPAddresses not assigned to a VRF
|
||||
are said to exist in the "global" table.)
|
||||
"""
|
||||
name = models.CharField(max_length=50)
|
||||
rd = models.CharField(max_length=21, unique=True, verbose_name='Route distinguisher')
|
||||
tenant = models.ForeignKey(Tenant, related_name='vrfs', blank=True, null=True, on_delete=models.PROTECT)
|
||||
enforce_unique = models.BooleanField(default=True, verbose_name='Enforce unique space',
|
||||
help_text="Prevent duplicate prefixes/IP addresses within this VRF")
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
rd = models.CharField(
|
||||
max_length=21,
|
||||
unique=True,
|
||||
verbose_name='Route distinguisher'
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vrfs',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
enforce_unique = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name='Enforce unique space',
|
||||
help_text='Prevent duplicate prefixes/IP addresses within this VRF'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
||||
|
||||
@ -70,10 +91,18 @@ class RIR(models.Model):
|
||||
A Regional Internet Registry (RIR) is responsible for the allocation of a large portion of the global IP address
|
||||
space. This can be an organization like ARIN or RIPE, or a governing standard such as RFC 1918.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
is_private = models.BooleanField(default=False, verbose_name='Private',
|
||||
help_text='IP space managed by this RIR is considered private')
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
is_private = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Private',
|
||||
help_text='IP space managed by this RIR is considered private'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'is_private']
|
||||
|
||||
@ -102,12 +131,29 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
||||
An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
|
||||
the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR.
|
||||
"""
|
||||
family = models.PositiveSmallIntegerField(choices=AF_CHOICES)
|
||||
family = models.PositiveSmallIntegerField(
|
||||
choices=AF_CHOICES
|
||||
)
|
||||
prefix = IPNetworkField()
|
||||
rir = models.ForeignKey('RIR', related_name='aggregates', on_delete=models.PROTECT, verbose_name='RIR')
|
||||
date_added = models.DateField(blank=True, null=True)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
rir = models.ForeignKey(
|
||||
to='ipam.RIR',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='aggregates',
|
||||
verbose_name='RIR'
|
||||
)
|
||||
date_added = models.DateField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = ['prefix', 'rir', 'date_added', 'description']
|
||||
|
||||
@ -178,9 +224,16 @@ class Role(models.Model):
|
||||
A Role represents the functional role of a Prefix or VLAN; for example, "Customer," "Infrastructure," or
|
||||
"Management."
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
weight = models.PositiveSmallIntegerField(default=1000)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
weight = models.PositiveSmallIntegerField(
|
||||
default=1000
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'weight']
|
||||
|
||||
@ -205,22 +258,71 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
|
||||
VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be
|
||||
assigned to a VLAN where appropriate.
|
||||
"""
|
||||
family = models.PositiveSmallIntegerField(choices=AF_CHOICES, editable=False)
|
||||
prefix = IPNetworkField(help_text="IPv4 or IPv6 network with mask")
|
||||
site = models.ForeignKey('dcim.Site', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True)
|
||||
vrf = models.ForeignKey('VRF', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True,
|
||||
verbose_name='VRF')
|
||||
tenant = models.ForeignKey(Tenant, related_name='prefixes', blank=True, null=True, on_delete=models.PROTECT)
|
||||
vlan = models.ForeignKey('VLAN', related_name='prefixes', on_delete=models.PROTECT, blank=True, null=True,
|
||||
verbose_name='VLAN')
|
||||
status = models.PositiveSmallIntegerField('Status', choices=PREFIX_STATUS_CHOICES, default=PREFIX_STATUS_ACTIVE,
|
||||
help_text="Operational status of this prefix")
|
||||
role = models.ForeignKey('Role', related_name='prefixes', on_delete=models.SET_NULL, blank=True, null=True,
|
||||
help_text="The primary function of this prefix")
|
||||
is_pool = models.BooleanField(verbose_name='Is a pool', default=False,
|
||||
help_text="All IP addresses within this prefix are considered usable")
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
family = models.PositiveSmallIntegerField(
|
||||
choices=AF_CHOICES,
|
||||
editable=False
|
||||
)
|
||||
prefix = IPNetworkField(
|
||||
help_text='IPv4 or IPv6 network with mask'
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
vrf = models.ForeignKey(
|
||||
to='ipam.VRF',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='VRF'
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
vlan = models.ForeignKey(
|
||||
to='ipam.VLAN',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='VLAN'
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=PREFIX_STATUS_CHOICES,
|
||||
default=PREFIX_STATUS_ACTIVE,
|
||||
verbose_name='Status',
|
||||
help_text='Operational status of this prefix'
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='ipam.Role',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='The primary function of this prefix'
|
||||
)
|
||||
is_pool = models.BooleanField(
|
||||
verbose_name='Is a pool',
|
||||
default=False,
|
||||
help_text='All IP addresses within this prefix are considered usable'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
objects = PrefixQuerySet.as_manager()
|
||||
|
||||
@ -400,25 +502,66 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
||||
for example, when mapping public addresses to private addresses. When an Interface has been assigned an IPAddress
|
||||
which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
|
||||
"""
|
||||
family = models.PositiveSmallIntegerField(choices=AF_CHOICES, editable=False)
|
||||
address = IPAddressField(help_text="IPv4 or IPv6 address (with mask)")
|
||||
vrf = models.ForeignKey('VRF', related_name='ip_addresses', on_delete=models.PROTECT, blank=True, null=True,
|
||||
verbose_name='VRF')
|
||||
tenant = models.ForeignKey(Tenant, related_name='ip_addresses', blank=True, null=True, on_delete=models.PROTECT)
|
||||
family = models.PositiveSmallIntegerField(
|
||||
choices=AF_CHOICES,
|
||||
editable=False
|
||||
)
|
||||
address = IPAddressField(
|
||||
help_text='IPv4 or IPv6 address (with mask)'
|
||||
)
|
||||
vrf = models.ForeignKey(
|
||||
to='ipam.VRF',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='ip_addresses',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='VRF'
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='ip_addresses',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
'Status', choices=IPADDRESS_STATUS_CHOICES, default=IPADDRESS_STATUS_ACTIVE,
|
||||
choices=IPADDRESS_STATUS_CHOICES,
|
||||
default=IPADDRESS_STATUS_ACTIVE,
|
||||
verbose_name='Status',
|
||||
help_text='The operational status of this IP'
|
||||
)
|
||||
role = models.PositiveSmallIntegerField(
|
||||
'Role', choices=IPADDRESS_ROLE_CHOICES, blank=True, null=True, help_text='The functional role of this IP'
|
||||
verbose_name='Role',
|
||||
choices=IPADDRESS_ROLE_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='The functional role of this IP'
|
||||
)
|
||||
interface = models.ForeignKey(
|
||||
to='dcim.Interface',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='ip_addresses',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
nat_inside = models.OneToOneField(
|
||||
to='self',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='nat_outside',
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name='NAT (Inside)',
|
||||
help_text='The IP for which this address is the "outside" IP'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
interface = models.ForeignKey(Interface, related_name='ip_addresses', on_delete=models.CASCADE, blank=True,
|
||||
null=True)
|
||||
nat_inside = models.OneToOneField('self', related_name='nat_outside', on_delete=models.SET_NULL, blank=True,
|
||||
null=True, verbose_name='NAT (Inside)',
|
||||
help_text="The IP for which this address is the \"outside\" IP")
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
|
||||
objects = IPAddressManager()
|
||||
|
||||
@ -509,9 +652,17 @@ class VLANGroup(models.Model):
|
||||
"""
|
||||
A VLAN group is an arbitrary collection of VLANs within which VLAN IDs and names must be unique.
|
||||
"""
|
||||
name = models.CharField(max_length=50)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
slug = models.SlugField()
|
||||
site = models.ForeignKey('dcim.Site', related_name='vlan_groups', on_delete=models.PROTECT, blank=True, null=True)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vlan_groups',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'site']
|
||||
|
||||
@ -558,18 +709,55 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
||||
Like Prefixes, each VLAN is assigned an operational status and optionally a user-defined Role. A VLAN can have zero
|
||||
or more Prefixes assigned to it.
|
||||
"""
|
||||
site = models.ForeignKey('dcim.Site', related_name='vlans', on_delete=models.PROTECT, blank=True, null=True)
|
||||
group = models.ForeignKey('VLANGroup', related_name='vlans', blank=True, null=True, on_delete=models.PROTECT)
|
||||
vid = models.PositiveSmallIntegerField(verbose_name='ID', validators=[
|
||||
MinValueValidator(1),
|
||||
MaxValueValidator(4094)
|
||||
])
|
||||
name = models.CharField(max_length=64)
|
||||
tenant = models.ForeignKey(Tenant, related_name='vlans', blank=True, null=True, on_delete=models.PROTECT)
|
||||
status = models.PositiveSmallIntegerField('Status', choices=VLAN_STATUS_CHOICES, default=1)
|
||||
role = models.ForeignKey('Role', related_name='vlans', on_delete=models.SET_NULL, blank=True, null=True)
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vlans',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
group = models.ForeignKey(
|
||||
to='ipam.VLANGroup',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vlans',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
vid = models.PositiveSmallIntegerField(
|
||||
verbose_name='ID',
|
||||
validators=[MinValueValidator(1), MaxValueValidator(4094)]
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vlans',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=VLAN_STATUS_CHOICES,
|
||||
default=1,
|
||||
verbose_name='Status'
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='ipam.Role',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='vlans',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||
|
||||
|
@ -13,7 +13,6 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_bytes, python_2_unicode_compatible
|
||||
|
||||
from dcim.models import Device
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
from .exceptions import InvalidKey
|
||||
from .hashers import SecretValidationHasher
|
||||
@ -54,9 +53,21 @@ class UserKey(CreatedUpdatedModel):
|
||||
copy of the master encryption key. The encrypted instance of the master key can be decrypted only with the user's
|
||||
matching (private) decryption key.
|
||||
"""
|
||||
user = models.OneToOneField(User, related_name='user_key', editable=False, on_delete=models.CASCADE)
|
||||
public_key = models.TextField(verbose_name='RSA public key')
|
||||
master_key_cipher = models.BinaryField(max_length=512, blank=True, null=True, editable=False)
|
||||
user = models.OneToOneField(
|
||||
to=User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='user_key',
|
||||
editable=False
|
||||
)
|
||||
public_key = models.TextField(
|
||||
verbose_name='RSA public key'
|
||||
)
|
||||
master_key_cipher = models.BinaryField(
|
||||
max_length=512,
|
||||
blank=True,
|
||||
null=True,
|
||||
editable=False
|
||||
)
|
||||
|
||||
objects = UserKeyQuerySet.as_manager()
|
||||
|
||||
@ -172,10 +183,23 @@ class SessionKey(models.Model):
|
||||
"""
|
||||
A SessionKey stores a User's temporary key to be used for the encryption and decryption of secrets.
|
||||
"""
|
||||
userkey = models.OneToOneField(UserKey, related_name='session_key', on_delete=models.CASCADE, editable=False)
|
||||
cipher = models.BinaryField(max_length=512, editable=False)
|
||||
hash = models.CharField(max_length=128, editable=False)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
userkey = models.OneToOneField(
|
||||
to='secrets.UserKey',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='session_key',
|
||||
editable=False
|
||||
)
|
||||
cipher = models.BinaryField(
|
||||
max_length=512,
|
||||
editable=False
|
||||
)
|
||||
hash = models.CharField(
|
||||
max_length=128,
|
||||
editable=False
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True
|
||||
)
|
||||
|
||||
key = None
|
||||
|
||||
@ -234,10 +258,23 @@ class SecretRole(models.Model):
|
||||
By default, only superusers will have access to decrypt Secrets. To allow other users to decrypt Secrets, grant them
|
||||
access to the appropriate SecretRoles either individually or by group.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
users = models.ManyToManyField(User, related_name='secretroles', blank=True)
|
||||
groups = models.ManyToManyField(Group, related_name='secretroles', blank=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
users = models.ManyToManyField(
|
||||
to=User,
|
||||
related_name='secretroles',
|
||||
blank=True
|
||||
)
|
||||
groups = models.ManyToManyField(
|
||||
to=Group,
|
||||
related_name='secretroles',
|
||||
blank=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
|
||||
@ -276,11 +313,28 @@ class Secret(CreatedUpdatedModel):
|
||||
A Secret can be up to 65,536 bytes (64KB) in length. Each secret string will be padded with random data to a minimum
|
||||
of 64 bytes during encryption in order to protect short strings from ciphertext analysis.
|
||||
"""
|
||||
device = models.ForeignKey(Device, related_name='secrets', on_delete=models.CASCADE)
|
||||
role = models.ForeignKey('SecretRole', related_name='secrets', on_delete=models.PROTECT)
|
||||
name = models.CharField(max_length=100, blank=True)
|
||||
ciphertext = models.BinaryField(editable=False, max_length=65568) # 16B IV + 2B pad length + {62-65550}B padded
|
||||
hash = models.CharField(max_length=128, editable=False)
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='secrets'
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='secrets.SecretRole',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='secrets'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
ciphertext = models.BinaryField(
|
||||
max_length=65568, # 16B IV + 2B pad length + {62-65550}B padded
|
||||
editable=False
|
||||
)
|
||||
hash = models.CharField(
|
||||
max_length=128,
|
||||
editable=False
|
||||
)
|
||||
|
||||
plaintext = None
|
||||
csv_headers = ['device', 'role', 'name', 'plaintext']
|
||||
|
@ -5,7 +5,7 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
from extras.models import CustomFieldModel
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
|
||||
|
||||
@ -14,8 +14,13 @@ class TenantGroup(models.Model):
|
||||
"""
|
||||
An arbitrary collection of Tenants.
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
|
||||
@ -41,12 +46,33 @@ class Tenant(CreatedUpdatedModel, CustomFieldModel):
|
||||
A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal
|
||||
department.
|
||||
"""
|
||||
name = models.CharField(max_length=30, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
group = models.ForeignKey('TenantGroup', related_name='tenants', blank=True, null=True, on_delete=models.SET_NULL)
|
||||
description = models.CharField(max_length=100, blank=True, help_text="Long-form name (optional)")
|
||||
comments = models.TextField(blank=True)
|
||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||
name = models.CharField(
|
||||
max_length=30,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
group = models.ForeignKey(
|
||||
to='tenancy.TenantGroup',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='tenants',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
help_text='Long-form name (optional)'
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
|
||||
|
||||
|
@ -16,12 +16,31 @@ class Token(models.Model):
|
||||
An API token used for user authentication. This extends the stock model to allow each user to have multiple tokens.
|
||||
It also supports setting an expiration time and toggling write ability.
|
||||
"""
|
||||
user = models.ForeignKey(User, related_name='tokens', on_delete=models.CASCADE)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
expires = models.DateTimeField(blank=True, null=True)
|
||||
key = models.CharField(max_length=40, unique=True, validators=[MinLengthValidator(40)])
|
||||
write_enabled = models.BooleanField(default=True, help_text="Permit create/update/delete operations using this key")
|
||||
description = models.CharField(max_length=100, blank=True)
|
||||
user = models.ForeignKey(
|
||||
to=User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='tokens'
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True
|
||||
)
|
||||
expires = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
key = models.CharField(
|
||||
max_length=40,
|
||||
unique=True,
|
||||
validators=[MinLengthValidator(40)]
|
||||
)
|
||||
write_enabled = models.BooleanField(
|
||||
default=True,
|
||||
help_text='Permit create/update/delete operations using this key'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
default_permissions = []
|
||||
|
@ -8,7 +8,7 @@ from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from dcim.models import Device
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
from extras.models import CustomFieldModel
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
from .constants import DEVICE_STATUS_ACTIVE, VM_STATUS_CHOICES, VM_STATUS_CLASSES
|
||||
|
||||
@ -119,7 +119,7 @@ class Cluster(CreatedUpdatedModel, CustomFieldModel):
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to=CustomFieldValue,
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
@ -167,7 +167,7 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
|
||||
A virtual machine which runs inside a Cluster.
|
||||
"""
|
||||
cluster = models.ForeignKey(
|
||||
to=Cluster,
|
||||
to='virtualization.Cluster',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='virtual_machines'
|
||||
)
|
||||
@ -196,9 +196,9 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='dcim.DeviceRole',
|
||||
limit_choices_to={'vm_role': True},
|
||||
on_delete=models.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
limit_choices_to={'vm_role': True},
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
@ -237,7 +237,7 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to=CustomFieldValue,
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
Reference in New Issue
Block a user