Custom fields support in Custom Scripts.

1,362 views
Skip to first unread message

Pravin V

unread,
May 20, 2021, 4:20:19 AM5/20/21
to NetBox
Hello Everyone,
I have a custom script which I use to add/update the devices.
I have created a few custom fields recently. Please advise how should I write in the code to add/update custom field values.

Here's my code.
from extras.scripts import *
from extras.models import CustomField, CustomFieldValue
from django.contrib.contenttypes.models import ContentType
from utilities.forms import APISelect

from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Platform, Site
from ipam.models import IPAddress
from tenancy.models import Tenant
from extras.models import Tag


name = "Inventory Tools"

class NewDeviceScript(Script):

    class Meta:
        name = "Add New Device to Inventory"
        description = "Provision a new host under a specified site"
        field_order = ['host_name', 'site_name', 'tenant', 'device_role', 'device_type', 'primary_ip', 'platform', 'status', 'tag']
        commit_default = False

    host_name = StringVar(
        label = 'Hostname',
        description = "Enter the devices hostname as it is configured on the box"
    )
    
    site_name = ObjectVar(
        label = 'DC/Office',
        queryset = Site.objects.all()
    )
    
    tenant = ObjectVar(
        label = 'Tenant',
        queryset = Tenant.objects.all()
    )
    
    device_role = ObjectVar(
        label = 'Device Role',
        description = "What is the function of this device?",
        queryset = DeviceRole.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/device-roles/',
            display_field = 'name'
        )
    )
    
    device_type = ObjectVar(
        label = 'Device Model',
        description = "What is the model for this device?",
        queryset = DeviceType.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/device-types/',
            display_field = 'display_name'
        )
    )
    
    platform = ObjectVar(
        required = False,
        label = 'Device OS',
        queryset = Platform.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/platforms/',
            display_field = 'display_name'
        )
    )
    
    primary_ip = IPAddressWithMaskVar(
        label = 'Primary IP',
        description = 'IP Address for MGMT Interface (i.e. alias entry)'
    )

    tag = ObjectVar(
        label = 'Tag',
        description = "Please select [no_backup] if you wish to exclude the backing up of this device",
        queryset = Tag.objects.all()
    )
    
    status_list = (
        ('active', 'Active'),
        ('inventory', 'Inventory'),
        ('planned', 'Planned'),
        ('reserved', 'Reserved'),
        ('staged', 'Staged'),
    )
    status = ChoiceVar(choices = status_list)

    def run(self, data, commit):

        device, created = Device.objects.get_or_create(
            name=data['host_name'],
            defaults={'status': data['status'],
                      'device_type': data['device_type'],
                      'device_role': data['device_role'],
                      'site': data['site_name'],
                      'tenant': data['tenant']}
        )
        if created == True:
            device.tags.add(data['tag'])
            device.save()
            self.log_success("Added new Device **{}**".format(device))
        else:
            self.log_warning("Existing Device **{}**".format(device))

        if created == True and data['primary_ip']:
            ipv4, created = IPAddress.objects.get_or_create(address=data['primary_ip'],defaults={'status':'active'})
            mgmt_int = Interface.objects.get(device=device,mgmt_only=True)

            if created == True:
                ipv4.save()
                self.log_success("IP Address **{}** created".format(ipv4))
            else:
                self.log_info("Existing IP Address **{}** to be used".format(ipv4))

            ipv4.interface = mgmt_int
            ipv4.save()
            self.log_success("IP Address `{}` assigned to interface **{}**".format(ipv4, mgmt_int))

            device.primary_ip4 = ipv4
            device.save()
            self.log_success("IP Address `{}` set as primary for **{}**".format(ipv4, device))

        if created == True and data['platform']:
            device.platform = data['platform']
            device.save()
            self.log_success("OS/Platform `{}` set for **{}**".format(device.platform, device))
        
        if created == True and data['tag']:
            device.tag = data['tag']
            device.save()
            self.log_success("tag `{}` set for **{}**".format(device.tag, device))


