Coverage for ipam/forms/bulk_edit.py: 100%

140 statements  

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

1from django import forms 

2from django.contrib.contenttypes.models import ContentType 

3from django.utils.translation import gettext_lazy as _ 

4 

5from dcim.forms.mixins import ScopedBulkEditForm 

6from dcim.models import Region, Site, SiteGroup 

7from ipam.choices import * 

8from ipam.constants import * 

9from ipam.forms.fields import PortMappingField 

10from ipam.models import * 

11from ipam.models import ASN 

12from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm 

13from tenancy.models import Tenant 

14from utilities.forms import GenericObjectFormMixin, add_blank_choice 

15from utilities.forms.fields import ( 

16 ChoiceField, 

17 DynamicModelChoiceField, 

18 DynamicModelMultipleChoiceField, 

19 GenericObjectChoiceField, 

20 NumericRangeArrayField, 

21) 

22from utilities.forms.rendering import FieldSet 

23from utilities.forms.widgets import BulkEditNullBooleanSelect 

24 

25__all__ = ( 

26 'ASNBulkEditForm', 

27 'ASNRangeBulkEditForm', 

28 'AggregateBulkEditForm', 

29 'FHRPGroupBulkEditForm', 

30 'IPAddressBulkEditForm', 

31 'IPRangeBulkEditForm', 

32 'PrefixBulkEditForm', 

33 'RIRBulkEditForm', 

34 'RoleBulkEditForm', 

35 'RouteTargetBulkEditForm', 

36 'ServiceBulkEditForm', 

37 'ServiceTemplateBulkEditForm', 

38 'VLANBulkEditForm', 

39 'VLANGroupBulkEditForm', 

40 'VLANTranslationPolicyBulkEditForm', 

41 'VLANTranslationRuleBulkEditForm', 

42 'VRFBulkEditForm', 

43) 

44 

45 

46class VRFBulkEditForm(PrimaryModelBulkEditForm): 

47 tenant = DynamicModelChoiceField( 

48 label=_('Tenant'), 

49 queryset=Tenant.objects.all(), 

50 required=False 

51 ) 

52 enforce_unique = forms.NullBooleanField( 

53 required=False, 

54 widget=BulkEditNullBooleanSelect(), 

55 label=_('Enforce unique space') 

56 ) 

57 

58 model = VRF 

59 fieldsets = ( 

60 FieldSet('tenant', 'enforce_unique', 'description'), 

61 ) 

62 nullable_fields = ('tenant', 'description', 'comments') 

63 

64 

65class RouteTargetBulkEditForm(PrimaryModelBulkEditForm): 

66 tenant = DynamicModelChoiceField( 

67 label=_('Tenant'), 

68 queryset=Tenant.objects.all(), 

69 required=False 

70 ) 

71 

72 model = RouteTarget 

73 fieldsets = ( 

74 FieldSet('tenant', 'description'), 

75 ) 

76 nullable_fields = ('tenant', 'description', 'comments') 

77 

78 

79class RIRBulkEditForm(OrganizationalModelBulkEditForm): 

80 is_private = forms.NullBooleanField( 

81 label=_('Is private'), 

82 required=False, 

83 widget=BulkEditNullBooleanSelect 

84 ) 

85 

86 model = RIR 

87 fieldsets = ( 

88 FieldSet('is_private', 'description'), 

89 ) 

90 nullable_fields = ('is_private', 'description', 'comments') 

91 

92 

93class ASNRangeBulkEditForm(OrganizationalModelBulkEditForm): 

94 rir = DynamicModelChoiceField( 

95 queryset=RIR.objects.all(), 

96 required=False, 

97 label=_('RIR') 

98 ) 

99 tenant = DynamicModelChoiceField( 

100 label=_('Tenant'), 

101 queryset=Tenant.objects.all(), 

102 required=False 

103 ) 

104 

105 model = ASNRange 

106 fieldsets = ( 

107 FieldSet('rir', 'tenant', 'description'), 

108 ) 

109 nullable_fields = ('description', 'comments') 

110 

111 

112class ASNBulkEditForm(PrimaryModelBulkEditForm): 

113 sites = DynamicModelMultipleChoiceField( 

114 label=_('Sites'), 

115 queryset=Site.objects.all(), 

116 required=False 

117 ) 

118 rir = DynamicModelChoiceField( 

119 queryset=RIR.objects.all(), 

120 required=False, 

121 label=_('RIR') 

122 ) 

