test_models.py 22 KB

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