filters.py 20 KB

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