123 role = DynamicModelChoiceField( 

124 queryset=Role.objects.all(), 

125 required=False, 

126 label=_('Role') 

127 ) 

128 tenant = DynamicModelChoiceField( 

129 label=_('Tenant'), 

130 queryset=Tenant.objects.all(), 

131 required=False 

132 ) 

133 

134 model = ASN 

135 fieldsets = ( 

136 FieldSet('sites', 'rir', 'role', 'tenant', 'description'), 

137 ) 

138 nullable_fields = ('role', 'tenant', 'description', 'comments') 

139 

140 

141class AggregateBulkEditForm(PrimaryModelBulkEditForm): 

142 rir = DynamicModelChoiceField( 

143 queryset=RIR.objects.all(), 

144 required=False, 

145 label=_('RIR') 

146 ) 

147 tenant = DynamicModelChoiceField( 

148 label=_('Tenant'), 

149 queryset=Tenant.objects.all(), 

150 required=False 

151 ) 

152 date_added = forms.DateField( 

153 label=_('Date added'), 

154 required=False 

155 ) 

156 

157 model = Aggregate 

158 fieldsets = ( 

159 FieldSet('rir', 'tenant', 'date_added', 'description'), 

160 ) 

161 nullable_fields = ('date_added', 'description', 'comments') 

162 

163 

164class RoleBulkEditForm(OrganizationalModelBulkEditForm): 

165 weight = forms.IntegerField( 

166 label=_('Weight'), 

167 required=False 

168 ) 

169 

170 model = Role 

171 fieldsets = ( 

172 FieldSet('weight', 'description'), 

173 ) 

174 nullable_fields = ('description', 'comments') 

175 

176 

177class PrefixBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm): 

178 vlan_group = DynamicModelChoiceField( 

179 queryset=VLANGroup.objects.all(), 

180 required=False, 

181 label=_('VLAN Group') 

182 ) 

183 vlan = DynamicModelChoiceField( 

184 queryset=VLAN.objects.all(), 

185 required=False, 

186 label=_('VLAN'), 

187 query_params={ 

188 'group_id': '$vlan_group', 

189 } 

190 ) 

191 vrf = DynamicModelChoiceField( 

192 queryset=VRF.objects.all(), 

193 required=False, 

194 label=_('VRF') 

195 ) 

196 prefix_length = forms.IntegerField( 

197 label=_('Prefix length'), 

198 min_value=PREFIX_LENGTH_MIN, 

199 max_value=PREFIX_LENGTH_MAX, 

200 required=False 

201 ) 

202 tenant = DynamicModelChoiceField( 

203 label=_('Tenant'), 

204 queryset=Tenant.objects.all(), 

205 required=False 

206 ) 

207 status = ChoiceField( 

208 label=_('Status'), 

209 choices=add_blank_choice(PrefixStatusChoices), 

210 required=False 

211 ) 

212 role = DynamicModelChoiceField( 

213 label=_('Role'), 

214 queryset=Role.objects.all(), 

215 required=False 

216 ) 

217 is_pool = forms.NullBooleanField( 

218 required=False, 

219 widget=BulkEditNullBooleanSelect(), 

220 label=_('Is a pool') 

221 ) 

222 mark_utilized = forms.NullBooleanField( 

223 required=False, 

224 widget=BulkEditNullBooleanSelect(), 

225 label=_('Treat as fully utilized') 

226 ) 

227 

228 model = Prefix 

229 fieldsets = ( 

230 FieldSet('tenant', 'status', 'role', 'description'), 

231 FieldSet('vrf', 'prefix_length', 'is_pool', 'mark_utilized', name=_('Addressing')), 

232 FieldSet('scope', name=_('Scope')), 

233 FieldSet('vlan_group', 'vlan', name=_('VLAN Assignment')), 

234 ) 

235 nullable_fields = ( 

236 'vlan', 'vrf', 'tenant', 'role', 'scope', 'description', 'comments', 

237 ) 

238 

239 

240class IPRangeBulkEditForm(PrimaryModelBulkEditForm): 

241 vrf = DynamicModelChoiceField( 

242 queryset=VRF.objects.all(), 

243 required=False, 

244 label=_('VRF') 

245 ) 

246 tenant = DynamicModelChoiceField( 

247 label=_('Tenant'), 

248 queryset=Tenant.objects.all(), 

249 required=False 

250 ) 

