Custom Scripts - Module & Script Attributes #9628

Closed
opened 2025-12-29 21:20:02 +01:00 by adam · 6 comments
Owner

Originally created by @Arshnika on GitHub (May 9, 2024).

Originally assigned to: @arthanson on GitHub.

Deployment Type

Self-hosted

NetBox Version

v4.0.0

Python Version

3.10

Steps to Reproduce

  1. Create a custom script module including multiple scripts
  2. Define a "name" for the module
  3. Define a "name" and "description" for the scripts.
  4. Add the script module to NetBox

Expected Behavior

When the script module is added, NetBox should reflect all configured attributes on the GUI interface.

image

Observed Behavior

  • NetBox uses the script module file name as the name instead of the defined "name" attribute
  • NetBox uses the script class name as the name of the script instead of the defined "name" attribute
  • NetBox does not show the defined "description" attribute.

Custom_Scripts

Originally created by @Arshnika on GitHub (May 9, 2024). Originally assigned to: @arthanson on GitHub. ### Deployment Type Self-hosted ### NetBox Version v4.0.0 ### Python Version 3.10 ### Steps to Reproduce 1. Create a custom script module including multiple scripts 2. Define a "name" for the module 3. Define a "name" and "description" for the scripts. 4. Add the script module to NetBox ### Expected Behavior When the script module is added, NetBox should reflect all configured attributes on the GUI interface. ![image](https://github.com/netbox-community/netbox/assets/95662911/d62c3fe2-d1d6-406b-9bcf-258557c8f607) ### Observed Behavior - NetBox uses the script module file name as the name instead of the defined "name" attribute - NetBox uses the script class name as the name of the script instead of the defined "name" attribute - NetBox does not show the defined "description" attribute. ![Custom_Scripts](https://github.com/netbox-community/netbox/assets/95662911/e62cb838-c0a4-4c86-a1fe-e1971fffab2a)
adam added the type: bugstatus: acceptedseverity: low labels 2025-12-29 21:20:02 +01:00
adam closed this issue 2025-12-29 21:20:02 +01:00
Author
Owner

@abhi1693 commented on GitHub (May 9, 2024):

Please share a sample script code for us to reproduce this.

@abhi1693 commented on GitHub (May 9, 2024): Please share a sample script code for us to reproduce this.
Author
Owner

@Arshnika commented on GitHub (May 10, 2024):

@abhi1693,

#!/usr/bin/env python3
from typing import Protocol
from extras.scripts import *
from extras.models import Tag
from dcim.choices import DeviceStatusChoices, SiteStatusChoices
from ipam.choices import PrefixStatusChoices, VLANStatusChoices
from ipam.models import IPAddress, Prefix, VLAN
from circuits.choices import CircuitStatusChoices
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site, Region, Interface, SiteGroup
from dcim.utils import ContentType
from tenancy.models import Tenant, Contact, ContactAssignment
from timezone_field import TimeZoneField, TimeZoneFormField
from django.utils.text import slugify
from django.forms import PasswordInput
from utilities import querysets
from extras.models import Tag
from circuits.models import Provider, CircuitType, Circuit, CircuitTermination
from meraki.exceptions import APIError

