class Region(MPTTModel, OrganizationalModel): """ Sites can be grouped within geographic Regions. """ parent = TreeForeignKey( to="self", on_delete=models.CASCADE, related_name="children", blank=True, null=True, db_index=True, ) name = models.CharField(max_length=100, unique=True) slug = AutoSlugField(populate_from="name") description = models.CharField(max_length=200, blank=True) objects = TreeManager() csv_headers = ["name", "slug", "parent", "description"] class MPTTMeta: order_insertion_by = ["name"] def __str__(self): return self.name def get_absolute_url(self): return reverse("dcim:region", args=[self.pk]) def to_csv(self): return ( self.name, self.slug, self.parent.name if self.parent else None, self.description, ) def get_site_count(self): return Site.objects.filter( Q(region=self) | Q(region__in=self.get_descendants())).count() def to_objectchange(self, action): # Remove MPTT-internal fields return ObjectChange( changed_object=self, object_repr=str(self), action=action, object_data=serialize_object( self, exclude=["level", "lft", "rght", "tree_id"]), )
class TenantGroup(MPTTModel, OrganizationalModel): """ An arbitrary collection of Tenants. """ name = models.CharField(max_length=100, unique=True) slug = AutoSlugField(populate_from="name") parent = TreeForeignKey( to="self", on_delete=models.CASCADE, related_name="children", blank=True, null=True, db_index=True, ) description = models.CharField(max_length=200, blank=True) objects = TreeManager() csv_headers = ["name", "slug", "parent", "description"] class Meta: ordering = ["name"] class MPTTMeta: order_insertion_by = ["name"] def __str__(self): return self.name def get_absolute_url(self): return reverse("tenancy:tenantgroup", args=[self.slug]) def to_csv(self): return ( self.name, self.slug, self.parent.name if self.parent else "", self.description, ) def to_objectchange(self, action): # Remove MPTT-internal fields return ObjectChange( changed_object=self, object_repr=str(self), action=action, object_data=serialize_object( self, exclude=["level", "lft", "rght", "tree_id"]), )
class RackGroup(MPTTModel, OrganizationalModel): """ Racks can be grouped as subsets within a Site. The scope of a group will depend on how Sites are defined. For 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=100) slug = models.SlugField(max_length=100) site = models.ForeignKey(to="dcim.Site", on_delete=models.CASCADE, related_name="rack_groups") parent = TreeForeignKey( to="self", on_delete=models.CASCADE, related_name="children", blank=True, null=True, db_index=True, ) description = models.CharField(max_length=200, blank=True) objects = TreeManager() csv_headers = ["site", "parent", "name", "slug", "description"] class Meta: ordering = ["site", "name"] unique_together = [ ["site", "name"], ["site", "slug"], ] class MPTTMeta: order_insertion_by = ["name"] def __str__(self): return self.name def get_absolute_url(self): return reverse("dcim:rackgroup", args=[self.pk]) def to_csv(self): return ( self.site, self.parent.name if self.parent else "", self.name, self.slug, self.description, ) def to_objectchange(self, action): # Remove MPTT-internal fields return ObjectChange( changed_object=self, object_repr=str(self), action=action, object_data=serialize_object(self, exclude=["level", "lft", "rght", "tree_id"]), ) def clean(self): super().clean() # Parent RackGroup (if any) must belong to the same Site if self.parent and self.parent.site != self.site: raise ValidationError(f"Parent rack group ({self.parent}) must belong to the same site ({self.site})")
class InventoryItem(MPTTModel, ComponentModel): """ 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. """ parent = TreeForeignKey( to="self", on_delete=models.CASCADE, related_name="child_items", blank=True, null=True, db_index=True, ) 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, help_text="Manufacturer-assigned part identifier", ) serial = models.CharField(max_length=50, verbose_name="Serial number", blank=True) asset_tag = models.CharField( 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, help_text="This item was automatically discovered") objects = TreeManager() csv_headers = [ "device", "name", "label", "manufacturer", "part_id", "serial", "asset_tag", "discovered", "description", ] class Meta: ordering = ("device__id", "parent__id", "_name") unique_together = ("device", "parent", "name") def get_absolute_url(self): return reverse("dcim:inventoryitem", kwargs={"pk": self.pk}) def to_csv(self): return ( self.device.name or "{{{}}}".format(self.device.pk), self.name, self.label, self.manufacturer.name if self.manufacturer else None, self.part_id, self.serial, self.asset_tag, self.discovered, self.description, )