Update Custom Field logic for Netbox v2.10.x

This commit is contained in:
Christian Mäder 2020-10-17 21:51:38 +02:00 committed by Christian Mäder
parent 036f94a450
commit 5c9bea8b50
24 changed files with 168 additions and 268 deletions

View File

@ -37,7 +37,7 @@
# weight: 10
# on_objects:
# - tenancy.models.Tenant
# select_field:
# legacy_select_field:
# type: select
# label: Choose between items
# required: false
@ -56,6 +56,21 @@
# weight: 50
# - value: Fourth Item
# weight: 40
# select_field:
# type: select
# label: Choose between items
# required: false
# filter_logic: exact
# weight: 30
# default: First Item
# on_objects:
# - dcim.models.Device
# choices:
# - First Item
# - Second Item
# - Third Item
# - Fifth Item
# - Fourth Item
# select_field_auto_weight:
# type: select
# label: Choose between items
@ -65,18 +80,17 @@
# on_objects:
# - dcim.models.Device
# choices:
# - value: A
# - value: B
# - value: C
# - value: "D like deprecated"
# weight: 999
# - value: E
# - A
# - B
# - C
# - E
# - D like deprecated
# boolean_field:
# type: boolean
# label: Yes Or No?
# required: true
# filter_logic: loose
# default: "false" # important: but "false" in quotes!
# default: "false" # important: put "false" in quotes!
# weight: 90
# on_objects:
# - dcim.models.Device

View File

@ -2,22 +2,22 @@
# manufacturer: Manufacturer 1
# slug: model-1
# u_height: 2
# custom_fields:
# custom_field_data:
# text_field: Description
# - model: Model 2
# manufacturer: Manufacturer 1
# slug: model-2
# custom_fields:
# custom_field_data:
# text_field: Description
# - model: Model 3
# manufacturer: Manufacturer 1
# slug: model-3
# is_full_depth: false
# u_height: 0
# custom_fields:
# custom_field_data:
# text_field: Description
# - model: Other
# manufacturer: No Name
# slug: other
# custom_fields:
# custom_field_data:
# text_field: Description

View File

@ -20,7 +20,7 @@
# rack: rack-01
# face: front
# position: 1
# custom_fields:
# custom_field_data:
# text_field: Description
# - name: server02
# device_role: server
@ -31,7 +31,7 @@
# position: 2
# primary_ip4: 10.1.1.2/24
# primary_ip6: 2001:db8:a000:1::2/64
# custom_fields:
# custom_field_data:
# text_field: Description
# - name: server03
# device_role: server
@ -40,5 +40,5 @@
# rack: rack-03
# face: front
# position: 3
# custom_fields:
# custom_field_data:
# text_field: Description

View File

@ -20,7 +20,7 @@
# type: 4-post-cabinet
# width: 19
# u_height: 47
# custom_fields:
# custom_field_data:
# text_field: Description
# - site: AMS 2
# name: rack-02
@ -28,7 +28,7 @@
# type: 4-post-cabinet
# width: 19
# u_height: 47
# custom_fields:
# custom_field_data:
# text_field: Description
# - site: SING 1
# name: rack-03
@ -37,5 +37,5 @@
# type: 4-post-cabinet
# width: 19
# u_height: 47
# custom_fields:
# custom_field_data:
# text_field: Description

View File

@ -4,29 +4,29 @@
# status: active
# facility: Amsterdam 1
# asn: 12345
# custom_fields:
# text_field: Description
# custom_field_data:
# text_field: Description for AMS1
# - name: AMS 2
# slug: ams2
# region: Downtown
# status: active
# facility: Amsterdam 2
# asn: 54321
# custom_fields:
# text_field: Description
# custom_field_data:
# text_field: Description for AMS2
# - name: AMS 3
# slug: ams3
# region: Suburbs
# status: active
# facility: Amsterdam 3
# asn: 67890
# custom_fields:
# text_field: Description
# custom_field_data:
# text_field: Description for AMS3
# - name: SING 1
# slug: sing1
# region: Singapore
# status: active
# facility: Singapore 1
# asn: 09876
# custom_fields:
# text_field: Description
# custom_field_data:
# text_field: Description for SING1

