Coverage for netbox_interface_name_rules / signals.py: 90.70%

39 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-10-16 10:13 +0000

1# SPDX-License-Identifier: Apache-2.0 

2# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com> 

3"""Django receivers: module, module bay and device saves go to the rename-trigger lifecycle with their alias. 

4 

5The receivers do not read ``raw``: a raw save, such as a fixture load, is a rename trigger too. 

6""" 

7 

8import logging 

9 

10from django.db.models.signals import post_save, pre_save 

11from django.dispatch import receiver 

12 

13from . import rename_triggers 

14 

15logger = logging.getLogger("netbox_interface_name_rules") 

16 

17# No Interface receiver: NetBox creates module interfaces with bulk_create(), so pre_save never runs. 

18 

19 

20@receiver(pre_save, sender="dcim.Module", dispatch_uid="interface_name_rules_pre_save_module") 

21def on_module_pre_save(sender, instance, using, **kwargs): 

22 """Pass a module save to the rename-trigger lifecycle before the row is written.""" 

23 rename_triggers.before_save(sender, instance, using) 

24 

25 

26@receiver(post_save, sender="dcim.Module", dispatch_uid="interface_name_rules_post_save_module") 

27def on_module_saved(sender, instance, created, using, **kwargs): 

28 """Pass a module save to the rename-trigger lifecycle after the row is written.""" 

29 rename_triggers.after_save(sender, instance, created, using) 

30 

31 

32@receiver(pre_save, sender="dcim.ModuleBay", dispatch_uid="interface_name_rules_pre_save_module_bay") 

33def on_module_bay_pre_save(sender, instance, using, **kwargs): 

34 """Pass a module bay save to the rename-trigger lifecycle before the row is written.""" 

35 rename_triggers.before_save(sender, instance, using) 

36 

37 

38@receiver(post_save, sender="dcim.ModuleBay", dispatch_uid="interface_name_rules_post_save_module_bay") 

39def on_module_bay_saved(sender, instance, created, using, **kwargs): 

40 """Pass a module bay save to the rename-trigger lifecycle after the row is written.""" 

41 rename_triggers.after_save(sender, instance, created, using) 

42 

43 

44@receiver(pre_save, sender="dcim.Device", dispatch_uid="interface_name_rules_pre_save_device") 

45def on_device_pre_save(sender, instance, using, **kwargs): 

46 """Pass a device save to the rename-trigger lifecycle before the row is written.""" 

47 rename_triggers.before_save(sender, instance, using) 

48 

49 

50@receiver(post_save, sender="dcim.Device", dispatch_uid="interface_name_rules_post_save_device") 

51def on_device_saved(sender, instance, created, using, **kwargs): 

52 """Pass a device save to the rename-trigger lifecycle after the row is written.""" 

53 rename_triggers.after_save(sender, instance, created, using) 

54 

55 

56# --------------------------------------------------------------------------- 

57# Cross-plugin integration: netbox-librenms-plugin name prediction 

58# --------------------------------------------------------------------------- 

59# When netbox-librenms-plugin is installed it exposes a Signal that asks 

60# subscribers to rewrite the list of interface names it predicts a module's 

61# templates will produce. Without this rewrite, its module-adoption lookup 

62# would only see NetBox's raw template output and miss interfaces we renamed 

63# after install. The import is guarded so INR works fine when librenms-plugin 

64# is absent. 

65 

66try: 

67 from netbox_librenms_plugin.signals import ( 

68 predict_module_interface_names as _librenms_predict_signal, 

69 ) 

70except ImportError: 

71 _librenms_predict_signal = None 

72 

73 

74def on_librenms_predict_module_interface_names(sender, device, module, names, **kwargs): 

75 """Rewrite librenms-plugin's predicted template names through our rule engine. 

76 

77 Always defined so it can be imported and tested in isolation even when 

78 netbox-librenms-plugin is not installed. Signal registration is guarded 

79 below so it only connects when the signal actually exists. 

80 """ 

81 module_bay = getattr(module, "module_bay", None) 

82 if module_bay is None: 82 ↛ 83line 82 didn't jump to line 83 because the condition on line 82 was never true

83 return None 

84 try: 

85 from .engine import predict_rule_output 

86 

87 return predict_rule_output(module, module_bay, names) 

88 except Exception: 

89 logger.exception( 

90 "predict_rule_output failed for module pk=%s; leaving names unchanged", 

91 getattr(module, "pk", "?"), 

92 ) 

93 return None 

94 

95 

96if _librenms_predict_signal is not None: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true

97 receiver( 

98 _librenms_predict_signal, 

99 dispatch_uid="interface_name_rules_predict_module_interface_names", 

100 )(on_librenms_predict_module_interface_names)