filtersets.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. import django_filters
  2. import netaddr
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.core.exceptions import ValidationError
  5. from django.db.models import Q
  6. from django.utils.translation import gettext as _
  7. from drf_spectacular.types import OpenApiTypes
  8. from drf_spectacular.utils import extend_schema_field
  9. from netaddr.core import AddrFormatError
  10. from circuits.models import Provider
  11. from dcim.models import Device, Interface, Region, Site, SiteGroup
  12. from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
  13. from tenancy.filtersets import TenancyFilterSet
  14. from utilities.filters import (
  15. ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter,
  16. )
  17. from virtualization.models import VirtualMachine, VMInterface
  18. from vpn.models import L2VPN
  19. from .choices import *
  20. from .models import *
  21. __all__ = (
  22. 'AggregateFilterSet',
  23. 'ASNFilterSet',
  24. 'ASNRangeFilterSet',
  25. 'FHRPGroupAssignmentFilterSet',
  26. 'FHRPGroupFilterSet',
  27. 'IPAddressFilterSet',
  28. 'IPRangeFilterSet',
  29. 'PrefixFilterSet',
  30. 'PrimaryIPFilterSet',
  31. 'RIRFilterSet',
  32. 'RoleFilterSet',
  33. 'RouteTargetFilterSet',
  34. 'ServiceFilterSet',
  35. 'ServiceTemplateFilterSet',
  36. 'VLANFilterSet',
  37. 'VLANGroupFilterSet',
  38. 'VRFFilterSet',
  39. )
  40. class VRFFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  41. import_target_id = django_filters.ModelMultipleChoiceFilter(
  42. field_name='import_targets',
  43. queryset=RouteTarget.objects.all(),
  44. label=_('Import target'),
  45. )
  46. import_target = django_filters.ModelMultipleChoiceFilter(
  47. field_name='import_targets__name',
  48. queryset=RouteTarget.objects.all(),
  49. to_field_name='name',
  50. label=_('Import target (name)'),
  51. )
  52. export_target_id = django_filters.ModelMultipleChoiceFilter(
  53. field_name='export_targets',
  54. queryset=RouteTarget.objects.all(),
  55. label=_('Export target'),
  56. )
  57. export_target = django_filters.ModelMultipleChoiceFilter(
  58. field_name='export_targets__name',
  59. queryset=RouteTarget.objects.all(),
  60. to_field_name='name',
  61. label=_('Export target (name)'),
  62. )
  63. def search(self, queryset, name, value):
  64. if not value.strip():
  65. return queryset
  66. return queryset.filter(
  67. Q(name__icontains=value) |
  68. Q(rd__icontains=value) |
  69. Q(description__icontains=value)
  70. )
  71. class Meta:
  72. model = VRF
  73. fields = ('id', 'name', 'rd', 'enforce_unique', 'description')
  74. class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  75. importing_vrf_id = django_filters.ModelMultipleChoiceFilter(
  76. field_name='importing_vrfs',
  77. queryset=VRF.objects.all(),
  78. label=_('Importing VRF'),
  79. )
  80. importing_vrf = django_filters.ModelMultipleChoiceFilter(
  81. field_name='importing_vrfs__rd',
  82. queryset=VRF.objects.all(),
  83. to_field_name='rd',
  84. label=_('Import VRF (RD)'),
  85. )
  86. exporting_vrf_id = django_filters.ModelMultipleChoiceFilter(
  87. field_name='exporting_vrfs',
  88. queryset=VRF.objects.all(),
  89. label=_('Exporting VRF'),
  90. )
  91. exporting_vrf = django_filters.ModelMultipleChoiceFilter(
  92. field_name='exporting_vrfs__rd',
  93. queryset=VRF.objects.all(),
  94. to_field_name='rd',
  95. label=_('Export VRF (RD)'),
  96. )
  97. importing_l2vpn_id = django_filters.ModelMultipleChoiceFilter(
  98. field_name='importing_l2vpns',
  99. queryset=L2VPN.objects.all(),
  100. label=_('Importing L2VPN'),
  101. )
  102. importing_l2vpn = django_filters.ModelMultipleChoiceFilter(
  103. field_name='importing_l2vpns__identifier',
  104. queryset=L2VPN.objects.all(),
  105. to_field_name='identifier',
  106. label=_('Importing L2VPN (identifier)'),
  107. )
  108. exporting_l2vpn_id = django_filters.ModelMultipleChoiceFilter(
  109. field_name='exporting_l2vpns',
  110. queryset=L2VPN.objects.all(),
  111. label=_('Exporting L2VPN'),
  112. )
  113. exporting_l2vpn = django_filters.ModelMultipleChoiceFilter(
  114. field_name='exporting_l2vpns__identifier',
  115. queryset=L2VPN.objects.all(),
  116. to_field_name='identifier',
  117. label=_('Exporting L2VPN (identifier)'),
  118. )
  119. def search(self, queryset, name, value):
  120. if not value.strip():
  121. return queryset
  122. return queryset.filter(
  123. Q(name__icontains=value) |
  124. Q(description__icontains=value)
  125. )
  126. class Meta:
  127. model = RouteTarget
  128. fields = ('id', 'name', 'description')
  129. class RIRFilterSet(OrganizationalModelFilterSet):
  130. class Meta:
  131. model = RIR
  132. fields = ('id', 'name', 'slug', 'is_private', 'description')
  133. class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  134. family = django_filters.NumberFilter(
  135. field_name='prefix',
  136. lookup_expr='family'
  137. )
  138. prefix = django_filters.CharFilter(
  139. method='filter_prefix',
  140. label=_('Prefix'),
  141. )
  142. rir_id = django_filters.ModelMultipleChoiceFilter(
  143. queryset=RIR.objects.all(),
  144. label=_('RIR (ID)'),
  145. )
  146. rir = django_filters.ModelMultipleChoiceFilter(
  147. field_name='rir__slug',
  148. queryset=RIR.objects.all(),
  149. to_field_name='slug',
  150. label=_('RIR (slug)'),
  151. )
  152. class Meta:
  153. model = Aggregate
  154. fields = ('id', 'date_added', 'description')
  155. def search(self, queryset, name, value):
  156. if not value.strip():
  157. return queryset
  158. qs_filter = Q(description__icontains=value)
  159. qs_filter |= Q(prefix__contains=value.strip())
  160. try:
  161. prefix = str(netaddr.IPNetwork(value.strip()).cidr)
  162. qs_filter |= Q(prefix__net_contains_or_equals=prefix)
  163. qs_filter |= Q(prefix__contains=value.strip())
  164. except (AddrFormatError, ValueError):
  165. pass
  166. return queryset.filter(qs_filter)
  167. def filter_prefix(self, queryset, name, value):
  168. if not value.strip():
  169. return queryset
  170. try:
  171. query = str(netaddr.IPNetwork(value).cidr)
  172. return queryset.filter(prefix=query)
  173. except (AddrFormatError, ValueError):
  174. return queryset.none()
  175. class ASNRangeFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
  176. rir_id = django_filters.ModelMultipleChoiceFilter(
  177. queryset=RIR.objects.all(),
  178. label=_('RIR (ID)'),
  179. )
  180. rir = django_filters.ModelMultipleChoiceFilter(
  181. field_name='rir__slug',
  182. queryset=RIR.objects.all(),
  183. to_field_name='slug',
  184. label=_('RIR (slug)'),
  185. )
  186. class Meta:
  187. model = ASNRange
  188. fields = ('id', 'name', 'slug', 'start', 'end', 'description')
  189. def search(self, queryset, name, value):
  190. if not value.strip():
  191. return queryset
  192. qs_filter = Q(description__icontains=value)
  193. return queryset.filter(qs_filter)
  194. class ASNFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
  195. rir_id = django_filters.ModelMultipleChoiceFilter(
  196. queryset=RIR.objects.all(),
  197. label=_('RIR (ID)'),
  198. )
  199. rir = django_filters.ModelMultipleChoiceFilter(
  200. field_name='rir__slug',
  201. queryset=RIR.objects.all(),
  202. to_field_name='slug',
  203. label=_('RIR (slug)'),
  204. )
  205. site_id = django_filters.ModelMultipleChoiceFilter(
  206. field_name='sites',
  207. queryset=Site.objects.all(),
  208. label=_('Site (ID)'),
  209. )
  210. site = django_filters.ModelMultipleChoiceFilter(
  211. field_name='sites__slug',
  212. queryset=Site.objects.all(),
  213. to_field_name='slug',
  214. label=_('Site (slug)'),
  215. )
  216. provider_id = django_filters.ModelMultipleChoiceFilter(
  217. field_name='providers',
  218. queryset=Provider.objects.all(),
  219. label=_('Provider (ID)'),
  220. )
  221. provider = django_filters.ModelMultipleChoiceFilter(
  222. field_name='providers__slug',
  223. queryset=Provider.objects.all(),
  224. to_field_name='slug',
  225. label=_('Provider (slug)'),
  226. )
  227. class Meta:
  228. model = ASN
  229. fields = ('id', 'asn', 'description')
  230. def search(self, queryset, name, value):
  231. if not value.strip():
  232. return queryset
  233. qs_filter = Q(description__icontains=value)
  234. try:
  235. qs_filter |= Q(asn=int(value))
  236. except ValueError:
  237. pass
  238. return queryset.filter(qs_filter)
  239. class RoleFilterSet(OrganizationalModelFilterSet):
  240. class Meta:
  241. model = Role
  242. fields = ('id', 'name', 'slug', 'description', 'weight')
  243. class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  244. family = django_filters.NumberFilter(
  245. field_name='prefix',
  246. lookup_expr='family'
  247. )
  248. prefix = MultiValueCharFilter(
  249. method='filter_prefix',
  250. label=_('Prefix'),
  251. )
  252. within = django_filters.CharFilter(
  253. method='search_within',
  254. label=_('Within prefix'),
  255. )
  256. within_include = django_filters.CharFilter(
  257. method='search_within_include',
  258. label=_('Within and including prefix'),
  259. )
  260. contains = django_filters.CharFilter(
  261. method='search_contains',
  262. label=_('Prefixes which contain this prefix or IP'),
  263. )
  264. depth = MultiValueNumberFilter(
  265. field_name='_depth'
  266. )
  267. children = MultiValueNumberFilter(
  268. field_name='_children'
  269. )
  270. mask_length = MultiValueNumberFilter(
  271. field_name='prefix',
  272. lookup_expr='net_mask_length',
  273. label=_('Mask length')
  274. )
  275. mask_length__gte = django_filters.NumberFilter(
  276. field_name='prefix',
  277. lookup_expr='net_mask_length__gte'
  278. )
  279. mask_length__lte = django_filters.NumberFilter(
  280. field_name='prefix',
  281. lookup_expr='net_mask_length__lte'
  282. )
  283. vrf_id = django_filters.ModelMultipleChoiceFilter(
  284. queryset=VRF.objects.all(),
  285. label=_('VRF'),
  286. )
  287. vrf = django_filters.ModelMultipleChoiceFilter(
  288. field_name='vrf__rd',
  289. queryset=VRF.objects.all(),
  290. to_field_name='rd',
  291. label=_('VRF (RD)'),
  292. )
  293. present_in_vrf_id = django_filters.ModelChoiceFilter(
  294. queryset=VRF.objects.all(),
  295. method='filter_present_in_vrf',
  296. label=_('VRF')
  297. )
  298. present_in_vrf = django_filters.ModelChoiceFilter(
  299. queryset=VRF.objects.all(),
  300. method='filter_present_in_vrf',
  301. to_field_name='rd',
  302. label=_('VRF (RD)'),
  303. )
  304. region_id = TreeNodeMultipleChoiceFilter(
  305. queryset=Region.objects.all(),
  306. field_name='site__region',
  307. lookup_expr='in',
  308. label=_('Region (ID)'),
  309. )
  310. region = TreeNodeMultipleChoiceFilter(
  311. queryset=Region.objects.all(),
  312. field_name='site__region',
  313. lookup_expr='in',
  314. to_field_name='slug',
  315. label=_('Region (slug)'),
  316. )
  317. site_group_id = TreeNodeMultipleChoiceFilter(
  318. queryset=SiteGroup.objects.all(),
  319. field_name='site__group',
  320. lookup_expr='in',
  321. label=_('Site group (ID)'),
  322. )
  323. site_group = TreeNodeMultipleChoiceFilter(
  324. queryset=SiteGroup.objects.all(),
  325. field_name='site__group',
  326. lookup_expr='in',
  327. to_field_name='slug',
  328. label=_('Site group (slug)'),
  329. )
  330. site_id = django_filters.ModelMultipleChoiceFilter(
  331. queryset=Site.objects.all(),
  332. label=_('Site (ID)'),
  333. )
  334. site = django_filters.ModelMultipleChoiceFilter(
  335. field_name='site__slug',
  336. queryset=Site.objects.all(),
  337. to_field_name='slug',
  338. label=_('Site (slug)'),
  339. )
  340. vlan_id = django_filters.ModelMultipleChoiceFilter(
  341. queryset=VLAN.objects.all(),
  342. label=_('VLAN (ID)'),
  343. )
  344. vlan_vid = django_filters.NumberFilter(
  345. field_name='vlan__vid',
  346. label=_('VLAN number (1-4094)'),
  347. )
  348. role_id = django_filters.ModelMultipleChoiceFilter(
  349. queryset=Role.objects.all(),
  350. label=_('Role (ID)'),
  351. )
  352. role = django_filters.ModelMultipleChoiceFilter(
  353. field_name='role__slug',
  354. queryset=Role.objects.all(),
  355. to_field_name='slug',
  356. label=_('Role (slug)'),
  357. )
  358. status = django_filters.MultipleChoiceFilter(
  359. choices=PrefixStatusChoices,
  360. null_value=None
  361. )
  362. class Meta:
  363. model = Prefix
  364. fields = ('id', 'is_pool', 'mark_utilized', 'description')
  365. def search(self, queryset, name, value):
  366. if not value.strip():
  367. return queryset
  368. qs_filter = Q(description__icontains=value)
  369. qs_filter |= Q(prefix__contains=value.strip())
  370. try:
  371. prefix = str(netaddr.IPNetwork(value.strip()).cidr)
  372. qs_filter |= Q(prefix__net_contains_or_equals=prefix)
  373. qs_filter |= Q(prefix__contains=value.strip())
  374. except (AddrFormatError, ValueError):
  375. pass
  376. return queryset.filter(qs_filter)
  377. def filter_prefix(self, queryset, name, value):
  378. query_values = []
  379. for v in value:
  380. try:
  381. query_values.append(netaddr.IPNetwork(v))
  382. except (AddrFormatError, ValueError):
  383. pass
  384. return queryset.filter(prefix__in=query_values)
  385. def search_within(self, queryset, name, value):
  386. value = value.strip()
  387. if not value:
  388. return queryset
  389. try:
  390. query = str(netaddr.IPNetwork(value).cidr)
  391. return queryset.filter(prefix__net_contained=query)
  392. except (AddrFormatError, ValueError):
  393. return queryset.none()
  394. def search_within_include(self, queryset, name, value):
  395. value = value.strip()
  396. if not value:
  397. return queryset
  398. try:
  399. query = str(netaddr.IPNetwork(value).cidr)
  400. return queryset.filter(prefix__net_contained_or_equal=query)
  401. except (AddrFormatError, ValueError):
  402. return queryset.none()
  403. def search_contains(self, queryset, name, value):
  404. value = value.strip()
  405. if not value:
  406. return queryset
  407. try:
  408. # Searching by prefix
  409. if '/' in value:
  410. return queryset.filter(prefix__net_contains_or_equals=str(netaddr.IPNetwork(value).cidr))
  411. # Searching by IP address
  412. else:
  413. return queryset.filter(prefix__net_contains=str(netaddr.IPAddress(value)))
  414. except (AddrFormatError, ValueError):
  415. return queryset.none()
  416. @extend_schema_field(OpenApiTypes.STR)
  417. def filter_present_in_vrf(self, queryset, name, vrf):
  418. if vrf is None:
  419. return queryset.none
  420. return queryset.filter(
  421. Q(vrf=vrf) |
  422. Q(vrf__export_targets__in=vrf.import_targets.all())
  423. ).distinct()
  424. class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
  425. family = django_filters.NumberFilter(
  426. field_name='start_address',
  427. lookup_expr='family'
  428. )
  429. start_address = MultiValueCharFilter(
  430. method='filter_address',
  431. label=_('Address'),
  432. )
  433. end_address = MultiValueCharFilter(
  434. method='filter_address',
  435. label=_('Address'),
  436. )
  437. contains = django_filters.CharFilter(
  438. method='search_contains',
  439. label=_('Ranges which contain this prefix or IP'),
  440. )
  441. vrf_id = django_filters.ModelMultipleChoiceFilter(
  442. queryset=VRF.objects.all(),
  443. label=_('VRF'),
  444. )
  445. vrf = django_filters.ModelMultipleChoiceFilter(
  446. field_name='vrf__rd',
  447. queryset=VRF.objects.all(),
  448. to_field_name='rd',
  449. label=_('VRF (RD)'),
  450. )
  451. role_id = django_filters.ModelMultipleChoiceFilter(
  452. queryset=Role.objects.all(),
  453. label=_('Role (ID)'),
  454. )
  455. role = django_filters.ModelMultipleChoiceFilter(
  456. field_name='role__slug',
  457. queryset=Role.objects.all(),
  458. to_field_name='slug',
  459. label=_('Role (slug)'),
  460. )
  461. status = django_filters.MultipleChoiceFilter(
  462. choices=IPRangeStatusChoices,
  463. null_value=None
  464. )
  465. parent = MultiValueCharFilter(
  466. method='search_by_parent',
  467. label=_('Parent prefix'),
  468. )
  469. class Meta:
  470. model = IPRange
  471. fields = ('id', 'mark_utilized', 'size', 'description')
  472. def search(self, queryset, name, value):
  473. if not value.strip():
  474. return queryset
  475. qs_filter = Q(description__icontains=value) | Q(start_address__contains=value) | Q(end_address__contains=value)
  476. try:
  477. ipaddress = str(netaddr.IPNetwork(value.strip()))
  478. qs_filter |= Q(start_address=ipaddress)
  479. qs_filter |= Q(end_address=ipaddress)
  480. except (AddrFormatError, ValueError):
  481. pass
  482. return queryset.filter(qs_filter)
  483. def search_contains(self, queryset, name, value):
  484. value = value.strip()
  485. if not value:
  486. return queryset
  487. try:
  488. # Strip mask
  489. ipaddress = netaddr.IPNetwork(value)
  490. return queryset.filter(start_address__lte=ipaddress, end_address__gte=ipaddress)
  491. except (AddrFormatError, ValueError):
  492. return queryset.none()
  493. def filter_address(self, queryset, name, value):
  494. try:
  495. return queryset.filter(**{f'{name}__net_in': value})
  496. except ValidationError:
  497. return queryset.none()
  498. def search_by_parent(self, queryset, name, value):
  499. if not value:
  500. return queryset
  501. q = Q()
  502. for prefix in value:
  503. try:
  504. query = str(netaddr.IPNetwork(prefix.strip()).cidr)
  505. q |= Q(start_address__net_host_contained=query, end_address__net_host_contained=query)
  506. except (AddrFormatError, ValueError):
  507. return queryset.none()
  508. return queryset.filter(q)
  509. class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  510. family = django_filters.NumberFilter(
  511. field_name='address',
  512. lookup_expr='family'
  513. )
  514. parent = MultiValueCharFilter(
  515. method='search_by_parent',
  516. label=_('Parent prefix'),
  517. )
  518. address = MultiValueCharFilter(
  519. method='filter_address',
  520. label=_('Address'),
  521. )
  522. mask_length = MultiValueNumberFilter(
  523. field_name='address',
  524. lookup_expr='net_mask_length',
  525. label=_('Mask length')
  526. )
  527. mask_length__gte = django_filters.NumberFilter(
  528. field_name='address',
  529. lookup_expr='net_mask_length__gte'
  530. )
  531. mask_length__lte = django_filters.NumberFilter(
  532. field_name='address',
  533. lookup_expr='net_mask_length__lte'
  534. )
  535. vrf_id = django_filters.ModelMultipleChoiceFilter(
  536. queryset=VRF.objects.all(),
  537. label=_('VRF'),
  538. )
  539. vrf = django_filters.ModelMultipleChoiceFilter(
  540. field_name='vrf__rd',
  541. queryset=VRF.objects.all(),
  542. to_field_name='rd',
  543. label=_('VRF (RD)'),
  544. )
  545. present_in_vrf_id = django_filters.ModelChoiceFilter(
  546. queryset=VRF.objects.all(),
  547. method='filter_present_in_vrf',
  548. label=_('VRF')
  549. )
  550. present_in_vrf = django_filters.ModelChoiceFilter(
  551. queryset=VRF.objects.all(),
  552. method='filter_present_in_vrf',
  553. to_field_name='rd',
  554. label=_('VRF (RD)'),
  555. )
  556. device = MultiValueCharFilter(
  557. method='filter_device',
  558. field_name='name',
  559. label=_('Device (name)'),
  560. )
  561. device_id = MultiValueNumberFilter(
  562. method='filter_device',
  563. field_name='pk',
  564. label=_('Device (ID)'),
  565. )
  566. virtual_machine = MultiValueCharFilter(
  567. method='filter_virtual_machine',
  568. field_name='name',
  569. label=_('Virtual machine (name)'),
  570. )
  571. virtual_machine_id = MultiValueNumberFilter(
  572. method='filter_virtual_machine',
  573. field_name='pk',
  574. label=_('Virtual machine (ID)'),
  575. )
  576. interface = django_filters.ModelMultipleChoiceFilter(
  577. field_name='interface__name',
  578. queryset=Interface.objects.all(),
  579. to_field_name='name',
  580. label=_('Interface (name)'),
  581. )
  582. interface_id = django_filters.ModelMultipleChoiceFilter(
  583. field_name='interface',
  584. queryset=Interface.objects.all(),
  585. label=_('Interface (ID)'),
  586. )
  587. vminterface = django_filters.ModelMultipleChoiceFilter(
  588. field_name='vminterface__name',
  589. queryset=VMInterface.objects.all(),
  590. to_field_name='name',
  591. label=_('VM interface (name)'),
  592. )
  593. vminterface_id = django_filters.ModelMultipleChoiceFilter(
  594. field_name='vminterface',
  595. queryset=VMInterface.objects.all(),
  596. label=_('VM interface (ID)'),
  597. )
  598. fhrpgroup_id = django_filters.ModelMultipleChoiceFilter(
  599. field_name='fhrpgroup',
  600. queryset=FHRPGroup.objects.all(),
  601. label=_('FHRP group (ID)'),
  602. )
  603. assigned_to_interface = django_filters.BooleanFilter(
  604. method='_assigned_to_interface',
  605. label=_('Is assigned to an interface'),
  606. )
  607. assigned = django_filters.BooleanFilter(
  608. method='_assigned',
  609. label=_('Is assigned'),
  610. )
  611. status = django_filters.MultipleChoiceFilter(
  612. choices=IPAddressStatusChoices,
  613. null_value=None
  614. )
  615. role = django_filters.MultipleChoiceFilter(
  616. choices=IPAddressRoleChoices
  617. )
  618. service_id = django_filters.ModelMultipleChoiceFilter(
  619. field_name='services',
  620. queryset=Service.objects.all(),
  621. label=_('Service (ID)'),
  622. )
  623. nat_inside_id = django_filters.ModelMultipleChoiceFilter(
  624. field_name='nat_inside',
  625. queryset=IPAddress.objects.all(),
  626. label=_('NAT inside IP address (ID)'),
  627. )
  628. class Meta:
  629. model = IPAddress
  630. fields = ('id', 'dns_name', 'description', 'assigned_object_type', 'assigned_object_id')
  631. def search(self, queryset, name, value):
  632. if not value.strip():
  633. return queryset
  634. qs_filter = (
  635. Q(dns_name__icontains=value) |
  636. Q(description__icontains=value) |
  637. Q(address__istartswith=value)
  638. )
  639. return queryset.filter(qs_filter)
  640. def search_by_parent(self, queryset, name, value):
  641. if not value:
  642. return queryset
  643. q = Q()
  644. for prefix in value:
  645. try:
  646. query = str(netaddr.IPNetwork(prefix.strip()).cidr)
  647. q |= Q(address__net_host_contained=query)
  648. except (AddrFormatError, ValueError):
  649. return queryset.none()
  650. return queryset.filter(q)
  651. def parse_inet_addresses(self, value):
  652. '''
  653. Parse networks or IP addresses and cast to a format
  654. acceptable by the Postgres inet type.
  655. Skips invalid values.
  656. '''
  657. parsed = []
  658. for addr in value:
  659. if netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr):
  660. parsed.append(addr)
  661. continue
  662. try:
  663. network = netaddr.IPNetwork(addr)
  664. parsed.append(str(network))
  665. except (AddrFormatError, ValueError):
  666. continue
  667. return parsed
  668. def filter_address(self, queryset, name, value):
  669. # Let's first parse the addresses passed
  670. # as argument. If they are all invalid,
  671. # we return an empty queryset
  672. value = self.parse_inet_addresses(value)
  673. if (len(value) == 0):
  674. return queryset.none()
  675. try:
  676. return queryset.filter(address__net_in=value)
  677. except ValidationError:
  678. return queryset.none()
  679. @extend_schema_field(OpenApiTypes.STR)
  680. def filter_present_in_vrf(self, queryset, name, vrf):
  681. if vrf is None:
  682. return queryset.none
  683. return queryset.filter(
  684. Q(vrf=vrf) |
  685. Q(vrf__export_targets__in=vrf.import_targets.all())
  686. ).distinct()
  687. def filter_device(self, queryset, name, value):
  688. devices = Device.objects.filter(**{'{}__in'.format(name): value})
  689. if not devices.exists():
  690. return queryset.none()
  691. interface_ids = []
  692. for device in devices:
  693. interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
  694. return queryset.filter(
  695. interface__in=interface_ids
  696. )
  697. def filter_virtual_machine(self, queryset, name, value):
  698. virtual_machines = VirtualMachine.objects.filter(**{'{}__in'.format(name): value})
  699. if not virtual_machines.exists():
  700. return queryset.none()
  701. interface_ids = []
  702. for vm in virtual_machines:
  703. interface_ids.extend(vm.interfaces.values_list('id', flat=True))
  704. return queryset.filter(
  705. vminterface__in=interface_ids
  706. )
  707. def _assigned_to_interface(self, queryset, name, value):
  708. content_types = ContentType.objects.get_for_models(Interface, VMInterface).values()
  709. if value:
  710. return queryset.filter(
  711. assigned_object_type__in=content_types,
  712. assigned_object_id__isnull=False
  713. )
  714. else:
  715. return queryset.exclude(
  716. assigned_object_type__in=content_types,
  717. assigned_object_id__isnull=False
  718. )
  719. def _assigned(self, queryset, name, value):
  720. if value:
  721. return queryset.exclude(
  722. assigned_object_type__isnull=True,
  723. assigned_object_id__isnull=True
  724. )
  725. else:
  726. return queryset.filter(
  727. assigned_object_type__isnull=True,
  728. assigned_object_id__isnull=True
  729. )
  730. class FHRPGroupFilterSet(NetBoxModelFilterSet):
  731. protocol = django_filters.MultipleChoiceFilter(
  732. choices=FHRPGroupProtocolChoices
  733. )
  734. auth_type = django_filters.MultipleChoiceFilter(
  735. choices=FHRPGroupAuthTypeChoices
  736. )
  737. related_ip = django_filters.ModelMultipleChoiceFilter(
  738. queryset=IPAddress.objects.all(),
  739. method='filter_related_ip'
  740. )
  741. class Meta:
  742. model = FHRPGroup
  743. fields = ('id', 'group_id', 'name', 'auth_key', 'description')
  744. def search(self, queryset, name, value):
  745. if not value.strip():
  746. return queryset
  747. return queryset.filter(
  748. Q(description__icontains=value) |
  749. Q(name__icontains=value)
  750. )
  751. @extend_schema_field(OpenApiTypes.STR)
  752. def filter_related_ip(self, queryset, name, value):
  753. """
  754. Filter by VRF & prefix of assigned IP addresses.
  755. """
  756. ip_filter = Q()
  757. for ipaddress in value:
  758. if ipaddress.vrf:
  759. q = Q(
  760. ip_addresses__address__net_contained_or_equal=ipaddress.address,
  761. ip_addresses__vrf=ipaddress.vrf
  762. )
  763. else:
  764. q = Q(
  765. ip_addresses__address__net_contained_or_equal=ipaddress.address,
  766. ip_addresses__vrf__isnull=True
  767. )
  768. ip_filter |= q
  769. return queryset.filter(ip_filter)
  770. class FHRPGroupAssignmentFilterSet(ChangeLoggedModelFilterSet):
  771. interface_type = ContentTypeFilter()
  772. group_id = django_filters.ModelMultipleChoiceFilter(
  773. queryset=FHRPGroup.objects.all(),
  774. label=_('Group (ID)'),
  775. )
  776. device = MultiValueCharFilter(
  777. method='filter_device',
  778. field_name='name',
  779. label=_('Device (name)'),
  780. )
  781. device_id = MultiValueNumberFilter(
  782. method='filter_device',
  783. field_name='pk',
  784. label=_('Device (ID)'),
  785. )
  786. virtual_machine = MultiValueCharFilter(
  787. method='filter_virtual_machine',
  788. field_name='name',
  789. label=_('Virtual machine (name)'),
  790. )
  791. virtual_machine_id = MultiValueNumberFilter(
  792. method='filter_virtual_machine',
  793. field_name='pk',
  794. label=_('Virtual machine (ID)'),
  795. )
  796. class Meta:
  797. model = FHRPGroupAssignment
  798. fields = ('id', 'group_id', 'interface_type', 'interface_id', 'priority')
  799. def filter_device(self, queryset, name, value):
  800. devices = Device.objects.filter(**{f'{name}__in': value})
  801. if not devices.exists():
  802. return queryset.none()
  803. interface_ids = []
  804. for device in devices:
  805. interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
  806. return queryset.filter(
  807. Q(interface_type=ContentType.objects.get_for_model(Interface), interface_id__in=interface_ids)
  808. )
  809. def filter_virtual_machine(self, queryset, name, value):
  810. virtual_machines = VirtualMachine.objects.filter(**{f'{name}__in': value})
  811. if not virtual_machines.exists():
  812. return queryset.none()
  813. interface_ids = []
  814. for vm in virtual_machines:
  815. interface_ids.extend(vm.interfaces.values_list('id', flat=True))
  816. return queryset.filter(
  817. Q(interface_type=ContentType.objects.get_for_model(VMInterface), interface_id__in=interface_ids)
  818. )
  819. class VLANGroupFilterSet(OrganizationalModelFilterSet):
  820. scope_type = ContentTypeFilter()
  821. region = django_filters.NumberFilter(
  822. method='filter_scope'
  823. )
  824. site_group = django_filters.NumberFilter(
  825. method='filter_scope'
  826. )
  827. site = django_filters.NumberFilter(
  828. method='filter_scope'
  829. )
  830. location = django_filters.NumberFilter(
  831. method='filter_scope'
  832. )
  833. rack = django_filters.NumberFilter(
  834. method='filter_scope'
  835. )
  836. cluster_group = django_filters.NumberFilter(
  837. method='filter_scope'
  838. )
  839. cluster = django_filters.NumberFilter(
  840. method='filter_scope'
  841. )
  842. contains_vid = django_filters.NumberFilter(
  843. method='filter_contains_vid'
  844. )
  845. class Meta:
  846. model = VLANGroup
  847. fields = ('id', 'name', 'slug', 'description', 'scope_id')
  848. def search(self, queryset, name, value):
  849. if not value.strip():
  850. return queryset
  851. qs_filter = (
  852. Q(name__icontains=value) |
  853. Q(description__icontains=value)
  854. )
  855. return queryset.filter(qs_filter)
  856. def filter_scope(self, queryset, name, value):
  857. model_name = name.replace('_', '')
  858. return queryset.filter(
  859. scope_type=ContentType.objects.get(model=model_name),
  860. scope_id=value
  861. )
  862. def filter_contains_vid(self, queryset, name, value):
  863. """
  864. Return all VLANGroups which contain the given VLAN ID.
  865. """
  866. table_name = VLANGroup._meta.db_table
  867. # TODO: See if this can be optimized without compromising queryset integrity
  868. # Expand VLAN ID ranges to query by integer
  869. groups = VLANGroup.objects.raw(
  870. f'SELECT id FROM {table_name}, unnest(vid_ranges) vid_range WHERE %s <@ vid_range',
  871. params=(value,)
  872. )
  873. return queryset.filter(
  874. pk__in=[g.id for g in groups]
  875. )
  876. class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
  877. region_id = TreeNodeMultipleChoiceFilter(
  878. queryset=Region.objects.all(),
  879. field_name='site__region',
  880. lookup_expr='in',
  881. label=_('Region (ID)'),
  882. )
  883. region = TreeNodeMultipleChoiceFilter(
  884. queryset=Region.objects.all(),
  885. field_name='site__region',
  886. lookup_expr='in',
  887. to_field_name='slug',
  888. label=_('Region (slug)'),
  889. )
  890. site_group_id = TreeNodeMultipleChoiceFilter(
  891. queryset=SiteGroup.objects.all(),
  892. field_name='site__group',
  893. lookup_expr='in',
  894. label=_('Site group (ID)'),
  895. )
  896. site_group = TreeNodeMultipleChoiceFilter(
  897. queryset=SiteGroup.objects.all(),
  898. field_name='site__group',
  899. lookup_expr='in',
  900. to_field_name='slug',
  901. label=_('Site group (slug)'),
  902. )
  903. site_id = django_filters.ModelMultipleChoiceFilter(
  904. queryset=Site.objects.all(),
  905. label=_('Site (ID)'),
  906. )
  907. site = django_filters.ModelMultipleChoiceFilter(
  908. field_name='site__slug',
  909. queryset=Site.objects.all(),
  910. to_field_name='slug',
  911. label=_('Site (slug)'),
  912. )
  913. group_id = django_filters.ModelMultipleChoiceFilter(
  914. queryset=VLANGroup.objects.all(),
  915. label=_('Group (ID)'),
  916. )
  917. group = django_filters.ModelMultipleChoiceFilter(
  918. field_name='group__slug',
  919. queryset=VLANGroup.objects.all(),
  920. to_field_name='slug',
  921. label=_('Group'),
  922. )
  923. role_id = django_filters.ModelMultipleChoiceFilter(
  924. queryset=Role.objects.all(),
  925. label=_('Role (ID)'),
  926. )
  927. role = django_filters.ModelMultipleChoiceFilter(
  928. field_name='role__slug',
  929. queryset=Role.objects.all(),
  930. to_field_name='slug',
  931. label=_('Role (slug)'),
  932. )
  933. status = django_filters.MultipleChoiceFilter(
  934. choices=VLANStatusChoices,
  935. null_value=None
  936. )
  937. available_at_site = django_filters.ModelChoiceFilter(
  938. queryset=Site.objects.all(),
  939. method='get_for_site'
  940. )
  941. available_on_device = django_filters.ModelChoiceFilter(
  942. queryset=Device.objects.all(),
  943. method='get_for_device'
  944. )
  945. available_on_virtualmachine = django_filters.ModelChoiceFilter(
  946. queryset=VirtualMachine.objects.all(),
  947. method='get_for_virtualmachine'
  948. )
  949. l2vpn_id = django_filters.ModelMultipleChoiceFilter(
  950. field_name='l2vpn_terminations__l2vpn',
  951. queryset=L2VPN.objects.all(),
  952. label=_('L2VPN (ID)'),
  953. )
  954. l2vpn = django_filters.ModelMultipleChoiceFilter(
  955. field_name='l2vpn_terminations__l2vpn__identifier',
  956. queryset=L2VPN.objects.all(),
  957. to_field_name='identifier',
  958. label=_('L2VPN'),
  959. )
  960. interface_id = django_filters.ModelChoiceFilter(
  961. queryset=Interface.objects.all(),
  962. method='filter_interface_id',
  963. label=_('Assigned interface')
  964. )
  965. vminterface_id = django_filters.ModelChoiceFilter(
  966. queryset=VMInterface.objects.all(),
  967. method='filter_vminterface_id',
  968. label=_('Assigned VM interface')
  969. )
  970. class Meta:
  971. model = VLAN
  972. fields = ('id', 'vid', 'name', 'description')
  973. def search(self, queryset, name, value):
  974. if not value.strip():
  975. return queryset
  976. qs_filter = Q(name__icontains=value) | Q(description__icontains=value)
  977. try:
  978. qs_filter |= Q(vid=int(value.strip()))
  979. except ValueError:
  980. pass
  981. return queryset.filter(qs_filter)
  982. @extend_schema_field(OpenApiTypes.STR)
  983. def get_for_site(self, queryset, name, value):
  984. return queryset.get_for_site(value)
  985. @extend_schema_field(OpenApiTypes.STR)
  986. def get_for_device(self, queryset, name, value):
  987. return queryset.get_for_device(value)
  988. @extend_schema_field(OpenApiTypes.STR)
  989. def get_for_virtualmachine(self, queryset, name, value):
  990. return queryset.get_for_virtualmachine(value)
  991. def filter_interface_id(self, queryset, name, value):
  992. if value is None:
  993. return queryset.none()
  994. return queryset.filter(
  995. Q(interfaces_as_tagged=value) |
  996. Q(interfaces_as_untagged=value)
  997. )
  998. def filter_vminterface_id(self, queryset, name, value):
  999. if value is None:
  1000. return queryset.none()
  1001. return queryset.filter(
  1002. Q(vminterfaces_as_tagged=value) |
  1003. Q(vminterfaces_as_untagged=value)
  1004. )
  1005. class ServiceTemplateFilterSet(NetBoxModelFilterSet):
  1006. port = NumericArrayFilter(
  1007. field_name='ports',
  1008. lookup_expr='contains'
  1009. )
  1010. class Meta:
  1011. model = ServiceTemplate
  1012. fields = ('id', 'name', 'protocol', 'description')
  1013. def search(self, queryset, name, value):
  1014. if not value.strip():
  1015. return queryset
  1016. qs_filter = (
  1017. Q(name__icontains=value) |
  1018. Q(description__icontains=value)
  1019. )
  1020. return queryset.filter(qs_filter)
  1021. class ServiceFilterSet(NetBoxModelFilterSet):
  1022. device_id = django_filters.ModelMultipleChoiceFilter(
  1023. queryset=Device.objects.all(),
  1024. label=_('Device (ID)'),
  1025. )
  1026. device = django_filters.ModelMultipleChoiceFilter(
  1027. field_name='device__name',
  1028. queryset=Device.objects.all(),
  1029. to_field_name='name',
  1030. label=_('Device (name)'),
  1031. )
  1032. virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
  1033. queryset=VirtualMachine.objects.all(),
  1034. label=_('Virtual machine (ID)'),
  1035. )
  1036. virtual_machine = django_filters.ModelMultipleChoiceFilter(
  1037. field_name='virtual_machine__name',
  1038. queryset=VirtualMachine.objects.all(),
  1039. to_field_name='name',
  1040. label=_('Virtual machine (name)'),
  1041. )
  1042. ip_address_id = django_filters.ModelMultipleChoiceFilter(
  1043. field_name='ipaddresses',
  1044. queryset=IPAddress.objects.all(),
  1045. label=_('IP address (ID)'),
  1046. )
  1047. ip_address = django_filters.ModelMultipleChoiceFilter(
  1048. field_name='ipaddresses__address',
  1049. queryset=IPAddress.objects.all(),
  1050. to_field_name='address',
  1051. label=_('IP address'),
  1052. )
  1053. port = NumericArrayFilter(
  1054. field_name='ports',
  1055. lookup_expr='contains'
  1056. )
  1057. class Meta:
  1058. model = Service
  1059. fields = ('id', 'name', 'protocol', 'description')
  1060. def search(self, queryset, name, value):
  1061. if not value.strip():
  1062. return queryset
  1063. qs_filter = Q(name__icontains=value) | Q(description__icontains=value)
  1064. return queryset.filter(qs_filter)
  1065. class PrimaryIPFilterSet(django_filters.FilterSet):
  1066. """
  1067. An inheritable FilterSet for models which support primary IP assignment.
  1068. """
  1069. primary_ip4_id = django_filters.ModelMultipleChoiceFilter(
  1070. field_name='primary_ip4',
  1071. queryset=IPAddress.objects.all(),
  1072. label=_('Primary IPv4 (ID)'),
  1073. )
  1074. primary_ip6_id = django_filters.ModelMultipleChoiceFilter(
  1075. field_name='primary_ip6',
  1076. queryset=IPAddress.objects.all(),
  1077. label=_('Primary IPv6 (ID)'),
  1078. )