test_models.py 18 KB

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