test_views.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  1. import datetime
  2. from django.test import override_settings
  3. from django.urls import reverse
  4. from netaddr import IPNetwork
  5. from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site, Interface
  6. from ipam.choices import *
  7. from ipam.models import *
  8. from tenancy.models import Tenant
  9. from utilities.testing import ViewTestCases, create_test_device, create_tags
  10. class ASNRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  11. model = ASNRange
  12. @classmethod
  13. def setUpTestData(cls):
  14. rirs = [
  15. RIR(name='RIR 1', slug='rir-1', is_private=True),
  16. RIR(name='RIR 2', slug='rir-2', is_private=True),
  17. ]
  18. RIR.objects.bulk_create(rirs)
  19. tenants = [
  20. Tenant(name='Tenant 1', slug='tenant-1'),
  21. Tenant(name='Tenant 2', slug='tenant-2'),
  22. ]
  23. Tenant.objects.bulk_create(tenants)
  24. asn_ranges = (
  25. ASNRange(name='ASN Range 1', slug='asn-range-1', rir=rirs[0], tenant=tenants[0], start=100, end=199),
  26. ASNRange(name='ASN Range 2', slug='asn-range-2', rir=rirs[0], tenant=tenants[0], start=200, end=299),
  27. ASNRange(name='ASN Range 3', slug='asn-range-3', rir=rirs[0], tenant=tenants[0], start=300, end=399),
  28. )
  29. ASNRange.objects.bulk_create(asn_ranges)
  30. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  31. cls.form_data = {
  32. 'name': 'ASN Range X',
  33. 'slug': 'asn-range-x',
  34. 'rir': rirs[1].pk,
  35. 'tenant': tenants[1].pk,
  36. 'start': 1000,
  37. 'end': 1099,
  38. 'description': 'A new ASN range',
  39. 'tags': [t.pk for t in tags],
  40. }
  41. cls.csv_data = (
  42. f"name,slug,rir,tenant,start,end,description",
  43. f"ASN Range 4,asn-range-4,{rirs[1].name},{tenants[1].name},400,499,Fourth range",
  44. f"ASN Range 5,asn-range-5,{rirs[1].name},{tenants[1].name},500,599,Fifth range",
  45. f"ASN Range 6,asn-range-6,{rirs[1].name},{tenants[1].name},600,699,Sixth range",
  46. )
  47. cls.csv_update_data = (
  48. "id,description",
  49. f"{asn_ranges[0].pk},New description 1",
  50. f"{asn_ranges[1].pk},New description 2",
  51. f"{asn_ranges[2].pk},New description 3",
  52. )
  53. cls.bulk_edit_data = {
  54. 'rir': rirs[1].pk,
  55. 'description': 'Next description',
  56. }
  57. class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  58. model = ASN
  59. @classmethod
  60. def setUpTestData(cls):
  61. rirs = [
  62. RIR(name='RIR 1', slug='rir-1', is_private=True),
  63. RIR(name='RIR 2', slug='rir-2', is_private=True),
  64. ]
  65. RIR.objects.bulk_create(rirs)
  66. sites = (
  67. Site(name='Site 1', slug='site-1'),
  68. Site(name='Site 2', slug='site-2')
  69. )
  70. Site.objects.bulk_create(sites)
  71. tenants = (
  72. Tenant(name='Tenant 1', slug='tenant-1'),
  73. Tenant(name='Tenant 2', slug='tenant-2'),
  74. )
  75. Tenant.objects.bulk_create(tenants)
  76. asns = (
  77. ASN(asn=65001, rir=rirs[0], tenant=tenants[0]),
  78. ASN(asn=65002, rir=rirs[1], tenant=tenants[1]),
  79. ASN(asn=4200000001, rir=rirs[0], tenant=tenants[0]),
  80. ASN(asn=4200000002, rir=rirs[1], tenant=tenants[1]),
  81. )
  82. ASN.objects.bulk_create(asns)
  83. asns[0].sites.set([sites[0]])
  84. asns[1].sites.set([sites[1]])
  85. asns[2].sites.set([sites[0]])
  86. asns[3].sites.set([sites[1]])
  87. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  88. cls.form_data = {
  89. 'asn': 65000,
  90. 'rir': rirs[0].pk,
  91. 'tenant': tenants[0].pk,
  92. 'site': sites[0].pk,
  93. 'description': 'A new ASN',
  94. 'tags': [t.pk for t in tags],
  95. }
  96. cls.csv_data = (
  97. "asn,rir",
  98. "65003,RIR 1",
  99. "65004,RIR 2",
  100. "4200000003,RIR 1",
  101. "4200000004,RIR 2",
  102. )
  103. cls.csv_update_data = (
  104. "id,description",
  105. f"{asns[0].pk},New description1",
  106. f"{asns[1].pk},New description2",
  107. f"{asns[2].pk},New description3",
  108. )
  109. cls.bulk_edit_data = {
  110. 'rir': rirs[1].pk,
  111. 'description': 'Next description',
  112. }
  113. class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  114. model = VRF
  115. @classmethod
  116. def setUpTestData(cls):
  117. tenants = (
  118. Tenant(name='Tenant A', slug='tenant-a'),
  119. Tenant(name='Tenant B', slug='tenant-b'),
  120. )
  121. Tenant.objects.bulk_create(tenants)
  122. vrfs = (
  123. VRF(name='VRF 1', rd='65000:1'),
  124. VRF(name='VRF 2', rd='65000:2'),
  125. VRF(name='VRF 3', rd='65000:3'),
  126. )
  127. VRF.objects.bulk_create(vrfs)
  128. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  129. cls.form_data = {
  130. 'name': 'VRF X',
  131. 'rd': '65000:999',
  132. 'tenant': tenants[0].pk,
  133. 'enforce_unique': True,
  134. 'description': 'A new VRF',
  135. 'tags': [t.pk for t in tags],
  136. }
  137. cls.csv_data = (
  138. "name",
  139. "VRF 4",
  140. "VRF 5",
  141. "VRF 6",
  142. )
  143. cls.csv_update_data = (
  144. "id,name",
  145. f"{vrfs[0].pk},VRF 7",
  146. f"{vrfs[1].pk},VRF 8",
  147. f"{vrfs[2].pk},VRF 9",
  148. )
  149. cls.bulk_edit_data = {
  150. 'tenant': tenants[1].pk,
  151. 'enforce_unique': False,
  152. 'description': 'New description',
  153. }
  154. class RouteTargetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  155. model = RouteTarget
  156. @classmethod
  157. def setUpTestData(cls):
  158. tenants = (
  159. Tenant(name='Tenant A', slug='tenant-a'),
  160. Tenant(name='Tenant B', slug='tenant-b'),
  161. )
  162. Tenant.objects.bulk_create(tenants)
  163. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  164. route_targets = (
  165. RouteTarget(name='65000:1001', tenant=tenants[0]),
  166. RouteTarget(name='65000:1002', tenant=tenants[1]),
  167. RouteTarget(name='65000:1003'),
  168. )
  169. RouteTarget.objects.bulk_create(route_targets)
  170. cls.form_data = {
  171. 'name': '65000:100',
  172. 'description': 'A new route target',
  173. 'tags': [t.pk for t in tags],
  174. }
  175. cls.csv_data = (
  176. "name,tenant,description",
  177. "65000:1004,Tenant A,Foo",
  178. "65000:1005,Tenant B,Bar",
  179. "65000:1006,,No tenant",
  180. )
  181. cls.csv_update_data = (
  182. "id,name,description",
  183. f"{route_targets[0].pk},65000:1007,New description1",
  184. f"{route_targets[1].pk},65000:1008,New description2",
  185. f"{route_targets[2].pk},65000:1009,New description3",
  186. )
  187. cls.bulk_edit_data = {
  188. 'tenant': tenants[1].pk,
  189. 'description': 'New description',
  190. }
  191. class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  192. model = RIR
  193. @classmethod
  194. def setUpTestData(cls):
  195. rirs = (
  196. RIR(name='RIR 1', slug='rir-1'),
  197. RIR(name='RIR 2', slug='rir-2'),
  198. RIR(name='RIR 3', slug='rir-3'),
  199. )
  200. RIR.objects.bulk_create(rirs)
  201. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  202. cls.form_data = {
  203. 'name': 'RIR X',
  204. 'slug': 'rir-x',
  205. 'is_private': True,
  206. 'description': 'A new RIR',
  207. 'tags': [t.pk for t in tags],
  208. }
  209. cls.csv_data = (
  210. "name,slug,description",
  211. "RIR 4,rir-4,Fourth RIR",
  212. "RIR 5,rir-5,Fifth RIR",
  213. "RIR 6,rir-6,Sixth RIR",
  214. )
  215. cls.csv_update_data = (
  216. "id,name,description",
  217. f"{rirs[0].pk},RIR 7,Fourth RIR7",
  218. f"{rirs[1].pk},RIR 8,Fifth RIR8",
  219. f"{rirs[2].pk},RIR 9,Sixth RIR9",
  220. )
  221. cls.bulk_edit_data = {
  222. 'description': 'New description',
  223. }
  224. class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  225. model = Aggregate
  226. @classmethod
  227. def setUpTestData(cls):
  228. rirs = (
  229. RIR(name='RIR 1', slug='rir-1'),
  230. RIR(name='RIR 2', slug='rir-2'),
  231. )
  232. RIR.objects.bulk_create(rirs)
  233. aggregates = (
  234. Aggregate(prefix=IPNetwork('10.1.0.0/16'), rir=rirs[0]),
  235. Aggregate(prefix=IPNetwork('10.2.0.0/16'), rir=rirs[0]),
  236. Aggregate(prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]),
  237. )
  238. Aggregate.objects.bulk_create(aggregates)
  239. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  240. cls.form_data = {
  241. 'prefix': IPNetwork('10.99.0.0/16'),
  242. 'rir': rirs[1].pk,
  243. 'date_added': datetime.date(2020, 1, 1),
  244. 'description': 'A new aggregate',
  245. 'tags': [t.pk for t in tags],
  246. }
  247. cls.csv_data = (
  248. "prefix,rir",
  249. "10.4.0.0/16,RIR 1",
  250. "10.5.0.0/16,RIR 1",
  251. "10.6.0.0/16,RIR 1",
  252. )
  253. cls.csv_update_data = (
  254. "id,description",
  255. f"{aggregates[0].pk},New description1",
  256. f"{aggregates[1].pk},New description2",
  257. f"{aggregates[2].pk},New description3",
  258. )
  259. cls.bulk_edit_data = {
  260. 'rir': rirs[1].pk,
  261. 'date_added': datetime.date(2020, 1, 1),
  262. 'description': 'New description',
  263. }
  264. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  265. def test_aggregate_prefixes(self):
  266. rir = RIR.objects.first()
  267. aggregate = Aggregate.objects.create(prefix=IPNetwork('192.168.0.0/16'), rir=rir)
  268. prefixes = (
  269. Prefix(prefix=IPNetwork('192.168.1.0/24')),
  270. Prefix(prefix=IPNetwork('192.168.2.0/24')),
  271. Prefix(prefix=IPNetwork('192.168.3.0/24')),
  272. )
  273. Prefix.objects.bulk_create(prefixes)
  274. self.assertEqual(aggregate.get_child_prefixes().count(), 3)
  275. url = reverse('ipam:aggregate_prefixes', kwargs={'pk': aggregate.pk})
  276. self.assertHttpStatus(self.client.get(url), 200)
  277. class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  278. model = Role
  279. @classmethod
  280. def setUpTestData(cls):
  281. roles = (
  282. Role(name='Role 1', slug='role-1'),
  283. Role(name='Role 2', slug='role-2'),
  284. Role(name='Role 3', slug='role-3'),
  285. )
  286. Role.objects.bulk_create(roles)
  287. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  288. cls.form_data = {
  289. 'name': 'Role X',
  290. 'slug': 'role-x',
  291. 'weight': 200,
  292. 'description': 'A new role',
  293. 'tags': [t.pk for t in tags],
  294. }
  295. cls.csv_data = (
  296. "name,slug,weight",
  297. "Role 4,role-4,1000",
  298. "Role 5,role-5,1000",
  299. "Role 6,role-6,1000",
  300. )
  301. cls.csv_update_data = (
  302. "id,name,description",
  303. f"{roles[0].pk},Role 7,New description7",
  304. f"{roles[1].pk},Role 8,New description8",
  305. f"{roles[2].pk},Role 9,New description9",
  306. )
  307. cls.bulk_edit_data = {
  308. 'description': 'New description',
  309. }
  310. class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  311. model = Prefix
  312. @classmethod
  313. def setUpTestData(cls):
  314. sites = (
  315. Site(name='Site 1', slug='site-1'),
  316. Site(name='Site 2', slug='site-2'),
  317. )
  318. Site.objects.bulk_create(sites)
  319. vrfs = (
  320. VRF(name='VRF 1', rd='65000:1'),
  321. VRF(name='VRF 2', rd='65000:2'),
  322. )
  323. VRF.objects.bulk_create(vrfs)
  324. roles = (
  325. Role(name='Role 1', slug='role-1'),
  326. Role(name='Role 2', slug='role-2'),
  327. )
  328. Role.objects.bulk_create(roles)
  329. prefixes = (
  330. Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
  331. Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
  332. Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
  333. )
  334. Prefix.objects.bulk_create(prefixes)
  335. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  336. cls.form_data = {
  337. 'prefix': IPNetwork('192.0.2.0/24'),
  338. 'site': sites[1].pk,
  339. 'vrf': vrfs[1].pk,
  340. 'tenant': None,
  341. 'vlan': None,
  342. 'status': PrefixStatusChoices.STATUS_RESERVED,
  343. 'role': roles[1].pk,
  344. 'is_pool': True,
  345. 'description': 'A new prefix',
  346. 'tags': [t.pk for t in tags],
  347. }
  348. cls.csv_data = (
  349. "vrf,prefix,status",
  350. "VRF 1,10.4.0.0/16,active",
  351. "VRF 1,10.5.0.0/16,active",
  352. "VRF 1,10.6.0.0/16,active",
  353. )
  354. cls.csv_update_data = (
  355. "id,description,status",
  356. f"{prefixes[0].pk},New description 7,{PrefixStatusChoices.STATUS_RESERVED}",
  357. f"{prefixes[1].pk},New description 8,{PrefixStatusChoices.STATUS_RESERVED}",
  358. f"{prefixes[2].pk},New description 9,{PrefixStatusChoices.STATUS_RESERVED}",
  359. )
  360. cls.bulk_edit_data = {
  361. 'site': sites[1].pk,
  362. 'vrf': vrfs[1].pk,
  363. 'tenant': None,
  364. 'status': PrefixStatusChoices.STATUS_RESERVED,
  365. 'role': roles[1].pk,
  366. 'is_pool': False,
  367. 'description': 'New description',
  368. }
  369. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  370. def test_prefix_prefixes(self):
  371. prefixes = (
  372. Prefix(prefix=IPNetwork('192.168.0.0/16')),
  373. Prefix(prefix=IPNetwork('192.168.1.0/24')),
  374. Prefix(prefix=IPNetwork('192.168.2.0/24')),
  375. Prefix(prefix=IPNetwork('192.168.3.0/24')),
  376. )
  377. Prefix.objects.bulk_create(prefixes)
  378. self.assertEqual(prefixes[0].get_child_prefixes().count(), 3)
  379. url = reverse('ipam:prefix_prefixes', kwargs={'pk': prefixes[0].pk})
  380. self.assertHttpStatus(self.client.get(url), 200)
  381. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  382. def test_prefix_ipranges(self):
  383. prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.0/16'))
  384. ip_ranges = (
  385. IPRange(start_address='192.168.0.1/24', end_address='192.168.0.100/24', size=99),
  386. IPRange(start_address='192.168.1.1/24', end_address='192.168.1.100/24', size=99),
  387. IPRange(start_address='192.168.2.1/24', end_address='192.168.2.100/24', size=99),
  388. )
  389. IPRange.objects.bulk_create(ip_ranges)
  390. self.assertEqual(prefix.get_child_ranges().count(), 3)
  391. url = reverse('ipam:prefix_ipranges', kwargs={'pk': prefix.pk})
  392. self.assertHttpStatus(self.client.get(url), 200)
  393. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  394. def test_prefix_ipaddresses(self):
  395. prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.0/16'))
  396. ip_addresses = (
  397. IPAddress(address=IPNetwork('192.168.0.1/16')),
  398. IPAddress(address=IPNetwork('192.168.0.2/16')),
  399. IPAddress(address=IPNetwork('192.168.0.3/16')),
  400. )
  401. IPAddress.objects.bulk_create(ip_addresses)
  402. self.assertEqual(prefix.get_child_ips().count(), 3)
  403. url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk})
  404. self.assertHttpStatus(self.client.get(url), 200)
  405. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  406. def test_prefix_import(self):
  407. """
  408. Custom import test for YAML-based imports (versus CSV)
  409. """
  410. IMPORT_DATA = """
  411. prefix: 10.1.1.0/24
  412. status: active
  413. vlan: 101
  414. site: Site 1
  415. """
  416. # Note, a site is not tied to the VLAN to verify the fix for #12622
  417. VLAN.objects.create(vid=101, name='VLAN101')
  418. # Add all required permissions to the test user
  419. self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
  420. form_data = {
  421. 'data': IMPORT_DATA,
  422. 'format': 'yaml'
  423. }
  424. response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
  425. self.assertHttpStatus(response, 200)
  426. prefix = Prefix.objects.get(prefix='10.1.1.0/24')
  427. self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
  428. self.assertEqual(prefix.vlan.vid, 101)
  429. self.assertEqual(prefix.site.name, "Site 1")
  430. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  431. def test_prefix_import_with_vlan_group(self):
  432. """
  433. This test covers a unique import edge case where VLAN group is specified during the import.
  434. """
  435. IMPORT_DATA = """
  436. prefix: 10.1.2.0/24
  437. status: active
  438. vlan: 102
  439. site: Site 1
  440. vlan_group: Group 1
  441. """
  442. vlan_group = VLANGroup.objects.create(name='Group 1', slug='group-1', scope=Site.objects.get(name="Site 1"))
  443. VLAN.objects.create(vid=102, name='VLAN102', group=vlan_group)
  444. # Add all required permissions to the test user
  445. self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
  446. form_data = {
  447. 'data': IMPORT_DATA,
  448. 'format': 'yaml'
  449. }
  450. response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
  451. self.assertHttpStatus(response, 200)
  452. prefix = Prefix.objects.get(prefix='10.1.2.0/24')
  453. self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
  454. self.assertEqual(prefix.vlan.vid, 102)
  455. self.assertEqual(prefix.site.name, "Site 1")
  456. class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  457. model = IPRange
  458. @classmethod
  459. def setUpTestData(cls):
  460. vrfs = (
  461. VRF(name='VRF 1', rd='65000:1'),
  462. VRF(name='VRF 2', rd='65000:2'),
  463. )
  464. VRF.objects.bulk_create(vrfs)
  465. roles = (
  466. Role(name='Role 1', slug='role-1'),
  467. Role(name='Role 2', slug='role-2'),
  468. )
  469. Role.objects.bulk_create(roles)
  470. ip_ranges = (
  471. IPRange(start_address='192.168.0.10/24', end_address='192.168.0.100/24', size=91),
  472. IPRange(start_address='192.168.1.10/24', end_address='192.168.1.100/24', size=91),
  473. IPRange(start_address='192.168.2.10/24', end_address='192.168.2.100/24', size=91),
  474. IPRange(start_address='192.168.3.10/24', end_address='192.168.3.100/24', size=91),
  475. IPRange(start_address='192.168.4.10/24', end_address='192.168.4.100/24', size=91),
  476. )
  477. IPRange.objects.bulk_create(ip_ranges)
  478. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  479. cls.form_data = {
  480. 'start_address': IPNetwork('192.0.5.10/24'),
  481. 'end_address': IPNetwork('192.0.5.100/24'),
  482. 'vrf': vrfs[1].pk,
  483. 'tenant': None,
  484. 'vlan': None,
  485. 'status': IPRangeStatusChoices.STATUS_RESERVED,
  486. 'role': roles[1].pk,
  487. 'is_pool': True,
  488. 'description': 'A new IP range',
  489. 'tags': [t.pk for t in tags],
  490. }
  491. cls.csv_data = (
  492. "vrf,start_address,end_address,status",
  493. "VRF 1,10.1.0.1/16,10.1.9.254/16,active",
  494. "VRF 1,10.2.0.1/16,10.2.9.254/16,active",
  495. "VRF 1,10.3.0.1/16,10.3.9.254/16,active",
  496. )
  497. cls.csv_update_data = (
  498. "id,description,status",
  499. f"{ip_ranges[0].pk},New description 7,{IPRangeStatusChoices.STATUS_RESERVED}",
  500. f"{ip_ranges[1].pk},New description 8,{IPRangeStatusChoices.STATUS_RESERVED}",
  501. f"{ip_ranges[2].pk},New description 9,{IPRangeStatusChoices.STATUS_RESERVED}",
  502. )
  503. cls.bulk_edit_data = {
  504. 'vrf': vrfs[1].pk,
  505. 'tenant': None,
  506. 'status': IPRangeStatusChoices.STATUS_RESERVED,
  507. 'role': roles[1].pk,
  508. 'description': 'New description',
  509. }
  510. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  511. def test_iprange_ipaddresses(self):
  512. iprange = IPRange.objects.create(
  513. start_address=IPNetwork('192.168.0.1/24'),
  514. end_address=IPNetwork('192.168.0.100/24'),
  515. size=99
  516. )
  517. ip_addresses = (
  518. IPAddress(address=IPNetwork('192.168.0.1/24')),
  519. IPAddress(address=IPNetwork('192.168.0.2/24')),
  520. IPAddress(address=IPNetwork('192.168.0.3/24')),
  521. )
  522. IPAddress.objects.bulk_create(ip_addresses)
  523. self.assertEqual(iprange.get_child_ips().count(), 3)
  524. url = reverse('ipam:iprange_ipaddresses', kwargs={'pk': iprange.pk})
  525. self.assertHttpStatus(self.client.get(url), 200)
  526. class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  527. model = IPAddress
  528. @classmethod
  529. def setUpTestData(cls):
  530. vrfs = (
  531. VRF(name='VRF 1', rd='65000:1'),
  532. VRF(name='VRF 2', rd='65000:2'),
  533. )
  534. VRF.objects.bulk_create(vrfs)
  535. ipaddresses = (
  536. IPAddress(address=IPNetwork('192.0.2.1/24'), vrf=vrfs[0]),
  537. IPAddress(address=IPNetwork('192.0.2.2/24'), vrf=vrfs[0]),
  538. IPAddress(address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]),
  539. )
  540. IPAddress.objects.bulk_create(ipaddresses)
  541. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  542. cls.form_data = {
  543. 'vrf': vrfs[1].pk,
  544. 'address': IPNetwork('192.0.2.99/24'),
  545. 'tenant': None,
  546. 'status': IPAddressStatusChoices.STATUS_RESERVED,
  547. 'role': IPAddressRoleChoices.ROLE_ANYCAST,
  548. 'nat_inside': None,
  549. 'dns_name': 'example',
  550. 'description': 'A new IP address',
  551. 'tags': [t.pk for t in tags],
  552. }
  553. cls.csv_data = (
  554. "vrf,address,status",
  555. "VRF 1,192.0.2.4/24,active",
  556. "VRF 1,192.0.2.5/24,active",
  557. "VRF 1,192.0.2.6/24,active",
  558. )
  559. cls.csv_update_data = (
  560. "id,description,status",
  561. f"{ipaddresses[0].pk},New description 7,{IPAddressStatusChoices.STATUS_RESERVED}",
  562. f"{ipaddresses[1].pk},New description 8,{IPAddressStatusChoices.STATUS_RESERVED}",
  563. f"{ipaddresses[2].pk},New description 9,{IPAddressStatusChoices.STATUS_RESERVED}",
  564. )
  565. cls.bulk_edit_data = {
  566. 'vrf': vrfs[1].pk,
  567. 'tenant': None,
  568. 'status': IPAddressStatusChoices.STATUS_RESERVED,
  569. 'role': IPAddressRoleChoices.ROLE_ANYCAST,
  570. 'dns_name': 'example',
  571. 'description': 'New description',
  572. }
  573. class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  574. model = FHRPGroup
  575. @classmethod
  576. def setUpTestData(cls):
  577. fhrp_groups = (
  578. FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'),
  579. FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'),
  580. FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30),
  581. )
  582. FHRPGroup.objects.bulk_create(fhrp_groups)
  583. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  584. cls.form_data = {
  585. 'protocol': FHRPGroupProtocolChoices.PROTOCOL_VRRP2,
  586. 'group_id': 99,
  587. 'auth_type': FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5,
  588. 'auth_key': 'abc123def456',
  589. 'description': 'Blah blah blah',
  590. 'name': 'test123 name',
  591. 'tags': [t.pk for t in tags],
  592. }
  593. cls.csv_data = (
  594. "protocol,group_id,auth_type,auth_key,description",
  595. "vrrp2,40,plaintext,foobar123,Foo",
  596. "vrrp3,50,md5,foobar123,Bar",
  597. "hsrp,60,,,",
  598. )
  599. cls.csv_update_data = (
  600. "id,name,description",
  601. f"{fhrp_groups[0].pk},FHRP Group 1,New description 1",
  602. f"{fhrp_groups[1].pk},FHRP Group 2,New description 2",
  603. f"{fhrp_groups[2].pk},FHRP Group 3,New description 3",
  604. )
  605. cls.bulk_edit_data = {
  606. 'protocol': FHRPGroupProtocolChoices.PROTOCOL_CARP,
  607. }
  608. class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  609. model = VLANGroup
  610. @classmethod
  611. def setUpTestData(cls):
  612. sites = (
  613. Site(name='Site 1', slug='site-1'),
  614. Site(name='Site 2', slug='site-2'),
  615. )
  616. Site.objects.bulk_create(sites)
  617. vlan_groups = (
  618. VLANGroup(name='VLAN Group 1', slug='vlan-group-1', scope=sites[0]),
  619. VLANGroup(name='VLAN Group 2', slug='vlan-group-2', scope=sites[0]),
  620. VLANGroup(name='VLAN Group 3', slug='vlan-group-3', scope=sites[0]),
  621. )
  622. VLANGroup.objects.bulk_create(vlan_groups)
  623. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  624. cls.form_data = {
  625. 'name': 'VLAN Group X',
  626. 'slug': 'vlan-group-x',
  627. 'min_vid': 1,
  628. 'max_vid': 4094,
  629. 'description': 'A new VLAN group',
  630. 'tags': [t.pk for t in tags],
  631. }
  632. cls.csv_data = (
  633. f"name,slug,scope_type,scope_id,description",
  634. f"VLAN Group 4,vlan-group-4,,,Fourth VLAN group",
  635. f"VLAN Group 5,vlan-group-5,dcim.site,{sites[0].pk},Fifth VLAN group",
  636. f"VLAN Group 6,vlan-group-6,dcim.site,{sites[1].pk},Sixth VLAN group",
  637. )
  638. cls.csv_update_data = (
  639. f"id,name,description",
  640. f"{vlan_groups[0].pk},VLAN Group 7,Fourth VLAN group7",
  641. f"{vlan_groups[1].pk},VLAN Group 8,Fifth VLAN group8",
  642. f"{vlan_groups[2].pk},VLAN Group 9,Sixth VLAN group9",
  643. )
  644. cls.bulk_edit_data = {
  645. 'description': 'New description',
  646. }
  647. class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  648. model = VLAN
  649. @classmethod
  650. def setUpTestData(cls):
  651. sites = (
  652. Site(name='Site 1', slug='site-1'),
  653. Site(name='Site 2', slug='site-2'),
  654. )
  655. Site.objects.bulk_create(sites)
  656. vlangroups = (
  657. VLANGroup(name='VLAN Group 1', slug='vlan-group-1', scope=sites[0]),
  658. VLANGroup(name='VLAN Group 2', slug='vlan-group-2', scope=sites[1]),
  659. )
  660. VLANGroup.objects.bulk_create(vlangroups)
  661. roles = (
  662. Role(name='Role 1', slug='role-1'),
  663. Role(name='Role 2', slug='role-2'),
  664. )
  665. Role.objects.bulk_create(roles)
  666. vlans = (
  667. VLAN(group=vlangroups[0], vid=101, name='VLAN101', site=sites[0], role=roles[0]),
  668. VLAN(group=vlangroups[0], vid=102, name='VLAN102', site=sites[0], role=roles[0]),
  669. VLAN(group=vlangroups[0], vid=103, name='VLAN103', site=sites[0], role=roles[0]),
  670. )
  671. VLAN.objects.bulk_create(vlans)
  672. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  673. cls.form_data = {
  674. 'site': sites[1].pk,
  675. 'group': vlangroups[1].pk,
  676. 'vid': 999,
  677. 'name': 'VLAN999',
  678. 'tenant': None,
  679. 'status': VLANStatusChoices.STATUS_RESERVED,
  680. 'role': roles[1].pk,
  681. 'description': 'A new VLAN',
  682. 'tags': [t.pk for t in tags],
  683. }
  684. cls.csv_data = (
  685. "vid,name,status",
  686. "104,VLAN104,active",
  687. "105,VLAN105,active",
  688. "106,VLAN106,active",
  689. )
  690. cls.csv_update_data = (
  691. "id,name,description",
  692. f"{vlans[0].pk},VLAN107,New description 7",
  693. f"{vlans[1].pk},VLAN108,New description 8",
  694. f"{vlans[2].pk},VLAN109,New description 9",
  695. )
  696. cls.bulk_edit_data = {
  697. 'site': sites[1].pk,
  698. 'group': vlangroups[1].pk,
  699. 'tenant': None,
  700. 'status': VLANStatusChoices.STATUS_RESERVED,
  701. 'role': roles[1].pk,
  702. 'description': 'New description',
  703. }
  704. class ServiceTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  705. model = ServiceTemplate
  706. @classmethod
  707. def setUpTestData(cls):
  708. service_templates = (
  709. ServiceTemplate(name='Service Template 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[101]),
  710. ServiceTemplate(name='Service Template 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[102]),
  711. ServiceTemplate(name='Service Template 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[103]),
  712. )
  713. ServiceTemplate.objects.bulk_create(service_templates)
  714. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  715. cls.form_data = {
  716. 'name': 'Service Template X',
  717. 'protocol': ServiceProtocolChoices.PROTOCOL_UDP,
  718. 'ports': '104,105',
  719. 'description': 'A new service template',
  720. 'tags': [t.pk for t in tags],
  721. }
  722. cls.csv_data = (
  723. "name,protocol,ports,description",
  724. "Service Template 4,tcp,1,First service template",
  725. "Service Template 5,tcp,2,Second service template",
  726. "Service Template 6,tcp,3,Third service template",
  727. )
  728. cls.csv_update_data = (
  729. "id,name,description",
  730. f"{service_templates[0].pk},Service Template 7,First service template7",
  731. f"{service_templates[1].pk},Service Template 8,Second service template8",
  732. f"{service_templates[2].pk},Service Template 9,Third service template9",
  733. )
  734. cls.bulk_edit_data = {
  735. 'protocol': ServiceProtocolChoices.PROTOCOL_UDP,
  736. 'ports': '106,107',
  737. 'description': 'New description',
  738. }
  739. class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  740. model = Service
  741. @classmethod
  742. def setUpTestData(cls):
  743. site = Site.objects.create(name='Site 1', slug='site-1')
  744. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  745. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
  746. role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  747. device = Device.objects.create(name='Device 1', site=site, device_type=devicetype, role=role)
  748. services = (
  749. Service(device=device, name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[101]),
  750. Service(device=device, name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[102]),
  751. Service(device=device, name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[103]),
  752. )
  753. Service.objects.bulk_create(services)
  754. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  755. cls.form_data = {
  756. 'device': device.pk,
  757. 'virtual_machine': None,
  758. 'name': 'Service X',
  759. 'protocol': ServiceProtocolChoices.PROTOCOL_TCP,
  760. 'ports': '104,105',
  761. 'ipaddresses': [],
  762. 'description': 'A new service',
  763. 'tags': [t.pk for t in tags],
  764. }
  765. cls.csv_data = (
  766. "device,name,protocol,ports,description",
  767. "Device 1,Service 1,tcp,1,First service",
  768. "Device 1,Service 2,tcp,2,Second service",
  769. "Device 1,Service 3,udp,3,Third service",
  770. )
  771. cls.csv_update_data = (
  772. "id,name,description",
  773. f"{services[0].pk},Service 7,First service7",
  774. f"{services[1].pk},Service 8,Second service8",
  775. f"{services[2].pk},Service 9,Third service9",
  776. )
  777. cls.bulk_edit_data = {
  778. 'protocol': ServiceProtocolChoices.PROTOCOL_UDP,
  779. 'ports': '106,107',
  780. 'description': 'New description',
  781. }
  782. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  783. def test_create_from_template(self):
  784. self.add_permissions('ipam.add_service')
  785. device = Device.objects.first()
  786. service_template = ServiceTemplate.objects.create(
  787. name='HTTP',
  788. protocol=ServiceProtocolChoices.PROTOCOL_TCP,
  789. ports=[80],
  790. description='Hypertext transfer protocol'
  791. )
  792. request = {
  793. 'path': self._get_url('add'),
  794. 'data': {
  795. 'device': device.pk,
  796. 'service_template': service_template.pk,
  797. },
  798. }
  799. self.assertHttpStatus(self.client.post(**request), 302)
  800. instance = self._get_queryset().order_by('pk').last()
  801. self.assertEqual(instance.device, device)
  802. self.assertEqual(instance.name, service_template.name)
  803. self.assertEqual(instance.protocol, service_template.protocol)
  804. self.assertEqual(instance.ports, service_template.ports)
  805. self.assertEqual(instance.description, service_template.description)
  806. class L2VPNTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  807. model = L2VPN
  808. @classmethod
  809. def setUpTestData(cls):
  810. rts = (
  811. RouteTarget(name='64534:123'),
  812. RouteTarget(name='64534:321')
  813. )
  814. RouteTarget.objects.bulk_create(rts)
  815. l2vpns = (
  816. L2VPN(name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650001'),
  817. L2VPN(name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650002'),
  818. L2VPN(name='L2VPN 3', slug='l2vpn-3', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650003')
  819. )
  820. L2VPN.objects.bulk_create(l2vpns)
  821. cls.csv_data = (
  822. 'name,slug,type,identifier',
  823. 'L2VPN 5,l2vpn-5,vxlan,456',
  824. 'L2VPN 6,l2vpn-6,vxlan,444',
  825. )
  826. cls.csv_update_data = (
  827. 'id,name,description',
  828. f'{l2vpns[0].pk},L2VPN 7,New description 7',
  829. f'{l2vpns[1].pk},L2VPN 8,New description 8',
  830. )
  831. cls.bulk_edit_data = {
  832. 'description': 'New Description',
  833. }
  834. cls.form_data = {
  835. 'name': 'L2VPN 8',
  836. 'slug': 'l2vpn-8',
  837. 'type': L2VPNTypeChoices.TYPE_VXLAN,
  838. 'identifier': 123,
  839. 'description': 'Description',
  840. 'import_targets': [rts[0].pk],
  841. 'export_targets': [rts[1].pk]
  842. }
  843. class L2VPNTerminationTestCase(
  844. ViewTestCases.GetObjectViewTestCase,
  845. ViewTestCases.GetObjectChangelogViewTestCase,
  846. ViewTestCases.CreateObjectViewTestCase,
  847. ViewTestCases.EditObjectViewTestCase,
  848. ViewTestCases.DeleteObjectViewTestCase,
  849. ViewTestCases.ListObjectsViewTestCase,
  850. ViewTestCases.BulkImportObjectsViewTestCase,
  851. ViewTestCases.BulkDeleteObjectsViewTestCase,
  852. ):
  853. model = L2VPNTermination
  854. @classmethod
  855. def setUpTestData(cls):
  856. device = create_test_device('Device 1')
  857. interface = Interface.objects.create(name='Interface 1', device=device, type='1000baset')
  858. l2vpns = (
  859. L2VPN(name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=650001),
  860. L2VPN(name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=650002),
  861. )
  862. L2VPN.objects.bulk_create(l2vpns)
  863. vlans = (
  864. VLAN(name='Vlan 1', vid=1001),
  865. VLAN(name='Vlan 2', vid=1002),
  866. VLAN(name='Vlan 3', vid=1003),
  867. VLAN(name='Vlan 4', vid=1004),
  868. VLAN(name='Vlan 5', vid=1005),
  869. VLAN(name='Vlan 6', vid=1006)
  870. )
  871. VLAN.objects.bulk_create(vlans)
  872. terminations = (
  873. L2VPNTermination(l2vpn=l2vpns[0], assigned_object=vlans[0]),
  874. L2VPNTermination(l2vpn=l2vpns[0], assigned_object=vlans[1]),
  875. L2VPNTermination(l2vpn=l2vpns[0], assigned_object=vlans[2])
  876. )
  877. L2VPNTermination.objects.bulk_create(terminations)
  878. cls.form_data = {
  879. 'l2vpn': l2vpns[0].pk,
  880. 'device': device.pk,
  881. 'interface': interface.pk,
  882. }
  883. cls.csv_data = (
  884. "l2vpn,vlan",
  885. "L2VPN 1,Vlan 4",
  886. "L2VPN 1,Vlan 5",
  887. "L2VPN 1,Vlan 6",
  888. )
  889. cls.csv_update_data = (
  890. f"id,l2vpn",
  891. f"{terminations[0].pk},{l2vpns[0].name}",
  892. f"{terminations[1].pk},{l2vpns[0].name}",
  893. f"{terminations[2].pk},{l2vpns[0].name}",
  894. )
  895. cls.bulk_edit_data = {}
  896. # TODO: Fix L2VPNTerminationImportForm validation to support bulk updates
  897. def test_bulk_update_objects_with_permission(self):
  898. pass
  899. #
  900. # Custom assertions
  901. #
  902. # TODO: Remove this
  903. def assertInstanceEqual(self, instance, data, exclude=None, api=False):
  904. """
  905. Override parent
  906. """
  907. if exclude is None:
  908. exclude = []
  909. fields = [k for k in data.keys() if k not in exclude]
  910. model_dict = self.model_to_dict(instance, fields=fields, api=api)
  911. # Omit any dictionary keys which are not instance attributes or have been excluded
  912. relevant_data = {
  913. k: v for k, v in data.items() if hasattr(instance, k) and k not in exclude
  914. }
  915. # Handle relations on the model
  916. for k, v in model_dict.items():
  917. if isinstance(v, object) and hasattr(v, 'first'):
  918. model_dict[k] = v.first().pk
  919. self.assertDictEqual(model_dict, relevant_data)