251 status = ChoiceField( 

252 label=_('Status'), 

253 choices=add_blank_choice(IPRangeStatusChoices), 

254 required=False 

255 ) 

256 role = DynamicModelChoiceField( 

257 label=_('Role'), 

258 queryset=Role.objects.all(), 

259 required=False 

260 ) 

261 mark_populated = forms.NullBooleanField( 

262 required=False, 

263 widget=BulkEditNullBooleanSelect(), 

264 label=_('Treat as populated') 

265 ) 

266 mark_utilized = forms.NullBooleanField( 

267 required=False, 

268 widget=BulkEditNullBooleanSelect(), 

269 label=_('Treat as fully utilized') 

270 ) 

271 

272 model = IPRange 

273 fieldsets = ( 

274 FieldSet('status', 'role', 'vrf', 'tenant', 'mark_utilized', 'description'), 

275 ) 

276 nullable_fields = ( 

277 'vrf', 'tenant', 'role', 'description', 'comments', 

278 ) 

279 

280 

281class IPAddressBulkEditForm(PrimaryModelBulkEditForm): 

282 vrf = DynamicModelChoiceField( 

283 queryset=VRF.objects.all(), 

284 required=False, 

285 label=_('VRF') 

286 ) 

287 mask_length = forms.IntegerField( 

288 label=_('Mask length'), 

289 min_value=IPADDRESS_MASK_LENGTH_MIN, 

290 max_value=IPADDRESS_MASK_LENGTH_MAX, 

291 required=False 

292 ) 

293 tenant = DynamicModelChoiceField( 

294 label=_('Tenant'), 

295 queryset=Tenant.objects.all(), 

296 required=False 

297 ) 

298 status = ChoiceField( 

299 label=_('Status'), 

300 choices=add_blank_choice(IPAddressStatusChoices), 

301 required=False 

302 ) 

303 role = ChoiceField( 

304 label=_('Role'), 

305 choices=add_blank_choice(IPAddressRoleChoices), 

306 required=False 

307 ) 

308 dns_name = forms.CharField( 

309 max_length=255, 

310 required=False, 

311 label=_('DNS name') 

312 ) 

313 

314 model = IPAddress 

315 fieldsets = ( 

316 FieldSet('status', 'role', 'tenant', 'description'), 

317 FieldSet('vrf', 'mask_length', 'dns_name', name=_('Addressing')), 

318 ) 

319 nullable_fields = ( 

320 'vrf', 'role', 'tenant', 'dns_name', 'description', 'comments', 

321 ) 

322 

323 

324class FHRPGroupBulkEditForm(PrimaryModelBulkEditForm): 

325 protocol = ChoiceField( 

326 label=_('Protocol'), 

327 choices=add_blank_choice(FHRPGroupProtocolChoices), 

328 required=False 

329 ) 

330 group_id = forms.IntegerField( 

331 min_value=0, 

332 required=False, 

333 label=_('Group ID') 

334 ) 

335 auth_type = ChoiceField( 

336 choices=add_blank_choice(FHRPGroupAuthTypeChoices), 

337 required=False, 

338 label=_('Authentication type') 

339 ) 

340 auth_key = forms.CharField( 

341 max_length=255, 

342 required=False, 

343 label=_('Authentication key') 

344 ) 

345 name = forms.CharField( 

346 label=_('Name'), 

347 max_length=100, 

348 required=False 

349 ) 

350 

351 model = FHRPGroup 

352 fieldsets = ( 

353 FieldSet('protocol', 'group_id', 'name', 'description'), 

354 FieldSet('auth_type', 'auth_key', name=_('Authentication')), 

355 ) 

356 nullable_fields = ('auth_type', 'auth_key', 'name', 'description', 'comments') 

357 

358 

359class VLANGroupBulkEditForm(GenericObjectFormMixin, OrganizationalModelBulkEditForm): 

360 scope = GenericObjectChoiceField( 

361 label=_('Scope'), 

362 content_type_queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES), 

363 required=False, 

364 selector=True, 

365 hx_method='post', 

366 ) 

367 vid_ranges = NumericRangeArrayField( 

368 label=_('VLAN ID ranges'), 

369 required=False 

370 ) 

371 tenant = DynamicModelChoiceField( 

372 label=_('Tenant'), 

373 queryset=Tenant.objects.all(), 

374 required=False 

375 ) 

