test_models.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. from django.core.exceptions import ValidationError
  2. from django.test import TestCase
  3. from dcim.choices import *
  4. from dcim.models import *
  5. from tenancy.models import Tenant
  6. class RackTestCase(TestCase):
  7. def setUp(self):
  8. self.site1 = Site.objects.create(
  9. name='TestSite1',
  10. slug='test-site-1'
  11. )
  12. self.site2 = Site.objects.create(
  13. name='TestSite2',
  14. slug='test-site-2'
  15. )
  16. self.group1 = RackGroup.objects.create(
  17. name='TestGroup1',
  18. slug='test-group-1',
  19. site=self.site1
  20. )
  21. self.group2 = RackGroup.objects.create(
  22. name='TestGroup2',
  23. slug='test-group-2',
  24. site=self.site2
  25. )
  26. self.rack = Rack.objects.create(
  27. name='TestRack1',
  28. facility_id='A101',
  29. site=self.site1,
  30. group=self.group1,
  31. u_height=42
  32. )
  33. self.manufacturer = Manufacturer.objects.create(
  34. name='Acme',
  35. slug='acme'
  36. )
  37. self.device_type = {
  38. 'ff2048': DeviceType.objects.create(
  39. manufacturer=self.manufacturer,
  40. model='FrameForwarder 2048',
  41. slug='ff2048'
  42. ),
  43. 'cc5000': DeviceType.objects.create(
  44. manufacturer=self.manufacturer,
  45. model='CurrentCatapult 5000',
  46. slug='cc5000',
  47. u_height=0
  48. ),
  49. }
  50. self.role = {
  51. 'Server': DeviceRole.objects.create(
  52. name='Server',
  53. slug='server',
  54. ),
  55. 'Switch': DeviceRole.objects.create(
  56. name='Switch',
  57. slug='switch',
  58. ),
  59. 'Console Server': DeviceRole.objects.create(
  60. name='Console Server',
  61. slug='console-server',
  62. ),
  63. 'PDU': DeviceRole.objects.create(
  64. name='PDU',
  65. slug='pdu',
  66. ),
  67. }
  68. def test_rack_device_outside_height(self):
  69. rack1 = Rack(
  70. name='TestRack2',
  71. facility_id='A102',
  72. site=self.site1,
  73. u_height=42
  74. )
  75. rack1.save()
  76. device1 = Device(
  77. name='TestSwitch1',
  78. device_type=DeviceType.objects.get(manufacturer__slug='acme', slug='ff2048'),
  79. device_role=DeviceRole.objects.get(slug='switch'),
  80. site=self.site1,
  81. rack=rack1,
  82. position=43,
  83. face=DeviceFaceChoices.FACE_FRONT,
  84. )
  85. device1.save()
  86. with self.assertRaises(ValidationError):
  87. rack1.clean()
  88. def test_rack_group_site(self):
  89. rack_invalid_group = Rack(
  90. name='TestRack2',
  91. facility_id='A102',
  92. site=self.site1,
  93. u_height=42,
  94. group=self.group2
  95. )
  96. rack_invalid_group.save()
  97. with self.assertRaises(ValidationError):
  98. rack_invalid_group.clean()
  99. def test_mount_single_device(self):
  100. device1 = Device(
  101. name='TestSwitch1',
  102. device_type=DeviceType.objects.get(manufacturer__slug='acme', slug='ff2048'),
  103. device_role=DeviceRole.objects.get(slug='switch'),
  104. site=self.site1,
  105. rack=self.rack,
  106. position=10,
  107. face=DeviceFaceChoices.FACE_REAR,
  108. )
  109. device1.save()
  110. # Validate rack height
  111. self.assertEqual(list(self.rack.units), list(reversed(range(1, 43))))
  112. # Validate inventory (front face)
  113. rack1_inventory_front = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_FRONT)
  114. self.assertEqual(rack1_inventory_front[-10]['device'], device1)
  115. del(rack1_inventory_front[-10])
  116. for u in rack1_inventory_front:
  117. self.assertIsNone(u['device'])
  118. # Validate inventory (rear face)
  119. rack1_inventory_rear = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_REAR)
  120. self.assertEqual(rack1_inventory_rear[-10]['device'], device1)
  121. del(rack1_inventory_rear[-10])
  122. for u in rack1_inventory_rear:
  123. self.assertIsNone(u['device'])
  124. def test_mount_zero_ru(self):
  125. pdu = Device.objects.create(
  126. name='TestPDU',
  127. device_role=self.role.get('PDU'),
  128. device_type=self.device_type.get('cc5000'),
  129. site=self.site1,
  130. rack=self.rack,
  131. position=None,
  132. face='',
  133. )
  134. self.assertTrue(pdu)
  135. class DeviceTestCase(TestCase):
  136. def setUp(self):
  137. self.site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  138. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  139. self.device_type = DeviceType.objects.create(
  140. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  141. )
  142. self.device_role = DeviceRole.objects.create(
  143. name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
  144. )
  145. # Create DeviceType components
  146. ConsolePortTemplate(
  147. device_type=self.device_type,
  148. name='Console Port 1'
  149. ).save()
  150. ConsoleServerPortTemplate(
  151. device_type=self.device_type,
  152. name='Console Server Port 1'
  153. ).save()
  154. ppt = PowerPortTemplate(
  155. device_type=self.device_type,
  156. name='Power Port 1',
  157. maximum_draw=1000,
  158. allocated_draw=500
  159. )
  160. ppt.save()
  161. PowerOutletTemplate(
  162. device_type=self.device_type,
  163. name='Power Outlet 1',
  164. power_port=ppt,
  165. feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
  166. ).save()
  167. InterfaceTemplate(
  168. device_type=self.device_type,
  169. name='Interface 1',
  170. type=InterfaceTypeChoices.TYPE_1GE_FIXED,
  171. mgmt_only=True
  172. ).save()
  173. rpt = RearPortTemplate(
  174. device_type=self.device_type,
  175. name='Rear Port 1',
  176. type=PortTypeChoices.TYPE_8P8C,
  177. positions=8
  178. )
  179. rpt.save()
  180. FrontPortTemplate(
  181. device_type=self.device_type,
  182. name='Front Port 1',
  183. type=PortTypeChoices.TYPE_8P8C,
  184. rear_port=rpt,
  185. rear_port_position=2
  186. ).save()
  187. DeviceBayTemplate(
  188. device_type=self.device_type,
  189. name='Device Bay 1'
  190. ).save()
  191. def test_device_creation(self):
  192. """
  193. Ensure that all Device components are copied automatically from the DeviceType.
  194. """
  195. d = Device(
  196. site=self.site,
  197. device_type=self.device_type,
  198. device_role=self.device_role,
  199. name='Test Device 1'
  200. )
  201. d.save()
  202. ConsolePort.objects.get(
  203. device=d,
  204. name='Console Port 1'
  205. )
  206. ConsoleServerPort.objects.get(
  207. device=d,
  208. name='Console Server Port 1'
  209. )
  210. pp = PowerPort.objects.get(
  211. device=d,
  212. name='Power Port 1',
  213. maximum_draw=1000,
  214. allocated_draw=500
  215. )
  216. PowerOutlet.objects.get(
  217. device=d,
  218. name='Power Outlet 1',
  219. power_port=pp,
  220. feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A
  221. )
  222. Interface.objects.get(
  223. device=d,
  224. name='Interface 1',
  225. type=InterfaceTypeChoices.TYPE_1GE_FIXED,
  226. mgmt_only=True
  227. )
  228. rp = RearPort.objects.get(
  229. device=d,
  230. name='Rear Port 1',
  231. type=PortTypeChoices.TYPE_8P8C,
  232. positions=8
  233. )
  234. FrontPort.objects.get(
  235. device=d,
  236. name='Front Port 1',
  237. type=PortTypeChoices.TYPE_8P8C,
  238. rear_port=rp,
  239. rear_port_position=2
  240. )
  241. DeviceBay.objects.get(
  242. device=d,
  243. name='Device Bay 1'
  244. )
  245. def test_multiple_unnamed_devices(self):
  246. device1 = Device(
  247. site=self.site,
  248. device_type=self.device_type,
  249. device_role=self.device_role,
  250. name=''
  251. )
  252. device1.save()
  253. device2 = Device(
  254. site=device1.site,
  255. device_type=device1.device_type,
  256. device_role=device1.device_role,
  257. name=''
  258. )
  259. device2.full_clean()
  260. device2.save()
  261. self.assertEqual(Device.objects.filter(name='').count(), 2)
  262. def test_device_duplicate_names(self):
  263. device1 = Device(
  264. site=self.site,
  265. device_type=self.device_type,
  266. device_role=self.device_role,
  267. name='Test Device 1'
  268. )
  269. device1.save()
  270. device2 = Device(
  271. site=device1.site,
  272. device_type=device1.device_type,
  273. device_role=device1.device_role,
  274. name=device1.name
  275. )
  276. # Two devices assigned to the same Site and no Tenant should fail validation
  277. with self.assertRaises(ValidationError):
  278. device2.full_clean()
  279. tenant = Tenant.objects.create(name='Test Tenant 1', slug='test-tenant-1')
  280. device1.tenant = tenant
  281. device1.save()
  282. device2.tenant = tenant
  283. # Two devices assigned to the same Site and the same Tenant should fail validation
  284. with self.assertRaises(ValidationError):
  285. device2.full_clean()
  286. device2.tenant = None
  287. # Two devices assigned to the same Site and different Tenants should pass validation
  288. device2.full_clean()
  289. device2.save()
  290. class CableTestCase(TestCase):
  291. def setUp(self):
  292. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  293. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  294. devicetype = DeviceType.objects.create(
  295. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  296. )
  297. devicerole = DeviceRole.objects.create(
  298. name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
  299. )
  300. self.device1 = Device.objects.create(
  301. device_type=devicetype, device_role=devicerole, name='TestDevice1', site=site
  302. )
  303. self.device2 = Device.objects.create(
  304. device_type=devicetype, device_role=devicerole, name='TestDevice2', site=site
  305. )
  306. self.interface1 = Interface.objects.create(device=self.device1, name='eth0')
  307. self.interface2 = Interface.objects.create(device=self.device2, name='eth0')
  308. self.cable = Cable(termination_a=self.interface1, termination_b=self.interface2)
  309. self.cable.save()
  310. self.power_port1 = PowerPort.objects.create(device=self.device2, name='psu1')
  311. self.patch_pannel = Device.objects.create(
  312. device_type=devicetype, device_role=devicerole, name='TestPatchPannel', site=site
  313. )
  314. self.rear_port = RearPort.objects.create(device=self.patch_pannel, name='R1', type=1000)
  315. self.front_port = FrontPort.objects.create(
  316. device=self.patch_pannel, name='F1', type=1000, rear_port=self.rear_port
  317. )
  318. def test_cable_creation(self):
  319. """
  320. When a new Cable is created, it must be cached on either termination point.
  321. """
  322. interface1 = Interface.objects.get(pk=self.interface1.pk)
  323. self.assertEqual(self.cable.termination_a, interface1)
  324. interface2 = Interface.objects.get(pk=self.interface2.pk)
  325. self.assertEqual(self.cable.termination_b, interface2)
  326. def test_cable_deletion(self):
  327. """
  328. When a Cable is deleted, the `cable` field on its termination points must be nullified. The str() method
  329. should still return the PK of the string even after being nullified.
  330. """
  331. self.cable.delete()
  332. self.assertIsNone(self.cable.pk)
  333. self.assertNotEqual(str(self.cable), '#None')
  334. interface1 = Interface.objects.get(pk=self.interface1.pk)
  335. self.assertIsNone(interface1.cable)
  336. interface2 = Interface.objects.get(pk=self.interface2.pk)
  337. self.assertIsNone(interface2.cable)
  338. def test_cabletermination_deletion(self):
  339. """
  340. When a CableTermination object is deleted, its attached Cable (if any) must also be deleted.
  341. """
  342. self.interface1.delete()
  343. cable = Cable.objects.filter(pk=self.cable.pk).first()
  344. self.assertIsNone(cable)
  345. def test_cable_validates_compatibale_types(self):
  346. """
  347. The clean method should have a check to ensure only compatible port types can be connected by a cable
  348. """
  349. # An interface cannot be connected to a power port
  350. cable = Cable(termination_a=self.interface1, termination_b=self.power_port1)
  351. with self.assertRaises(ValidationError):
  352. cable.clean()
  353. def test_cable_cannot_have_the_same_terminination_on_both_ends(self):
  354. """
  355. A cable cannot be made with the same A and B side terminations
  356. """
  357. cable = Cable(termination_a=self.interface1, termination_b=self.interface1)
  358. with self.assertRaises(ValidationError):
  359. cable.clean()
  360. def test_cable_front_port_cannot_connect_to_corresponding_rear_port(self):
  361. """
  362. A cable cannot connect a front port to its corresponding rear port
  363. """
  364. cable = Cable(termination_a=self.front_port, termination_b=self.rear_port)
  365. with self.assertRaises(ValidationError):
  366. cable.clean()
  367. def test_cable_cannot_terminate_to_an_existing_connection(self):
  368. """
  369. Either side of a cable cannot be terminated when that side already has a connection
  370. """
  371. # Try to create a cable with the same interface terminations
  372. cable = Cable(termination_a=self.interface2, termination_b=self.interface1)
  373. with self.assertRaises(ValidationError):
  374. cable.clean()
  375. def test_cable_cannot_terminate_to_a_virtual_inteface(self):
  376. """
  377. A cable cannot terminate to a virtual interface
  378. """
  379. virtual_interface = Interface(device=self.device1, name="V1", type=InterfaceTypeChoices.TYPE_VIRTUAL)
  380. cable = Cable(termination_a=self.interface2, termination_b=virtual_interface)
  381. with self.assertRaises(ValidationError):
  382. cable.clean()
  383. def test_cable_cannot_terminate_to_a_wireless_inteface(self):
  384. """
  385. A cable cannot terminate to a wireless interface
  386. """
  387. wireless_interface = Interface(device=self.device1, name="W1", type=InterfaceTypeChoices.TYPE_80211A)
  388. cable = Cable(termination_a=self.interface2, termination_b=wireless_interface)
  389. with self.assertRaises(ValidationError):
  390. cable.clean()
  391. class CablePathTestCase(TestCase):
  392. def setUp(self):
  393. site = Site.objects.create(name='Test Site 1', slug='test-site-1')
  394. manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
  395. devicetype = DeviceType.objects.create(
  396. manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
  397. )
  398. devicerole = DeviceRole.objects.create(
  399. name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
  400. )
  401. self.device1 = Device.objects.create(
  402. device_type=devicetype, device_role=devicerole, name='Test Device 1', site=site
  403. )
  404. self.device2 = Device.objects.create(
  405. device_type=devicetype, device_role=devicerole, name='Test Device 2', site=site
  406. )
  407. self.interface1 = Interface.objects.create(device=self.device1, name='eth0')
  408. self.interface2 = Interface.objects.create(device=self.device2, name='eth0')
  409. self.panel1 = Device.objects.create(
  410. device_type=devicetype, device_role=devicerole, name='Test Panel 1', site=site
  411. )
  412. self.panel2 = Device.objects.create(
  413. device_type=devicetype, device_role=devicerole, name='Test Panel 2', site=site
  414. )
  415. self.rear_port1 = RearPort.objects.create(
  416. device=self.panel1, name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C
  417. )
  418. self.front_port1 = FrontPort.objects.create(
  419. device=self.panel1, name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=self.rear_port1
  420. )
  421. self.rear_port2 = RearPort.objects.create(
  422. device=self.panel2, name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C
  423. )
  424. self.front_port2 = FrontPort.objects.create(
  425. device=self.panel2, name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=self.rear_port2
  426. )
  427. def test_path_completion(self):
  428. # First segment
  429. cable1 = Cable(termination_a=self.interface1, termination_b=self.front_port1)
  430. cable1.save()
  431. interface1 = Interface.objects.get(pk=self.interface1.pk)
  432. self.assertIsNone(interface1.connected_endpoint)
  433. self.assertIsNone(interface1.connection_status)
  434. # Second segment
  435. cable2 = Cable(termination_a=self.rear_port1, termination_b=self.rear_port2)
  436. cable2.save()
  437. interface1 = Interface.objects.get(pk=self.interface1.pk)
  438. self.assertIsNone(interface1.connected_endpoint)
  439. self.assertIsNone(interface1.connection_status)
  440. # Third segment
  441. cable3 = Cable(
  442. termination_a=self.front_port2,
  443. termination_b=self.interface2,
  444. status=CableStatusChoices.STATUS_PLANNED
  445. )
  446. cable3.save()
  447. interface1 = Interface.objects.get(pk=self.interface1.pk)
  448. self.assertEqual(interface1.connected_endpoint, self.interface2)
  449. self.assertFalse(interface1.connection_status)
  450. # Switch third segment from planned to connected
  451. cable3.status = CableStatusChoices.STATUS_CONNECTED
  452. cable3.save()
  453. interface1 = Interface.objects.get(pk=self.interface1.pk)
  454. self.assertEqual(interface1.connected_endpoint, self.interface2)
  455. self.assertTrue(interface1.connection_status)
  456. def test_path_teardown(self):
  457. # Build the path
  458. cable1 = Cable(termination_a=self.interface1, termination_b=self.front_port1)
  459. cable1.save()
  460. cable2 = Cable(termination_a=self.rear_port1, termination_b=self.rear_port2)
  461. cable2.save()
  462. cable3 = Cable(termination_a=self.front_port2, termination_b=self.interface2)
  463. cable3.save()
  464. interface1 = Interface.objects.get(pk=self.interface1.pk)
  465. self.assertEqual(interface1.connected_endpoint, self.interface2)
  466. self.assertTrue(interface1.connection_status)
  467. # Remove a cable
  468. cable2.delete()
  469. interface1 = Interface.objects.get(pk=self.interface1.pk)
  470. self.assertIsNone(interface1.connected_endpoint)
  471. self.assertIsNone(interface1.connection_status)
  472. interface2 = Interface.objects.get(pk=self.interface2.pk)
  473. self.assertIsNone(interface2.connected_endpoint)
  474. self.assertIsNone(interface2.connection_status)