View File

@ -1,8 +1,8 @@
from extras.models import CustomField, CustomFieldChoice
from startup_script_utils import load_yaml
import sys
from extras.models import CustomField
from startup_script_utils import load_yaml
def get_class_for_class_path(class_path):
import importlib
from django.contrib.contenttypes.models import ContentType
@ -21,34 +21,37 @@ for cf_name, cf_details in customfields.items():
custom_field, created = CustomField.objects.get_or_create(name = cf_name)
if created:
if cf_details.get('default', 0):
if cf_details.get('default', False):
custom_field.default = cf_details['default']
if cf_details.get('description', 0):
if cf_details.get('description', False):
custom_field.description = cf_details['description']
if cf_details.get('label', 0):
if cf_details.get('label', False):
custom_field.label = cf_details['label']
for object_type in cf_details.get('on_objects', []):
custom_field.obj_type.add(get_class_for_class_path(object_type))
custom_field.content_types.add(get_class_for_class_path(object_type))
if cf_details.get('required', 0):
if cf_details.get('required', False):
custom_field.required = cf_details['required']
if cf_details.get('type', 0):
if cf_details.get('type', False):
custom_field.type = cf_details['type']
if cf_details.get('weight', 0):
if cf_details.get('weight', -1) >= 0:
custom_field.weight = cf_details['weight']
if cf_details.get('choices', False):
custom_field.choices = []
for _, choice_detail in enumerate(cf_details.get('choices', [])):
if isinstance(choice_detail, str):
custom_field.choices.append(choice_detail)
else: # legacy mode
print(f"⚠️ Please migrate the 'choices' of '{cf_name}' to the new format, as 'weight' is no longer supported!")
custom_field.choices.append(choice_detail['value'])
custom_field.save()
for idx, choice_details in enumerate(cf_details.get('choices', [])):
choice, _ = CustomFieldChoice.objects.get_or_create(
field=custom_field,
value=choice_details['value'],
defaults={'weight': idx * 10}
)
print("🔧 Created custom field", cf_name)

View File

@ -1,9 +1,9 @@
from dcim.models import Region, Site
from extras.models import CustomField, CustomFieldValue
from tenancy.models import Tenant
from startup_script_utils import load_yaml
import sys
from dcim.models import Region, Site
from startup_script_utils import *
from tenancy.models import Tenant
sites = load_yaml('/opt/netbox/initializers/sites.yml')
if sites is None:
@ -15,7 +15,7 @@ optional_assocs = {
}
for params in sites:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in optional_assocs.items():
if assoc in params:
@ -27,15 +27,6 @@ for params in sites:
site, created = Site.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=site,
value=cf_value
)
site.custom_field_values.add(custom_field_value)
set_custom_fields_values(site, custom_field_data)
print("📍 Created site", site.name)

View File

@ -1,9 +1,9 @@
from dcim.models import DeviceType, Manufacturer, Region
from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from dcim.models import DeviceType, Manufacturer, Region
from startup_script_utils import *
from tenancy.models import Tenant
device_types = load_yaml('/opt/netbox/initializers/device_types.yml')
if device_types is None:
@ -19,7 +19,7 @@ optional_assocs = {
}
for params in device_types:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
@ -37,15 +37,6 @@ for params in device_types:
device_type, created = DeviceType.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=device_type,
value=cf_value
)
device_type.custom_field_values.add(custom_field_value)
set_custom_fields_values(device_type, custom_field_data)
print("🔡 Created device type", device_type.manufacturer, device_type.model)

View File