376 

377 model = VLANGroup 

378 fieldsets = ( 

379 FieldSet('site', 'vid_ranges', 'description'), 

380 FieldSet('scope', name=_('Scope')), 

381 FieldSet('tenant', name=_('Tenancy')), 

382 ) 

383 nullable_fields = ('description', 'scope', 'comments') 

384 

385 

386class VLANBulkEditForm(PrimaryModelBulkEditForm): 

387 region = DynamicModelChoiceField( 

388 label=_('Region'), 

389 queryset=Region.objects.all(), 

390 required=False 

391 ) 

392 site_group = DynamicModelChoiceField( 

393 label=_('Site group'), 

394 queryset=SiteGroup.objects.all(), 

395 required=False 

396 ) 

397 site = DynamicModelChoiceField( 

398 label=_('Site'), 

399 queryset=Site.objects.all(), 

400 required=False, 

401 query_params={ 

402 'region_id': '$region', 

403 'group_id': '$site_group', 

404 } 

405 ) 

406 group = DynamicModelChoiceField( 

407 label=_('Group'), 

408 queryset=VLANGroup.objects.all(), 

409 required=False, 

410 query_params={ 

411 'site_id': '$site' 

412 } 

413 ) 

414 tenant = DynamicModelChoiceField( 

415 label=_('Tenant'), 

416 queryset=Tenant.objects.all(), 

417 required=False 

418 ) 

419 status = ChoiceField( 

420 label=_('Status'), 

421 choices=add_blank_choice(VLANStatusChoices), 

422 required=False 

423 ) 

424 role = DynamicModelChoiceField( 

425 label=_('Role'), 

426 queryset=Role.objects.all(), 

427 required=False 

428 ) 

429 qinq_role = ChoiceField( 

430 label=_('Q-in-Q role'), 

431 choices=add_blank_choice(VLANQinQRoleChoices), 

432 required=False 

433 ) 

434 qinq_svlan = DynamicModelChoiceField( 

435 label=_('Q-in-Q SVLAN'), 

436 queryset=VLAN.objects.all(), 

437 required=False, 

438 query_params={ 

439 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE, 

440 } 

441 ) 

442 

443 model = VLAN 

444 fieldsets = ( 

445 FieldSet('status', 'role', 'tenant', 'description'), 

446 FieldSet('qinq_role', 'qinq_svlan', name=_('Q-in-Q')), 

447 FieldSet('region', 'site_group', 'site', 'group', name=_('Site & Group')), 

448 ) 

449 nullable_fields = ( 

450 'site', 'group', 'tenant', 'role', 'description', 'qinq_role', 'qinq_svlan', 'comments', 

451 ) 

452 

453 

454class VLANTranslationPolicyBulkEditForm(PrimaryModelBulkEditForm): 

455 model = VLANTranslationPolicy 

456 fieldsets = ( 

457 FieldSet('description'), 

458 ) 

459 nullable_fields = ('description',) 

460 

461 

462class VLANTranslationRuleBulkEditForm(NetBoxModelBulkEditForm): 

463 policy = DynamicModelChoiceField( 

464 label=_('Policy'), 

465 queryset=VLANTranslationPolicy.objects.all(), 

466 selector=True 

467 ) 

468 local_vid = forms.IntegerField(required=False) 

469 remote_vid = forms.IntegerField(required=False) 

470 

471 model = VLANTranslationRule 

472 fieldsets = ( 

473 FieldSet('policy', 'local_vid', 'remote_vid'), 

474 ) 

475 fields = ('policy', 'local_vid', 'remote_vid') 

476 

477 

478class ServiceTemplateBulkEditForm(PrimaryModelBulkEditForm): 

479 add_port_mappings = PortMappingField( 

480 label=_('Add port mappings'), 

481 required=False, 

482 help_text=_("Port mappings to add to each selected object"), 

483 ) 

484 remove_port_mappings = PortMappingField( 

485 label=_('Remove port mappings'), 

486 required=False, 

487 help_text=_("Port mappings to remove from each selected object (if present)"), 

488 ) 

489 

490 model = ServiceTemplate 

491 fieldsets = ( 

492 FieldSet('add_port_mappings', 'remove_port_mappings', 'description'), 

493 ) 

494 nullable_fields = ('description', 'comments') 

495 

496 

497class ServiceBulkEditForm(ServiceTemplateBulkEditForm): 

498 model = Service