class Meraki_Get_CDP_LLDP_Neighbors(Script):

    class Meta:
            name = 'Get CDP/LLDP Neighbors'
            description = 'Get CDP/LLDP neighbors from Meraki Dashboard API'
            commit_default = False
            #field_order = ['site_name', 'switch_count', 'switch_model']

    #-----------------------------------------------------------
    #--- Get Values from HTML Form -----------------------------
    #-----------------------------------------------------------

    Store_Name = ObjectVar(model=Site, label='Store Name:')
    Protocols = (
        ('lldp', 'LLDP'),
        ('cdp', 'CDP'),
    )
    Protocol = ChoiceVar(choices=Protocols)

    def run(self, data, commit):
        
        import pynetbox
        import meraki
        import requests
        import os
        from dotenv import load_dotenv
        requests.packages.urllib3.disable_warnings()

        directory = os.path.dirname(os.path.abspath(__file__))
        dotenv_path = directory + '/.env'
        load_dotenv(dotenv_path)
        API_KEY_Meraki = os.environ.get("meraki_api", "default")
        API_KEY_NetBox = os.environ.get("netbox_api", "default")

        def printNei(m, serial, name, protocol):

            #----------------------------------------------------------
            #---- Print device neighbors. -----------------------------
            #---- Filters per protocol if protocol is provided. -------
            #----------------------------------------------------------
            
            CDP_Neighbors = ''
            LLDP_Neighbors = ''

            dp = m.devices.getDeviceLldpCdp(serial)
            for port in dp.get("ports", []):
                for proto in dp.get("ports").get(port):
                    nei = dp.get("ports").get(port).get(proto)
                    ip = nei.get("address", nei.get("managementAddress"))
                    if proto == "cdp" and protocol != "lldp":
                        systemName = nei.get("deviceId", "noname")
                        CDP_Neighbors += f'{proto.upper():4} LOCAL {name[:35]:35} SOURCE-PORT {nei.get("sourcePort"):8} REMOTE DEVICE {systemName.split(".")[0][:45]:45} REMOTE PORT {nei.get("portId"):24} REMOTE IP {ip}'
                        CDP_Neighbors += '\n'
                    elif proto == "lldp" and protocol != "cdp":
                        systemName = nei.get("systemName", "noname")
                        LLDP_Neighbors += f'{proto.upper():4} LOCAL {name[:35]:35} SOURCE-PORT {nei.get("sourcePort"):8} REMOTE DEVICE {systemName.split(".")[0][:45]:45} REMOTE PORT {nei.get("portId"):24} REMOTE IP {ip}'
                        LLDP_Neighbors += '\n'
            
            if protocol == 'cdp':
                return CDP_Neighbors
            else:
                return LLDP_Neighbors

        def main():

            org_id = '602356450160806405'
            protocol = data['Protocol']
            store_name = str(data['Store_Name'])

            nb = pynetbox.api (
                'https://netbox.sul.local',
                token=API_KEY_NetBox,
                threading = True,
            )
            nb.http_session.verify = False

            m = meraki.DashboardAPI(API_KEY_Meraki,suppress_logging=True)
            org_networks = m.organizations.getOrganizationNetworks(org_id)

            nb_site = nb.dcim.sites.get(name=store_name)
            nb_mer_sitename = nb_site.custom_fields['meraki_sitename']

            Neighbors = ''
            for net in org_networks:
                mer_netid = net['id']
                mer_sitename = net['name'][+9:]
                
                if mer_sitename == nb_mer_sitename:
                    mer_devices = m.networks.getNetworkDevices(mer_netid)
                    for mer_device in mer_devices:
                        serial, name = mer_device.get("serial"), mer_device.get(
                            "name", mer_device.get("serial", "MISSING")
                        )
                        Neighbors += printNei(m, serial, name, protocol)
                    break
            
            if Neighbors == '':
                Neighbors = 'Operation was not completed.\nEither the Meraki sitename is not the same as the Meraki sitename in NetBox or the Meraki site has not been created in NetBox.'
            return Neighbors
            
        return main()