@ -1,9 +1,9 @@
from dcim.models import Site, RackRole, Rack, RackGroup
from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from dcim.models import Site, RackRole, Rack, RackGroup
from startup_script_utils import *
from tenancy.models import Tenant
racks = load_yaml('/opt/netbox/initializers/racks.yml')
if racks is None:
@ -20,7 +20,7 @@ optional_assocs = {
}
for params in racks:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
@ -38,15 +38,6 @@ for params in racks:
rack, created = Rack.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=rack,
value=cf_value
)
rack.custom_field_values.add(custom_field_value)
set_custom_fields_values(rack, custom_field_data)
print("🔳 Created rack", rack.site, rack.name)

View File

@ -1,8 +1,8 @@
from tenancy.models import Tenant, TenantGroup
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from startup_script_utils import *
from tenancy.models import Tenant, TenantGroup
tenants = load_yaml('/opt/netbox/initializers/tenants.yml')
if tenants is None:
@ -13,7 +13,7 @@ optional_assocs = {
}
for params in tenants:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in optional_assocs.items():
if assoc in params:
@ -25,15 +25,6 @@ for params in tenants:
tenant, created = Tenant.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=tenant,
value=cf_value
)
tenant.custom_field_values.add(custom_field_value)
set_custom_fields_values(tenant, custom_field_data)
print("👩‍💻 Created Tenant", tenant.name)

View File

@ -1,10 +1,10 @@
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
from virtualization.models import Cluster
from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from dcim.models import Site, Rack, DeviceRole, DeviceType, Device, Platform
from startup_script_utils import *
from tenancy.models import Tenant
from virtualization.models import Cluster
devices = load_yaml('/opt/netbox/initializers/devices.yml')
if devices is None:
@ -24,7 +24,8 @@ optional_assocs = {
}
for params in devices:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
# primary ips are handled later in `270_primary_ips.py`
params.pop('primary_ip4', None)
params.pop('primary_ip6', None)
@ -45,15 +46,6 @@ for params in devices:
device, created = Device.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=device,
value=cf_value
)
device.custom_field_values.add(custom_field_value)
set_custom_fields_values(device, custom_field_data)
print("🖥️ Created device", device.name)

View File

@ -1,11 +1,9 @@
from ipam.models import Aggregate, RIR
from extras.models import CustomField, CustomFieldValue
from netaddr import IPNetwork
from startup_script_utils import load_yaml
import sys
from ipam.models import Aggregate, RIR
from netaddr import IPNetwork
from startup_script_utils import *
aggregates = load_yaml('/opt/netbox/initializers/aggregates.yml')
if aggregates is None:
@ -16,7 +14,8 @@ required_assocs = {
}
for params in aggregates:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
params['prefix'] = IPNetwork(params['prefix'])
for assoc, details in required_assocs.items():
@ -28,15 +27,6 @@ for params in aggregates:
aggregate, created = Aggregate.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=aggregate,
value=cf_value
)
aggregate.custom_field_values.add(custom_field_value)
set_custom_fields_values(aggregate, custom_field_data)
print("🗞️ Created Aggregate", aggregate.prefix)

View File

@ -1,9 +1,9 @@
from dcim.models import Site
from virtualization.models import Cluster, ClusterType, ClusterGroup
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from dcim.models import Site
from startup_script_utils import *
from virtualization.models import Cluster, ClusterType, ClusterGroup
clusters = load_yaml('/opt/netbox/initializers/clusters.yml')
if clusters is None:
@ -19,7 +19,7 @@ optional_assocs = {
}
for params in clusters:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
@ -37,15 +37,6 @@ for params in clusters:
cluster, created = Cluster.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=cluster,
value=cf_value
)
cluster.custom_field_values.add(custom_field_value)
set_custom_fields_values(cluster, custom_field_data)
print("🗄️ Created cluster", cluster.name)

View File

