test_api.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  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.constants import IP_PROTOCOL_TCP, IP_PROTOCOL_UDP
  7. from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  8. from utilities.testing import APITestCase
  9. class VRFTest(APITestCase):
  10. def setUp(self):
  11. super().setUp()
  12. self.vrf1 = VRF.objects.create(name='Test VRF 1', rd='65000:1')
  13. self.vrf2 = VRF.objects.create(name='Test VRF 2', rd='65000:2')
  14. self.vrf3 = VRF.objects.create(name='Test VRF 3') # No RD
  15. def test_get_vrf(self):
  16. url = reverse('ipam-api:vrf-detail', kwargs={'pk': self.vrf1.pk})
  17. response = self.client.get(url, **self.header)
  18. self.assertEqual(response.data['name'], self.vrf1.name)
  19. def test_list_vrfs(self):
  20. url = reverse('ipam-api:vrf-list')
  21. response = self.client.get(url, **self.header)
  22. self.assertEqual(response.data['count'], 3)
  23. def test_list_vrfs_brief(self):
  24. url = reverse('ipam-api:vrf-list')
  25. response = self.client.get('{}?brief=1'.format(url), **self.header)
  26. self.assertEqual(
  27. sorted(response.data['results'][0]),
  28. ['id', 'name', 'prefix_count', 'rd', 'url']
  29. )
  30. def test_create_vrf(self):
  31. data_list = [
  32. # VRF with RD
  33. {
  34. 'name': 'Test VRF 4',
  35. 'rd': '65000:4',
  36. },
  37. # VRF without RD
  38. {
  39. 'name': 'Test VRF 5',
  40. }
  41. ]
  42. url = reverse('ipam-api:vrf-list')
  43. for data in data_list:
  44. response = self.client.post(url, data, format='json', **self.header)
  45. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  46. vrf = VRF.objects.get(pk=response.data['id'])
  47. self.assertEqual(vrf.name, data['name'])
  48. self.assertEqual(vrf.rd, data['rd'] if 'rd' in data else None)
  49. def test_create_vrf_bulk(self):
  50. data = [
  51. {
  52. 'name': 'Test VRF 4',
  53. 'rd': '65000:4',
  54. },
  55. {
  56. 'name': 'Test VRF 5',
  57. 'rd': '65000:5',
  58. },
  59. {
  60. 'name': 'Test VRF 6',
  61. 'rd': '65000:6',
  62. },
  63. ]
  64. url = reverse('ipam-api:vrf-list')
  65. response = self.client.post(url, data, format='json', **self.header)
  66. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  67. self.assertEqual(VRF.objects.count(), 6)
  68. self.assertEqual(response.data[0]['name'], data[0]['name'])
  69. self.assertEqual(response.data[1]['name'], data[1]['name'])
  70. self.assertEqual(response.data[2]['name'], data[2]['name'])
  71. def test_update_vrf(self):
  72. data = {
  73. 'name': 'Test VRF X',
  74. 'rd': '65000:99',
  75. }
  76. url = reverse('ipam-api:vrf-detail', kwargs={'pk': self.vrf1.pk})
  77. response = self.client.put(url, data, format='json', **self.header)
  78. self.assertHttpStatus(response, status.HTTP_200_OK)
  79. self.assertEqual(VRF.objects.count(), 3)
  80. vrf1 = VRF.objects.get(pk=response.data['id'])
  81. self.assertEqual(vrf1.name, data['name'])
  82. self.assertEqual(vrf1.rd, data['rd'])
  83. def test_delete_vrf(self):
  84. url = reverse('ipam-api:vrf-detail', kwargs={'pk': self.vrf1.pk})
  85. response = self.client.delete(url, **self.header)
  86. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  87. self.assertEqual(VRF.objects.count(), 2)
  88. class RIRTest(APITestCase):
  89. def setUp(self):
  90. super().setUp()
  91. self.rir1 = RIR.objects.create(name='Test RIR 1', slug='test-rir-1')
  92. self.rir2 = RIR.objects.create(name='Test RIR 2', slug='test-rir-2')
  93. self.rir3 = RIR.objects.create(name='Test RIR 3', slug='test-rir-3')
  94. def test_get_rir(self):
  95. url = reverse('ipam-api:rir-detail', kwargs={'pk': self.rir1.pk})
  96. response = self.client.get(url, **self.header)
  97. self.assertEqual(response.data['name'], self.rir1.name)
  98. def test_list_rirs(self):
  99. url = reverse('ipam-api:rir-list')
  100. response = self.client.get(url, **self.header)
  101. self.assertEqual(response.data['count'], 3)
  102. def test_list_rirs_brief(self):
  103. url = reverse('ipam-api:rir-list')
  104. response = self.client.get('{}?brief=1'.format(url), **self.header)
  105. self.assertEqual(
  106. sorted(response.data['results'][0]),
  107. ['aggregate_count', 'id', 'name', 'slug', 'url']
  108. )
  109. def test_create_rir(self):
  110. data = {
  111. 'name': 'Test RIR 4',
  112. 'slug': 'test-rir-4',
  113. }
  114. url = reverse('ipam-api:rir-list')
  115. response = self.client.post(url, data, format='json', **self.header)
  116. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  117. self.assertEqual(RIR.objects.count(), 4)
  118. rir4 = RIR.objects.get(pk=response.data['id'])
  119. self.assertEqual(rir4.name, data['name'])
  120. self.assertEqual(rir4.slug, data['slug'])
  121. def test_create_rir_bulk(self):
  122. data = [
  123. {
  124. 'name': 'Test RIR 4',
  125. 'slug': 'test-rir-4',
  126. },
  127. {
  128. 'name': 'Test RIR 5',
  129. 'slug': 'test-rir-5',
  130. },
  131. {
  132. 'name': 'Test RIR 6',
  133. 'slug': 'test-rir-6',
  134. },
  135. ]
  136. url = reverse('ipam-api:rir-list')
  137. response = self.client.post(url, data, format='json', **self.header)
  138. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  139. self.assertEqual(RIR.objects.count(), 6)
  140. self.assertEqual(response.data[0]['name'], data[0]['name'])
  141. self.assertEqual(response.data[1]['name'], data[1]['name'])
  142. self.assertEqual(response.data[2]['name'], data[2]['name'])
  143. def test_update_rir(self):
  144. data = {
  145. 'name': 'Test RIR X',
  146. 'slug': 'test-rir-x',
  147. }
  148. url = reverse('ipam-api:rir-detail', kwargs={'pk': self.rir1.pk})
  149. response = self.client.put(url, data, format='json', **self.header)
  150. self.assertHttpStatus(response, status.HTTP_200_OK)
  151. self.assertEqual(RIR.objects.count(), 3)
  152. rir1 = RIR.objects.get(pk=response.data['id'])
  153. self.assertEqual(rir1.name, data['name'])
  154. self.assertEqual(rir1.slug, data['slug'])
  155. def test_delete_rir(self):
  156. url = reverse('ipam-api:rir-detail', kwargs={'pk': self.rir1.pk})
  157. response = self.client.delete(url, **self.header)
  158. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  159. self.assertEqual(RIR.objects.count(), 2)
  160. class AggregateTest(APITestCase):
  161. def setUp(self):
  162. super().setUp()
  163. self.rir1 = RIR.objects.create(name='Test RIR 1', slug='test-rir-1')
  164. self.rir2 = RIR.objects.create(name='Test RIR 2', slug='test-rir-2')
  165. self.aggregate1 = Aggregate.objects.create(prefix=IPNetwork('10.0.0.0/8'), rir=self.rir1)
  166. self.aggregate2 = Aggregate.objects.create(prefix=IPNetwork('172.16.0.0/12'), rir=self.rir1)
  167. self.aggregate3 = Aggregate.objects.create(prefix=IPNetwork('192.168.0.0/16'), rir=self.rir1)
  168. def test_get_aggregate(self):
  169. url = reverse('ipam-api:aggregate-detail', kwargs={'pk': self.aggregate1.pk})
  170. response = self.client.get(url, **self.header)
  171. self.assertEqual(response.data['prefix'], str(self.aggregate1.prefix))
  172. def test_list_aggregates(self):
  173. url = reverse('ipam-api:aggregate-list')
  174. response = self.client.get(url, **self.header)
  175. self.assertEqual(response.data['count'], 3)
  176. def test_list_aggregates_brief(self):
  177. url = reverse('ipam-api:aggregate-list')
  178. response = self.client.get('{}?brief=1'.format(url), **self.header)
  179. self.assertEqual(
  180. sorted(response.data['results'][0]),
  181. ['family', 'id', 'prefix', 'url']
  182. )
  183. def test_create_aggregate(self):
  184. data = {
  185. 'prefix': '192.0.2.0/24',
  186. 'rir': self.rir1.pk,
  187. }
  188. url = reverse('ipam-api:aggregate-list')
  189. response = self.client.post(url, data, format='json', **self.header)
  190. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  191. self.assertEqual(Aggregate.objects.count(), 4)
  192. aggregate4 = Aggregate.objects.get(pk=response.data['id'])
  193. self.assertEqual(str(aggregate4.prefix), data['prefix'])
  194. self.assertEqual(aggregate4.rir_id, data['rir'])
  195. def test_create_aggregate_bulk(self):
  196. data = [
  197. {
  198. 'prefix': '100.0.0.0/8',
  199. 'rir': self.rir1.pk,
  200. },
  201. {
  202. 'prefix': '101.0.0.0/8',
  203. 'rir': self.rir1.pk,
  204. },
  205. {
  206. 'prefix': '102.0.0.0/8',
  207. 'rir': self.rir1.pk,
  208. },
  209. ]
  210. url = reverse('ipam-api:aggregate-list')
  211. response = self.client.post(url, data, format='json', **self.header)
  212. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  213. self.assertEqual(Aggregate.objects.count(), 6)
  214. self.assertEqual(response.data[0]['prefix'], data[0]['prefix'])
  215. self.assertEqual(response.data[1]['prefix'], data[1]['prefix'])
  216. self.assertEqual(response.data[2]['prefix'], data[2]['prefix'])
  217. def test_update_aggregate(self):
  218. data = {
  219. 'prefix': '11.0.0.0/8',
  220. 'rir': self.rir2.pk,
  221. }
  222. url = reverse('ipam-api:aggregate-detail', kwargs={'pk': self.aggregate1.pk})
  223. response = self.client.put(url, data, format='json', **self.header)
  224. self.assertHttpStatus(response, status.HTTP_200_OK)
  225. self.assertEqual(Aggregate.objects.count(), 3)
  226. aggregate1 = Aggregate.objects.get(pk=response.data['id'])
  227. self.assertEqual(str(aggregate1.prefix), data['prefix'])
  228. self.assertEqual(aggregate1.rir_id, data['rir'])
  229. def test_delete_aggregate(self):
  230. url = reverse('ipam-api:aggregate-detail', kwargs={'pk': self.aggregate1.pk})
  231. response = self.client.delete(url, **self.header)
  232. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  233. self.assertEqual(Aggregate.objects.count(), 2)
  234. class RoleTest(APITestCase):
  235. def setUp(self):
  236. super().setUp()
  237. self.role1 = Role.objects.create(name='Test Role 1', slug='test-role-1')
  238. self.role2 = Role.objects.create(name='Test Role 2', slug='test-role-2')
  239. self.role3 = Role.objects.create(name='Test Role 3', slug='test-role-3')
  240. def test_get_role(self):
  241. url = reverse('ipam-api:role-detail', kwargs={'pk': self.role1.pk})
  242. response = self.client.get(url, **self.header)
  243. self.assertEqual(response.data['name'], self.role1.name)
  244. def test_list_roles(self):
  245. url = reverse('ipam-api:role-list')
  246. response = self.client.get(url, **self.header)
  247. self.assertEqual(response.data['count'], 3)
  248. def test_list_roles_brief(self):
  249. url = reverse('ipam-api:role-list')
  250. response = self.client.get('{}?brief=1'.format(url), **self.header)
  251. self.assertEqual(
  252. sorted(response.data['results'][0]),
  253. ['id', 'name', 'prefix_count', 'slug', 'url', 'vlan_count']
  254. )
  255. def test_create_role(self):
  256. data = {
  257. 'name': 'Test Role 4',
  258. 'slug': 'test-role-4',
  259. }
  260. url = reverse('ipam-api:role-list')
  261. response = self.client.post(url, data, format='json', **self.header)
  262. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  263. self.assertEqual(Role.objects.count(), 4)
  264. role4 = Role.objects.get(pk=response.data['id'])
  265. self.assertEqual(role4.name, data['name'])
  266. self.assertEqual(role4.slug, data['slug'])
  267. def test_create_role_bulk(self):
  268. data = [
  269. {
  270. 'name': 'Test Role 4',
  271. 'slug': 'test-role-4',
  272. },
  273. {
  274. 'name': 'Test Role 5',
  275. 'slug': 'test-role-5',
  276. },
  277. {
  278. 'name': 'Test Role 6',
  279. 'slug': 'test-role-6',
  280. },
  281. ]
  282. url = reverse('ipam-api:role-list')
  283. response = self.client.post(url, data, format='json', **self.header)
  284. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  285. self.assertEqual(Role.objects.count(), 6)
  286. self.assertEqual(response.data[0]['name'], data[0]['name'])
  287. self.assertEqual(response.data[1]['name'], data[1]['name'])
  288. self.assertEqual(response.data[2]['name'], data[2]['name'])
  289. def test_update_role(self):
  290. data = {
  291. 'name': 'Test Role X',
  292. 'slug': 'test-role-x',
  293. }
  294. url = reverse('ipam-api:role-detail', kwargs={'pk': self.role1.pk})
  295. response = self.client.put(url, data, format='json', **self.header)
  296. self.assertHttpStatus(response, status.HTTP_200_OK)
  297. self.assertEqual(Role.objects.count(), 3)
  298. role1 = Role.objects.get(pk=response.data['id'])
  299. self.assertEqual(role1.name, data['name'])
  300. self.assertEqual(role1.slug, data['slug'])
  301. def test_delete_role(self):
  302. url = reverse('ipam-api:role-detail', kwargs={'pk': self.role1.pk})
  303. response = self.client.delete(url, **self.header)
  304. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  305. self.assertEqual(Role.objects.count(), 2)
  306. class PrefixTest(APITestCase):
  307. def setUp(self):
  308. super().setUp()
  309. self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
  310. self.vrf1 = VRF.objects.create(name='Test VRF 1', rd='65000:1')
  311. self.vlan1 = VLAN.objects.create(vid=1, name='Test VLAN 1')
  312. self.role1 = Role.objects.create(name='Test Role 1', slug='test-role-1')
  313. self.prefix1 = Prefix.objects.create(prefix=IPNetwork('192.168.1.0/24'))
  314. self.prefix2 = Prefix.objects.create(prefix=IPNetwork('192.168.2.0/24'))
  315. self.prefix3 = Prefix.objects.create(prefix=IPNetwork('192.168.3.0/24'))
  316. def test_get_prefix(self):
  317. url = reverse('ipam-api:prefix-detail', kwargs={'pk': self.prefix1.pk})
  318. response = self.client.get(url, **self.header)
  319. self.assertEqual(response.data['prefix'], str(self.prefix1.prefix))
  320. def test_list_prefixes(self):
  321. url = reverse('ipam-api:prefix-list')
  322. response = self.client.get(url, **self.header)
  323. self.assertEqual(response.data['count'], 3)
  324. def test_list_prefixes_brief(self):
  325. url = reverse('ipam-api:prefix-list')
  326. response = self.client.get('{}?brief=1'.format(url), **self.header)
  327. self.assertEqual(
  328. sorted(response.data['results'][0]),
  329. ['family', 'id', 'prefix', 'url']
  330. )
  331. def test_create_prefix(self):
  332. data = {
  333. 'prefix': '192.168.4.0/24',
  334. 'site': self.site1.pk,
  335. 'vrf': self.vrf1.pk,
  336. 'vlan': self.vlan1.pk,
  337. 'role': self.role1.pk,
  338. }
  339. url = reverse('ipam-api:prefix-list')
  340. response = self.client.post(url, data, format='json', **self.header)
  341. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  342. self.assertEqual(Prefix.objects.count(), 4)
  343. prefix4 = Prefix.objects.get(pk=response.data['id'])
  344. self.assertEqual(str(prefix4.prefix), data['prefix'])
  345. self.assertEqual(prefix4.site_id, data['site'])
  346. self.assertEqual(prefix4.vrf_id, data['vrf'])
  347. self.assertEqual(prefix4.vlan_id, data['vlan'])
  348. self.assertEqual(prefix4.role_id, data['role'])
  349. def test_create_prefix_bulk(self):
  350. data = [
  351. {
  352. 'prefix': '10.0.1.0/24',
  353. },
  354. {
  355. 'prefix': '10.0.2.0/24',
  356. },
  357. {
  358. 'prefix': '10.0.3.0/24',
  359. },
  360. ]
  361. url = reverse('ipam-api:prefix-list')
  362. response = self.client.post(url, data, format='json', **self.header)
  363. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  364. self.assertEqual(Prefix.objects.count(), 6)
  365. self.assertEqual(response.data[0]['prefix'], data[0]['prefix'])
  366. self.assertEqual(response.data[1]['prefix'], data[1]['prefix'])
  367. self.assertEqual(response.data[2]['prefix'], data[2]['prefix'])
  368. def test_update_prefix(self):
  369. data = {
  370. 'prefix': '192.168.99.0/24',
  371. 'site': self.site1.pk,
  372. 'vrf': self.vrf1.pk,
  373. 'vlan': self.vlan1.pk,
  374. 'role': self.role1.pk,
  375. }
  376. url = reverse('ipam-api:prefix-detail', kwargs={'pk': self.prefix1.pk})
  377. response = self.client.put(url, data, format='json', **self.header)
  378. self.assertHttpStatus(response, status.HTTP_200_OK)
  379. self.assertEqual(Prefix.objects.count(), 3)
  380. prefix1 = Prefix.objects.get(pk=response.data['id'])
  381. self.assertEqual(str(prefix1.prefix), data['prefix'])
  382. self.assertEqual(prefix1.site_id, data['site'])
  383. self.assertEqual(prefix1.vrf_id, data['vrf'])
  384. self.assertEqual(prefix1.vlan_id, data['vlan'])
  385. self.assertEqual(prefix1.role_id, data['role'])
  386. def test_delete_prefix(self):
  387. url = reverse('ipam-api:prefix-detail', kwargs={'pk': self.prefix1.pk})
  388. response = self.client.delete(url, **self.header)
  389. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  390. self.assertEqual(Prefix.objects.count(), 2)
  391. def test_list_available_prefixes(self):
  392. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'))
  393. Prefix.objects.create(prefix=IPNetwork('192.0.2.64/26'))
  394. Prefix.objects.create(prefix=IPNetwork('192.0.2.192/27'))
  395. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  396. # Retrieve all available IPs
  397. response = self.client.get(url, **self.header)
  398. available_prefixes = ['192.0.2.0/26', '192.0.2.128/26', '192.0.2.224/27']
  399. for i, p in enumerate(response.data):
  400. self.assertEqual(p['prefix'], available_prefixes[i])
  401. def test_create_single_available_prefix(self):
  402. vrf = VRF.objects.create(name='Test VRF 1', rd='1234')
  403. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/28'), vrf=vrf, is_pool=True)
  404. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  405. # Create four available prefixes with individual requests
  406. prefixes_to_be_created = [
  407. '192.0.2.0/30',
  408. '192.0.2.4/30',
  409. '192.0.2.8/30',
  410. '192.0.2.12/30',
  411. ]
  412. for i in range(4):
  413. data = {
  414. 'prefix_length': 30,
  415. 'description': 'Test Prefix {}'.format(i + 1)
  416. }
  417. response = self.client.post(url, data, format='json', **self.header)
  418. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  419. self.assertEqual(response.data['prefix'], prefixes_to_be_created[i])
  420. self.assertEqual(response.data['vrf']['id'], vrf.pk)
  421. self.assertEqual(response.data['description'], data['description'])
  422. # Try to create one more prefix
  423. response = self.client.post(url, {'prefix_length': 30}, **self.header)
  424. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  425. self.assertIn('detail', response.data)
  426. def test_create_multiple_available_prefixes(self):
  427. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/28'), is_pool=True)
  428. url = reverse('ipam-api:prefix-available-prefixes', kwargs={'pk': prefix.pk})
  429. # Try to create five /30s (only four are available)
  430. data = [
  431. {'prefix_length': 30, 'description': 'Test Prefix 1'},
  432. {'prefix_length': 30, 'description': 'Test Prefix 2'},
  433. {'prefix_length': 30, 'description': 'Test Prefix 3'},
  434. {'prefix_length': 30, 'description': 'Test Prefix 4'},
  435. {'prefix_length': 30, 'description': 'Test Prefix 5'},
  436. ]
  437. response = self.client.post(url, data, format='json', **self.header)
  438. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  439. self.assertIn('detail', response.data)
  440. # Verify that no prefixes were created (the entire /28 is still available)
  441. response = self.client.get(url, **self.header)
  442. self.assertEqual(response.data[0]['prefix'], '192.0.2.0/28')
  443. # Create four /30s in a single request
  444. response = self.client.post(url, data[:4], format='json', **self.header)
  445. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  446. self.assertEqual(len(response.data), 4)
  447. def test_list_available_ips(self):
  448. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/29'), is_pool=True)
  449. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  450. # Retrieve all available IPs
  451. response = self.client.get(url, **self.header)
  452. self.assertEqual(len(response.data), 8) # 8 because prefix.is_pool = True
  453. # Change the prefix to not be a pool and try again
  454. prefix.is_pool = False
  455. prefix.save()
  456. response = self.client.get(url, **self.header)
  457. self.assertEqual(len(response.data), 6) # 8 - 2 because prefix.is_pool = False
  458. def test_create_single_available_ip(self):
  459. vrf = VRF.objects.create(name='Test VRF 1', rd='1234')
  460. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/30'), vrf=vrf, is_pool=True)
  461. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  462. # Create all four available IPs with individual requests
  463. for i in range(1, 5):
  464. data = {
  465. 'description': 'Test IP {}'.format(i)
  466. }
  467. response = self.client.post(url, data, format='json', **self.header)
  468. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  469. self.assertEqual(response.data['vrf']['id'], vrf.pk)
  470. self.assertEqual(response.data['description'], data['description'])
  471. # Try to create one more IP
  472. response = self.client.post(url, {}, **self.header)
  473. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  474. self.assertIn('detail', response.data)
  475. def test_create_multiple_available_ips(self):
  476. prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/29'), is_pool=True)
  477. url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
  478. # Try to create nine IPs (only eight are available)
  479. data = [{'description': 'Test IP {}'.format(i)} for i in range(1, 10)] # 9 IPs
  480. response = self.client.post(url, data, format='json', **self.header)
  481. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  482. self.assertIn('detail', response.data)
  483. # Verify that no IPs were created (eight are still available)
  484. response = self.client.get(url, **self.header)
  485. self.assertEqual(len(response.data), 8)
  486. # Create all eight available IPs in a single request
  487. data = [{'description': 'Test IP {}'.format(i)} for i in range(1, 9)] # 8 IPs
  488. response = self.client.post(url, data, format='json', **self.header)
  489. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  490. self.assertEqual(len(response.data), 8)
  491. class IPAddressTest(APITestCase):
  492. def setUp(self):
  493. super().setUp()
  494. self.vrf1 = VRF.objects.create(name='Test VRF 1', rd='65000:1')
  495. self.ipaddress1 = IPAddress.objects.create(address=IPNetwork('192.168.0.1/24'))
  496. self.ipaddress2 = IPAddress.objects.create(address=IPNetwork('192.168.0.2/24'))
  497. self.ipaddress3 = IPAddress.objects.create(address=IPNetwork('192.168.0.3/24'))
  498. def test_get_ipaddress(self):
  499. url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': self.ipaddress1.pk})
  500. response = self.client.get(url, **self.header)
  501. self.assertEqual(response.data['address'], str(self.ipaddress1.address))
  502. def test_list_ipaddresss(self):
  503. url = reverse('ipam-api:ipaddress-list')
  504. response = self.client.get(url, **self.header)
  505. self.assertEqual(response.data['count'], 3)
  506. def test_list_ipaddresses_brief(self):
  507. url = reverse('ipam-api:ipaddress-list')
  508. response = self.client.get('{}?brief=1'.format(url), **self.header)
  509. self.assertEqual(
  510. sorted(response.data['results'][0]),
  511. ['address', 'family', 'id', 'url']
  512. )
  513. def test_create_ipaddress(self):
  514. data = {
  515. 'address': '192.168.0.4/24',
  516. 'vrf': self.vrf1.pk,
  517. }
  518. url = reverse('ipam-api:ipaddress-list')
  519. response = self.client.post(url, data, format='json', **self.header)
  520. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  521. self.assertEqual(IPAddress.objects.count(), 4)
  522. ipaddress4 = IPAddress.objects.get(pk=response.data['id'])
  523. self.assertEqual(str(ipaddress4.address), data['address'])
  524. self.assertEqual(ipaddress4.vrf_id, data['vrf'])
  525. def test_create_ipaddress_bulk(self):
  526. data = [
  527. {
  528. 'address': '192.168.0.4/24',
  529. },
  530. {
  531. 'address': '192.168.0.5/24',
  532. },
  533. {
  534. 'address': '192.168.0.6/24',
  535. },
  536. ]
  537. url = reverse('ipam-api:ipaddress-list')
  538. response = self.client.post(url, data, format='json', **self.header)
  539. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  540. self.assertEqual(IPAddress.objects.count(), 6)
  541. self.assertEqual(response.data[0]['address'], data[0]['address'])
  542. self.assertEqual(response.data[1]['address'], data[1]['address'])
  543. self.assertEqual(response.data[2]['address'], data[2]['address'])
  544. def test_update_ipaddress(self):
  545. data = {
  546. 'address': '192.168.0.99/24',
  547. 'vrf': self.vrf1.pk,
  548. }
  549. url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': self.ipaddress1.pk})
  550. response = self.client.put(url, data, format='json', **self.header)
  551. self.assertHttpStatus(response, status.HTTP_200_OK)
  552. self.assertEqual(IPAddress.objects.count(), 3)
  553. ipaddress1 = IPAddress.objects.get(pk=response.data['id'])
  554. self.assertEqual(str(ipaddress1.address), data['address'])
  555. self.assertEqual(ipaddress1.vrf_id, data['vrf'])
  556. def test_delete_ipaddress(self):
  557. url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': self.ipaddress1.pk})
  558. response = self.client.delete(url, **self.header)
  559. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  560. self.assertEqual(IPAddress.objects.count(), 2)
  561. class VLANGroupTest(APITestCase):
  562. def setUp(self):
  563. super().setUp()
  564. self.vlangroup1 = VLANGroup.objects.create(name='Test VLAN Group 1', slug='test-vlan-group-1')
  565. self.vlangroup2 = VLANGroup.objects.create(name='Test VLAN Group 2', slug='test-vlan-group-2')
  566. self.vlangroup3 = VLANGroup.objects.create(name='Test VLAN Group 3', slug='test-vlan-group-3')
  567. def test_get_vlangroup(self):
  568. url = reverse('ipam-api:vlangroup-detail', kwargs={'pk': self.vlangroup1.pk})
  569. response = self.client.get(url, **self.header)
  570. self.assertEqual(response.data['name'], self.vlangroup1.name)
  571. def test_list_vlangroups(self):
  572. url = reverse('ipam-api:vlangroup-list')
  573. response = self.client.get(url, **self.header)
  574. self.assertEqual(response.data['count'], 3)
  575. def test_list_vlangroups_brief(self):
  576. url = reverse('ipam-api:vlangroup-list')
  577. response = self.client.get('{}?brief=1'.format(url), **self.header)
  578. self.assertEqual(
  579. sorted(response.data['results'][0]),
  580. ['id', 'name', 'slug', 'url', 'vlan_count']
  581. )
  582. def test_create_vlangroup(self):
  583. data = {
  584. 'name': 'Test VLAN Group 4',
  585. 'slug': 'test-vlan-group-4',
  586. }
  587. url = reverse('ipam-api:vlangroup-list')
  588. response = self.client.post(url, data, format='json', **self.header)
  589. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  590. self.assertEqual(VLANGroup.objects.count(), 4)
  591. vlangroup4 = VLANGroup.objects.get(pk=response.data['id'])
  592. self.assertEqual(vlangroup4.name, data['name'])
  593. self.assertEqual(vlangroup4.slug, data['slug'])
  594. def test_create_vlangroup_bulk(self):
  595. data = [
  596. {
  597. 'name': 'Test VLAN Group 4',
  598. 'slug': 'test-vlan-group-4',
  599. },
  600. {
  601. 'name': 'Test VLAN Group 5',
  602. 'slug': 'test-vlan-group-5',
  603. },
  604. {
  605. 'name': 'Test VLAN Group 6',
  606. 'slug': 'test-vlan-group-6',
  607. },
  608. ]
  609. url = reverse('ipam-api:vlangroup-list')
  610. response = self.client.post(url, data, format='json', **self.header)
  611. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  612. self.assertEqual(VLANGroup.objects.count(), 6)
  613. self.assertEqual(response.data[0]['name'], data[0]['name'])
  614. self.assertEqual(response.data[1]['name'], data[1]['name'])
  615. self.assertEqual(response.data[2]['name'], data[2]['name'])
  616. def test_update_vlangroup(self):
  617. data = {
  618. 'name': 'Test VLAN Group X',
  619. 'slug': 'test-vlan-group-x',
  620. }
  621. url = reverse('ipam-api:vlangroup-detail', kwargs={'pk': self.vlangroup1.pk})
  622. response = self.client.put(url, data, format='json', **self.header)
  623. self.assertHttpStatus(response, status.HTTP_200_OK)
  624. self.assertEqual(VLANGroup.objects.count(), 3)
  625. vlangroup1 = VLANGroup.objects.get(pk=response.data['id'])
  626. self.assertEqual(vlangroup1.name, data['name'])
  627. self.assertEqual(vlangroup1.slug, data['slug'])
  628. def test_delete_vlangroup(self):
  629. url = reverse('ipam-api:vlangroup-detail', kwargs={'pk': self.vlangroup1.pk})
  630. response = self.client.delete(url, **self.header)
  631. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  632. self.assertEqual(VLANGroup.objects.count(), 2)
  633. class VLANTest(APITestCase):
  634. def setUp(self):
  635. super().setUp()
  636. self.vlan1 = VLAN.objects.create(vid=1, name='Test VLAN 1')
  637. self.vlan2 = VLAN.objects.create(vid=2, name='Test VLAN 2')
  638. self.vlan3 = VLAN.objects.create(vid=3, name='Test VLAN 3')
  639. self.prefix1 = Prefix.objects.create(prefix=IPNetwork('192.168.1.0/24'))
  640. def test_get_vlan(self):
  641. url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
  642. response = self.client.get(url, **self.header)
  643. self.assertEqual(response.data['name'], self.vlan1.name)
  644. def test_list_vlans(self):
  645. url = reverse('ipam-api:vlan-list')
  646. response = self.client.get(url, **self.header)
  647. self.assertEqual(response.data['count'], 3)
  648. def test_list_vlans_brief(self):
  649. url = reverse('ipam-api:vlan-list')
  650. response = self.client.get('{}?brief=1'.format(url), **self.header)
  651. self.assertEqual(
  652. sorted(response.data['results'][0]),
  653. ['display_name', 'id', 'name', 'url', 'vid']
  654. )
  655. def test_create_vlan(self):
  656. data = {
  657. 'vid': 4,
  658. 'name': 'Test VLAN 4',
  659. }
  660. url = reverse('ipam-api:vlan-list')
  661. response = self.client.post(url, data, format='json', **self.header)
  662. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  663. self.assertEqual(VLAN.objects.count(), 4)
  664. vlan4 = VLAN.objects.get(pk=response.data['id'])
  665. self.assertEqual(vlan4.vid, data['vid'])
  666. self.assertEqual(vlan4.name, data['name'])
  667. def test_create_vlan_bulk(self):
  668. data = [
  669. {
  670. 'vid': 4,
  671. 'name': 'Test VLAN 4',
  672. },
  673. {
  674. 'vid': 5,
  675. 'name': 'Test VLAN 5',
  676. },
  677. {
  678. 'vid': 6,
  679. 'name': 'Test VLAN 6',
  680. },
  681. ]
  682. url = reverse('ipam-api:vlan-list')
  683. response = self.client.post(url, data, format='json', **self.header)
  684. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  685. self.assertEqual(VLAN.objects.count(), 6)
  686. self.assertEqual(response.data[0]['name'], data[0]['name'])
  687. self.assertEqual(response.data[1]['name'], data[1]['name'])
  688. self.assertEqual(response.data[2]['name'], data[2]['name'])
  689. def test_update_vlan(self):
  690. data = {
  691. 'vid': 99,
  692. 'name': 'Test VLAN X',
  693. }
  694. url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
  695. response = self.client.put(url, data, format='json', **self.header)
  696. self.assertHttpStatus(response, status.HTTP_200_OK)
  697. self.assertEqual(VLAN.objects.count(), 3)
  698. vlan1 = VLAN.objects.get(pk=response.data['id'])
  699. self.assertEqual(vlan1.vid, data['vid'])
  700. self.assertEqual(vlan1.name, data['name'])
  701. def test_delete_vlan(self):
  702. url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
  703. response = self.client.delete(url, **self.header)
  704. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  705. self.assertEqual(VLAN.objects.count(), 2)
  706. def test_delete_vlan_with_prefix(self):
  707. self.prefix1.vlan = self.vlan1
  708. self.prefix1.save()
  709. url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
  710. response = self.client.delete(url, **self.header)
  711. # can't use assertHttpStatus here because we don't have response.data
  712. self.assertEqual(response.status_code, 409)
  713. content = json.loads(response.content.decode('utf-8'))
  714. self.assertIn('detail', content)
  715. self.assertTrue(content['detail'].startswith('Unable to delete object.'))
  716. class ServiceTest(APITestCase):
  717. def setUp(self):
  718. super().setUp()
  719. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  720. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  721. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Test Device Type 1')
  722. devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1')
  723. self.device1 = Device.objects.create(
  724. name='Test Device 1', site=site, device_type=devicetype, device_role=devicerole
  725. )
  726. self.device2 = Device.objects.create(
  727. name='Test Device 2', site=site, device_type=devicetype, device_role=devicerole
  728. )
  729. self.service1 = Service.objects.create(
  730. device=self.device1, name='Test Service 1', protocol=IP_PROTOCOL_TCP, port=1
  731. )
  732. self.service1 = Service.objects.create(
  733. device=self.device1, name='Test Service 2', protocol=IP_PROTOCOL_TCP, port=2
  734. )
  735. self.service1 = Service.objects.create(
  736. device=self.device1, name='Test Service 3', protocol=IP_PROTOCOL_TCP, port=3
  737. )
  738. def test_get_service(self):
  739. url = reverse('ipam-api:service-detail', kwargs={'pk': self.service1.pk})
  740. response = self.client.get(url, **self.header)
  741. self.assertEqual(response.data['name'], self.service1.name)
  742. def test_list_services(self):
  743. url = reverse('ipam-api:service-list')
  744. response = self.client.get(url, **self.header)
  745. self.assertEqual(response.data['count'], 3)
  746. def test_create_service(self):
  747. data = {
  748. 'device': self.device1.pk,
  749. 'name': 'Test Service 4',
  750. 'protocol': IP_PROTOCOL_TCP,
  751. 'port': 4,
  752. }
  753. url = reverse('ipam-api:service-list')
  754. response = self.client.post(url, data, format='json', **self.header)
  755. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  756. self.assertEqual(Service.objects.count(), 4)
  757. service4 = Service.objects.get(pk=response.data['id'])
  758. self.assertEqual(service4.device_id, data['device'])
  759. self.assertEqual(service4.name, data['name'])
  760. self.assertEqual(service4.protocol, data['protocol'])
  761. self.assertEqual(service4.port, data['port'])
  762. def test_create_service_bulk(self):
  763. data = [
  764. {
  765. 'device': self.device1.pk,
  766. 'name': 'Test Service 4',
  767. 'protocol': IP_PROTOCOL_TCP,
  768. 'port': 4,
  769. },
  770. {
  771. 'device': self.device1.pk,
  772. 'name': 'Test Service 5',
  773. 'protocol': IP_PROTOCOL_TCP,
  774. 'port': 5,
  775. },
  776. {
  777. 'device': self.device1.pk,
  778. 'name': 'Test Service 6',
  779. 'protocol': IP_PROTOCOL_TCP,
  780. 'port': 6,
  781. },
  782. ]
  783. url = reverse('ipam-api:service-list')
  784. response = self.client.post(url, data, format='json', **self.header)
  785. self.assertHttpStatus(response, status.HTTP_201_CREATED)
  786. self.assertEqual(Service.objects.count(), 6)
  787. self.assertEqual(response.data[0]['name'], data[0]['name'])
  788. self.assertEqual(response.data[1]['name'], data[1]['name'])
  789. self.assertEqual(response.data[2]['name'], data[2]['name'])
  790. def test_update_service(self):
  791. data = {
  792. 'device': self.device2.pk,
  793. 'name': 'Test Service X',
  794. 'protocol': IP_PROTOCOL_UDP,
  795. 'port': 99,
  796. }
  797. url = reverse('ipam-api:service-detail', kwargs={'pk': self.service1.pk})
  798. response = self.client.put(url, data, format='json', **self.header)
  799. self.assertHttpStatus(response, status.HTTP_200_OK)
  800. self.assertEqual(Service.objects.count(), 3)
  801. service1 = Service.objects.get(pk=response.data['id'])
  802. self.assertEqual(service1.device_id, data['device'])
  803. self.assertEqual(service1.name, data['name'])
  804. self.assertEqual(service1.protocol, data['protocol'])
  805. self.assertEqual(service1.port, data['port'])
  806. def test_delete_service(self):
  807. url = reverse('ipam-api:service-detail', kwargs={'pk': self.service1.pk})
  808. response = self.client.delete(url, **self.header)
  809. self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
  810. self.assertEqual(Service.objects.count(), 2)