|
|
@@ -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',
|
|
|
- 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')
|
|
|
+ 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(
|
|
|
+ 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')
|
|
|
+ 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(
|
|
|
- 'Manufacturer', models.PROTECT, related_name='inventory_items', blank=True, null=True
|
|
|
+ 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',
|