Coverage for ipam/tables/ip.py: 96%

122 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1import django_tables2 as tables 

2from django.utils.safestring import mark_safe 

3from django.utils.translation import gettext_lazy as _ 

4from django_tables2.utils import Accessor 

5 

6from ipam.models import * 

7from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns 

8from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin, TenantColumn 

9 

10from .template_code import * 

11 

12__all__ = ( 

13 'AggregateTable', 

14 'AnnotatedIPAddressTable', 

15 'AssignedIPAddressesTable', 

16 'IPAddressAssignTable', 

17 'IPAddressTable', 

18 'IPRangeTable', 

19 'PrefixTable', 

20 'RIRTable', 

21 'RoleTable', 

22) 

23 

24AVAILABLE_LABEL = mark_safe('<span class="badge text-bg-success">Available</span>') 

25 

26 

27# 

28# RIRs 

29# 

30 

31class RIRTable(OrganizationalModelTable): 

32 name = tables.Column( 

33 verbose_name=_('Name'), 

34 linkify=True 

35 ) 

36 is_private = columns.BooleanColumn( 

37 verbose_name=_('Private'), 

38 false_mark=None 

39 ) 

40 aggregate_count = columns.LinkedCountColumn( 

41 viewname='ipam:aggregate_list', 

42 url_params={'rir_id': 'pk'}, 

43 verbose_name=_('Aggregates') 

44 ) 

45 tags = columns.TagColumn( 

46 url_name='ipam:rir_list' 

47 ) 

48 

49 class Meta(OrganizationalModelTable.Meta): 

50 model = RIR 

51 fields = ( 

52 'pk', 'id', 'name', 'slug', 'is_private', 'aggregate_count', 'description', 'comments', 'tags', 'created', 

53 'last_updated', 'actions', 

54 ) 

55 default_columns = ('pk', 'name', 'is_private', 'aggregate_count', 'description') 

56 

57 

58# 

59# Aggregates 

60# 

61 

62class AggregateTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): 

63 prefix = tables.Column( 

64 linkify=True, 

65 verbose_name=_('Aggregate'), 

66 attrs={ 

67 # Allow the aggregate to be copied to the clipboard 

68 'a': {'id': lambda record: f"aggregate_{record.pk}"} 

69 } 

70 ) 

71 date_added = tables.DateColumn( 

72 format="Y-m-d", 

73 verbose_name=_('Added') 

74 ) 

75 child_count = tables.Column( 

76 verbose_name=_('Prefixes') 

77 ) 

78 utilization = columns.UtilizationColumn( 

79 verbose_name=_('Utilization'), 

80 accessor='get_utilization', 

81 orderable=False 

82 ) 

83 tags = columns.TagColumn( 

84 url_name='ipam:aggregate_list' 

85 ) 

86 actions = columns.ActionsColumn( 

87 extra_buttons=AGGREGATE_COPY_BUTTON 

88 ) 

89 

90 class Meta(PrimaryModelTable.Meta): 

91 model = Aggregate 

92 fields = ( 

93 'pk', 'id', 'prefix', 'rir', 'tenant', 'tenant_group', 'child_count', 'utilization', 'date_added', 

94 'description', 'contacts', 'comments', 'tags', 'created', 'last_updated', 

95 ) 

96 default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description') 

97 

98 

99# 

100# Roles 

101# 

102 

103class RoleTable(OrganizationalModelTable): 

104 name = tables.Column( 

105 verbose_name=_('Name'), 

106 linkify=True 

107 ) 

108 prefix_count = columns.LinkedCountColumn( 

109 viewname='ipam:prefix_list', 

110 url_params={'role_id': 'pk'}, 

111 verbose_name=_('Prefixes') 

112 ) 

113 iprange_count = columns.LinkedCountColumn( 

114 viewname='ipam:iprange_list', 

115 url_params={'role_id': 'pk'}, 

116 verbose_name=_('IP Ranges') 

117 ) 

118 vlan_count = columns.LinkedCountColumn( 

119 viewname='ipam:vlan_list', 

120 url_params={'role_id': 'pk'}, 

121 verbose_name=_('VLANs') 

122 ) 

123 asn_count = columns.LinkedCountColumn( 

124 viewname='ipam:asn_list', 

125 url_params={'role_id': 'pk'}, 

126 verbose_name=_('ASNs') 

127 ) 

128 tags = columns.TagColumn( 

129 url_name='ipam:role_list' 

130 ) 

131 

132 class Meta(OrganizationalModelTable.Meta): 

133 model = Role 

134 fields = ( 

135 'pk', 'id', 'name', 'slug', 'prefix_count', 'iprange_count', 'vlan_count', 'asn_count', 'description', 

136 'weight', 'comments', 'tags', 'created', 'last_updated', 'actions', 

137 ) 

