Skip to content

Models

nautobot_chatops.models

Django models for recording user interactions with Nautobot.

AccessGrant

Bases: PrimaryModel

Record of a single form of access granted to the chatbot.

Source code in nautobot_chatops/models.py
class AccessGrant(PrimaryModel):
    """Record of a single form of access granted to the chatbot."""

    command = models.CharField(max_length=64, help_text=ACCESS_GRANT_COMMAND_HELP_TEXT)
    subcommand = models.CharField(
        max_length=64,
        help_text=ACCESS_GRANT_SUBCOMMAND_HELP_TEXT,
    )

    grant_type = models.CharField(max_length=32, choices=AccessGrantTypeChoices)

    name = models.CharField(max_length=255, help_text=ACCESS_GRANT_NAME_HELP_TEXT)
    value = models.CharField(
        max_length=255,
        help_text=ACCESS_GRANT_VALUE_HELP_TEXT,
    )

    def clean(self):
        """Model validation logic."""
        super().clean()
        if self.command == "*" and self.subcommand != "*":
            raise ValidationError("Use of a command wildcard with a non-wildcard subcommand is not permitted")

    def __str__(self):
        """String representation of an AccessGrant."""
        return f'cmd: "{self.command} {self.subcommand}", {self.grant_type}: "{self.name}" ({self.value})'

    def get_absolute_url(self, api=False):
        """Override the objects absolute url since we have no detail view."""
        return reverse("plugins:nautobot_chatops:accessgrant_list")

    class Meta:
        """Meta-attributes of an AccessGrant."""

        ordering = ["command", "subcommand", "grant_type"]
        unique_together = [["command", "subcommand", "grant_type", "value"]]

Meta

Meta-attributes of an AccessGrant.

Source code in nautobot_chatops/models.py
class Meta:
    """Meta-attributes of an AccessGrant."""

    ordering = ["command", "subcommand", "grant_type"]
    unique_together = [["command", "subcommand", "grant_type", "value"]]

__str__()

String representation of an AccessGrant.

Source code in nautobot_chatops/models.py
def __str__(self):
    """String representation of an AccessGrant."""
    return f'cmd: "{self.command} {self.subcommand}", {self.grant_type}: "{self.name}" ({self.value})'

clean()

Model validation logic.

Source code in nautobot_chatops/models.py
def clean(self):
    """Model validation logic."""
    super().clean()
    if self.command == "*" and self.subcommand != "*":
        raise ValidationError("Use of a command wildcard with a non-wildcard subcommand is not permitted")

get_absolute_url(api=False)

Override the objects absolute url since we have no detail view.

Source code in nautobot_chatops/models.py
def get_absolute_url(self, api=False):
    """Override the objects absolute url since we have no detail view."""
    return reverse("plugins:nautobot_chatops:accessgrant_list")

Bases: PrimaryModel

Connect ChatOps User with Nautobot User.

Source code in nautobot_chatops/models.py
class ChatOpsAccountLink(PrimaryModel):
    """Connect ChatOps User with Nautobot User."""

    nautobot_user = models.ForeignKey(
        to=settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="chatops_user",
        verbose_name="User",
    )
    platform = models.CharField(max_length=32, choices=PlatformChoices)
    user_id = models.CharField(max_length=255, help_text=CHATOPS_USER_ID_HELP_TEXT, verbose_name="Chat User ID")
    email = models.EmailField(blank=True)

    def __str__(self):
        """String representation of a ChatOps Account Link."""
        return f"{self.nautobot_user.username} -> {self.platform} {self.user_id}"

    class Meta:
        """Metadata for ChatOps Account Link."""

        unique_together = [["user_id", "platform"]]
        verbose_name = "ChatOps Account Link"

Meta

Metadata for ChatOps Account Link.

Source code in nautobot_chatops/models.py
class Meta:
    """Metadata for ChatOps Account Link."""

    unique_together = [["user_id", "platform"]]
    verbose_name = "ChatOps Account Link"

__str__()

String representation of a ChatOps Account Link.

Source code in nautobot_chatops/models.py
def __str__(self):
    """String representation of a ChatOps Account Link."""
    return f"{self.nautobot_user.username} -> {self.platform} {self.user_id}"

CommandLog

Bases: PrimaryModel

Record of a single fully-executed Nautobot command.

Incomplete commands (those requiring additional user input) should not be recorded, nor should any "help" commands or invalid command entries.

