1515NB_TENANTS = "tenants"
1616NB_TENANT_GROUPS = "tenant_groups"
1717NB_CONTACTS = "contacts"
18+ NB_CONTACT_ASSIGNMENTS = "contact_assignments"
1819NB_CONTACT_GROUPS = "contact_groups"
1920NB_CONTACT_ROLES = "contact_roles"
2021
2122
23+ OBJECT_ENDPOINTS = {
24+ "circuit" : "circuits" ,
25+ "cluster" : "clusters" ,
26+ "cluster_group" : "cluster_groups" ,
27+ "contact" : "contacts" ,
28+ "contact_role" : "contact_roles" ,
29+ "device" : "devices" ,
30+ "location" : "locations" ,
31+ "manufacturer" : "manufacturers" ,
32+ "power_panel" : "power_panels" ,
33+ "provider" : "providers" ,
34+ "rack" : "racks" ,
35+ "region" : "regions" ,
36+ "site" : "sites" ,
37+ "site_group" : "site_groups" ,
38+ "tenant" : "tenants" ,
39+ "virtual_machine" : "virtual_machines" ,
40+ }
41+ # See https://netboxlabs.com/docs/netbox/features/contacts/#contacts-1
42+ OBJECT_TYPES = {
43+ "circuit" : "circuits.circuit" ,
44+ "cluster" : "virtualization.cluster" ,
45+ "cluster_group" : "virtualization.clustergroup" ,
46+ "device" : "dcim.device" ,
47+ "location" : "dcim.location" ,
48+ "manufacturer" : "dcim.manufacturer" ,
49+ "power_panel" : "dcim.powerpanel" ,
50+ "provider" : "circuits.provider" ,
51+ "rack" : "dcim.rack" ,
52+ "region" : "dcim.region" ,
53+ "site" : "dcim.site" ,
54+ "site_group" : "dcim.sitegroup" ,
55+ "tenant" : "tenancy.tenant" ,
56+ "virtual_machine" : "virtualization.virtualmachine" ,
57+ }
58+ OBJECT_NAME_FIELD = {
59+ "circuit" : "cid" ,
60+ # If unspecified, the default is "name"
61+ }
62+
63+
2264class NetboxTenancyModule (NetboxModule ):
2365 def __init__ (self , module , endpoint ):
2466 super ().__init__ (module , endpoint )
2567
68+ def get_object_by_name (self , object_type : str , object_name : str ):
69+ endpoint = OBJECT_ENDPOINTS [object_type ]
70+ app = self ._find_app (endpoint )
71+ nb_app = getattr (self .nb , app )
72+ nb_endpoint = getattr (nb_app , endpoint )
73+
74+ name_field = OBJECT_NAME_FIELD .get (object_type )
75+ if name_field is None :
76+ name_field = "name"
77+
78+ query_params = {name_field : object_name }
79+ result = self ._nb_endpoint_get (nb_endpoint , query_params , object_name )
80+ if not result :
81+ self ._handle_errors (
82+ msg = "Could not resolve id of %s: %s" % (object_type , object_name )
83+ )
84+ return result
85+
2686 def run (self ):
2787 """
2888 This function should have all necessary code for endpoints within the application
@@ -31,7 +91,9 @@ def run(self):
3191 - tenants
3292 - tenant groups
3393 - contacts
94+ - contact assignments
3495 - contact groups
96+ - contact roles
3597 """
3698 # Used to dynamically set key when returning results
3799 endpoint_name = ENDPOINT_NAME_MAPPING [self .endpoint ]
@@ -45,6 +107,23 @@ def run(self):
45107
46108 data = self .data
47109
110+ # For ease and consistency of use, the contact assignment module takes the name of the contact, role, and target object rather than an ID or slug.
111+ # We must massage the data a bit by looking up the ID corresponding to the given name so that we can pass the ID to the API.
112+ if self .endpoint == "contact_assignments" :
113+ # Not an identifier, just to populate the message field
114+ name = f"{ data ['contact' ]} -> { data ['object_name' ]} "
115+
116+ object_type = data ["object_type" ]
117+ obj = self .get_object_by_name (object_type , data ["object_name" ])
118+ contact = self .get_object_by_name ("contact" , data ["contact" ])
119+ role = self .get_object_by_name ("contact_role" , data ["role" ])
120+
121+ data ["object_type" ] = OBJECT_TYPES [object_type ]
122+ data ["object_id" ] = obj .id
123+ del data ["object_name" ] # object_id replaces object_name
124+ data ["contact" ] = contact .id
125+ data ["role" ] = role .id
126+
48127 # Used for msg output
49128 if data .get ("name" ):
50129 name = data ["name" ]
@@ -58,6 +137,12 @@ def run(self):
58137 object_query_params = self ._build_query_params (
59138 endpoint_name , data , user_query_params
60139 )
140+
141+ # For some reason, when creating a new contact assignment, role must be an ID
142+ # But when querying contact assignments, the role must be a slug
143+ if self .endpoint == "contact_assignments" :
144+ object_query_params ["role" ] = role .slug
145+
61146 self .nb_object = self ._nb_endpoint_get (nb_endpoint , object_query_params , name )
62147
63148 if self .state == "present" :
0 commit comments