test_models.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. from django.core.exceptions import ValidationError
  2. from django.test import TestCase
  3. from circuits.models import *
  4. from core.models import ObjectType
  5. from dcim.choices import *
  6. from dcim.models import *
  7. from extras.models import CustomField
  8. from netbox.choices import WeightUnitChoices
  9. from tenancy.models import Tenant
  10. from utilities.data import drange
  11. from virtualization.models import Cluster, ClusterType
  12. class LocationTestCase(TestCase):
  13. def test_change_location_site(self):
  14. """
  15. Check that all child Locations and Racks get updated when a Location is moved to a new Site. Topology:
  16. Site A
  17. - Location A1
  18. - Location A2
  19. - Rack 2
  20. - Device 2
  21. - Rack 1
  22. - Device 1
  23. """
  24. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  25. device_type = DeviceType.objects.create(
  26. manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
  27. )
  28. role = DeviceRole.objects.create(
  29. name='Device Role 1', slug='device-role-1', color='ff0000'
  30. )
  31. site_a = Site.objects.create(name='Site A', slug='site-a')
  32. site_b = Site.objects.create(name='Site B', slug='site-b')
  33. location_a1 = Location(site=site_a, name='Location A1', slug='location-a1')
  34. location_a1.save()
  35. location_a2 = Location(site=site_a, parent=location_a1, name='Location A2', slug='location-a2')
  36. location_a2.save()
  37. rack1 = Rack.objects.create(site=site_a, location=location_a1, name='Rack 1')
  38. rack2 = Rack.objects.create(site=site_a, location=location_a2, name='Rack 2')
  39. device1 = Device.objects.create(
  40. site=site_a,
  41. location=location_a1,
  42. name='Device 1',
  43. device_type=device_type,
  44. role=role
  45. )
  46. device2 = Device.objects.create(
  47. site=site_a,
  48. location=location_a2,
  49. name='Device 2',
  50. device_type=device_type,
  51. role=role
  52. )
  53. powerpanel1 = PowerPanel.objects.create(site=site_a, location=location_a1, name='Power Panel 1')
  54. # Move Location A1 to Site B
  55. location_a1.site = site_b
  56. location_a1.save()
  57. # Check that all objects within Location A1 now belong to Site B
  58. self.assertEqual(Location.objects.get(pk=location_a1.pk).site, site_b)
  59. self.assertEqual(Location.objects.get(pk=location_a2.pk).site, site_b)
  60. self.assertEqual(Rack.objects.get(pk=rack1.pk).site, site_b)
  61. self.assertEqual(Rack.objects.get(pk=rack2.pk).site, site_b)
  62. self.assertEqual(Device.objects.get(pk=device1.pk).site, site_b)
  63. self.assertEqual(Device.objects.get(pk=device2.pk).site, site_b)
  64. self.assertEqual(PowerPanel.objects.get(pk=powerpanel1.pk).site, site_b)
  65. class RackTypeTestCase(TestCase):
  66. @classmethod
  67. def setUpTestData(cls):
  68. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  69. RackType.objects.create(
  70. manufacturer=manufacturer,
  71. model='RackType 1',
  72. slug='rack-type-1',
  73. width=11,
  74. u_height=22,
  75. starting_unit=3,
  76. desc_units=True,
  77. outer_width=444,
  78. outer_depth=5,
  79. outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER,
  80. weight=66,
  81. weight_unit=WeightUnitChoices.UNIT_POUND,
  82. max_weight=7777,
  83. mounting_depth=8,
  84. )
  85. def test_rack_creation(self):
  86. rack_type = RackType.objects.first()
  87. sites = (
  88. Site(name='Site 1', slug='site-1'),
  89. )
  90. Site.objects.bulk_create(sites)
  91. locations = (
  92. Location(name='Location 1', slug='location-1', site=sites[0]),
  93. )
  94. for location in locations:
  95. location.save()
  96. rack = Rack.objects.create(
  97. name='Rack 1',
  98. facility_id='A101',
  99. site=sites[0],
  100. location=locations[0],
  101. rack_type=rack_type
  102. )
  103. self.assertEqual(rack.width, rack_type.width)
  104. self.assertEqual(rack.u_height, rack_type.u_height)
  105. self.assertEqual(rack.starting_unit, rack_type.starting_unit)
  106. self.assertEqual(rack.desc_units, rack_type.desc_units)
  107. self.assertEqual(rack.outer_width, rack_type.outer_width)
  108. self.assertEqual(rack.outer_depth, rack_type.outer_depth)
  109. self.assertEqual(rack.outer_unit, rack_type.outer_unit)
  110. self.assertEqual(rack.weight, rack_type.weight)
  111. self.assertEqual(rack.weight_unit, rack_type.weight_unit)
  112. self.assertEqual(rack.max_weight, rack_type.max_weight)
  113. self.assertEqual(rack.mounting_depth, rack_type.mounting_depth)
  114. class RackTestCase(TestCase):
  115. @classmethod
  116. def setUpTestData(cls):
  117. sites = (
  118. Site(name='Site 1', slug='site-1'),
  119. Site(name='Site 2', slug='site-2'),
  120. )
  121. Site.objects.bulk_create(sites)
  122. locations = (
  123. Location(name='Location 1', slug='location-1', site=sites[0]),
  124. Location(name='Location 2', slug='location-2', site=sites[1]),
  125. )
  126. for location in locations:
  127. location.save()
  128. Rack.objects.create(
  129. name='Rack 1',
  130. facility_id='A101',
  131. site=sites[0],
  132. location=locations[0],
  133. u_height=42
  134. )
  135. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  136. device_types = (
  137. DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1', u_height=1),
  138. DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2', u_height=0),
  139. DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3', u_height=0.5),
  140. )
  141. DeviceType.objects.bulk_create(device_types)
  142. DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  143. def test_rack_device_outside_height(self):
  144. site = Site.objects.first()
  145. rack = Rack.objects.first()
  146. device1 = Device(
  147. name='Device 1',
  148. device_type=DeviceType.objects.first(),
  149. role=DeviceRole.objects.first(),
  150. site=site,
  151. rack=rack,
  152. position=43,
  153. face=DeviceFaceChoices.FACE_FRONT,
  154. )
  155. device1.save()
  156. with self.assertRaises(ValidationError):
  157. rack.clean()
  158. def test_location_site(self):
  159. site1 = Site.objects.get(name='Site 1')
  160. location2 = Location.objects.get(name='Location 2')
  161. rack2 = Rack(
  162. name='Rack 2',
  163. site=site1,
  164. location=location2,
  165. u_height=42
  166. )
  167. rack2.save()
  168. with self.assertRaises(ValidationError):
  169. rack2.clean()
  170. def test_mount_single_device(self):
  171. site = Site.objects.first()
  172. rack = Rack.objects.first()
  173. device1 = Device(
  174. name='TestSwitch1',
  175. device_type=DeviceType.objects.first(),
  176. role=DeviceRole.objects.first(),
  177. site=site,
  178. rack=rack,
  179. position=10.0,
  180. face=DeviceFaceChoices.FACE_REAR,
  181. )
  182. device1.save()
  183. # Validate rack height
  184. self.assertEqual(list(rack.units), list(drange(42.5, 0.5, -0.5)))
  185. # Validate inventory (front face)
  186. rack1_inventory_front = {
  187. u['id']: u for u in rack.get_rack_units(face=DeviceFaceChoices.FACE_FRONT)
  188. }
  189. self.assertEqual(rack1_inventory_front[10.0]['device'], device1)
  190. self.assertEqual(rack1_inventory_front[10.5]['device'], device1)
  191. del rack1_inventory_front[10.0]
  192. del rack1_inventory_front[10.5]
  193. for u in rack1_inventory_front.values():
  194. self.assertIsNone(u['device'])
  195. # Validate inventory (rear face)
  196. rack1_inventory_rear = {
  197. u['id']: u for u in rack.get_rack_units(face=DeviceFaceChoices.FACE_REAR)
  198. }
  199. self.assertEqual(rack1_inventory_rear[10.0]['device'], device1)
  200. self.assertEqual(rack1_inventory_rear[10.5]['device'], device1)
  201. del rack1_inventory_rear[10.0]
  202. del rack1_inventory_rear[10.5]
  203. for u in rack1_inventory_rear.values():
  204. self.assertIsNone(u['device'])
  205. def test_mount_zero_ru(self):
  206. """
  207. Check that a 0RU device can be mounted in a rack with no face/position.
  208. """
  209. site = Site.objects.first()
  210. rack = Rack.objects.first()
  211. Device(
  212. name='Device 1',
  213. role=DeviceRole.objects.first(),
  214. device_type=DeviceType.objects.first(),
  215. site=site,
  216. rack=rack
  217. ).save()
  218. def test_mount_half_u_devices(self):
  219. """
  220. Check that two 0.5U devices can be mounted in the same rack unit.
  221. """
  222. rack = Rack.objects.first()
  223. attrs = {
  224. 'device_type': DeviceType.objects.get(u_height=0.5),
  225. 'role': DeviceRole.objects.first(),
  226. 'site': Site.objects.first(),
  227. 'rack': rack,
  228. 'face': DeviceFaceChoices.FACE_FRONT,
  229. }
  230. Device(name='Device 1', position=1, **attrs).save()
  231. Device(name='Device 2', position=1.5, **attrs).save()
  232. self.assertEqual(len(rack.get_available_units()), rack.u_height * 2 - 3)
  233. def test_change_rack_site(self):
  234. """
  235. Check that child Devices get updated when a Rack is moved to a new Site.
  236. """
  237. site_a = Site.objects.create(name='Site A', slug='site-a')
  238. site_b = Site.objects.create(name='Site B', slug='site-b')
  239. # Create Rack1 in Site A
  240. rack1 = Rack.objects.create(site=site_a, name='Rack 1')
  241. # Create Device1 in Rack1
  242. device1 = Device.objects.create(
  243. site=site_a,
  244. rack=rack1,
  245. device_type=DeviceType.objects.first(),
  246. role=DeviceRole.objects.first()
  247. )
  248. # Move Rack1 to Site B
  249. rack1.site = site_b
  250. rack1.save()
  251. # Check that Device1 is now assigned to Site B
  252. self.assertEqual(Device.objects.get(pk=device1.pk).site, site_b)
  253. def test_utilization(self):
  254. site = Site.objects.first()
  255. rack = Rack.objects.first()
  256. Device(
  257. name='Device 1',
  258. role=DeviceRole.objects.first(),
  259. device_type=DeviceType.objects.first(),
  260. site=site,
  261. rack=rack,
  262. position=1
  263. ).save()
  264. rack.refresh_from_db()
  265. self.assertEqual(rack.get_utilization(), 1 / 42 * 100)
  266. # create device excluded from utilization calculations
  267. dt = DeviceType.objects.create(
  268. manufacturer=Manufacturer.objects.first(),
  269. model='Device Type 4',
  270. slug='device-type-4',
  271. u_height=1,
  272. exclude_from_utilization=True
  273. )
  274. Device(
  275. name='Device 2',
  276. role=DeviceRole.objects.first(),
  277. device_type=dt,
  278. site=site,
  279. rack=rack,
  280. position=5
  281. ).save()
  282. rack.refresh_from_db()
  283. self.assertEqual(rack.get_utilization(), 1 / 42 * 100)
  284. class DeviceTestCase(TestCase):
  285. @classmethod
  286. def setUpTestData(cls):
  287. Site.objects.create(name='Test Site 1', slug='test-site-1')
  288. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  289. device_type = DeviceType.objects.create(
  290. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  291. )
  292. roles = (
  293. DeviceRole(name='Test Role 1', slug='test-role-1'),
  294. DeviceRole(name='Test Role 2', slug='test-role-2'),
  295. )
  296. DeviceRole.objects.bulk_create(roles)
  297. # Create a CustomField with a default value & assign it to all component models
  298. cf1 = CustomField.objects.create(name='cf1', default='foo')
  299. cf1.object_types.set(
  300. ObjectType.objects.filter(app_label='dcim', model__in=[
  301. 'consoleport',
  302. 'consoleserverport',
  303. 'powerport',
  304. 'poweroutlet',
  305. 'interface',
  306. 'rearport',
  307. 'frontport',
  308. 'modulebay',
  309. 'devicebay',
  310. 'inventoryitem',
  311. ])
  312. )
  313. # Create DeviceType components
  314. ConsolePortTemplate(
  315. device_type=device_type,
  316. name='Console Port 1'
  317. ).save()
  318. ConsoleServerPortTemplate(
  319. device_type=device_type,
  320. name='Console Server Port 1'
  321. ).save()
  322. powerport = PowerPortTemplate(
  323. device_type=device_type,
  324. name='Power Port 1',
  325. maximum_draw=1000,
  326. allocated_draw=500
  327. )
  328. powerport.save()
  329. PowerOutletTemplate(
  330. device_type=device_type,
  331. name='Power Outlet 1',
  332. power_port=powerport,
  333. feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
  334. ).save()
  335. InterfaceTemplate(
  336. device_type=device_type,
  337. name='Interface 1',
  338. type=InterfaceTypeChoices.TYPE_1GE_FIXED,
  339. mgmt_only=True
  340. ).save()
  341. rearport = RearPortTemplate(
  342. device_type=device_type,
  343. name='Rear Port 1',
  344. type=PortTypeChoices.TYPE_8P8C,
  345. positions=8
  346. )
  347. rearport.save()
  348. FrontPortTemplate(
  349. device_type=device_type,
  350. name='Front Port 1',
  351. type=PortTypeChoices.TYPE_8P8C,
  352. rear_port=rearport,
  353. rear_port_position=2
  354. ).save()
  355. ModuleBayTemplate(
  356. device_type=device_type,
  357. name='Module Bay 1'
  358. ).save()
  359. DeviceBayTemplate(
  360. device_type=device_type,
  361. name='Device Bay 1'
  362. ).save()
  363. InventoryItemTemplate(
  364. device_type=device_type,
  365. name='Inventory Item 1'
  366. ).save()
  367. def test_device_creation(self):
  368. """
  369. Ensure that all Device components are copied automatically from the DeviceType.
  370. """
  371. device = Device(
  372. site=Site.objects.first(),
  373. device_type=DeviceType.objects.first(),
  374. role=DeviceRole.objects.first(),
  375. name='Test Device 1'
  376. )
  377. device.save()
  378. consoleport = ConsolePort.objects.get(
  379. device=device,
  380. name='Console Port 1'
  381. )
  382. self.assertEqual(consoleport.cf['cf1'], 'foo')
  383. consoleserverport = ConsoleServerPort.objects.get(
  384. device=device,
  385. name='Console Server Port 1'
  386. )
  387. self.assertEqual(consoleserverport.cf['cf1'], 'foo')
  388. powerport = PowerPort.objects.get(
  389. device=device,
  390. name='Power Port 1',
  391. maximum_draw=1000,
  392. allocated_draw=500
  393. )
  394. self.assertEqual(powerport.cf['cf1'], 'foo')
  395. poweroutlet = PowerOutlet.objects.get(
  396. device=device,
  397. name='Power Outlet 1',
  398. power_port=powerport,
  399. feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
  400. )
  401. self.assertEqual(poweroutlet.cf['cf1'], 'foo')
  402. interface = Interface.objects.get(
  403. device=device,
  404. name='Interface 1',
  405. type=InterfaceTypeChoices.TYPE_1GE_FIXED,
  406. mgmt_only=True
  407. )
  408. self.assertEqual(interface.cf['cf1'], 'foo')
  409. rearport = RearPort.objects.get(
  410. device=device,
  411. name='Rear Port 1',
  412. type=PortTypeChoices.TYPE_8P8C,
  413. positions=8
  414. )
  415. self.assertEqual(rearport.cf['cf1'], 'foo')
  416. frontport = FrontPort.objects.get(
  417. device=device,
  418. name='Front Port 1',
  419. type=PortTypeChoices.TYPE_8P8C,
  420. rear_port=rearport,
  421. rear_port_position=2
  422. )
  423. self.assertEqual(frontport.cf['cf1'], 'foo')
  424. modulebay = ModuleBay.objects.get(
  425. device=device,
  426. name='Module Bay 1'
  427. )
  428. self.assertEqual(modulebay.cf['cf1'], 'foo')
  429. devicebay = DeviceBay.objects.get(
  430. device=device,
  431. name='Device Bay 1'
  432. )
  433. self.assertEqual(devicebay.cf['cf1'], 'foo')
  434. inventoryitem = InventoryItem.objects.get(
  435. device=device,
  436. name='Inventory Item 1'
  437. )
  438. self.assertEqual(inventoryitem.cf['cf1'], 'foo')
  439. def test_multiple_unnamed_devices(self):
  440. device1 = Device(
  441. site=Site.objects.first(),
  442. device_type=DeviceType.objects.first(),
  443. role=DeviceRole.objects.first(),
  444. name=None
  445. )
  446. device1.save()
  447. device2 = Device(
  448. site=device1.site,
  449. device_type=device1.device_type,
  450. role=device1.role,
  451. name=None
  452. )
  453. device2.full_clean()
  454. device2.save()
  455. self.assertEqual(Device.objects.filter(name__isnull=True).count(), 2)
  456. def test_device_name_case_sensitivity(self):
  457. device1 = Device(
  458. site=Site.objects.first(),
  459. device_type=DeviceType.objects.first(),
  460. role=DeviceRole.objects.first(),
  461. name='device 1'
  462. )
  463. device1.save()
  464. device2 = Device(
  465. site=device1.site,
  466. device_type=device1.device_type,
  467. role=device1.role,
  468. name='DEVICE 1'
  469. )
  470. # Uniqueness validation for name should ignore case
  471. with self.assertRaises(ValidationError):
  472. device2.full_clean()
  473. def test_device_duplicate_names(self):
  474. device1 = Device(
  475. site=Site.objects.first(),
  476. device_type=DeviceType.objects.first(),
  477. role=DeviceRole.objects.first(),
  478. name='Test Device 1'
  479. )
  480. device1.save()
  481. device2 = Device(
  482. site=device1.site,
  483. device_type=device1.device_type,
  484. role=device1.role,
  485. name=device1.name
  486. )
  487. # Two devices assigned to the same Site and no Tenant should fail validation
  488. with self.assertRaises(ValidationError):
  489. device2.full_clean()
  490. tenant = Tenant.objects.create(name='Test Tenant 1', slug='test-tenant-1')
  491. device1.tenant = tenant
  492. device1.save()
  493. device2.tenant = tenant
  494. # Two devices assigned to the same Site and the same Tenant should fail validation
  495. with self.assertRaises(ValidationError):
  496. device2.full_clean()
  497. device2.tenant = None
  498. # Two devices assigned to the same Site and different Tenants should pass validation
  499. device2.full_clean()
  500. device2.save()
  501. def test_device_mismatched_site_cluster(self):
  502. cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
  503. Cluster.objects.create(name='Cluster 1', type=cluster_type)
  504. sites = (
  505. Site(name='Site 1', slug='site-1'),
  506. Site(name='Site 2', slug='site-2'),
  507. )
  508. Site.objects.bulk_create(sites)
  509. clusters = (
  510. Cluster(name='Cluster 1', type=cluster_type, site=sites[0]),
  511. Cluster(name='Cluster 2', type=cluster_type, site=sites[1]),
  512. Cluster(name='Cluster 3', type=cluster_type, site=None),
  513. )
  514. Cluster.objects.bulk_create(clusters)
  515. device_type = DeviceType.objects.first()
  516. device_role = DeviceRole.objects.first()
  517. # Device with site only should pass
  518. Device(name='device1', site=sites[0], device_type=device_type, role=device_role).full_clean()
  519. # Device with site, cluster non-site should pass
  520. Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[2]).full_clean()
  521. # Device with mismatched site & cluster should fail
  522. with self.assertRaises(ValidationError):
  523. Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[1]).full_clean()
  524. class ModuleBayTestCase(TestCase):
  525. @classmethod
  526. def setUpTestData(cls):
  527. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  528. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  529. device_type = DeviceType.objects.create(
  530. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  531. )
  532. device_role = DeviceRole.objects.create(name='Test Role 1', slug='test-role-1')
  533. # Create a CustomField with a default value & assign it to all component models
  534. location = Location.objects.create(name='Location 1', slug='location-1', site=site)
  535. rack = Rack.objects.create(name='Rack 1', site=site)
  536. device = Device.objects.create(name='Device 1', device_type=device_type, role=device_role, site=site, location=location, rack=rack)
  537. module_bays = (
  538. ModuleBay(device=device, name='Module Bay 1', label='A', description='First'),
  539. ModuleBay(device=device, name='Module Bay 2', label='B', description='Second'),
  540. ModuleBay(device=device, name='Module Bay 3', label='C', description='Third'),
  541. )
  542. for module_bay in module_bays:
  543. module_bay.save()
  544. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  545. module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
  546. modules = (
  547. Module(device=device, module_bay=module_bays[0], module_type=module_type),
  548. Module(device=device, module_bay=module_bays[1], module_type=module_type),
  549. Module(device=device, module_bay=module_bays[2], module_type=module_type),
  550. )
  551. # M3 -> MB3 -> M2 -> MB2 -> M1 -> MB1
  552. Module.objects.bulk_create(modules)
  553. module_bays[1].module = modules[0]
  554. module_bays[1].clean()
  555. module_bays[1].save()
  556. module_bays[2].module = modules[1]
  557. module_bays[2].clean()
  558. module_bays[2].save()
  559. def test_module_bay_recursion(self):
  560. module_bay_1 = ModuleBay.objects.get(name='Module Bay 1')
  561. module_bay_2 = ModuleBay.objects.get(name='Module Bay 2')
  562. module_bay_3 = ModuleBay.objects.get(name='Module Bay 3')
  563. module_1 = Module.objects.get(module_bay=module_bay_1)
  564. module_2 = Module.objects.get(module_bay=module_bay_2)
  565. module_3 = Module.objects.get(module_bay=module_bay_3)
  566. # Confirm error if ModuleBay recurses
  567. with self.assertRaises(ValidationError):
  568. module_bay_1.module = module_3
  569. module_bay_1.clean()
  570. module_bay_1.save()
  571. # Confirm error if Module recurses
  572. with self.assertRaises(ValidationError):
  573. module_1.module_bay = module_bay_3
  574. module_1.clean()
  575. module_1.save()
  576. def test_single_module_token(self):
  577. module_bays = ModuleBay.objects.all()
  578. modules = Module.objects.all()
  579. device_type = DeviceType.objects.first()
  580. device_role = DeviceRole.objects.first()
  581. site = Site.objects.first()
  582. location = Location.objects.first()
  583. rack = Rack.objects.first()
  584. # Create DeviceType components
  585. ConsolePortTemplate.objects.create(
  586. device_type=device_type,
  587. name='{module}',
  588. label='{module}',
  589. )
  590. ModuleBayTemplate.objects.create(
  591. device_type=device_type,
  592. name='Module Bay 1'
  593. )
  594. device = Device.objects.create(
  595. name='Device 2',
  596. device_type=device_type,
  597. role=device_role,
  598. site=site,
  599. location=location,
  600. rack=rack
  601. )
  602. cp = device.consoleports.first()
  603. def test_nested_module_token(self):
  604. pass
  605. class CableTestCase(TestCase):
  606. @classmethod
  607. def setUpTestData(cls):
  608. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  609. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  610. devicetype = DeviceType.objects.create(
  611. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  612. )
  613. role = DeviceRole.objects.create(
  614. name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
  615. )
  616. device1 = Device.objects.create(
  617. device_type=devicetype, role=role, name='TestDevice1', site=site
  618. )
  619. device2 = Device.objects.create(
  620. device_type=devicetype, role=role, name='TestDevice2', site=site
  621. )
  622. interface1 = Interface.objects.create(device=device1, name='eth0')
  623. interface2 = Interface.objects.create(device=device2, name='eth0')
  624. interface3 = Interface.objects.create(device=device2, name='eth1')
  625. Cable(a_terminations=[interface1], b_terminations=[interface2]).save()
  626. power_port1 = PowerPort.objects.create(device=device2, name='psu1')
  627. patch_pannel = Device.objects.create(
  628. device_type=devicetype, role=role, name='TestPatchPanel', site=site
  629. )
  630. rear_port1 = RearPort.objects.create(device=patch_pannel, name='RP1', type='8p8c')
  631. front_port1 = FrontPort.objects.create(
  632. device=patch_pannel, name='FP1', type='8p8c', rear_port=rear_port1, rear_port_position=1
  633. )
  634. rear_port2 = RearPort.objects.create(device=patch_pannel, name='RP2', type='8p8c', positions=2)
  635. front_port2 = FrontPort.objects.create(
  636. device=patch_pannel, name='FP2', type='8p8c', rear_port=rear_port2, rear_port_position=1
  637. )
  638. rear_port3 = RearPort.objects.create(device=patch_pannel, name='RP3', type='8p8c', positions=3)
  639. front_port3 = FrontPort.objects.create(
  640. device=patch_pannel, name='FP3', type='8p8c', rear_port=rear_port3, rear_port_position=1
  641. )
  642. rear_port4 = RearPort.objects.create(device=patch_pannel, name='RP4', type='8p8c', positions=3)
  643. front_port4 = FrontPort.objects.create(
  644. device=patch_pannel, name='FP4', type='8p8c', rear_port=rear_port4, rear_port_position=1
  645. )
  646. provider = Provider.objects.create(name='Provider 1', slug='provider-1')
  647. provider_network = ProviderNetwork.objects.create(name='Provider Network 1', provider=provider)
  648. circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
  649. circuit1 = Circuit.objects.create(provider=provider, type=circuittype, cid='1')
  650. circuit2 = Circuit.objects.create(provider=provider, type=circuittype, cid='2')
  651. circuittermination1 = CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='A')
  652. circuittermination2 = CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='Z')
  653. circuittermination3 = CircuitTermination.objects.create(circuit=circuit2, provider_network=provider_network, term_side='A')
  654. def test_cable_creation(self):
  655. """
  656. When a new Cable is created, it must be cached on either termination point.
  657. """
  658. interface1 = Interface.objects.get(device__name='TestDevice1', name='eth0')
  659. interface2 = Interface.objects.get(device__name='TestDevice2', name='eth0')
  660. cable = Cable.objects.first()
  661. self.assertEqual(interface1.cable, cable)
  662. self.assertEqual(interface2.cable, cable)
  663. self.assertEqual(interface1.cable_end, 'A')
  664. self.assertEqual(interface2.cable_end, 'B')
  665. self.assertEqual(interface1.link_peers, [interface2])
  666. self.assertEqual(interface2.link_peers, [interface1])
  667. def test_cable_deletion(self):
  668. """
  669. When a Cable is deleted, the `cable` field on its termination points must be nullified. The str() method
  670. should still return the PK of the string even after being nullified.
  671. """
  672. interface1 = Interface.objects.get(device__name='TestDevice1', name='eth0')
  673. interface2 = Interface.objects.get(device__name='TestDevice2', name='eth0')
  674. cable = Cable.objects.first()
  675. cable.delete()
  676. self.assertIsNone(cable.pk)
  677. self.assertNotEqual(str(cable), '#None')
  678. interface1 = Interface.objects.get(pk=interface1.pk)
  679. self.assertIsNone(interface1.cable)
  680. self.assertListEqual(interface1.link_peers, [])
  681. interface2 = Interface.objects.get(pk=interface2.pk)
  682. self.assertIsNone(interface2.cable)
  683. self.assertListEqual(interface2.link_peers, [])
  684. def test_cable_validates_same_parent_object(self):
  685. """
  686. The clean method should ensure that all terminations at either end of a Cable belong to the same parent object.
  687. """
  688. interface1 = Interface.objects.get(device__name='TestDevice1', name='eth0')
  689. powerport1 = PowerPort.objects.get(device__name='TestDevice2', name='psu1')
  690. cable = Cable(a_terminations=[interface1], b_terminations=[powerport1])
  691. with self.assertRaises(ValidationError):
  692. cable.clean()
  693. def test_cable_validates_same_type(self):
  694. """
  695. The clean method should ensure that all terminations at either end of a Cable are of the same type.
  696. """
  697. interface1 = Interface.objects.get(device__name='TestDevice1', name='eth0')
  698. frontport1 = FrontPort.objects.get(device__name='TestPatchPanel', name='FP1')
  699. rearport1 = RearPort.objects.get(device__name='TestPatchPanel', name='RP1')
  700. cable = Cable(a_terminations=[frontport1, rearport1], b_terminations=[interface1])
  701. with self.assertRaises(ValidationError):
  702. cable.clean()
  703. def test_cable_validates_compatible_types(self):
  704. """
  705. The clean method should have a check to ensure only compatible port types can be connected by a cable
  706. """
  707. interface1 = Interface.objects.get(device__name='TestDevice1', name='eth0')
  708. powerport1 = PowerPort.objects.get(device__name='TestDevice2', name='psu1')
  709. # An interface cannot be connected to a power port, for example
  710. cable = Cable(a_terminations=[interface1], b_terminations=[powerport1])
  711. with self.assertRaises(ValidationError):
  712. cable.clean()
  713. def test_cable_cannot_terminate_to_a_provider_network_circuittermination(self):
  714. """
  715. Neither side of a cable can be terminated to a CircuitTermination which is attached to a ProviderNetwork
  716. """
  717. interface3 = Interface.objects.get(device__name='TestDevice2', name='eth1')
  718. circuittermination3 = CircuitTermination.objects.get(circuit__cid='2', term_side='A')
  719. cable = Cable(a_terminations=[interface3], b_terminations=[circuittermination3])
  720. with self.assertRaises(ValidationError):
  721. cable.clean()
  722. def test_cable_cannot_terminate_to_a_virtual_interface(self):
  723. """
  724. A cable cannot terminate to a virtual interface
  725. """
  726. device1 = Device.objects.get(name='TestDevice1')
  727. interface2 = Interface.objects.get(device__name='TestDevice2', name='eth0')
  728. virtual_interface = Interface(device=device1, name="V1", type=InterfaceTypeChoices.TYPE_VIRTUAL)
  729. cable = Cable(a_terminations=[interface2], b_terminations=[virtual_interface])
  730. with self.assertRaises(ValidationError):
  731. cable.clean()
  732. def test_cable_cannot_terminate_to_a_wireless_interface(self):
  733. """
  734. A cable cannot terminate to a wireless interface
  735. """
  736. device1 = Device.objects.get(name='TestDevice1')
  737. interface2 = Interface.objects.get(device__name='TestDevice2', name='eth0')
  738. wireless_interface = Interface(device=device1, name="W1", type=InterfaceTypeChoices.TYPE_80211A)
  739. cable = Cable(a_terminations=[interface2], b_terminations=[wireless_interface])
  740. with self.assertRaises(ValidationError):
  741. cable.clean()
  742. class VirtualDeviceContextTestCase(TestCase):
  743. @classmethod
  744. def setUpTestData(cls):
  745. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  746. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  747. devicetype = DeviceType.objects.create(
  748. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  749. )
  750. role = DeviceRole.objects.create(
  751. name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
  752. )
  753. Device.objects.create(
  754. device_type=devicetype, role=role, name='TestDevice1', site=site
  755. )
  756. def test_vdc_and_interface_creation(self):
  757. device = Device.objects.first()
  758. vdc = VirtualDeviceContext(device=device, name="VDC 1", identifier=1, status='active')
  759. vdc.full_clean()
  760. vdc.save()
  761. interface = Interface(device=device, name='Eth1/1', type='10gbase-t')
  762. interface.full_clean()
  763. interface.save()
  764. interface.vdcs.set([vdc])
  765. def test_vdc_duplicate_name(self):
  766. device = Device.objects.first()
  767. vdc1 = VirtualDeviceContext(device=device, name="VDC 1", identifier=1, status='active')
  768. vdc1.full_clean()
  769. vdc1.save()
  770. vdc2 = VirtualDeviceContext(device=device, name="VDC 1", identifier=2, status='active')
  771. with self.assertRaises(ValidationError):
  772. vdc2.full_clean()
  773. def test_vdc_duplicate_identifier(self):
  774. device = Device.objects.first()
  775. vdc1 = VirtualDeviceContext(device=device, name="VDC 1", identifier=1, status='active')
  776. vdc1.full_clean()
  777. vdc1.save()
  778. vdc2 = VirtualDeviceContext(device=device, name="VDC 2", identifier=1, status='active')
  779. with self.assertRaises(ValidationError):
  780. vdc2.full_clean()