Source code in nautobot_chatops/models.py
class CommandLog(PrimaryModel):  # pylint: disable=nb-string-field-blank-null
    """Record of a single fully-executed Nautobot command.

    Incomplete commands (those requiring additional user input) should not be recorded,
    nor should any "help" commands or invalid command entries.
    """

    start_time = models.DateTimeField(null=True)
    runtime = models.DurationField(null=True)

    user_name = models.CharField(max_length=255, help_text=COMMAND_LOG_USER_NAME_HELP_TEXT)
    user_id = models.CharField(max_length=255, help_text=COMMAND_LOG_USER_ID_HELP_TEXT)
    platform = models.CharField(max_length=64, help_text=COMMAND_LOG_PLATFORM_HELP_TEXT)
    platform_color = ColorField()

    command = models.CharField(max_length=64, help_text=COMMAND_LOG_COMMAND_TEXT)
    subcommand = models.CharField(max_length=64, help_text=COMMAND_LOG_SUBCOMMAND_HELP_TEXT)

    params = models.JSONField(default=list, help_text=COMMAND_LOG_PARAMS_HELP_TEXT)

    status = models.CharField(
        max_length=32,
        choices=CommandStatusChoices,
        default=CommandStatusChoices.STATUS_SUCCEEDED,
    )
    details = models.CharField(max_length=255, default="")
    nautobot_user = models.ForeignKey(
        to=settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name="command_log",
    )

    @property
    def status_label_class(self):
        """Bootstrap CSS label class for each status value."""
        # pylint: disable=no-else-return
        if self.status == CommandStatusChoices.STATUS_SUCCEEDED:
            return "success"
        elif self.status == CommandStatusChoices.STATUS_BLOCKED:
            return "default"
        elif self.status == CommandStatusChoices.STATUS_FAILED:
            return "warning"
        else:  # STATUS_ERRORED, STATUS_UNKNOWN
            return "danger"

    def __str__(self):
        """String representation of a CommandLog entry."""
        return f"{self.user_name} on {self.platform}: {self.command} {self.subcommand} {self.params} ({self.status})"

    def get_absolute_url(self, api=False):
        """Override the objects absolute url since we have no detail view."""
        return reverse("plugins:nautobot_chatops:commandlog_list")

    class Meta:
        """Meta-attributes of a CommandLog."""

        ordering = ["start_time"]

status_label_class property

Bootstrap CSS label class for each status value.

Meta

Meta-attributes of a CommandLog.

Source code in nautobot_chatops/models.py
class Meta:
    """Meta-attributes of a CommandLog."""

    ordering = ["start_time"]

__str__()

String representation of a CommandLog entry.

Source code in nautobot_chatops/models.py
def __str__(self):
    """String representation of a CommandLog entry."""
    return f"{self.user_name} on {self.platform}: {self.command} {self.subcommand} {self.params} ({self.status})"

get_absolute_url(api=False)

Override the objects absolute url since we have no detail view.

Source code in nautobot_chatops/models.py
def get_absolute_url(self, api=False):
    """Override the objects absolute url since we have no detail view."""
    return reverse("plugins:nautobot_chatops:commandlog_list")

CommandToken

Bases: PrimaryModel

Record of a Token granted for the chat platform and chat command.

Source code in nautobot_chatops/models.py
class CommandToken(PrimaryModel):
    """Record of a Token granted for the chat platform and chat command."""

    comment = models.CharField(max_length=255, help_text=COMMAND_TOKEN_COMMENT_HELP_TEXT, blank=True, default="")
    platform = models.CharField(max_length=32, choices=PlatformChoices)
    token = models.CharField(max_length=255, help_text=COMMAND_TOKEN_TOKEN_HELP_TEXT)

    def __str__(self):
        """String representation of a CommandToken."""
        return f'platform: "{self.platform}", token: "{self.token}", comment: "{self.comment}"'

    def get_absolute_url(self, api=False):
        """Override the objects absolute url since we have no detail view."""
        return reverse("plugins:nautobot_chatops:commandtoken_list")

    class Meta:
        """Meta-attributes of a CommandToken."""

        ordering = ["platform", "token", "comment"]
        unique_together = [["platform", "token"]]

Meta

Meta-attributes of a CommandToken.

Source code in nautobot_chatops/models.py
class Meta:
    """Meta-attributes of a CommandToken."""

    ordering = ["platform", "token", "comment"]
    unique_together = [["platform", "token"]]

__str__()

String representation of a CommandToken.

Source code in nautobot_chatops/models.py
def __str__(self):
    """String representation of a CommandToken."""
    return f'platform: "{self.platform}", token: "{self.token}", comment: "{self.comment}"'

get_absolute_url(api=False)

Override the objects absolute url since we have no detail view.

Source code in nautobot_chatops/models.py
def get_absolute_url(self, api=False):
    """Override the objects absolute url since we have no detail view."""
    return reverse("plugins:nautobot_chatops:commandtoken_list")

GrafanaDashboard

Bases: PrimaryModel

Model for dashboards.

Source code in nautobot_chatops/integrations/grafana/models.py
@extras_features(
    "custom_fields",
    "custom_links",
    "custom_validators",
    "export_templates",
    "relationships",
    "statuses",
    "webhooks",
)
# pylint: disable-next=too-many-ancestors
class GrafanaDashboard(PrimaryModel):
    """Model for dashboards."""

    dashboard_slug = models.CharField(max_length=255, unique=True, blank=False)
    friendly_name = models.CharField(max_length=255, default="", blank=True)
    dashboard_uid = models.CharField(max_length=64, unique=True, blank=False)

    class Meta:
        """Metadata for the model."""

        ordering = ["dashboard_slug"]

    def __str__(self):
        """String value for HTML rendering."""
        return f"{self.dashboard_slug}"

Meta

Metadata for the model.

