Compare commits
2 Commits
fc0a232808
...
b715019845
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b715019845 | ||
|
|
3268274f69 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
/.venv/
|
/.venv/
|
||||||
/.idea/
|
/.idea/
|
||||||
|
/.netbox/netbox
|
||||||
|
/.vscode/
|
||||||
|
|||||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.12
|
||||||
@@ -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)
|
|
||||||
7
pyproject.toml
Normal file
7
pyproject.toml
Normal file
@@ -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 = []
|
||||||
12
pyrightconfig.json
Normal file
12
pyrightconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extraPaths": ["netbox/netbox"],
|
||||||
|
"typeCheckingMode": "off",
|
||||||
|
"reportMissingImports": "warning",
|
||||||
|
"reportMissingTypeStubs": "none",
|
||||||
|
"reportGeneralTypeIssues": "none",
|
||||||
|
"reportOptionalMemberAccess": "none",
|
||||||
|
"reportOptionalSubscript": "none",
|
||||||
|
"reportArgumentType": "none",
|
||||||
|
"reportAssignmentType": "none",
|
||||||
|
"reportReturnType": "none"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user