filters.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. from __future__ import unicode_literals
  2. import django_filters
  3. from netaddr import EUI
  4. from netaddr.core import AddrFormatError
  5. from django.contrib.auth.models import User
  6. from django.db.models import Q
  7. from extras.filters import CustomFieldFilterSet
  8. from tenancy.models import Tenant
  9. from utilities.filters import NullableCharFieldFilter, NullableModelMultipleChoiceFilter, NumericInFilter
  10. from virtualization.models import Cluster
  11. from .models import (
  12. ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  13. DeviceBayTemplate, DeviceRole, DeviceType, STATUS_CHOICES, IFACE_FF_LAG, Interface, InterfaceConnection,
  14. InterfaceTemplate, Manufacturer, InventoryItem, NONCONNECTABLE_IFACE_TYPES, Platform, PowerOutlet,
  15. PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, Region, Site,
  16. VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES,
  17. )
  18. class RegionFilter(django_filters.FilterSet):
  19. parent_id = NullableModelMultipleChoiceFilter(
  20. queryset=Region.objects.all(),
  21. label='Parent region (ID)',
  22. )
  23. parent = NullableModelMultipleChoiceFilter(
  24. queryset=Region.objects.all(),
  25. to_field_name='slug',
  26. label='Parent region (slug)',
  27. )
  28. class Meta:
  29. model = Region
  30. fields = ['name', 'slug']
  31. class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
  32. id__in = NumericInFilter(name='id', lookup_expr='in')
  33. q = django_filters.CharFilter(
  34. method='search',
  35. label='Search',
  36. )
  37. region_id = NullableModelMultipleChoiceFilter(
  38. queryset=Region.objects.all(),
  39. label='Region (ID)',
  40. )
  41. region = NullableModelMultipleChoiceFilter(
  42. queryset=Region.objects.all(),
  43. to_field_name='slug',
  44. label='Region (slug)',
  45. )
  46. tenant_id = NullableModelMultipleChoiceFilter(
  47. queryset=Tenant.objects.all(),
  48. label='Tenant (ID)',
  49. )
  50. tenant = NullableModelMultipleChoiceFilter(
  51. queryset=Tenant.objects.all(),
  52. to_field_name='slug',
  53. label='Tenant (slug)',
  54. )
  55. class Meta:
  56. model = Site
  57. fields = ['q', 'name', 'slug', 'facility', 'asn', 'contact_name', 'contact_phone', 'contact_email']
  58. def search(self, queryset, name, value):
  59. if not value.strip():
  60. return queryset
  61. qs_filter = (
  62. Q(name__icontains=value) |
  63. Q(facility__icontains=value) |
  64. Q(physical_address__icontains=value) |
  65. Q(shipping_address__icontains=value) |
  66. Q(contact_name__icontains=value) |
  67. Q(contact_phone__icontains=value) |
  68. Q(contact_email__icontains=value) |
  69. Q(comments__icontains=value)
  70. )
  71. try:
  72. qs_filter |= Q(asn=int(value.strip()))
  73. except ValueError:
  74. pass
  75. return queryset.filter(qs_filter)
  76. class RackGroupFilter(django_filters.FilterSet):
  77. site_id = django_filters.ModelMultipleChoiceFilter(
  78. queryset=Site.objects.all(),
  79. label='Site (ID)',
  80. )
  81. site = django_filters.ModelMultipleChoiceFilter(
  82. name='site__slug',
  83. queryset=Site.objects.all(),
  84. to_field_name='slug',
  85. label='Site (slug)',
  86. )
  87. class Meta:
  88. model = RackGroup
  89. fields = ['site_id', 'name', 'slug']
  90. class RackRoleFilter(django_filters.FilterSet):
  91. class Meta:
  92. model = RackRole
  93. fields = ['name', 'slug', 'color']
  94. class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
  95. id__in = NumericInFilter(name='id', lookup_expr='in')
  96. q = django_filters.CharFilter(
  97. method='search',
  98. label='Search',
  99. )
  100. facility_id = NullableCharFieldFilter()
  101. site_id = django_filters.ModelMultipleChoiceFilter(
  102. queryset=Site.objects.all(),
  103. label='Site (ID)',
  104. )
  105. site = django_filters.ModelMultipleChoiceFilter(
  106. name='site__slug',
  107. queryset=Site.objects.all(),
  108. to_field_name='slug',
  109. label='Site (slug)',
  110. )
  111. group_id = NullableModelMultipleChoiceFilter(
  112. queryset=RackGroup.objects.all(),
  113. label='Group (ID)',
  114. )
  115. group = NullableModelMultipleChoiceFilter(
  116. name='group',
  117. queryset=RackGroup.objects.all(),
  118. to_field_name='slug',
  119. label='Group',
  120. )
  121. tenant_id = NullableModelMultipleChoiceFilter(
  122. queryset=Tenant.objects.all(),
  123. label='Tenant (ID)',
  124. )
  125. tenant = NullableModelMultipleChoiceFilter(
  126. name='tenant',
  127. queryset=Tenant.objects.all(),
  128. to_field_name='slug',
  129. label='Tenant (slug)',
  130. )
  131. role_id = NullableModelMultipleChoiceFilter(
  132. queryset=RackRole.objects.all(),
  133. label='Role (ID)',
  134. )
  135. role = NullableModelMultipleChoiceFilter(
  136. name='role',
  137. queryset=RackRole.objects.all(),
  138. to_field_name='slug',
  139. label='Role (slug)',
  140. )
  141. class Meta:
  142. model = Rack
  143. fields = ['type', 'width', 'u_height', 'desc_units']
  144. def search(self, queryset, name, value):
  145. if not value.strip():
  146. return queryset
  147. return queryset.filter(
  148. Q(name__icontains=value) |
  149. Q(facility_id__icontains=value) |
  150. Q(comments__icontains=value)
  151. )
  152. class RackReservationFilter(django_filters.FilterSet):
  153. id__in = NumericInFilter(name='id', lookup_expr='in')
  154. q = django_filters.CharFilter(
  155. method='search',
  156. label='Search',
  157. )
  158. rack_id = django_filters.ModelMultipleChoiceFilter(
  159. queryset=Rack.objects.all(),
  160. label='Rack (ID)',
  161. )
  162. site_id = django_filters.ModelMultipleChoiceFilter(
  163. name='rack__site',
  164. queryset=Site.objects.all(),
  165. label='Site (ID)',
  166. )
  167. site = django_filters.ModelMultipleChoiceFilter(
  168. name='rack__site__slug',
  169. queryset=Site.objects.all(),
  170. to_field_name='slug',
  171. label='Site (slug)',
  172. )
  173. group_id = NullableModelMultipleChoiceFilter(
  174. name='rack__group',
  175. queryset=RackGroup.objects.all(),
  176. label='Group (ID)',
  177. )
  178. group = NullableModelMultipleChoiceFilter(
  179. name='rack__group',
  180. queryset=RackGroup.objects.all(),
  181. to_field_name='slug',
  182. label='Group',
  183. )
  184. user_id = django_filters.ModelMultipleChoiceFilter(
  185. queryset=User.objects.all(),
  186. label='User (ID)',
  187. )
  188. user = django_filters.ModelMultipleChoiceFilter(
  189. name='user',
  190. queryset=User.objects.all(),
  191. to_field_name='username',
  192. label='User (name)',
  193. )
  194. class Meta:
  195. model = RackReservation
  196. fields = ['created']
  197. def search(self, queryset, name, value):
  198. if not value.strip():
  199. return queryset
  200. return queryset.filter(
  201. Q(rack__name__icontains=value) |
  202. Q(rack__facility_id__icontains=value) |
  203. Q(user__username__icontains=value) |
  204. Q(description__icontains=value)
  205. )
  206. class ManufacturerFilter(django_filters.FilterSet):
  207. class Meta:
  208. model = Manufacturer
  209. fields = ['name', 'slug']
  210. class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
  211. id__in = NumericInFilter(name='id', lookup_expr='in')
  212. q = django_filters.CharFilter(
  213. method='search',
  214. label='Search',
  215. )
  216. manufacturer_id = django_filters.ModelMultipleChoiceFilter(
  217. queryset=Manufacturer.objects.all(),
  218. label='Manufacturer (ID)',
  219. )
  220. manufacturer = django_filters.ModelMultipleChoiceFilter(
  221. name='manufacturer__slug',
  222. queryset=Manufacturer.objects.all(),
  223. to_field_name='slug',
  224. label='Manufacturer (slug)',
  225. )
  226. class Meta:
  227. model = DeviceType
  228. fields = [
  229. 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
  230. 'is_network_device', 'subdevice_role',
  231. ]
  232. def search(self, queryset, name, value):
  233. if not value.strip():
  234. return queryset
  235. return queryset.filter(
  236. Q(manufacturer__name__icontains=value) |
  237. Q(model__icontains=value) |
  238. Q(part_number__icontains=value) |
  239. Q(comments__icontains=value)
  240. )
  241. class DeviceTypeComponentFilterSet(django_filters.FilterSet):
  242. devicetype_id = django_filters.ModelMultipleChoiceFilter(
  243. queryset=DeviceType.objects.all(),
  244. name='device_type_id',
  245. label='Device type (ID)',
  246. )
  247. class ConsolePortTemplateFilter(DeviceTypeComponentFilterSet):
  248. class Meta:
  249. model = ConsolePortTemplate
  250. fields = ['name']
  251. class ConsoleServerPortTemplateFilter(DeviceTypeComponentFilterSet):
  252. class Meta:
  253. model = ConsoleServerPortTemplate
  254. fields = ['name']
  255. class PowerPortTemplateFilter(DeviceTypeComponentFilterSet):
  256. class Meta:
  257. model = PowerPortTemplate
  258. fields = ['name']
  259. class PowerOutletTemplateFilter(DeviceTypeComponentFilterSet):
  260. class Meta:
  261. model = PowerOutletTemplate
  262. fields = ['name']
  263. class InterfaceTemplateFilter(DeviceTypeComponentFilterSet):
  264. class Meta:
  265. model = InterfaceTemplate
  266. fields = ['name', 'form_factor', 'mgmt_only']
  267. class DeviceBayTemplateFilter(DeviceTypeComponentFilterSet):
  268. class Meta:
  269. model = DeviceBayTemplate
  270. fields = ['name']
  271. class DeviceRoleFilter(django_filters.FilterSet):
  272. class Meta:
  273. model = DeviceRole
  274. fields = ['name', 'slug', 'color']
  275. class PlatformFilter(django_filters.FilterSet):
  276. class Meta:
  277. model = Platform
  278. fields = ['name', 'slug']
  279. class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
  280. id__in = NumericInFilter(name='id', lookup_expr='in')
  281. q = django_filters.CharFilter(
  282. method='search',
  283. label='Search',
  284. )
  285. manufacturer_id = django_filters.ModelMultipleChoiceFilter(
  286. name='device_type__manufacturer',
  287. queryset=Manufacturer.objects.all(),
  288. label='Manufacturer (ID)',
  289. )
  290. manufacturer = django_filters.ModelMultipleChoiceFilter(
  291. name='device_type__manufacturer__slug',
  292. queryset=Manufacturer.objects.all(),
  293. to_field_name='slug',
  294. label='Manufacturer (slug)',
  295. )
  296. device_type_id = django_filters.ModelMultipleChoiceFilter(
  297. queryset=DeviceType.objects.all(),
  298. label='Device type (ID)',
  299. )
  300. role_id = django_filters.ModelMultipleChoiceFilter(
  301. name='device_role_id',
  302. queryset=DeviceRole.objects.all(),
  303. label='Role (ID)',
  304. )
  305. role = django_filters.ModelMultipleChoiceFilter(
  306. name='device_role__slug',
  307. queryset=DeviceRole.objects.all(),
  308. to_field_name='slug',
  309. label='Role (slug)',
  310. )
  311. tenant_id = NullableModelMultipleChoiceFilter(
  312. queryset=Tenant.objects.all(),
  313. label='Tenant (ID)',
  314. )
  315. tenant = NullableModelMultipleChoiceFilter(
  316. name='tenant',
  317. queryset=Tenant.objects.all(),
  318. to_field_name='slug',
  319. label='Tenant (slug)',
  320. )
  321. platform_id = NullableModelMultipleChoiceFilter(
  322. queryset=Platform.objects.all(),
  323. label='Platform (ID)',
  324. )
  325. platform = NullableModelMultipleChoiceFilter(
  326. name='platform',
  327. queryset=Platform.objects.all(),
  328. to_field_name='slug',
  329. label='Platform (slug)',
  330. )
  331. name = NullableCharFieldFilter()
  332. asset_tag = NullableCharFieldFilter()
  333. site_id = django_filters.ModelMultipleChoiceFilter(
  334. queryset=Site.objects.all(),
  335. label='Site (ID)',
  336. )
  337. site = django_filters.ModelMultipleChoiceFilter(
  338. name='site__slug',
  339. queryset=Site.objects.all(),
  340. to_field_name='slug',
  341. label='Site name (slug)',
  342. )
  343. rack_group_id = django_filters.ModelMultipleChoiceFilter(
  344. name='rack__group',
  345. queryset=RackGroup.objects.all(),
  346. label='Rack group (ID)',
  347. )
  348. rack_id = NullableModelMultipleChoiceFilter(
  349. name='rack',
  350. queryset=Rack.objects.all(),
  351. label='Rack (ID)',
  352. )
  353. cluster_id = NullableModelMultipleChoiceFilter(
  354. queryset=Cluster.objects.all(),
  355. label='VM cluster (ID)',
  356. )
  357. model = django_filters.ModelMultipleChoiceFilter(
  358. name='device_type__slug',
  359. queryset=DeviceType.objects.all(),
  360. to_field_name='slug',
  361. label='Device model (slug)',
  362. )
  363. status = django_filters.MultipleChoiceFilter(
  364. choices=STATUS_CHOICES
  365. )
  366. is_full_depth = django_filters.BooleanFilter(
  367. name='device_type__is_full_depth',
  368. label='Is full depth',
  369. )
  370. is_console_server = django_filters.BooleanFilter(
  371. name='device_type__is_console_server',
  372. label='Is a console server',
  373. )
  374. is_pdu = django_filters.BooleanFilter(
  375. name='device_type__is_pdu',
  376. label='Is a PDU',
  377. )
  378. is_network_device = django_filters.BooleanFilter(
  379. name='device_type__is_network_device',
  380. label='Is a network device',
  381. )
  382. mac_address = django_filters.CharFilter(
  383. method='_mac_address',
  384. label='MAC address',
  385. )
  386. has_primary_ip = django_filters.BooleanFilter(
  387. method='_has_primary_ip',
  388. label='Has a primary IP',
  389. )
  390. class Meta:
  391. model = Device
  392. fields = ['serial']
  393. def search(self, queryset, name, value):
  394. if not value.strip():
  395. return queryset
  396. return queryset.filter(
  397. Q(name__icontains=value) |
  398. Q(serial__icontains=value.strip()) |
  399. Q(inventory_items__serial__icontains=value.strip()) |
  400. Q(asset_tag=value.strip()) |
  401. Q(comments__icontains=value)
  402. ).distinct()
  403. def _mac_address(self, queryset, name, value):
  404. value = value.strip()
  405. if not value:
  406. return queryset
  407. try:
  408. mac = EUI(value.strip())
  409. return queryset.filter(interfaces__mac_address=mac).distinct()
  410. except AddrFormatError:
  411. return queryset.none()
  412. def _has_primary_ip(self, queryset, name, value):
  413. if value:
  414. return queryset.filter(
  415. Q(primary_ip4__isnull=False) |
  416. Q(primary_ip6__isnull=False)
  417. )
  418. else:
  419. return queryset.exclude(
  420. Q(primary_ip4__isnull=False) |
  421. Q(primary_ip6__isnull=False)
  422. )
  423. class DeviceComponentFilterSet(django_filters.FilterSet):
  424. device_id = django_filters.ModelChoiceFilter(
  425. queryset=Device.objects.all(),
  426. label='Device (ID)',
  427. )
  428. device = django_filters.ModelChoiceFilter(
  429. queryset=Device.objects.all(),
  430. to_field_name='name',
  431. label='Device (name)',
  432. )
  433. class ConsolePortFilter(DeviceComponentFilterSet):
  434. class Meta:
  435. model = ConsolePort
  436. fields = ['name']
  437. class ConsoleServerPortFilter(DeviceComponentFilterSet):
  438. class Meta:
  439. model = ConsoleServerPort
  440. fields = ['name']
  441. class PowerPortFilter(DeviceComponentFilterSet):
  442. class Meta:
  443. model = PowerPort
  444. fields = ['name']
  445. class PowerOutletFilter(DeviceComponentFilterSet):
  446. class Meta:
  447. model = PowerOutlet
  448. fields = ['name']
  449. class InterfaceFilter(django_filters.FilterSet):
  450. """
  451. Not using DeviceComponentFilterSet for Interfaces because we need to glean the ordering logic from the parent
  452. Device's DeviceType.
  453. """
  454. device = django_filters.CharFilter(
  455. method='filter_device',
  456. name='name',
  457. label='Device',
  458. )
  459. device_id = django_filters.NumberFilter(
  460. method='filter_device',
  461. name='pk',
  462. label='Device (ID)',
  463. )
  464. type = django_filters.CharFilter(
  465. method='filter_type',
  466. label='Interface type',
  467. )
  468. lag_id = django_filters.ModelMultipleChoiceFilter(
  469. name='lag',
  470. queryset=Interface.objects.all(),
  471. label='LAG interface (ID)',
  472. )
  473. mac_address = django_filters.CharFilter(
  474. method='_mac_address',
  475. label='MAC address',
  476. )
  477. class Meta:
  478. model = Interface
  479. fields = ['name', 'form_factor', 'enabled', 'mtu', 'mgmt_only']
  480. def filter_device(self, queryset, name, value):
  481. try:
  482. device = Device.objects.select_related('device_type').get(**{name: value})
  483. ordering = device.device_type.interface_ordering
  484. return queryset.filter(device=device).order_naturally(ordering)
  485. except Device.DoesNotExist:
  486. return queryset.none()
  487. def filter_type(self, queryset, name, value):
  488. value = value.strip().lower()
  489. return {
  490. 'physical': queryset.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES),
  491. 'virtual': queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES),
  492. 'wireless': queryset.filter(form_factor__in=WIRELESS_IFACE_TYPES),
  493. 'lag': queryset.filter(form_factor=IFACE_FF_LAG),
  494. }.get(value, queryset.none())
  495. def _mac_address(self, queryset, name, value):
  496. value = value.strip()
  497. if not value:
  498. return queryset
  499. try:
  500. mac = EUI(value.strip())
  501. return queryset.filter(mac_address=mac)
  502. except AddrFormatError:
  503. return queryset.none()
  504. class DeviceBayFilter(DeviceComponentFilterSet):
  505. class Meta:
  506. model = DeviceBay
  507. fields = ['name']
  508. class InventoryItemFilter(DeviceComponentFilterSet):
  509. parent_id = NullableModelMultipleChoiceFilter(
  510. queryset=InventoryItem.objects.all(),
  511. label='Parent inventory item (ID)',
  512. )
  513. manufacturer_id = django_filters.ModelMultipleChoiceFilter(
  514. queryset=Manufacturer.objects.all(),
  515. label='Manufacturer (ID)',
  516. )
  517. manufacturer = django_filters.ModelMultipleChoiceFilter(
  518. name='manufacturer__slug',
  519. queryset=Manufacturer.objects.all(),
  520. to_field_name='slug',
  521. label='Manufacturer (slug)',
  522. )
  523. asset_tag = NullableCharFieldFilter()
  524. class Meta:
  525. model = InventoryItem
  526. fields = ['name', 'part_id', 'serial', 'discovered']
  527. class ConsoleConnectionFilter(django_filters.FilterSet):
  528. site = django_filters.CharFilter(
  529. method='filter_site',
  530. label='Site (slug)',
  531. )
  532. device = django_filters.CharFilter(
  533. method='filter_device',
  534. label='Device',
  535. )
  536. class Meta:
  537. model = ConsolePort
  538. fields = ['name', 'connection_status']
  539. def filter_site(self, queryset, name, value):
  540. if not value.strip():
  541. return queryset
  542. return queryset.filter(cs_port__device__site__slug=value)
  543. def filter_device(self, queryset, name, value):
  544. if not value.strip():
  545. return queryset
  546. return queryset.filter(
  547. Q(device__name__icontains=value) |
  548. Q(cs_port__device__name__icontains=value)
  549. )
  550. class PowerConnectionFilter(django_filters.FilterSet):
  551. site = django_filters.CharFilter(
  552. method='filter_site',
  553. label='Site (slug)',
  554. )
  555. device = django_filters.CharFilter(
  556. method='filter_device',
  557. label='Device',
  558. )
  559. class Meta:
  560. model = PowerPort
  561. fields = ['name', 'connection_status']
  562. def filter_site(self, queryset, name, value):
  563. if not value.strip():
  564. return queryset
  565. return queryset.filter(power_outlet__device__site__slug=value)
  566. def filter_device(self, queryset, name, value):
  567. if not value.strip():
  568. return queryset
  569. return queryset.filter(
  570. Q(device__name__icontains=value) |
  571. Q(power_outlet__device__name__icontains=value)
  572. )
  573. class InterfaceConnectionFilter(django_filters.FilterSet):
  574. site = django_filters.CharFilter(
  575. method='filter_site',
  576. label='Site (slug)',
  577. )
  578. device = django_filters.CharFilter(
  579. method='filter_device',
  580. label='Device',
  581. )
  582. class Meta:
  583. model = InterfaceConnection
  584. fields = ['connection_status']
  585. def filter_site(self, queryset, name, value):
  586. if not value.strip():
  587. return queryset
  588. return queryset.filter(
  589. Q(interface_a__device__site__slug=value) |
  590. Q(interface_b__device__site__slug=value)
  591. )
  592. def filter_device(self, queryset, name, value):
  593. if not value.strip():
  594. return queryset
  595. return queryset.filter(
  596. Q(interface_a__device__name__icontains=value) |
  597. Q(interface_b__device__name__icontains=value)
  598. )