@Arshnika commented on GitHub (May 10, 2024): @abhi1693, ``` #!/usr/bin/env python3 from typing import Protocol from extras.scripts import * from extras.models import Tag from dcim.choices import DeviceStatusChoices, SiteStatusChoices from ipam.choices import PrefixStatusChoices, VLANStatusChoices from ipam.models import IPAddress, Prefix, VLAN from circuits.choices import CircuitStatusChoices from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site, Region, Interface, SiteGroup from dcim.utils import ContentType from tenancy.models import Tenant, Contact, ContactAssignment from timezone_field import TimeZoneField, TimeZoneFormField from django.utils.text import slugify from django.forms import PasswordInput from utilities import querysets from extras.models import Tag from circuits.models import Provider, CircuitType, Circuit, CircuitTermination from meraki.exceptions import APIError class Meraki_Get_CDP_LLDP_Neighbors(Script): class Meta: name = 'Get CDP/LLDP Neighbors' description = 'Get CDP/LLDP neighbors from Meraki Dashboard API' commit_default = False #field_order = ['site_name', 'switch_count', 'switch_model'] #----------------------------------------------------------- #--- Get Values from HTML Form ----------------------------- #----------------------------------------------------------- Store_Name = ObjectVar(model=Site, label='Store Name:') Protocols = ( ('lldp', 'LLDP'), ('cdp', 'CDP'), ) Protocol = ChoiceVar(choices=Protocols) def run(self, data, commit): import pynetbox import meraki import requests import os from dotenv import load_dotenv requests.packages.urllib3.disable_warnings() directory = os.path.dirname(os.path.abspath(__file__)) dotenv_path = directory + '/.env' load_dotenv(dotenv_path) API_KEY_Meraki = os.environ.get("meraki_api", "default") API_KEY_NetBox = os.environ.get("netbox_api", "default") def printNei(m, serial, name, protocol): #---------------------------------------------------------- #---- Print device neighbors. ----------------------------- #---- Filters per protocol if protocol is provided. ------- #---------------------------------------------------------- CDP_Neighbors = '' LLDP_Neighbors = '' dp = m.devices.getDeviceLldpCdp(serial) for port in dp.get("ports", []): for proto in dp.get("ports").get(port): nei = dp.get("ports").get(port).get(proto) ip = nei.get("address", nei.get("managementAddress")) if proto == "cdp" and protocol != "lldp": systemName = nei.get("deviceId", "noname") CDP_Neighbors += f'{proto.upper():4} LOCAL {name[:35]:35} SOURCE-PORT {nei.get("sourcePort"):8} REMOTE DEVICE {systemName.split(".")[0][:45]:45} REMOTE PORT {nei.get("portId"):24} REMOTE IP {ip}' CDP_Neighbors += '\n' elif proto == "lldp" and protocol != "cdp": systemName = nei.get("systemName", "noname") LLDP_Neighbors += f'{proto.upper():4} LOCAL {name[:35]:35} SOURCE-PORT {nei.get("sourcePort"):8} REMOTE DEVICE {systemName.split(".")[0][:45]:45} REMOTE PORT {nei.get("portId"):24} REMOTE IP {ip}' LLDP_Neighbors += '\n' if protocol == 'cdp': return CDP_Neighbors else: return LLDP_Neighbors def main(): org_id = '602356450160806405' protocol = data['Protocol'] store_name = str(data['Store_Name']) nb = pynetbox.api ( 'https://netbox.sul.local', token=API_KEY_NetBox, threading = True, ) nb.http_session.verify = False m = meraki.DashboardAPI(API_KEY_Meraki,suppress_logging=True) org_networks = m.organizations.getOrganizationNetworks(org_id) nb_site = nb.dcim.sites.get(name=store_name) nb_mer_sitename = nb_site.custom_fields['meraki_sitename'] Neighbors = '' for net in org_networks: mer_netid = net['id'] mer_sitename = net['name'][+9:] if mer_sitename == nb_mer_sitename: mer_devices = m.networks.getNetworkDevices(mer_netid) for mer_device in mer_devices: serial, name = mer_device.get("serial"), mer_device.get( "name", mer_device.get("serial", "MISSING") ) Neighbors += printNei(m, serial, name, protocol) break if Neighbors == '': Neighbors = 'Operation was not completed.\nEither the Meraki sitename is not the same as the Meraki sitename in NetBox or the Meraki site has not been created in NetBox.' return Neighbors return main() ```
Author
Owner

@Arshnika commented on GitHub (May 10, 2024):

Hi @abhi1693,

BTW, we have also upgraded NetBox to version 4.0.1 but to no avail.

@Arshnika commented on GitHub (May 10, 2024): Hi @abhi1693, BTW, we have also upgraded NetBox to version 4.0.1 but to no avail.
Author
Owner

@Arshnika commented on GitHub (May 13, 2024):

Hi @abhi1693,

Just wondering whether you have been able to find the root cause of the issue?

Thanks.

@Arshnika commented on GitHub (May 13, 2024): Hi @abhi1693, Just wondering whether you have been able to find the root cause of the issue? Thanks.
Author
Owner

@hendrikbl commented on GitHub (May 15, 2024):

We just updated to v4.0.2 and are facing the same issue. Neither the modules name attribute nor the scripts Meta name and description value are used.

@hendrikbl commented on GitHub (May 15, 2024): We just updated to v4.0.2 and are facing the same issue. Neither the modules `name` attribute nor the scripts Meta `name` and `description` value are used.
Author
Owner

@jeremystretch commented on GitHub (May 22, 2024):

@arthanson please update the status & severity for this bug report.

@jeremystretch commented on GitHub (May 22, 2024): @arthanson please update the status & severity for this bug report.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#9628