138 default_columns = ('pk', 'name', 'prefix_count', 'iprange_count', 'vlan_count', 'asn_count', 'description') 

139 

140 

141# 

142# Prefixes 

143# 

144 

145class PrefixUtilizationColumn(columns.UtilizationColumn): 

146 """ 

147 Extend UtilizationColumn to allow disabling the warning & danger thresholds for prefixes 

148 marked as fully utilized. 

149 """ 

150 template_code = """ 

151 {% load helpers %} 

152 {% if record.pk and record.mark_utilized %} 

153 {% utilization_graph value warning_threshold=0 danger_threshold=0 %} 

154 {% elif record.pk %} 

155 {% utilization_graph value %} 

156 {% endif %} 

157 """ 

158 

159 

160class PrefixTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): 

161 prefix = columns.TemplateColumn( 

162 verbose_name=_('Prefix'), 

163 template_code=PREFIX_LINK_WITH_DEPTH, 

164 export_raw=True, 

165 attrs={'td': {'class': 'text-nowrap'}} 

166 ) 

167 prefix_flat = columns.TemplateColumn( 

168 accessor=Accessor('prefix'), 

169 template_code=PREFIX_LINK, 

170 export_raw=True, 

171 verbose_name=_('Prefix (Flat)') 

172 ) 

173 depth = tables.Column( 

174 accessor=Accessor('_depth'), 

175 verbose_name=_('Depth') 

176 ) 

177 children = columns.LinkedCountColumn( 

178 accessor=Accessor('_children'), 

179 viewname='ipam:prefix_list', 

180 url_params={ 

181 'vrf_id': 'vrf_id', 

182 'within': 'prefix', 

183 }, 

184 verbose_name=_('Children') 

185 ) 

186 status = columns.ChoiceFieldColumn( 

187 verbose_name=_('Status'), 

188 default=AVAILABLE_LABEL 

189 ) 

190 vrf = tables.TemplateColumn( 

191 template_code=VRF_LINK, 

192 verbose_name=_('VRF') 

193 ) 

194 scope_type = columns.ContentTypeColumn( 

195 verbose_name=_('Scope Type'), 

196 ) 

197 scope = tables.Column( 

198 verbose_name=_('Scope'), 

199 linkify=True, 

200 orderable=False 

201 ) 

202 vlan_group = tables.Column( 

203 accessor='vlan__group', 

204 linkify=True, 

205 verbose_name=_('VLAN Group') 

206 ) 

207 vlan = tables.Column( 

208 order_by=('vlan__vid', 'vlan__pk'), 

209 linkify=True, 

210 verbose_name=_('VLAN') 

211 ) 

212 role = tables.Column( 

213 verbose_name=_('Role'), 

214 linkify=True 

215 ) 

216 is_pool = columns.BooleanColumn( 

217 verbose_name=_('Pool'), 

218 false_mark=None 

219 ) 

220 mark_utilized = columns.BooleanColumn( 

221 verbose_name=_('Marked Utilized'), 

222 false_mark=None 

223 ) 

224 utilization = PrefixUtilizationColumn( 

225 verbose_name=_('Utilization'), 

226 accessor='get_utilization', 

227 orderable=False 

228 ) 

229 tags = columns.TagColumn( 

230 url_name='ipam:prefix_list' 

231 ) 

232 actions = columns.ActionsColumn( 

233 extra_buttons=PREFIX_COPY_BUTTON 

234 ) 

235 

236 class Meta(PrimaryModelTable.Meta): 

237 model = Prefix 

238 fields = ( 

239 'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group', 

240 'scope', 'scope_type', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'contacts', 

241 'comments', 'tags', 'created', 'last_updated', 

242 ) 

243 default_columns = ( 

244 'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'scope', 'vlan', 'role', 

245 'description', 

246 ) 

247 row_attrs = { 

248 'class': lambda record: 'success' if not record.pk else '', 

249 } 

250 

251 

252# 

253# IP ranges 

254# 

255 

256class IPRangeTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): 

257 start_address = tables.Column( 

258 verbose_name=_('Start address'), 

259 linkify=True 

260 ) 

261 vrf = tables.TemplateColumn( 

262 template_code=VRF_LINK, 

263 verbose_name=_('VRF') 

264 ) 

265 status = columns.ChoiceFieldColumn( 

266 verbose_name=_('Status'), 

267 default=AVAILABLE_LABEL 

268 ) 

269 role = tables.Column( 

270 verbose_name=_('Role'), 

271 linkify=True 

272 ) 

273 mark_populated = columns.BooleanColumn( 

274 verbose_name=_('Marked Populated'), 

275 false_mark=None 

276 ) 

277 mark_utilized = columns.BooleanColumn( 

278 verbose_name=_('Marked Utilized'), 

279 false_mark=None 

280 ) 

281 utilization = columns.UtilizationColumn( 

282 verbose_name=_('Utilization'), 

283 accessor='utilization', 

284 orderable=False 

285 ) 

