Skip to content

Netbox Get Interfaces Task¤

task api name: get_interfaces

Get Interfaces Sample Usage¤

NORFAB Netbox Get Interfaces Command Shell Reference¤

NorFab shell supports these command options for Netbox get_interfaces task:

nf#man tree netbox.get.interfaces
root
└── netbox:    Netbox service
    └── get:    Query data from Netbox
        └── interfaces:    Query Netbox device interfaces data
            ├── instance:    Netbox instance name to target
            ├── workers:    Filter worker to target, default 'any'
            ├── timeout:    Job timeout
            ├── *devices:    Devices to retrieve interface for
            ├── ip-addresses:    Retrieves interface IP addresses
            ├── inventory-items:    Retrieves interface inventory items
            └── dry-run:    Only return query content, do not run it
nf#

Python API Reference¤

Retrieve device interfaces from Netbox using Pynetbox REST API.

Parameters:

Name Type Description Default
job Job

NorFab Job object containing relevant metadata

required
instance str

Netbox instance name.

None
devices list

List of devices to retrieve interfaces for.

None
interface_list list

List of interface names to retrieve.

None
interface_regex str

Regex pattern to match interfaces by name.

None
ip_addresses bool

If True, retrieves interface IPs. Defaults to False.

False
inventory_items bool

If True, retrieves interface inventory items. Defaults to False.

False
dry_run bool

If True, only return REST filter params, do not run. Defaults to False.

False
cache Union[None, bool, str]

True - use cache if up to date; False - skip cache; "refresh" - fetch and overwrite cache; "force" - use cache without staleness check

None
brief bool

If True, return stripped-down interface data for MCP/LLM context window optimization. Full data is still fetched; brief mode only affects what is returned.

False

Returns:

Name Type Description
dict Result

Dictionary keyed by device name with interface details.

This task performs no filtering of data returned by pynetbox.dcim.interfaces.filter, as a result use netbox REST API explorer to understand resulting data structure, REST API browser available at http://<netbox url>/api/dcim/interfaces/. In addition use nfcli to explore interfaces data:

nf#netbox get interfaces devices fceos4 interface-list eth201 | kv

netbox-worker-1.1.fceos4.eth201.display: eth201
netbox-worker-1.1.fceos4.eth201.device.name: fceos4
...

Can also pipe results through json, yaml, nested or pprint formatters to output results in certain format.