class UpdateDeviceScript(Script):

    class Meta:
        name = "Update/Decomm a Device from Inventory"
        description = "Update a host under a specified site"
        field_order = ['host_name', 'device_role', 'device_type', 'platform', 'status', 'tag']
        commit_default = False

    host_name = ObjectVar(
        label = 'Hostname',
        queryset = Device.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/devices/',
            display_field = 'name'
        )
    )
    device_role = ObjectVar(
        required = False,
        label = 'Device Role',
        description = "What is the new function of this device?",
        queryset = DeviceRole.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/device-roles/',
            display_field = 'name'
        )
    )
    device_type = ObjectVar(
        required = False,
        label = 'Device Model',
        description = "What is the new type of this device?",
        queryset = DeviceType.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/device-types/',
            display_field = 'display_name'
        )
    )
    platform = ObjectVar(
        required = False,
        label = 'Device OS',
        description = "What is the new platform of this device?",
        queryset = Platform.objects.all(),
        widget = APISelect(
            api_url = '/api/dcim/platforms/',
            display_field = 'display_name'
        )
    )

    tag = ObjectVar(
        label = 'Tag',
        description = "Please select [no_backup] if you wish to exclude the backing up of this device.",
        queryset = Tag.objects.all()
    )

    status_list = (
        ('',''),
        ('active', 'Active'),
        ('decommissioning', 'Decomm'),
        ('inventory', 'Inventory'),
        ('offline', 'Maintenance'),
        ('planned', 'Planned'),
        ('reserved', 'Reserved'),
        ('staged', 'Staged'),
    )
    status = ChoiceVar(
        required = False,
        choices = status_list
    )

    def run(self, data, commit):

        device = Device.objects.get(name=data['host_name'])

        if data['device_role']:
            device.device_role = data['device_role']
            device.save()
            self.log_success("Device Role `{}` set for **{}**".format(device.device_role, device))
        if data['device_type']:
            device.device_type = data['device_type']
            device.save()
            self.log_success("Device Type `{}` set for **{}**".format(device.device_type.display_name, device))
        if data['platform']:
            device.platform = data['platform']
            device.save()
            self.log_success("OS/Platform `{}` set for **{}**".format(device.platform, device))
        if data['status']:
            device.status = data['status']
            device.save()
            self.log_success("Device **{}** set to status `{}`".format(device, device.status))
        
        if data['tag']:
            device.tags.add(data['tag'])
            device.tag = data['tag']
            device.save()
            self.log_success("tag `{}` set for **{}**".format(device.tag, device))


Thanks in advance.

Pravin V

unread,
May 20, 2021, 4:21:46 AM5/20/21
to NetBox
Here is the detail of my custom field.

  owner:
    type: Text
    label: Owner
    description: Asset Owner
    required: False
    filter logic: Loose
    default: 
    weight: 100
    on_objects:
      - dcim.device

Brian Candler

unread,
May 20, 2021, 7:32:53 AM5/20/21
to NetBox
On Thursday, 20 May 2021 at 09:20:19 UTC+1 pravee...@gmail.com wrote:
I have a custom script which I use to add/update the devices.
I have created a few custom fields recently. Please advise how should I write in the code to add/update custom field values.

Which Netbox version are you using?  For v2.10 or later it's straightforward, see
 

Pravin V

unread,
May 20, 2021, 8:52:07 AM5/20/21
to Brian Candler, NetBox
Hello Brian,
I'm on 2.8.5. 

I have so many other scripts as well, not sure if I upgrade, how it will work with those scripts.

--
You received this message because you are subscribed to a topic in the Google Groups "NetBox" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/netbox-discuss/Tvc_aJOLZNk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to netbox-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netbox-discuss/99a0a6c4-aaae-4a69-9cf9-efc48678cfd9n%40googlegroups.com.

Brian Candler

unread,
May 20, 2021, 9:40:14 AM5/20/21
to NetBox
I do recommend you upgrade.  The changes to custom fields in 2.10 were substantial (and a huge improvement), and handling custom fields prior to 2.10 was painful.  The details are in the group archives, if you care to search: there was a separate model called CustomFieldValue that you had to manipulate explicitly, creating/updating/deleting instances of this model for each field value.  Now the custom fields just sit directly on each object as a single JSON attribute.