286 tags = columns.TagColumn( 

287 url_name='ipam:iprange_list' 

288 ) 

289 

290 class Meta(PrimaryModelTable.Meta): 

291 model = IPRange 

292 fields = ( 

293 'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 

294 'mark_populated', 'mark_utilized', 'utilization', 'description', 'contacts', 'comments', 'tags', 

295 'created', 'last_updated', 

296 ) 

297 default_columns = ( 

298 'pk', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description', 

299 ) 

300 row_attrs = { 

301 'class': lambda record: 'success' if not record.pk else '', 

302 } 

303 

304 

305# 

306# IP Addresses 

307# 

308 

309class IPAddressTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): 

310 address = tables.TemplateColumn( 

311 template_code=IPADDRESS_LINK, 

312 verbose_name=_('IP Address') 

313 ) 

314 vrf = tables.TemplateColumn( 

315 template_code=VRF_LINK, 

316 verbose_name=_('VRF') 

317 ) 

318 status = columns.ChoiceFieldColumn( 

319 verbose_name=_('Status'), 

320 default=AVAILABLE_LABEL 

321 ) 

322 role = columns.ChoiceFieldColumn( 

323 verbose_name=_('Role'), 

324 ) 

325 assigned_object = tables.Column( 

326 linkify=True, 

327 orderable=False, 

328 verbose_name=_('Interface') 

329 ) 

330 assigned_object_parent = tables.Column( 

331 accessor='assigned_object__parent_object', 

332 linkify=True, 

333 orderable=False, 

334 verbose_name=_('Parent') 

335 ) 

336 nat_inside = tables.Column( 

337 linkify=True, 

338 orderable=False, 

339 verbose_name=_('NAT (Inside)') 

340 ) 

341 nat_outside = columns.ManyToManyColumn( 

342 linkify_item=True, 

343 orderable=False, 

344 verbose_name=_('NAT (Outside)') 

345 ) 

346 assigned = columns.BooleanColumn( 

347 accessor='assigned_object_id', 

348 linkify=lambda record: record.assigned_object.get_absolute_url(), 

349 verbose_name=_('Assigned'), 

350 false_mark=None 

351 ) 

352 tags = columns.TagColumn( 

353 url_name='ipam:ipaddress_list' 

354 ) 

355 actions = columns.ActionsColumn( 

356 extra_buttons=IPADDRESS_COPY_BUTTON 

357 ) 

358 

359 class Meta(PrimaryModelTable.Meta): 

360 model = IPAddress 

361 fields = ( 

362 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'nat_outside', 

363 'assigned', 'dns_name', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated', 

364 ) 

365 default_columns = ( 

366 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description', 

367 ) 

368 row_attrs = { 

369 'class': lambda record: 'success' if not isinstance(record, IPAddress) else '', 

370 } 

371 

372 

373class AnnotatedIPAddressTable(IPAddressTable): 

374 address = tables.TemplateColumn( 

375 template_code=IPADDRESS_LINK, 

376 verbose_name=_('IP Address') 

377 ) 

378 

379 def render_pk(self, value, record, bound_column): 

380 if type(record) is not self._meta.model: 

381 return '' 

382 return bound_column.column.render(value, bound_column, record) 

383 

384 class Meta(IPAddressTable.Meta): 

385 pass 

386 

387 

388class IPAddressAssignTable(NetBoxTable): 

389 address = tables.TemplateColumn( 

390 template_code=IPADDRESS_ASSIGN_LINK, 

391 verbose_name=_('IP Address') 

392 ) 

393 status = columns.ChoiceFieldColumn( 

394 verbose_name=_('Status'), 

395 ) 

396 assigned_object = tables.Column( 

397 verbose_name=_('Assigned Object'), 

398 orderable=False 

399 ) 

400 

401 class Meta(NetBoxTable.Meta): 

402 model = IPAddress 

403 fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'assigned_object', 'description') 

404 exclude = ('id', ) 

405 orderable = False 

406 

407 

408class AssignedIPAddressesTable(NetBoxTable): 

409 """ 

410 List IP addresses assigned to an object. 

411 """ 

412 address = tables.Column( 

413 linkify=True, 

414 verbose_name=_('IP Address') 

415 ) 

416 vrf = tables.TemplateColumn( 

417 template_code=VRF_LINK, 

418 verbose_name=_('VRF') 

419 ) 

420 status = columns.ChoiceFieldColumn( 

421 verbose_name=_('Status'), 

422 ) 

423 tenant = TenantColumn( 

424 verbose_name=_('Tenant'), 

425 ) 

426 

427 class Meta(NetBoxTable.Meta): 

428 model = IPAddress 

429 fields = ('address', 'vrf', 'status', 'role', 'tenant', 'description') 

430 exclude = ('id', )