test_models.py 20 KB

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