6
6
try :
7
7
import pynetbox
8
8
from netaddr import *
9
- except ImportError , e :
9
+ except ImportError as e :
10
10
sys .exit ("Please install required python modules: pynetbox netaddr" )
11
11
12
- class NetboxInventory :
12
+ class NetboxInventory ( dict ) :
13
13
def __init__ (self ):
14
+ super (NetboxInventory , self ).__init__ ()
14
15
self .config = self .load_config ()
15
16
16
- if self .config [ 'debug' ] :
17
+ if self .config . get ( 'debug' , False ) :
17
18
logging .basicConfig (filename = 'netbox_inventory.log' , level = logging .DEBUG )
18
19
19
20
self .netbox = self .connect_netbox (self .config ['url' ], self .config ['token' ])
20
- self .result = {
21
+ self .update ( {
21
22
"all" : {
22
23
"hosts" : []
23
24
},
@@ -30,7 +31,7 @@ def __init__(self):
30
31
"vlans" : {}
31
32
}
32
33
}
33
- }
34
+ })
34
35
35
36
def load_config (self ):
36
37
with open (os .path .join (os .path .dirname (__file__ ), "netbox.yml" ), 'r' ) as stream :
@@ -50,12 +51,16 @@ def connect_netbox(self, url, token):
50
51
def lookup_ip_for_interfaces (self , interfaces , device ):
51
52
ips = self .netbox .ipam .ip_addresses .filter (device = device )
52
53
for ip in ips :
54
+ if (str (ip .address ) == str (device .primary_ip4 ) or (str (ip .address ) == str (device .primary_ip6 ))):
55
+ ip .isPrimary = True
56
+ else :
57
+ ip .isPrimary = False
53
58
if ip .family == 4 :
54
59
interfaces [ip .interface .name ]['v4_address' ].append (str (ip .address ))
55
- self .create_dns_records_v4 (ip .interface .name , device , str ( ip . address ) )
60
+ self .create_dns_records_v4 (ip .interface .name , device , ip )
56
61
elif ip .family == 6 :
57
62
interfaces [ip .interface .name ]['v6_address' ].append (str (ip .address ))
58
- self .create_dns_records_v6 (ip .interface .name , device , str ( ip . address ) )
63
+ self .create_dns_records_v6 (ip .interface .name , device , ip )
59
64
60
65
return interfaces
61
66
@@ -71,32 +76,64 @@ def create_dns_record(self, domain, record_name, record_type, value):
71
76
}
72
77
73
78
def create_dns_records_v4 (self , interface , device , ip ):
79
+ try :
80
+ ipIsPrimary = ip .isPrimary
81
+ except :
82
+ ipIsPrimary = False
83
+ ip = str (ip .address )
74
84
ip = str (IPNetwork (ip ).ip )
75
85
pre = ip .split ("." )
76
86
record_name = pre .pop ()
77
87
domain = "." .join (pre [::- 1 ] + ['in-addr' , 'arpa' ])
78
88
intf = self .sanitize_dns_name (interface )
79
89
80
- self .result ['ungrouped' ]['vars' ]['dns_entries' ].append (
81
- self .create_dns_record (domain , record_name , "PTR" , "." .join ([intf , device .name , self .config ['dns_name' ]]))
82
- )
83
- self .result ['ungrouped' ]['vars' ]['dns_entries' ].append (
84
- self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "A" , ip )
85
- )
90
+ if (ipIsPrimary == True ):
91
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
92
+ self .create_dns_record (domain , IPAddress (ip ).reverse_dns , "PTR" , "." .join ([device .name , self .config ['dns_name' ]]))
93
+ )
94
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
95
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([device .name , self .config ['dns_name' ]]), "A" , ip )
96
+ )
97
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
98
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "A" , ip )
99
+ )
100
+ else :
101
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
102
+ self .create_dns_record (domain , IPAddress (ip ).reverse_dns , "PTR" , "." .join ([intf , device .name , self .config ['dns_name' ]]))
103
+ )
104
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
105
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "A" , ip )
106
+ )
86
107
87
108
def create_dns_records_v6 (self , interface , device , ip ):
109
+ try :
110
+ ipIsPrimary = ip .isPrimary
111
+ except :
112
+ ipIsPrimary = False
113
+ ip = str (ip .address )
88
114
ip = str (IPNetwork (ip ).ip )
89
115
pre = ip .split ("." )
90
116
record_name = pre .pop ()
91
- domain = "." .join (pre [::- 1 ] + ['in-addr ' , 'arpa' ])
117
+ domain = "." .join (pre [::- 1 ] + ['ip6 ' , 'arpa' ])
92
118
intf = self .sanitize_dns_name (interface )
93
119
94
- self .result ['ungrouped' ]['vars' ]['dns_entries' ].append (
95
- self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "AAAA" , ip )
96
- )
97
- self .result ['ungrouped' ]['vars' ]['dns_entries' ].append (
98
- self .create_dns_record (self .config ['ipv6_ptr_domain' ], IPAddress (ip ).reverse_dns , "PTR" , "." .join ([intf , device .name , self .config ['dns_name' ]]))
99
- )
120
+ if (ipIsPrimary == True ):
121
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
122
+ self .create_dns_record (domain , IPAddress (ip ).reverse_dns , "PTR" , "." .join ([device .name , self .config ['dns_name' ]]))
123
+ )
124
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
125
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([device .name , self .config ['dns_name' ]]), "AAAA" , ip )
126
+ )
127
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
128
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "AAAA" , ip )
129
+ )
130
+ else :
131
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
132
+ self .create_dns_record (domain , IPAddress (ip ).reverse_dns , "PTR" , "." .join ([intf , device .name , self .config ['dns_name' ]]))
133
+ )
134
+ self ['ungrouped' ]['vars' ]['dns_entries' ].append (
135
+ self .create_dns_record (self .config ['dns_name' ], "." .join ([intf , device .name , self .config ['dns_name' ]]), "AAAA" , ip )
136
+ )
100
137
101
138
def lookup_circuits (self , circuit_id , interface_id , mtu_size , interface_mode , vlans = {}):
102
139
circuit = self .netbox .circuits .circuits .get (circuit_id )
@@ -129,11 +166,10 @@ def create_slug(self, text):
129
166
return slug if len (slug ) <= 32 else slug [0 :32 ]
130
167
131
168
def save_vlan_global (self , vlan_id , vlan_name ):
132
- if vlan_id not in self .result ['ungrouped' ]['vars' ]['vlans' ]:
133
- self .result ['ungrouped' ]['vars' ]['vlans' ][vlan_id ] = {
134
- "name" : vlan_name ,
135
- "slug" : self .create_slug (vlan_name )
136
- }
169
+ self ['ungrouped' ]['vars' ]['vlans' ].setdefault (vlan_id , {
170
+ "name" : vlan_name ,
171
+ "slug" : self .create_slug (vlan_name )
172
+ })
137
173
138
174
def return_vlans (self , interface ):
139
175
r = []
@@ -165,84 +201,91 @@ def get_interfaces(self, device):
165
201
def return_interface (self , description , int_type , id , mtu = '' , interface_mode = 'Untagged' , vlans = []):
166
202
intf = { 'description' : description , 'type' : int_type , 'id' : id , 'interface_mode' : interface_mode , 'vlans' : vlans , 'v4_address' : [], 'v6_address' : [] }
167
203
168
- if mtu != None :
204
+ if mtu is not None :
169
205
intf ['mtu' ] = mtu
170
206
171
207
return intf
172
208
173
209
def create_group_entry (self , group , hostname ):
174
- if group not in self .result :
175
- self .result [group ] = {}
176
- self .result [group ]["hosts" ] = []
177
-
178
- self .result [group ]["hosts" ].append (hostname )
210
+ gdict = self .setdefault (group , {})
211
+ hosts = gdict .setdefault ('hosts' , [])
212
+ hosts .append (hostname )
179
213
180
214
def fetch_netbox_devices (self ):
181
215
for host in self .netbox .dcim .devices .filter (status = 1 ):
182
- if host .name == None :
216
+ if host .name is None :
183
217
continue
184
218
185
219
h = host .name
186
- self . result ["all" ]["hosts" ].append (h )
220
+ self ["all" ]["hosts" ].append (h )
187
221
188
222
# Create hostgroups by device_role, device_type and site
189
223
self .create_group_entry (host .device_role .slug , h )
190
224
self .create_group_entry (host .device_type .slug , h )
191
225
self .create_group_entry (host .site .slug , h )
192
226
193
- if not host in self .result ["_meta" ]["hostvars" ]:
194
- self .result ["_meta" ]["hostvars" ][h ] = { "interfaces" : {}}
227
+ hvar = self ["_meta" ]["hostvars" ].setdefault (h , { "interfaces" : {}})
195
228
196
- if host .virtual_chassis != None :
197
- self . result [ "_meta" ][ "hostvars" ][ h ] ['virtual_chassis' ] = []
229
+ if host .virtual_chassis is not None :
230
+ hvar ['virtual_chassis' ] = []
198
231
vc_members = self .netbox .dcim .devices .filter (virtual_chassis_id = host .virtual_chassis .id )
199
232
for vcm in vc_members :
200
233
if vcm .vc_position == 0 or vcm .vc_position == 1 :
201
234
role = 'routing-engine'
202
235
else :
203
236
role = 'line-card'
204
237
205
- self .result ["_meta" ]["hostvars" ][h ]['virtual_chassis' ].append ({ 'serial' : vcm .serial , 'role' : role , 'device_name' : vcm .name , 'vc_position' : vcm .vc_position })
206
-
207
- if host .custom_fields != None :
208
- for k ,v in host .custom_fields .items ():
209
- self .result ["_meta" ]["hostvars" ][h ][k ] = v
238
+ hvar ['virtual_chassis' ].append ({ 'serial' : vcm .serial , 'role' : role , 'device_name' : vcm .name , 'vc_position' : vcm .vc_position })
210
239
211
- self .result ["_meta" ]["hostvars" ][h ]['site' ] = host .site .slug
212
- self .result ["_meta" ]["hostvars" ][h ]['serial' ] = host .serial
213
- self .result ["_meta" ]["hostvars" ][h ]['siteid' ] = host .site .id
214
- self .result ["_meta" ]["hostvars" ][h ]['model' ] = host .device_type .slug
215
- self .result ["_meta" ]["hostvars" ][h ]['manufacturer' ] = host .device_type .manufacturer .slug
240
+ hvar .update (host .custom_fields or {})
241
+ hvar .update ({
242
+ 'site' : host .site .slug ,
243
+ 'serial' : host .serial ,
244
+ 'siteid' : host .site .id ,
245
+ 'model' : host .device_type .slug ,
246
+ 'manufacturer' : host .device_type .manufacturer .slug ,
247
+ })
216
248
217
249
# Lookup site details
218
250
site = self .netbox .dcim .sites .filter (slug = host .site .slug )
219
251
if len (site ) > 0 :
220
- self .result ["_meta" ]["hostvars" ][h ]['latitude' ] = site [0 ].latitude
221
- self .result ["_meta" ]["hostvars" ][h ]['longitude' ] = site [0 ].longitude
252
+ hvar .update ({
253
+ 'latitude' : site [0 ].latitude ,
254
+ 'longitude' : site [0 ].longitude ,
255
+ })
222
256
223
- self . result [ "_meta" ][ "hostvars" ][ h ] ["interfaces" ] = self .get_interfaces (host )
257
+ hvar ["interfaces" ] = self .get_interfaces (host )
224
258
if host .primary_ip != None :
225
- self . result [ "_meta" ][ "hostvars" ][ h ] ['ansible_ssh_host' ] = str (host .primary_ip .address .ip )
259
+ hvar ['ansible_ssh_host' ] = str (host .primary_ip .address .ip )
226
260
227
- return self . result
261
+ return self
228
262
229
263
def fetch_virtual_machines (self ):
230
264
for host in self .netbox .virtualization .virtual_machines .filter (status = 1 ):
231
- self . result ["all" ]["hosts" ].append (host .name )
265
+ self ["all" ]["hosts" ].append (host .name )
232
266
233
267
# Create hostgroups by cluster, role
234
268
if host .cluster is not None :
235
269
self .create_group_entry (host .cluster .name , host .name )
236
270
if host .role is not None :
237
271
self .create_group_entry (host .role .slug , host .name )
238
272
239
- if not host in self .result ["_meta" ]["hostvars" ]:
240
- self .result ["_meta" ]["hostvars" ][host .name ] = {}
241
-
273
+ hvar = self ["_meta" ]["hostvars" ].setdefault (host .name , {})
242
274
if host .primary_ip != None :
243
- self .result ["_meta" ]["hostvars" ][host .name ]['ansible_ssh_host' ] = str (IPNetwork (host .primary_ip .address ).ip )
275
+ hvar ['ansible_ssh_host' ] = str (IPNetwork (host .primary_ip .address ).ip )
276
+
277
+ return self
278
+
279
+ def fetch_prefixes (self ):
280
+ prefixes = self ['ungrouped' ]['vars' ].setdefault ('prefixes' , {})
281
+
282
+ for prefix in self .netbox .ipam .prefixes .filter (status = 1 ):
283
+ prefixes [str (prefix )] = {
284
+ 'vlan' : prefix .vlan .vid ,
285
+ }
286
+ prefixes [str (prefix )].update (prefix .custom_fields )
244
287
245
- return self . result
288
+ return self
246
289
247
290
class OFNetboxInventory (NetboxInventory ):
248
291
def __init__ (self ):
@@ -308,9 +351,10 @@ def modification_date(filename):
308
351
def cache_inventory (cache_file ):
309
352
n = NetboxInventory ()
310
353
n .fetch_virtual_machines ()
311
- hosts = n .fetch_netbox_devices ()
354
+ n .fetch_netbox_devices ()
355
+ n .fetch_prefixes ()
312
356
313
- cache = json .dumps (hosts , sort_keys = True , indent = 3 )
357
+ cache = json .dumps (n , sort_keys = True , indent = 3 )
314
358
with open (cache_file , 'w' ) as f :
315
359
f .write (cache )
316
360
0 commit comments