import re from tenancy.models import Tenant, TenantGroup, Contact, ContactRole, ContactAssignment from ipam.models import Prefix, VRF, Role 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) указывается с маской подсети" ) prefix_vrf = ObjectVar(label="VRF проекта", model=VRF, required=True) prefix_role = ObjectVar(label="Роль", model=Role, required=True) prefix_description = StringVar(label="Описание префикса", required=False) contacts_fio = StringVar(label="ФИО", 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(): self.log_failure( f"Оператор с именем '{data['tenant_name']}' уже существует" ) 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: self.log_failure(f"Ошибка валидации: {e}") raise e except Exception as e: self.log_failure(f"Ошибка при выполнении скрипта: {e}") if commit: self.log_failure("Изменения отменены из-за ошибки")