From 3268274f69e1300e3c96480d3517550d7dd0cc6c Mon Sep 17 00:00:00 2001 From: IluaAir Date: Sun, 1 Feb 2026 15:56:37 +0300 Subject: [PATCH] reorder file paths --- .python-version | 1 + README.md | 0 add_tenant_with_prefix.py | 324 -------------------------------------- pyproject.toml | 7 + uv.lock | 8 + 5 files changed, 16 insertions(+), 324 deletions(-) create mode 100644 .python-version create mode 100644 README.md delete mode 100644 add_tenant_with_prefix.py create mode 100644 pyproject.toml create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/add_tenant_with_prefix.py b/add_tenant_with_prefix.py deleted file mode 100644 index 7eda0d5..0000000 --- a/add_tenant_with_prefix.py +++ /dev/null @@ -1,324 +0,0 @@ -import re - -from tenancy.models import Tenant, TenantGroup, Contact, ContactRole, ContactAssignment -from ipam.models import Prefix, VRF, Role -from dcim.models import DeviceType, DeviceRole, Device -from extras.scripts import Script, StringVar, IPNetworkVar, ChoiceVar, ObjectVar -from django.core.exceptions import ValidationError -from extras.models import CustomField -from utilities.exceptions import AbortScript - - -def slugify(text: str): - CYRILLIC_TO_LATIN = { - "а": "a", - "б": "b", - "в": "v", - "г": "g", - "д": "d", - "е": "e", - "ё": "e", - "ж": "zh", - "з": "z", - "и": "i", - "й": "i", - "к": "k", - "л": "l", - "м": "m", - "н": "n", - "о": "o", - "п": "p", - "р": "r", - "с": "s", - "т": "t", - "у": "u", - "ф": "f", - "х": "h", - "ц": "c", - "ч": "ch", - "ш": "sh", - "щ": "shch", - "ъ": "", - "ы": "y", - "ь": "", - "э": "e", - "ю": "yu", - "я": "ya", - "А": "A", - "Б": "B", - "В": "V", - "Г": "G", - "Д": "D", - "Е": "E", - "Ё": "E", - "Ж": "ZH", - "З": "Z", - "И": "I", - "Й": "I", - "К": "K", - "Л": "L", - "М": "M", - "Н": "N", - "О": "O", - "П": "P", - "Р": "R", - "С": "S", - "Т": "T", - "У": "U", - "Ф": "F", - "Х": "H", - "Ц": "C", - "Ч": "CH", - "Ш": "SH", - "Щ": "SHCH", - "Ъ": "", - "Ы": "Y", - "Ь": "", - "Э": "E", - "Ю": "YU", - "Я": "YA", - } - text = text.replace("ООО", "").lstrip() - text = "".join(CYRILLIC_TO_LATIN.get(c, c) for c in text) - text = text.lower() - text = re.sub(r"\s+", "_", text) - text = re.sub(r"[^a-z0-9_-]", "", text) - text = re.sub(r"[-_]{2,}", "_", text).strip("_-") - - return text - - -class CreateTenant(Script): - class Meta: - name = "Создание Оператора" - description = "Создание оператора и связанного префикса" - scheduling_enabled = False - fieldsets = ( - ( - "Оператор", - ( - "tenant_name", - "tenant_group", - "tenant_connection_type", - "tenant_description", - ), - ), - ( - "Префикс", - ("prefix_cidr", "prefix_vrf", "prefix_role", "prefix_description"), - ), - ("Документы", ("tenant_contract", "tenant_prefix_task", "tenant_tr")), - ("Контакты", ("contacts_fio", "contacts_phone", "contacts_email")), - ) - - connection_types = CustomField.objects.get(name="connection_type_tenant") - - tenant_name = StringVar(label="Имя оператора") - tenant_group = ObjectVar( - label="Группа оператора", model=TenantGroup, required=False - ) - tenant_connection_type = ChoiceVar( - label="Тип подключения", choices=connection_types.choices, required=True - ) - tenant_description = StringVar(label="Описание оператора", required=False) - tenant_contract = StringVar( - label="Договор", - description="Номер договора на разработку комплекса", - required=False, - ) - tenant_tr = StringVar( - label="Проект/ТР", description="Пример: DOCXXXXX", required=False - ) - tenant_prefix_task = StringVar( - label="Задача", - description="URL или номер задачи из redmine, jira, CRM", - required=False, - ) - - prefix_cidr = IPNetworkVar( - label="Префикс для оператора (CIDR)", - description="Указывается с маской подсети /xx", - ) - prefix_vrf = ObjectVar(label="VRF проекта", model=VRF, required=True) - prefix_role = ObjectVar( - label="Роль", - description="Роль для малых операторов IPMI-SSH-MGMT", - model=Role, - required=True, - ) - prefix_description = StringVar(label="Описание префикса", required=False) - - contacts_fio = StringVar( - label="ФИО", - description="Указывается в формате Иванов Иван Иванович, остальные значения игнорируются при отсутсвии ФИО", - required=False, - ) - contacts_phone = StringVar(label="Номер тел.", required=False) - contacts_email = StringVar(label="E-mail", required=False) - contacts_role = ObjectVar(label="Роль контакта", model=ContactRole, required=False) - - def check_tenant_exists(self, data): - if Tenant.objects.filter(name=data["tenant_name"]).exists(): - raise ValidationError( - f"Оператор с именем '{data['tenant_name']}' уже существует" - ) - - def check_prefix_exists(self, data): - if Prefix.objects.filter(prefix=data["prefix_cidr"]).exists(): - self.log_warning( - f"Префикс {data['prefix_cidr']} уже существует, оператор будет создан без префикса" - ) - return False - return True - - def run(self, data, commit): - try: - self.check_tenant_exists(data) - validate_prefix = self.check_prefix_exists(data) - - tenant_slug = slugify(data["tenant_name"]) - tenant_data = { - "name": data["tenant_name"], - "slug": tenant_slug, - "group": data["tenant_group"], - } - - if data.get("tenant_description"): - tenant_data["description"] = data["tenant_description"] - - tenant = Tenant(**tenant_data) - tenant.custom_field_data["dogovor"] = data["tenant_contract"] - tenant.custom_field_data["connection_type_tenant"] = data[ - "tenant_connection_type" - ] - tenant.custom_field_data["link_on_docs"] = data["tenant_tr"] - tenant.full_clean() - if commit: - tenant.save() - self.log_success( - f"Создан новый оператор: {tenant.name} - {tenant.slug}" - ) - if validate_prefix: - prefix = Prefix( - prefix=data["prefix_cidr"], - tenant=tenant, - description=data.get("prefix_description") or "", - vrf=data["prefix_vrf"], - role=data["prefix_role"], - ) - prefix.custom_field_data["Project"] = data["tenant_tr"] - prefix.custom_field_data["zadacha"] = data["tenant_prefix_task"] - prefix.full_clean() - prefix.save() - self.log_success( - f"Создан префикс {prefix.prefix} для оператора: {tenant.name}" - ) - if data.get("contacts_fio"): - contact = Contact( - name=data["contacts_fio"], - phone=data["contacts_phone"], - email=data["contacts_email"], - ) - try: - contact.full_clean() - contact.save() - self.log_success(f"Создан контакт: {contact.name}") - except Exception as e: - self.log_failure(f"Ошибка при создании контакта: {e}") - try: - role = data.get("contacts_role") or ContactRole.objects.first() - assignment = ContactAssignment( - contact=contact, - role=role, - object=tenant, - ) - assignment.full_clean() - assignment.save() - self.log_success( - f"Связан контакт {contact.name} с оператором: {tenant.name}" - ) - except Exception as e: - self.log_failure( - f"Ошибка при связывании контакта с оператором: {e}" - ) - - else: - self.log_info( - f"Тестовый режим: Будет создан оператор: {tenant.name} (slug: {tenant.slug})" - ) - self.log_info( - f"Тестовый режим: Будет создан префикс: {data['prefix_cidr']}" - ) - try: - tenant.full_clean() - prefix = Prefix( - prefix=data["prefix_cidr"], - tenant=tenant, - description=data.get("prefix_description") or "", - ) - prefix.full_clean() - contact = Contact( - name=data["contacts_fio"], - phone=data["contacts_phone"], - email=data["contacts_email"], - ) - contact.full_clean() - role = data.get("contacts_role") or ContactRole.objects.first() - assignment = ContactAssignment( - contact=contact, - role=role, - object=tenant, - ) - assignment.full_clean() - self.log_success("Все данные валидны") - except ValidationError as e: - self.log_failure(f"Ошибка валидации: {e}") - except ValidationError as e: - raise AbortScript(f"Ошибка валидации: {e}") - except Exception as e: - self.log_failure(f"Ошибка при выполнении скрипта: {e}") - if commit: - self.log_failure("Изменения отменены из-за ошибки") - raise - - -class CreateDevice(Script): - class Meta: - name = "Создание устройства" - description = "Создание устройства и привязка к оператору" - scheduling_enabled = False - fieldsets = ( - ( - "Устройство", - ( - "device_name", - "device_type", - "device_role", - "device_description", - "tenant_group", - "device_tenant", - ), - ), - ) - - device_name = StringVar(label="Имя устройства") - device_role = ObjectVar(label="Роль устройства", model=DeviceRole, required=True) - device_type = ObjectVar(label="Тип устройства", model=DeviceType, required=True) - device_description = StringVar(label="Описание устройства", required=False) - tenant_group = ObjectVar( - label="Группа оператора", model=TenantGroup, required=False - ) - device_tenant = ObjectVar( - label="Оператор", - model=Tenant, - required=True, - query_params={ - "group_id": "$tenant_group", - }, - ) - - def run(self, data, commit): - pass - - -script_order = (CreateTenant, CreateDevice) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0f2e3be --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "netbox-scripts" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..0450df7 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "netbox-scripts" +version = "0.1.0" +source = { virtual = "." }