test_api.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. import json
  2. from django.urls import reverse
  3. from netaddr import IPNetwork
  4. from rest_framework import status
  5. from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
  6. from ipam.choices import *
  7. from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  8. from utilities.testing import APITestCase, APIViewTestCases, disable_warnings
  9. class AppTest(APITestCase):
  10. def test_root(self):
  11. url = reverse('ipam-api:api-root')
  12. response = self.client.get('{}?format=api'.format(url), **self.header)
  13. self.assertEqual(response.status_code, 200)
  14. class VRFTest(APIViewTestCases.APIViewTestCase):
  15. model = VRF
  16. brief_fields = ['id', 'name', 'prefix_count', 'rd', 'url']
  17. create_data = [
  18. {
  19. 'name': 'VRF 4',
  20. 'rd': '65000:4',
  21. },
  22. {
  23. 'name': 'VRF 5',
  24. 'rd': '65000:5',
  25. },
  26. {
  27. 'name': 'VRF 6',
  28. 'rd': '65000:6',
  29. },
  30. ]
  31. @classmethod
  32. def setUpTestData(cls):
  33. vrfs = (
  34. VRF(name='VRF 1', rd='65000:1'),
  35. VRF(name='VRF 2', rd='65000:2'),
  36. VRF(name='VRF 3'), # No RD
  37. )
  38. VRF.objects.bulk_create(vrfs)
  39. class RIRTest(APIViewTestCases.APIViewTestCase):
  40. model = RIR
  41. brief_fields = ['aggregate_count', 'id', 'name', 'slug', 'url']
  42. create_data = [
  43. {
  44. 'name': 'RIR 4',
  45. 'slug': 'rir-4',
  46. },
  47. {
  48. 'name': 'RIR 5',
  49. 'slug': 'rir-5',
  50. },
  51. {
  52. 'name': 'RIR 6',
  53. 'slug': 'rir-6',
  54. },
  55. ]
  56. @classmethod
  57. def setUpTestData(cls):
  58. rirs = (
  59. RIR(name='RIR 1', slug='rir-1'),
  60. RIR(name='RIR 2', slug='rir-2'),
  61. RIR(name='RIR 3', slug='rir-3'),
  62. )
  63. RIR.objects.bulk_create(rirs)
  64. class AggregateTest(APIViewTestCases.APIViewTestCase):
  65. model = Aggregate
  66. brief_fields = ['family', 'id', 'prefix', 'url']
  67. @classmethod
  68. def setUpTestData(cls):
  69. rirs = (
  70. RIR(name='RIR 1', slug='rir-1'),
  71. RIR(name='RIR 2', slug='rir-2'),
  72. )
  73. RIR.objects.bulk_create(rirs)
  74. aggregates = (
  75. Aggregate(prefix=IPNetwork('10.0.0.0/8'), rir=rirs[0]),
  76. Aggregate(prefix=IPNetwork('172.16.0.0/12'), rir=rirs[0]),
  77. Aggregate(prefix=IPNetwork('192.168.0.0/16'), rir=rirs[0]),
  78. )
  79. Aggregate.objects.bulk_create(aggregates)
  80. cls.create_data = [
  81. {
  82. 'prefix': '100.0.0.0/8',
  83. 'rir': rirs[1].pk,
  84. },
  85. {
  86. 'prefix': '101.0.0.0/8',
  87. 'rir': rirs[1].pk,
  88. },
  89. {
  90. 'prefix': '102.0.0.0/8',
  91. 'rir': rirs[1].pk,
  92. },
  93. ]
  94. class RoleTest(APIViewTestCases.APIViewTestCase):
  95. model = Role
  96. brief_fields = ['id', 'name', 'prefix_count', 'slug', 'url', 'vlan_count']
  97. create_data = [
  98. {
  99. 'name': 'Role 4',
  100. 'slug': 'role-4',
  101. },
  102. {
  103. 'name': 'Role 5',
  104. 'slug': 'role-5',
  105. },
  106. {
  107. 'name': 'Role 6',
  108. 'slug': 'role-6',
  109. },
  110. ]
  111. @classmethod
  112. def setUpTestData(cls):
  113. roles = (
  114. Role(name='Role 1', slug='role-1'),
  115. Role(name='Role 2', slug='role-2'),
  116. Role(name='Role 3', slug='role-3'),
  117. )
  118. Role.objects.bulk_create(roles)
  119. class PrefixTest(APIViewTestCases.APIViewTestCase):
  120. model = Prefix
  121. brief_fields = ['family', 'id', 'prefix', 'url']
  122. create_data = [
  123. {
  124. 'prefix': '192.168.4.0/24',
  125. },
  126. {
  127. 'prefix': '192.168.5.0/24',
  128. },
  129. {
  130. 'prefix': '192.168.6.0/24',
  131. },
  132. ]
  133. @classmethod
  134. def setUpTestData(cls):
  135. prefixes = (
  136. Prefix(prefix=IPNetwork('192.168.1.0/24')),
  137. Prefix(prefix=IPNetwork('192.168.2.0/24')),
  138. Prefix(prefix=IPNetwork('192.168.3.0/24')),
  139. )
  140. Prefix.objects.bulk_create(prefixes)
  141. def test_list_available_prefixes(self):
  142. """
  143. Test retrieval of all available prefixes within a parent prefix.
  144. """
  145. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'))
  146. Prefix.objects.create(prefix=IPNetwork('192.0.2.64/26'))
  147. Prefix.objects.create(prefix=IPNetwork('192.0.2.192/27'))
  148. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  149. self.add_permissions('ipam.view_prefix')
  150. # Retrieve all available IPs
  151. response = self.client.get(url, **self.header)
  152. available_prefixes = ['192.0.2.0/26', '192.0.2.128/26', '192.0.2.224/27']
  153. for i, p in enumerate(response.data):
  154. self.assertEqual(p['prefix'], available_prefixes[i])
  155. def test_create_single_available_prefix(self):
  156. """
  157. Test retrieval of the first available prefix within a parent prefix.
  158. """
  159. vrf = VRF.objects.create(name='Test VRF 1', rd='1234')
  160. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/28'), vrf=vrf, is_pool=True)
  161. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  162. self.add_permissions('ipam.add_prefix')
  163. # Create four available prefixes with individual requests
  164. prefixes_to_be_created = [
  165. '192.0.2.0/30',
  166. '192.0.2.4/30',
  167. '192.0.2.8/30',
  168. '192.0.2.12/30',
  169. ]
  170. for i in range(4):
  171. data = {
  172. 'prefix_length': 30,
  173. 'description': 'Test Prefix {}'.format(i + 1)
  174. }
  175. response = self.client.post(url, data, format='json', **self.header)
  176. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  177. self.assertEqual(response.data['prefix'], prefixes_to_be_created[i])
  178. self.assertEqual(response.data['vrf']['id'], vrf.pk)
  179. self.assertEqual(response.data['description'], data['description'])
  180. # Try to create one more prefix
  181. response = self.client.post(url, {'prefix_length': 30}, format='json', **self.header)
  182. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  183. self.assertIn('detail', response.data)
  184. # Try to create invalid prefix type
  185. response = self.client.post(url, {'prefix_length': '30'}, format='json', **self.header)
  186. self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
  187. self.assertIn('prefix_length', response.data[0])
  188. def test_create_multiple_available_prefixes(self):
  189. """
  190. Test the creation of available prefixes within a parent prefix.
  191. """
  192. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/28'), is_pool=True)
  193. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  194. self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
  195. # Try to create five /30s (only four are available)
  196. data = [
  197. {'prefix_length': 30, 'description': 'Test Prefix 1'},
  198. {'prefix_length': 30, 'description': 'Test Prefix 2'},
  199. {'prefix_length': 30, 'description': 'Test Prefix 3'},
  200. {'prefix_length': 30, 'description': 'Test Prefix 4'},
  201. {'prefix_length': 30, 'description': 'Test Prefix 5'},
  202. ]
  203. response = self.client.post(url, data, format='json', **self.header)
  204. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  205. self.assertIn('detail', response.data)
  206. # Verify that no prefixes were created (the entire /28 is still available)
  207. response = self.client.get(url, **self.header)
  208. self.assertHttpStatus(response, status.HTTP_200_OK)
  209. self.assertEqual(response.data[0]['prefix'], '192.0.2.0/28')
  210. # Create four /30s in a single request
  211. response = self.client.post(url, data[:4], format='json', **self.header)
  212. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  213. self.assertEqual(len(response.data), 4)
  214. def test_list_available_ips(self):
  215. """
  216. Test retrieval of all available IP addresses within a parent prefix.
  217. """
  218. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/29'), is_pool=True)
  219. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  220. self.add_permissions('ipam.view_prefix', 'ipam.view_ipaddress')
  221. # Retrieve all available IPs
  222. response = self.client.get(url, **self.header)
  223. self.assertEqual(len(response.data), 8) # 8 because prefix.is_pool = True
  224. # Change the prefix to not be a pool and try again
  225. prefix.is_pool = False
  226. prefix.save()
  227. response = self.client.get(url, **self.header)
  228. self.assertEqual(len(response.data), 6) # 8 - 2 because prefix.is_pool = False
  229. def test_create_single_available_ip(self):
  230. """
  231. Test retrieval of the first available IP address within a parent prefix.
  232. """
  233. vrf = VRF.objects.create(name='Test VRF 1', rd='1234')
  234. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/30'), vrf=vrf, is_pool=True)
  235. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  236. # TODO: ipam.add_prefix should not be required
  237. self.add_permissions('ipam.add_prefix', 'ipam.add_ipaddress')
  238. # Create all four available IPs with individual requests
  239. for i in range(1, 5):
  240. data = {
  241. 'description': 'Test IP {}'.format(i)
  242. }
  243. response = self.client.post(url, data, format='json', **self.header)
  244. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  245. self.assertEqual(response.data['vrf']['id'], vrf.pk)
  246. self.assertEqual(response.data['description'], data['description'])
  247. # Try to create one more IP
  248. response = self.client.post(url, {}, **self.header)
  249. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  250. self.assertIn('detail', response.data)
  251. def test_create_multiple_available_ips(self):
  252. """
  253. Test the creation of available IP addresses within a parent prefix.
  254. """
  255. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/29'), is_pool=True)
  256. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  257. # TODO: ipam.add_prefix, ipam.view_prefix should not be required
  258. self.add_permissions('ipam.add_prefix', 'ipam.view_prefix', 'ipam.view_ipaddress', 'ipam.add_ipaddress')
  259. # Try to create nine IPs (only eight are available)
  260. data = [{'description': 'Test IP {}'.format(i)} for i in range(1, 10)] # 9 IPs
  261. response = self.client.post(url, data, format='json', **self.header)
  262. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  263. self.assertIn('detail', response.data)
  264. # Verify that no IPs were created (eight are still available)
  265. response = self.client.get(url, **self.header)
  266. self.assertHttpStatus(response, status.HTTP_200_OK)
  267. self.assertEqual(len(response.data), 8)
  268. # Create all eight available IPs in a single request
  269. data = [{'description': 'Test IP {}'.format(i)} for i in range(1, 9)] # 8 IPs
  270. response = self.client.post(url, data, format='json', **self.header)
  271. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  272. self.assertEqual(len(response.data), 8)
  273. class IPAddressTest(APIViewTestCases.APIViewTestCase):
  274. model = IPAddress
  275. brief_fields = ['address', 'family', 'id', 'url']
  276. create_data = [
  277. {
  278. 'address': '192.168.0.4/24',
  279. },
  280. {
  281. 'address': '192.168.0.5/24',
  282. },
  283. {
  284. 'address': '192.168.0.6/24',
  285. },
  286. ]
  287. @classmethod
  288. def setUpTestData(cls):
  289. ip_addresses = (
  290. IPAddress(address=IPNetwork('192.168.0.1/24')),
  291. IPAddress(address=IPNetwork('192.168.0.2/24')),
  292. IPAddress(address=IPNetwork('192.168.0.3/24')),
  293. )
  294. IPAddress.objects.bulk_create(ip_addresses)
  295. class VLANGroupTest(APIViewTestCases.APIViewTestCase):
  296. model = VLANGroup
  297. brief_fields = ['id', 'name', 'slug', 'url', 'vlan_count']
  298. create_data = [
  299. {
  300. 'name': 'VLAN Group 4',
  301. 'slug': 'vlan-group-4',
  302. },
  303. {
  304. 'name': 'VLAN Group 5',
  305. 'slug': 'vlan-group-5',
  306. },
  307. {
  308. 'name': 'VLAN Group 6',
  309. 'slug': 'vlan-group-6',
  310. },
  311. ]
  312. @classmethod
  313. def setUpTestData(cls):
  314. vlan_groups = (
  315. VLANGroup(name='VLAN Group 1', slug='vlan-group-1'),
  316. VLANGroup(name='VLAN Group 2', slug='vlan-group-2'),
  317. VLANGroup(name='VLAN Group 3', slug='vlan-group-3'),
  318. )
  319. VLANGroup.objects.bulk_create(vlan_groups)
  320. class VLANTest(APIViewTestCases.APIViewTestCase):
  321. model = VLAN
  322. brief_fields = ['display_name', 'id', 'name', 'url', 'vid']
  323. @classmethod
  324. def setUpTestData(cls):
  325. vlan_groups = (
  326. VLANGroup(name='VLAN Group 1', slug='vlan-group-1'),
  327. VLANGroup(name='VLAN Group 2', slug='vlan-group-2'),
  328. )
  329. VLANGroup.objects.bulk_create(vlan_groups)
  330. vlans = (
  331. VLAN(name='VLAN 1', vid=1, group=vlan_groups[0]),
  332. VLAN(name='VLAN 2', vid=2, group=vlan_groups[0]),
  333. VLAN(name='VLAN 3', vid=3, group=vlan_groups[0]),
  334. )
  335. VLAN.objects.bulk_create(vlans)
  336. cls.create_data = [
  337. {
  338. 'vid': 4,
  339. 'name': 'VLAN 4',
  340. 'group': vlan_groups[1].pk,
  341. },
  342. {
  343. 'vid': 5,
  344. 'name': 'VLAN 5',
  345. 'group': vlan_groups[1].pk,
  346. },
  347. {
  348. 'vid': 6,
  349. 'name': 'VLAN 6',
  350. 'group': vlan_groups[1].pk,
  351. },
  352. ]
  353. def test_delete_vlan_with_prefix(self):
  354. """
  355. Attempt and fail to delete a VLAN with a Prefix assigned to it.
  356. """
  357. vlan = VLAN.objects.first()
  358. Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'), vlan=vlan)
  359. self.add_permissions('ipam.delete_vlan')
  360. url = reverse('ipam-api:vlan-detail', kwargs={'pk': vlan.pk})
  361. with disable_warnings('django.request'):
  362. response = self.client.delete(url, **self.header)
  363. self.assertHttpStatus(response, status.HTTP_409_CONFLICT)
  364. content = json.loads(response.content.decode('utf-8'))
  365. self.assertIn('detail', content)
  366. self.assertTrue(content['detail'].startswith('Unable to delete object.'))
  367. class ServiceTest(APIViewTestCases.APIViewTestCase):
  368. model = Service
  369. brief_fields = ['id', 'name', 'port', 'protocol', 'url']
  370. @classmethod
  371. def setUpTestData(cls):
  372. site = Site.objects.create(name='Site 1', slug='site-1')
  373. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  374. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
  375. devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  376. devices = (
  377. Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole),
  378. Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole),
  379. )
  380. Device.objects.bulk_create(devices)
  381. services = (
  382. Service(device=devices[0], name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, port=1),
  383. Service(device=devices[0], name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, port=2),
  384. Service(device=devices[0], name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, port=3),
  385. )
  386. Service.objects.bulk_create(services)
  387. cls.create_data = [
  388. {
  389. 'device': devices[1].pk,
  390. 'name': 'Service 4',
  391. 'protocol': ServiceProtocolChoices.PROTOCOL_TCP,
  392. 'port': 4,
  393. },
  394. {
  395. 'device': devices[1].pk,
  396. 'name': 'Service 5',
  397. 'protocol': ServiceProtocolChoices.PROTOCOL_TCP,
  398. 'port': 5,
  399. },
  400. {
  401. 'device': devices[1].pk,
  402. 'name': 'Service 6',
  403. 'protocol': ServiceProtocolChoices.PROTOCOL_TCP,
  404. 'port': 6,
  405. },
  406. ]