Source code in norfab\workers\netbox_worker\interfaces_tasks.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
@Task(
    fastapi={"methods": ["GET"], "schema": NetboxFastApiArgs.model_json_schema()},
    input=GetInterfacesInput,
    output=GetInterfacesResult,
    mcp={
        "annotations": {
            "title": "Get Interfaces",
            "readOnlyHint": True,
            "destructiveHint": False,
            "idempotentHint": True,
            "openWorldHint": True,
        }
    },
)
def get_interfaces(
    self,
    job: Job,
    instance: Union[None, str] = None,
    devices: Union[None, list] = None,
    interface_list: Union[None, list] = None,
    interface_regex: Union[None, str] = None,
    ip_addresses: bool = False,
    inventory_items: bool = False,
    dry_run: bool = False,
    cache: Union[None, bool, str] = None,
    branch: Union[None, str] = None,
    brief: bool = False,
) -> Result:
    """
    Retrieve device interfaces from Netbox using Pynetbox REST API.

    Args:
        job: NorFab Job object containing relevant metadata
        instance (str, optional): Netbox instance name.
        devices (list, optional): List of devices to retrieve interfaces for.
        interface_list (list, optional): List of interface names to retrieve.
        interface_regex (str, optional): Regex pattern to match interfaces by name.
        ip_addresses (bool, optional): If True, retrieves interface IPs. Defaults to False.
        inventory_items (bool, optional): If True, retrieves interface inventory items. Defaults to False.
        dry_run (bool, optional): If True, only return REST filter params, do not run. Defaults to False.
        cache: ``True`` - use cache if up to date; ``False`` - skip cache;
            ``"refresh"`` - fetch and overwrite cache; ``"force"`` - use cache without staleness check
        brief (bool, optional): If True, return stripped-down interface data for MCP/LLM context
            window optimization. Full data is still fetched; brief mode only affects what is returned.

    Returns:
        dict: Dictionary keyed by device name with interface details.

    This task performs no filtering of data returned by `pynetbox.dcim.interfaces.filter`,
    as a result use netbox REST API explorer to understand resulting data structure, REST
    API browser available at `http://<netbox url>/api/dcim/interfaces/`. In addition
    use nfcli to explore interfaces data:

    ```
    nf#netbox get interfaces devices fceos4 interface-list eth201 | kv

    netbox-worker-1.1.fceos4.eth201.display: eth201
    netbox-worker-1.1.fceos4.eth201.device.name: fceos4
    ...
    ```

    Can also pipe results through `json`, `yaml`, `nested` or `pprint`  formatters to output
    results in  certain format.
    """
    instance = instance or self.default_instance
    nb = self._get_pynetbox(instance, branch=branch)
    devices = devices or []
    cache = self.cache_use if cache is None else cache
    log.info(
        f"{self.name} - Get interfaces: Fetching interfaces for {len(devices)} device(s) from '{instance}'"
    )
    ret = Result(
        task=f"{self.name}:get_interfaces_pynetbox",
        result={d: {} for d in devices},
        resources=[instance],
    )
    filter_params = {}
    all_interfaces = []
    children_by_parent_id = {}  # parent_id -> [child intf, ...]
    member_intf_by_lag_id = {}  # lag_id    -> [member intf, ...]
    ip_by_intf_id = {}
    inv_by_intf_id = {}
    last_updated_by_device = {}

    # build REST filter params
    if devices:
        filter_params["device"] = devices
    if interface_list:
        filter_params["name"] = interface_list
    if interface_regex:
        filter_params["name__regex"] = interface_regex

    if dry_run is True:
        ret.result = {"filter_params": filter_params}
        ret.dry_run = True
        return ret

    job.event(f"retrieving interfaces for {len(devices)} device(s)")

    devices_to_fetch = list(devices)

    if cache == True or cache == "force":
        job.event(f"checking cache for {len(devices)} device(s)")
        # quick REST call to get current last_updated for all matching interfaces
        result = nb.dcim.interfaces.filter(
            **filter_params, fields="name,last_updated,device"
        )
        # build per-device last_updated map
        for intf in result:
            last_updated_by_device.setdefault(intf.device.name, {})[
                intf.name
            ] = intf.last_updated

        self.cache.expire()  # remove expired items from cache
        devices_to_fetch = []
        for device_name, intf_last_updated in last_updated_by_device.items():
            device_cache_key = f"get_interfaces::{device_name}"
            if device_cache_key in self.cache and (
                cache == "force"
                or all(
                    self.cache[device_cache_key]
                    .get(intf_name, {})
                    .get("last_updated")
                    == lu
                    for intf_name, lu in intf_last_updated.items()
                )
            ):
                # serve requested interfaces from cache
                for intf_name in intf_last_updated.keys():
                    ret.result[device_name][intf_name] = self.cache[
                        device_cache_key
                    ][intf_name]
                job.event(
                    f"serving '{device_name}' interfaces from cache ({len(intf_last_updated)} interface(s))"
                )
            else:
                devices_to_fetch.append(device_name)
                job.event(
                    f"'{device_name}' cache miss or stale, fetching fresh data"
                )
    elif cache == False or cache == "refresh":
        pass  # fetch all devices fresh

    # build fetch filter params restricted to devices needing fresh data
    if devices and cache in (True, "force"):
        fetch_filter_params = {**filter_params, "device": devices_to_fetch}
    else:
        fetch_filter_params = filter_params

    # fetch all matching interfaces in one call
    if devices_to_fetch:
        job.event(
            f"fetching interfaces from NetBox for {len(devices_to_fetch)} device(s)"
        )
        all_interfaces = list(nb.dcim.interfaces.filter(**fetch_filter_params))
        job.event(f"retrieved {len(all_interfaces)} interface(s) from NetBox")

    if not all_interfaces and not any(ret.result.get(d) for d in devices):
        raise Exception(
            f"{self.name} - no interfaces data returned by '{instance}' "
            f"for devices {', '.join(devices)}"
        )

    # build relationship lookup maps from fetched data
    for intf in all_interfaces:
        if intf.parent:
            children_by_parent_id.setdefault(intf.parent.id, []).append(intf)
        if intf.lag:
            member_intf_by_lag_id.setdefault(intf.lag.id, []).append(intf)

    # fetch IP addresses if requested (one bulk call keyed by assigned_object_id)
    if ip_addresses and devices_to_fetch:
        job.event(f"fetching IP addresses for {len(devices_to_fetch)} device(s)")
        for ip in self.bulk_filter(
            nb.ipam.ip_addresses, "device", devices_to_fetch
        ):
            if (
                ip.assigned_object_id
                and ip.assigned_object_type == "dcim.interface"
            ):
                ip_by_intf_id.setdefault(ip.assigned_object_id, []).append(dict(ip))

    # fetch inventory items if requested (one bulk call keyed by component_id)
    if inventory_items and devices_to_fetch:
        job.event(f"fetching inventory items for {len(devices_to_fetch)} device(s)")
        for item in self.bulk_filter(
            nb.dcim.inventory_items, "device", devices_to_fetch
        ):
            if item.component_id and item.component_type == "dcim.interface":
                inv_by_intf_id.setdefault(item.component_id, []).append(dict(item))

    # transform pynetbox records into result dict keyed by device / interface name
    for intf in all_interfaces:
        device_name = intf.device.name
        if device_name not in ret.result:  # Netbox issue #16299
            continue

        intf_data = dict(intf)
        # add extra fields
        intf_data["child_interfaces"] = [
            {
                "name": c.name,
                "vrf": c.vrf.name if c.vrf else None,
                "ip_addresses": ip_by_intf_id.get(c.id, []),
            }
            for c in children_by_parent_id.get(intf.id, [])
        ]
        intf_data["member_interfaces"] = [
            m.name for m in member_intf_by_lag_id.get(intf.id, [])
        ]
        intf_data["ip_addresses"] = ip_by_intf_id.get(intf.id, [])
        intf_data["inventory_items"] = inv_by_intf_id.get(intf.id, [])
        ret.result[device_name][intf.name] = intf_data

    # cache freshly fetched interfaces per device
    if cache != False:
        job.event(f"caching interfaces data for {len(devices_to_fetch)} device(s)")
        for device_name in devices_to_fetch:
            if device_name in ret.result:
                cache_key = f"get_interfaces::{device_name}"
                self.cache.set(
                    cache_key,
                    ret.result[device_name],
                    expire=self.cache_ttl,
                )

    if brief:
        ret.result = make_interfaces_brief(ret.result)

    return ret