@ -1,11 +1,9 @@
from ipam.models import VRF
from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from ipam.models import VRF
from startup_script_utils import *
from tenancy.models import Tenant
vrfs = load_yaml('/opt/netbox/initializers/vrfs.yml')
if vrfs is None:
@ -16,7 +14,7 @@ optional_assocs = {
}
for params in vrfs:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in optional_assocs.items():
if assoc in params:
@ -28,15 +26,6 @@ for params in vrfs:
vrf, created = VRF.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=vrf,
value=cf_value
)
vrf.custom_field_values.add(custom_field_value)
set_custom_fields_values(vrf, custom_field_data)
print("📦 Created VRF", vrf.name)

View File

@ -1,8 +1,8 @@
import sys
from dcim.models import Site
from ipam.models import VLANGroup
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from startup_script_utils import *
vlan_groups = load_yaml('/opt/netbox/initializers/vlan_groups.yml')
@ -14,7 +14,7 @@ optional_assocs = {
}
for params in vlan_groups:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in optional_assocs.items():
if assoc in params:
@ -26,15 +26,6 @@ for params in vlan_groups:
vlan_group, created = VLANGroup.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=vlan_group,
value=cf_value
)
vlan_group.custom_field_values.add(custom_field_value)
set_custom_fields_values(vlan_group, custom_field_data)
print("🏘️ Created VLAN Group", vlan_group.name)

View File

@ -1,9 +1,9 @@
import sys
from dcim.models import Site
from ipam.models import VLAN, VLANGroup, Role
from startup_script_utils import *
from tenancy.models import Tenant, TenantGroup
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
vlans = load_yaml('/opt/netbox/initializers/vlans.yml')
@ -19,7 +19,7 @@ optional_assocs = {
}
for params in vlans:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in optional_assocs.items():
if assoc in params:
@ -31,15 +31,6 @@ for params in vlans:
vlan, created = VLAN.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=vlan,
value=cf_value
)
vlan.custom_field_values.add(custom_field_value)
set_custom_fields_values(vlan, custom_field_data)
print("🏠 Created VLAN", vlan.name)

View File

@ -1,10 +1,10 @@
import sys
from dcim.models import Site
from ipam.models import Prefix, VLAN, Role, VRF
from tenancy.models import Tenant, TenantGroup
from extras.models import CustomField, CustomFieldValue
from netaddr import IPNetwork
from startup_script_utils import load_yaml
import sys
from startup_script_utils import *
from tenancy.models import Tenant, TenantGroup
prefixes = load_yaml('/opt/netbox/initializers/prefixes.yml')
@ -21,7 +21,8 @@ optional_assocs = {
}
for params in prefixes:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
params['prefix'] = IPNetwork(params['prefix'])
for assoc, details in optional_assocs.items():
@ -33,14 +34,6 @@ for params in prefixes:
prefix, created = Prefix.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=prefix,
value=cf_value
)
prefix.custom_field_values.add(custom_field_value)
set_custom_fields_values(prefix, custom_field_data)
print("📌 Created Prefix", prefix.prefix)

View File

@ -1,10 +1,10 @@
from dcim.models import Site, Platform, DeviceRole
from virtualization.models import Cluster, VirtualMachine
from tenancy.models import Tenant
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from dcim.models import Platform, DeviceRole
from startup_script_utils import *
from tenancy.models import Tenant
from virtualization.models import Cluster, VirtualMachine
virtual_machines = load_yaml('/opt/netbox/initializers/virtual_machines.yml')
if virtual_machines is None:
@ -21,7 +21,8 @@ optional_assocs = {
}
for params in virtual_machines:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
# primary ips are handled later in `270_primary_ips.py`
params.pop('primary_ip4', None)
params.pop('primary_ip6', None)
@ -42,15 +43,6 @@ for params in virtual_machines:
virtual_machine, created = VirtualMachine.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=virtual_machine,
value=cf_value
)
virtual_machine.custom_field_values.add(custom_field_value)
set_custom_fields_values(virtual_machine, custom_field_data)
print("🖥️ Created virtual machine", virtual_machine.name)

View File