Source code in nautobot_chatops/integrations/grafana/models.py
class Meta:
    """Metadata for the model."""

    ordering = ["dashboard_slug"]

__str__()

String value for HTML rendering.

Source code in nautobot_chatops/integrations/grafana/models.py
def __str__(self):
    """String value for HTML rendering."""
    return f"{self.dashboard_slug}"

GrafanaPanel

Bases: OrganizationalModel

Model for GrafanaDashboard Panels.

Source code in nautobot_chatops/integrations/grafana/models.py
@extras_features(
    "custom_fields",
    "custom_links",
    "custom_validators",
    "export_templates",
    "relationships",
    "webhooks",
)
# pylint: disable-next=too-many-ancestors
class GrafanaPanel(OrganizationalModel):
    """Model for GrafanaDashboard Panels."""

    dashboard = models.ForeignKey(GrafanaDashboard, on_delete=models.CASCADE)
    command_name = models.CharField(max_length=64, blank=False)
    friendly_name = models.CharField(max_length=64, default="", blank=False)
    panel_id = models.IntegerField(blank=False)
    active = models.BooleanField(default=False)

    class Meta:
        """Metadata for the model."""

        ordering = ["command_name", "dashboard"]

    def __str__(self):
        """String value for HTML rendering."""
        return f"{self.command_name}"

Meta

Metadata for the model.

Source code in nautobot_chatops/integrations/grafana/models.py
class Meta:
    """Metadata for the model."""

    ordering = ["command_name", "dashboard"]

__str__()

String value for HTML rendering.

Source code in nautobot_chatops/integrations/grafana/models.py
def __str__(self):
    """String value for HTML rendering."""
    return f"{self.command_name}"

GrafanaPanelVariable

Bases: OrganizationalModel

Model for GrafanaDashboard GrafanaPanel Variables.

Source code in nautobot_chatops/integrations/grafana/models.py
@extras_features(
    "custom_fields",
    "custom_links",
    "custom_validators",
    "export_templates",
    "relationships",
    "webhooks",
)
# pylint: disable-next=too-many-ancestors
class GrafanaPanelVariable(OrganizationalModel):
    """Model for GrafanaDashboard GrafanaPanel Variables."""

    panel = models.ForeignKey(GrafanaPanel, on_delete=models.CASCADE)
    name = models.CharField(max_length=32, blank=False)
    friendly_name = models.CharField(max_length=64)
    query = models.CharField(max_length=64, verbose_name="Model")
    includeincmd = models.BooleanField(default=False)
    includeinurl = models.BooleanField(default=True)
    modelattr = models.CharField(max_length=64)
    value = models.TextField(max_length=64)
    response = models.CharField(max_length=255)
    filter = models.JSONField(blank=True, default=dict, encoder=DjangoJSONEncoder)
    positional_order = models.IntegerField(default=100)

    class Meta:
        """Metadata for the model."""

        ordering = ["name"]

    def __str__(self):
        """String value for HTML rendering."""
        return f"{self.name}"

    def clean(self):
        """Override clean to do custom validation."""
        super().clean()

        # Raise error if a query (model) is specified but an associated attribute is not.
        if self.query and not self.modelattr:
            raise ValidationError(_("A modelattr must be specified when a query is set!"))

        # Validate that the model name passed in is correct, and that the modelattr is an element
        # on the model.
        for nb_model in VALID_MODELS:
            if hasattr(nb_model, str(self.query)):
                model = getattr(nb_model, str(self.query))
                if hasattr(model, str(self.modelattr)):
                    return

                raise ValidationError(
                    _(
                        f"Nautobot model `{self.query}` does not have an attribute of `{self.modelattr}`."
                        f" {[f.name for f in model._meta.fields if not f.name.startswith('_')]}"
                    )
                )

        raise ValidationError(_(f"`{self.query}` is not a valid Nautobot model."))

Meta

Metadata for the model.

Source code in nautobot_chatops/integrations/grafana/models.py
class Meta:
    """Metadata for the model."""

    ordering = ["name"]

__str__()

String value for HTML rendering.

Source code in nautobot_chatops/integrations/grafana/models.py
def __str__(self):
    """String value for HTML rendering."""
    return f"{self.name}"

clean()

Override clean to do custom validation.

Source code in nautobot_chatops/integrations/grafana/models.py
def clean(self):
    """Override clean to do custom validation."""
    super().clean()

    # Raise error if a query (model) is specified but an associated attribute is not.
    if self.query and not self.modelattr:
        raise ValidationError(_("A modelattr must be specified when a query is set!"))

    # Validate that the model name passed in is correct, and that the modelattr is an element
    # on the model.
    for nb_model in VALID_MODELS:
        if hasattr(nb_model, str(self.query)):
            model = getattr(nb_model, str(self.query))
            if hasattr(model, str(self.modelattr)):
                return

            raise ValidationError(
                _(
                    f"Nautobot model `{self.query}` does not have an attribute of `{self.modelattr}`."
                    f" {[f.name for f in model._meta.fields if not f.name.startswith('_')]}"
                )
            )

    raise ValidationError(_(f"`{self.query}` is not a valid Nautobot model."))