It's true that upgrading may have knock-on consequences: the Django data model and REST API are not stable between versions.  However as you're on 2.8 you've caught most of the big changes.  Check the release notes for v2.9.0, v2.10.0, v2.11.0.  The biggest issues I think you might hit are VMInterfaces in a separate model (2.9), and RackGroup changing to Location (2.11).

Pravin V

unread,
May 24, 2021, 5:09:22 AM5/24/21
to NetBox
Hello All,
This is how I wrote the code to achive -

    Owner = StringVar(
        label = 'Owner',
        description = "Enter the Owner detail of Device"
    )
    
    status_list = (
        ('active', 'Active'),
        ('inventory', 'Inventory'),
        ('planned', 'Planned'),
        ('reserved', 'Reserved'),
        ('staged', 'Staged'),
    )
    status = ChoiceVar(choices = status_list)

    def run(self, data, commit):

        device, created = Device.objects.get_or_create(
            name=data['host_name'],
#            owner=data['Owner'],
#            ct = ContentType.objects.get_for_model(Device),
#            cf = CustomField.objects.get(obj_type=ct, name="owner"),
            
            defaults={'status': data['status'],
                      'device_type': data['device_type'],
                      'device_role': data['device_role'],
                      'site': data['site_name'],
                      'tenant': data['tenant']}
        )

        deviceowner = Device.objects.get(name=data['host_name'])

        if created == True and data['Owner']:
            ct = ContentType.objects.get_for_model(Device)
            cf = CustomField.objects.get(obj_type=ct, name="owner"),
            cfv, created = CustomFieldValue.objects.get_or_create(field=cf, obj_type=ct, obj_id=deviceowner.id)

            if created == True:
                cfv.value=data['Owner']
                cfv.save()
                self.log_success("Owner Added: {}".format(cfv))
            else:
                self.log_warning("Owner Addition Failed: {}".format(cfv))
    Owner = StringVar(
        label = 'Owner',
        description = "Enter the Owner detail of Device"
    )

    tag = ObjectVar(
        label = 'Tag',
        description = "Please select [no_backup] if you wish to exclude the backing up of this device.",
        queryset = Tag.objects.all()
    )

    status_list = (
        ('',''),
        ('active', 'Active'),
        ('decommissioning', 'Decomm'),
        ('inventory', 'Inventory'),
        ('offline', 'Maintenance'),
        ('planned', 'Planned'),
        ('reserved', 'Reserved'),
        ('staged', 'Staged'),
    )
    status = ChoiceVar(
        required = False,
        choices = status_list
    )

    def run(self, data, commit):

        device = Device.objects.get(name=data['host_name'])

        ct = ContentType.objects.get_for_model(Device)
        cfowner = CustomField.objects.get(obj_type=ct, name='owner')
        
        cfvowner, created = CustomFieldValue.objects.get_or_create(field=cfowner, obj_type=ct, obj_id=device.id)

        #if created == True and data['Owner']:
            
            
            

        if data['Owner']:
            cfv.value=data['Owner']
            cfv.save()
            self.log_success("Owner Added: {}".format(cfv))
        else:
            self.log_warning("Owner Addition Failed: {}".format(cfv))

        if data['device_role']:
            device.device_role = data['device_role']
            device.save()
            self.log_success("Device Role `{}` set for **{}**".format(device.device_role, device))
        if data['device_type']:
            device.device_type = data['device_type']
            device.save()
            self.log_success("Device Type `{}` set for **{}**".format(device.device_type.display_name, device))
        if data['platform']:
            device.platform = data['platform']
            device.save()
            self.log_success("OS/Platform `{}` set for **{}**".format(device.platform, device))
        if data['status']:
            device.status = data['status']
            device.save()
            self.log_success("Device **{}** set to status `{}`".format(device, device.status))
        
        if data['tag']:
            device.tags.add(data['tag'])
            device.tag = data['tag']
            device.save()
            self.log_success("tag `{}` set for **{}**".format(device.tag, device))

Just incase anyone faces the same issue, they could also write the code the way I wrote.

Reply all
Reply to author
Forward
0 new messages