@ -1,8 +1,8 @@
from virtualization.models import VirtualMachine, VMInterface
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
from startup_script_utils import *
from virtualization.models import VirtualMachine, VMInterface
interfaces = load_yaml('/opt/netbox/initializers/virtualization_interfaces.yml')
if interfaces is None:
@ -13,7 +13,7 @@ required_assocs = {
}
for params in interfaces:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
@ -24,15 +24,6 @@ for params in interfaces:
interface, created = VMInterface.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=interface,
value=cf_value
)
interface.custom_field_values.add(custom_field_value)
set_custom_fields_values(interface, custom_field_data)
print("🧷 Created interface", interface.name, interface.virtual_machine.name)

View File

@ -1,9 +1,9 @@
from dcim.models import Interface, Device
from extras.models import CustomField, CustomFieldValue
from startup_script_utils import load_yaml
import sys
interfaces= load_yaml('/opt/netbox/initializers/dcim_interfaces.yml')
from dcim.models import Interface, Device
from startup_script_utils import *
interfaces = load_yaml('/opt/netbox/initializers/dcim_interfaces.yml')
if interfaces is None:
sys.exit()
@ -13,7 +13,7 @@ required_assocs = {
}
for params in interfaces:
custom_fields = params.pop('custom_fields', None)
custom_field_data = pop_custom_fields(params)
for assoc, details in required_assocs.items():
model, field = details
@ -24,15 +24,6 @@ for params in interfaces:
interface, created = Interface.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=interface,
value=cf_value
)
interface.custom_field_values.add(custom_field_value)
set_custom_fields_values(interface, custom_field_data)
print("🧷 Created interface", interface.name, interface.device.name)

View File

@ -3,10 +3,9 @@ import sys
from dcim.models import Device, Interface
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from extras.models import CustomField, CustomFieldValue
from ipam.models import VRF, IPAddress
from netaddr import IPNetwork
from startup_script_utils import load_yaml
from startup_script_utils import *
from tenancy.models import Tenant
from virtualization.models import VirtualMachine, VMInterface
@ -25,9 +24,10 @@ vm_interface_ct = ContentType.objects.filter(Q(app_label='virtualization', model
interface_ct = ContentType.objects.filter(Q(app_label='dcim', model='interface')).first()
for params in ip_addresses:
custom_field_data = pop_custom_fields(params)
vm = params.pop('virtual_machine', None)
device = params.pop('device', None)
custom_fields = params.pop('custom_fields', None)
params['address'] = IPNetwork(params['address'])
if vm and device:
@ -55,15 +55,6 @@ for params in ip_addresses:
ip_address, created = IPAddress.objects.get_or_create(**params)
if created:
if custom_fields is not None:
for cf_name, cf_value in custom_fields.items():
custom_field = CustomField.objects.get(name=cf_name)
custom_field_value = CustomFieldValue.objects.create(
field=custom_field,
obj=ip_address,
value=cf_value
)
ip_address.custom_field_values.add(custom_field_value)
set_custom_fields_values(ip_address, custom_field_data)
print("🧬 Created IP Address", ip_address.address)

View File

@ -1,2 +1,3 @@
from .load_yaml import load_yaml
from .permissions import set_permissions
from .custom_fields import set_custom_fields_values, pop_custom_fields

View File

@ -0,0 +1,15 @@
def set_custom_fields_values(entity, custom_field_data):
if not custom_field_data:
return
entity.custom_field_data = custom_field_data
return entity.save()
def pop_custom_fields(params):
if 'custom_field_data' in params:
return params.pop('custom_field_data')
elif 'custom_fields' in params:
print("⚠️ Please rename 'custom_fields' to 'custom_field_data'!")
return params.pop('custom_fields')
return None

View File

@ -1,5 +1,6 @@
from ruamel.yaml import YAML
from pathlib import Path
from ruamel.yaml import YAML
def load_yaml(yaml_file: str):
yf = Path(yaml_file)