class ObjectPermission(BaseModel): """ A mapping of view, add, change, and/or delete permission for users and/or groups to an arbitrary set of objects identified by ORM query parameters. """ name = models.CharField(max_length=100) description = models.CharField(max_length=200, blank=True) enabled = models.BooleanField(default=True) object_types = models.ManyToManyField( to=ContentType, limit_choices_to=Q( ~Q(app_label__in=[ "admin", "auth", "contenttypes", "sessions", "taggit", "users", ]) | Q(app_label="auth", model__in=["group", "user"]) | Q(app_label="users", model__in=["objectpermission", "token"])), related_name="object_permissions", ) groups = models.ManyToManyField(to=Group, blank=True, related_name="object_permissions") users = models.ManyToManyField(to=settings.AUTH_USER_MODEL, blank=True, related_name="object_permissions") actions = JSONArrayField( base_field=models.CharField(max_length=30), help_text="The list of actions granted by this permission", ) constraints = models.JSONField( encoder=DjangoJSONEncoder, blank=True, null=True, help_text= "Queryset filter matching the applicable objects of the selected type(s)", ) objects = RestrictedQuerySet.as_manager() class Meta: ordering = ["name"] verbose_name = "permission" def __str__(self): return self.name def list_constraints(self): """ Return all constraint sets as a list (even if only a single set is defined). """ if type(self.constraints) is not list: return [self.constraints] return self.constraints
class Service(PrimaryModel): """ A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may optionally be tied to one or more specific IPAddresses belonging to its parent. """ device = models.ForeignKey( to="dcim.Device", on_delete=models.CASCADE, related_name="services", verbose_name="device", null=True, blank=True, ) virtual_machine = models.ForeignKey( to="virtualization.VirtualMachine", on_delete=models.CASCADE, related_name="services", null=True, blank=True, ) name = models.CharField(max_length=100) protocol = models.CharField(max_length=50, choices=ServiceProtocolChoices) ports = JSONArrayField( base_field=models.PositiveIntegerField(validators=[ MinValueValidator(SERVICE_PORT_MIN), MaxValueValidator(SERVICE_PORT_MAX), ]), verbose_name="Port numbers", ) ipaddresses = models.ManyToManyField( to="ipam.IPAddress", related_name="services", blank=True, verbose_name="IP addresses", ) description = models.CharField(max_length=200, blank=True) csv_headers = [ "device", "virtual_machine", "name", "protocol", "ports", "description", ] class Meta: ordering = ( "protocol", "ports", ) # (protocol, port) may be non-unique def __str__(self): return f"{self.name} ({self.get_protocol_display()}/{self.port_list})" def get_absolute_url(self): return reverse("ipam:service", args=[self.pk]) @property def parent(self): return self.device or self.virtual_machine def clean(self): super().clean() # A Service must belong to a Device *or* to a VirtualMachine if self.device and self.virtual_machine: raise ValidationError( "A service cannot be associated with both a device and a virtual machine." ) if not self.device and not self.virtual_machine: raise ValidationError( "A service must be associated with either a device or a virtual machine." ) def to_csv(self): return ( self.device.name if self.device else None, self.virtual_machine.name if self.virtual_machine else None, self.name, self.get_protocol_display(), self.ports, self.description, ) @property def port_list(self): return array_to_string(self.ports)
class RackReservation(PrimaryModel): """ One or more reserved units within a Rack. """ rack = models.ForeignKey(to="dcim.Rack", on_delete=models.CASCADE, related_name="reservations") units = JSONArrayField(base_field=models.PositiveSmallIntegerField()) tenant = models.ForeignKey( to="tenancy.Tenant", on_delete=models.PROTECT, related_name="rackreservations", blank=True, null=True, ) user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT) description = models.CharField(max_length=200) csv_headers = [ "site", "rack_group", "rack", "units", "tenant", "user", "description", ] class Meta: ordering = ["created"] def __str__(self): return "Reservation for rack {}".format(self.rack) def get_absolute_url(self): return reverse("dcim:rackreservation", args=[self.pk]) def clean(self): super().clean() if hasattr(self, "rack") and self.units: # Validate that all specified units exist in the Rack. invalid_units = [u for u in self.units if u not in self.rack.units] if invalid_units: raise ValidationError( { "units": "Invalid unit(s) for {}U rack: {}".format( self.rack.u_height, ", ".join([str(u) for u in invalid_units]), ), } ) # Check that none of the units has already been reserved for this Rack. reserved_units = [] for resv in self.rack.reservations.exclude(pk=self.pk): reserved_units += resv.units conflicting_units = [u for u in self.units if u in reserved_units] if conflicting_units: raise ValidationError( { "units": "The following units have already been reserved: {}".format( ", ".join([str(u) for u in conflicting_units]), ) } ) def to_csv(self): return ( self.rack.site.name, self.rack.group if self.rack.group else None, self.rack.name, ",".join([str(u) for u in self.units]), self.tenant.name if self.tenant else None, self.user.username, self.description, ) @property def unit_list(self): return array_to_string(self.units)