test_cablepaths2.py 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060
  1. from unittest import skip
  2. from circuits.models import CircuitTermination
  3. from dcim.choices import CableProfileChoices
  4. from dcim.models import *
  5. from dcim.svg import CableTraceSVG
  6. from dcim.tests.utils import CablePathTestCase
  7. class CablePathTests(CablePathTestCase):
  8. """
  9. Test the creation of CablePaths for Cables with different profiles applied.
  10. Tests are numbered as follows:
  11. 1XX: Test direct connections using each profile
  12. 2XX: Topology tests replicated from the legacy test case and adapted to use profiles
  13. 3XX: Dynamic port mapping and termination changes
  14. """
  15. def test_101_cable_profile_single_1c1p(self):
  16. """
  17. [IF1] --C1-- [IF2]
  18. Cable profile: Single connector, single position
  19. """
  20. interfaces = [
  21. Interface.objects.create(device=self.device, name='Interface 1'),
  22. Interface.objects.create(device=self.device, name='Interface 2'),
  23. ]
  24. # Create cable 1
  25. cable1 = Cable(
  26. profile=CableProfileChoices.SINGLE_1C1P,
  27. a_terminations=[interfaces[0]],
  28. b_terminations=[interfaces[1]],
  29. )
  30. cable1.clean()
  31. cable1.save()
  32. path1 = self.assertPathExists(
  33. (interfaces[0], cable1, interfaces[1]),
  34. is_complete=True,
  35. is_active=True
  36. )
  37. path2 = self.assertPathExists(
  38. (interfaces[1], cable1, interfaces[0]),
  39. is_complete=True,
  40. is_active=True
  41. )
  42. self.assertEqual(CablePath.objects.count(), 2)
  43. interfaces[0].refresh_from_db()
  44. interfaces[1].refresh_from_db()
  45. self.assertPathIsSet(interfaces[0], path1)
  46. self.assertPathIsSet(interfaces[1], path2)
  47. self.assertEqual(interfaces[0].cable_connector, 1)
  48. self.assertEqual(interfaces[0].cable_positions, [1])
  49. self.assertEqual(interfaces[1].cable_connector, 1)
  50. self.assertEqual(interfaces[1].cable_positions, [1])
  51. # Test SVG generation
  52. CableTraceSVG(interfaces[0]).render()
  53. # Delete cable 1
  54. cable1.delete()
  55. # Check that all CablePaths have been deleted
  56. self.assertEqual(CablePath.objects.count(), 0)
  57. def test_102_cable_profile_single_1c2p(self):
  58. """
  59. [IF1] --C1-- [FP1][RP1] --C3-- [RP2][FP3] --C4-- [IF3]
  60. [IF2] --C2-- [FP2] [FP4] --C5-- [IF4]
  61. Cable profile: Single connector, multiple positions
  62. """
  63. interfaces = [
  64. Interface.objects.create(device=self.device, name='Interface 1'),
  65. Interface.objects.create(device=self.device, name='Interface 2'),
  66. Interface.objects.create(device=self.device, name='Interface 3'),
  67. Interface.objects.create(device=self.device, name='Interface 4'),
  68. ]
  69. rear_ports = [
  70. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2),
  71. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2),
  72. ]
  73. front_ports = [
  74. FrontPort.objects.create(device=self.device, name='Front Port 1'),
  75. FrontPort.objects.create(device=self.device, name='Front Port 2'),
  76. FrontPort.objects.create(device=self.device, name='Front Port 3'),
  77. FrontPort.objects.create(device=self.device, name='Front Port 4'),
  78. ]
  79. PortMapping.objects.bulk_create([
  80. PortMapping(
  81. device=self.device,
  82. front_port=front_ports[0],
  83. front_port_position=1,
  84. rear_port=rear_ports[0],
  85. rear_port_position=1,
  86. ),
  87. PortMapping(
  88. device=self.device,
  89. front_port=front_ports[1],
  90. front_port_position=1,
  91. rear_port=rear_ports[0],
  92. rear_port_position=2,
  93. ),
  94. PortMapping(
  95. device=self.device,
  96. front_port=front_ports[2],
  97. front_port_position=1,
  98. rear_port=rear_ports[1],
  99. rear_port_position=1,
  100. ),
  101. PortMapping(
  102. device=self.device,
  103. front_port=front_ports[3],
  104. front_port_position=1,
  105. rear_port=rear_ports[1],
  106. rear_port_position=2,
  107. ),
  108. ])
  109. # Create cables
  110. cable1 = Cable(
  111. a_terminations=[interfaces[0]],
  112. b_terminations=[front_ports[0]],
  113. )
  114. cable1.clean()
  115. cable1.save()
  116. cable2 = Cable(
  117. a_terminations=[interfaces[1]],
  118. b_terminations=[front_ports[1]],
  119. )
  120. cable2.clean()
  121. cable2.save()
  122. cable3 = Cable(
  123. profile=CableProfileChoices.SINGLE_1C2P,
  124. a_terminations=[rear_ports[0]],
  125. b_terminations=[rear_ports[1]],
  126. )
  127. cable3.clean()
  128. cable3.save()
  129. cable4 = Cable(
  130. a_terminations=[interfaces[2]],
  131. b_terminations=[front_ports[2]],
  132. )
  133. cable4.clean()
  134. cable4.save()
  135. cable5 = Cable(
  136. a_terminations=[interfaces[3]],
  137. b_terminations=[front_ports[3]],
  138. )
  139. cable5.clean()
  140. cable5.save()
  141. path1 = self.assertPathExists(
  142. (
  143. interfaces[0], cable1, front_ports[0], rear_ports[0], cable3, rear_ports[1], front_ports[2], cable4,
  144. interfaces[2],
  145. ),
  146. is_complete=True,
  147. is_active=True
  148. )
  149. path2 = self.assertPathExists(
  150. (
  151. interfaces[1], cable2, front_ports[1], rear_ports[0], cable3, rear_ports[1], front_ports[3], cable5,
  152. interfaces[3],
  153. ),
  154. is_complete=True,
  155. is_active=True
  156. )
  157. path3 = self.assertPathExists(
  158. (
  159. interfaces[2], cable4, front_ports[2], rear_ports[1], cable3, rear_ports[0], front_ports[0], cable1,
  160. interfaces[0],
  161. ),
  162. is_complete=True,
  163. is_active=True
  164. )
  165. path4 = self.assertPathExists(
  166. (
  167. interfaces[3], cable5, front_ports[3], rear_ports[1], cable3, rear_ports[0], front_ports[1], cable2,
  168. interfaces[1],
  169. ),
  170. is_complete=True,
  171. is_active=True
  172. )
  173. self.assertEqual(CablePath.objects.count(), 4)
  174. for iface in interfaces:
  175. iface.refresh_from_db()
  176. self.assertPathIsSet(interfaces[0], path1)
  177. self.assertPathIsSet(interfaces[1], path2)
  178. self.assertPathIsSet(interfaces[2], path3)
  179. self.assertPathIsSet(interfaces[3], path4)
  180. for rear_port in rear_ports:
  181. rear_port.refresh_from_db()
  182. self.assertEqual(rear_ports[0].cable_connector, 1)
  183. self.assertEqual(rear_ports[0].cable_positions, [1, 2])
  184. self.assertEqual(rear_ports[1].cable_connector, 1)
  185. self.assertEqual(rear_ports[1].cable_positions, [1, 2])
  186. # Test SVG generation
  187. CableTraceSVG(interfaces[0]).render()
  188. def test_103_cable_profile_trunk_2c1p(self):
  189. """
  190. [IF1] --C1-- [IF3]
  191. [IF2] [IF4]
  192. Cable profile: Multiple connectors, single position
  193. """
  194. interfaces = [
  195. Interface.objects.create(device=self.device, name='Interface 1'),
  196. Interface.objects.create(device=self.device, name='Interface 2'),
  197. Interface.objects.create(device=self.device, name='Interface 3'),
  198. Interface.objects.create(device=self.device, name='Interface 4'),
  199. ]
  200. # Create cable 1
  201. cable1 = Cable(
  202. profile=CableProfileChoices.TRUNK_2C1P,
  203. a_terminations=[interfaces[0], interfaces[1]],
  204. b_terminations=[interfaces[2], interfaces[3]],
  205. )
  206. cable1.clean()
  207. cable1.save()
  208. path1 = self.assertPathExists(
  209. (interfaces[0], cable1, interfaces[2]),
  210. is_complete=True,
  211. is_active=True
  212. )
  213. path2 = self.assertPathExists(
  214. (interfaces[1], cable1, interfaces[3]),
  215. is_complete=True,
  216. is_active=True
  217. )
  218. path3 = self.assertPathExists(
  219. (interfaces[2], cable1, interfaces[0]),
  220. is_complete=True,
  221. is_active=True
  222. )
  223. path4 = self.assertPathExists(
  224. (interfaces[3], cable1, interfaces[1]),
  225. is_complete=True,
  226. is_active=True
  227. )
  228. self.assertEqual(CablePath.objects.count(), 4)
  229. for interface in interfaces:
  230. interface.refresh_from_db()
  231. self.assertPathIsSet(interfaces[0], path1)
  232. self.assertPathIsSet(interfaces[1], path2)
  233. self.assertPathIsSet(interfaces[2], path3)
  234. self.assertPathIsSet(interfaces[3], path4)
  235. self.assertEqual(interfaces[0].cable_connector, 1)
  236. self.assertEqual(interfaces[0].cable_positions, [1])
  237. self.assertEqual(interfaces[1].cable_connector, 2)
  238. self.assertEqual(interfaces[1].cable_positions, [1])
  239. self.assertEqual(interfaces[2].cable_connector, 1)
  240. self.assertEqual(interfaces[2].cable_positions, [1])
  241. self.assertEqual(interfaces[3].cable_connector, 2)
  242. self.assertEqual(interfaces[3].cable_positions, [1])
  243. # Test SVG generation
  244. CableTraceSVG(interfaces[0]).render()
  245. # Delete cable 1
  246. cable1.delete()
  247. # Check that all CablePaths have been deleted
  248. self.assertEqual(CablePath.objects.count(), 0)
  249. def test_104_cable_profile_trunk_2c2p(self):
  250. """
  251. [IF1] --C1-- [FP1][RP1] --C9-- [RP3][FP5] --C5-- [IF5]
  252. [IF2] --C2-- [FP2] [FP6] --C6-- [IF6]
  253. [IF3] --C3-- [FP3][RP2] [RP4][FP7] --C7-- [IF7]
  254. [IF4] --C4-- [FP4] [FP8] --C8-- [IF8]
  255. Cable profile: Multiple connectors, multiple positions
  256. """
  257. interfaces = [
  258. Interface.objects.create(device=self.device, name='Interface 1'),
  259. Interface.objects.create(device=self.device, name='Interface 2'),
  260. Interface.objects.create(device=self.device, name='Interface 3'),
  261. Interface.objects.create(device=self.device, name='Interface 4'),
  262. Interface.objects.create(device=self.device, name='Interface 5'),
  263. Interface.objects.create(device=self.device, name='Interface 6'),
  264. Interface.objects.create(device=self.device, name='Interface 7'),
  265. Interface.objects.create(device=self.device, name='Interface 8'),
  266. ]
  267. rear_ports = [
  268. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2),
  269. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2),
  270. RearPort.objects.create(device=self.device, name='Rear Port 3', positions=2),
  271. RearPort.objects.create(device=self.device, name='Rear Port 4', positions=2),
  272. ]
  273. front_ports = [
  274. FrontPort.objects.create(device=self.device, name='Front Port 1'),
  275. FrontPort.objects.create(device=self.device, name='Front Port 2'),
  276. FrontPort.objects.create(device=self.device, name='Front Port 3'),
  277. FrontPort.objects.create(device=self.device, name='Front Port 4'),
  278. FrontPort.objects.create(device=self.device, name='Front Port 5'),
  279. FrontPort.objects.create(device=self.device, name='Front Port 6'),
  280. FrontPort.objects.create(device=self.device, name='Front Port 7'),
  281. FrontPort.objects.create(device=self.device, name='Front Port 8'),
  282. ]
  283. PortMapping.objects.bulk_create([
  284. PortMapping(
  285. device=self.device,
  286. front_port=front_ports[0],
  287. front_port_position=1,
  288. rear_port=rear_ports[0],
  289. rear_port_position=1,
  290. ),
  291. PortMapping(
  292. device=self.device,
  293. front_port=front_ports[1],
  294. front_port_position=1,
  295. rear_port=rear_ports[0],
  296. rear_port_position=2,
  297. ),
  298. PortMapping(
  299. device=self.device,
  300. front_port=front_ports[2],
  301. front_port_position=1,
  302. rear_port=rear_ports[1],
  303. rear_port_position=1,
  304. ),
  305. PortMapping(
  306. device=self.device,
  307. front_port=front_ports[3],
  308. front_port_position=1,
  309. rear_port=rear_ports[1],
  310. rear_port_position=2,
  311. ),
  312. PortMapping(
  313. device=self.device,
  314. front_port=front_ports[4],
  315. front_port_position=1,
  316. rear_port=rear_ports[2],
  317. rear_port_position=1,
  318. ),
  319. PortMapping(
  320. device=self.device,
  321. front_port=front_ports[5],
  322. front_port_position=1,
  323. rear_port=rear_ports[2],
  324. rear_port_position=2,
  325. ),
  326. PortMapping(
  327. device=self.device,
  328. front_port=front_ports[6],
  329. front_port_position=1,
  330. rear_port=rear_ports[3],
  331. rear_port_position=1,
  332. ),
  333. PortMapping(
  334. device=self.device,
  335. front_port=front_ports[7],
  336. front_port_position=1,
  337. rear_port=rear_ports[3],
  338. rear_port_position=2,
  339. ),
  340. ])
  341. # Create cables
  342. cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[front_ports[0]])
  343. cable1.clean()
  344. cable1.save()
  345. cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[front_ports[1]])
  346. cable2.clean()
  347. cable2.save()
  348. cable3 = Cable(a_terminations=[interfaces[2]], b_terminations=[front_ports[2]])
  349. cable3.clean()
  350. cable3.save()
  351. cable4 = Cable(a_terminations=[interfaces[3]], b_terminations=[front_ports[3]])
  352. cable4.clean()
  353. cable4.save()
  354. cable5 = Cable(a_terminations=[interfaces[4]], b_terminations=[front_ports[4]])
  355. cable5.clean()
  356. cable5.save()
  357. cable6 = Cable(a_terminations=[interfaces[5]], b_terminations=[front_ports[5]])
  358. cable6.clean()
  359. cable6.save()
  360. cable7 = Cable(a_terminations=[interfaces[6]], b_terminations=[front_ports[6]])
  361. cable7.clean()
  362. cable7.save()
  363. cable8 = Cable(a_terminations=[interfaces[7]], b_terminations=[front_ports[7]])
  364. cable8.clean()
  365. cable8.save()
  366. cable9 = Cable(
  367. profile=CableProfileChoices.TRUNK_2C2P,
  368. a_terminations=[rear_ports[0], rear_ports[1]],
  369. b_terminations=[rear_ports[2], rear_ports[3]]
  370. )
  371. cable9.clean()
  372. cable9.save()
  373. path1 = self.assertPathExists(
  374. (
  375. interfaces[0], cable1, front_ports[0], rear_ports[0], cable9, rear_ports[2], front_ports[4], cable5,
  376. interfaces[4],
  377. ),
  378. is_complete=True,
  379. is_active=True
  380. )
  381. path2 = self.assertPathExists(
  382. (
  383. interfaces[1], cable2, front_ports[1], rear_ports[0], cable9, rear_ports[2], front_ports[5], cable6,
  384. interfaces[5],
  385. ),
  386. is_complete=True,
  387. is_active=True
  388. )
  389. path3 = self.assertPathExists(
  390. (
  391. interfaces[2], cable3, front_ports[2], rear_ports[1], cable9, rear_ports[3], front_ports[6], cable7,
  392. interfaces[6],
  393. ),
  394. is_complete=True,
  395. is_active=True
  396. )
  397. path4 = self.assertPathExists(
  398. (
  399. interfaces[3], cable4, front_ports[3], rear_ports[1], cable9, rear_ports[3], front_ports[7], cable8,
  400. interfaces[7],
  401. ),
  402. is_complete=True,
  403. is_active=True
  404. )
  405. path5 = self.assertPathExists(
  406. (
  407. interfaces[4], cable5, front_ports[4], rear_ports[2], cable9, rear_ports[0], front_ports[0], cable1,
  408. interfaces[0],
  409. ),
  410. is_complete=True,
  411. is_active=True
  412. )
  413. path6 = self.assertPathExists(
  414. (
  415. interfaces[5], cable6, front_ports[5], rear_ports[2], cable9, rear_ports[0], front_ports[1], cable2,
  416. interfaces[1],
  417. ),
  418. is_complete=True,
  419. is_active=True
  420. )
  421. path7 = self.assertPathExists(
  422. (
  423. interfaces[6], cable7, front_ports[6], rear_ports[3], cable9, rear_ports[1], front_ports[2], cable3,
  424. interfaces[2],
  425. ),
  426. is_complete=True,
  427. is_active=True
  428. )
  429. path8 = self.assertPathExists(
  430. (
  431. interfaces[7], cable8, front_ports[7], rear_ports[3], cable9, rear_ports[1], front_ports[3], cable4,
  432. interfaces[3],
  433. ),
  434. is_complete=True,
  435. is_active=True
  436. )
  437. self.assertEqual(CablePath.objects.count(), 8)
  438. for iface in interfaces:
  439. iface.refresh_from_db()
  440. self.assertPathIsSet(interfaces[0], path1)
  441. self.assertPathIsSet(interfaces[1], path2)
  442. self.assertPathIsSet(interfaces[2], path3)
  443. self.assertPathIsSet(interfaces[3], path4)
  444. self.assertPathIsSet(interfaces[4], path5)
  445. self.assertPathIsSet(interfaces[5], path6)
  446. self.assertPathIsSet(interfaces[6], path7)
  447. self.assertPathIsSet(interfaces[7], path8)
  448. for rear_port in rear_ports:
  449. rear_port.refresh_from_db()
  450. self.assertEqual(rear_ports[0].cable_connector, 1)
  451. self.assertEqual(rear_ports[0].cable_positions, [1, 2])
  452. self.assertEqual(rear_ports[1].cable_connector, 2)
  453. self.assertEqual(rear_ports[1].cable_positions, [1, 2])
  454. self.assertEqual(rear_ports[2].cable_connector, 1)
  455. self.assertEqual(rear_ports[2].cable_positions, [1, 2])
  456. self.assertEqual(rear_ports[3].cable_connector, 2)
  457. self.assertEqual(rear_ports[3].cable_positions, [1, 2])
  458. # Test SVG generation
  459. CableTraceSVG(interfaces[0]).render()
  460. def test_105_cable_profile_breakout(self):
  461. """
  462. [IF1] --C1-- [FP1][RP1] --C2-- [IF5]
  463. [IF2] --C3-- [FP2] [IF6]
  464. [IF3] --C4-- [FP3] [IF7]
  465. [IF4] --C5-- [FP4] [IF8]
  466. Cable profile: 1:4 breakout
  467. """
  468. interfaces = [
  469. Interface.objects.create(device=self.device, name='Interface 1'),
  470. Interface.objects.create(device=self.device, name='Interface 2'),
  471. Interface.objects.create(device=self.device, name='Interface 3'),
  472. Interface.objects.create(device=self.device, name='Interface 4'),
  473. Interface.objects.create(device=self.device, name='Interface 5'),
  474. Interface.objects.create(device=self.device, name='Interface 6'),
  475. Interface.objects.create(device=self.device, name='Interface 7'),
  476. Interface.objects.create(device=self.device, name='Interface 8'),
  477. ]
  478. rear_ports = [
  479. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=4),
  480. ]
  481. front_ports = [
  482. FrontPort.objects.create(device=self.device, name='Front Port 1'),
  483. FrontPort.objects.create(device=self.device, name='Front Port 2'),
  484. FrontPort.objects.create(device=self.device, name='Front Port 3'),
  485. FrontPort.objects.create(device=self.device, name='Front Port 4'),
  486. ]
  487. PortMapping.objects.bulk_create([
  488. PortMapping(
  489. device=self.device,
  490. front_port=front_ports[0],
  491. front_port_position=1,
  492. rear_port=rear_ports[0],
  493. rear_port_position=1,
  494. ),
  495. PortMapping(
  496. device=self.device,
  497. front_port=front_ports[1],
  498. front_port_position=1,
  499. rear_port=rear_ports[0],
  500. rear_port_position=2,
  501. ),
  502. PortMapping(
  503. device=self.device,
  504. front_port=front_ports[2],
  505. front_port_position=1,
  506. rear_port=rear_ports[0],
  507. rear_port_position=3,
  508. ),
  509. PortMapping(
  510. device=self.device,
  511. front_port=front_ports[3],
  512. front_port_position=1,
  513. rear_port=rear_ports[0],
  514. rear_port_position=4,
  515. ),
  516. ])
  517. # Create cables
  518. cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[front_ports[0]])
  519. cable1.clean()
  520. cable1.save()
  521. cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[front_ports[1]])
  522. cable2.clean()
  523. cable2.save()
  524. cable3 = Cable(a_terminations=[interfaces[2]], b_terminations=[front_ports[2]])
  525. cable3.clean()
  526. cable3.save()
  527. cable4 = Cable(a_terminations=[interfaces[3]], b_terminations=[front_ports[3]])
  528. cable4.clean()
  529. cable4.save()
  530. cable5 = Cable(
  531. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  532. a_terminations=[rear_ports[0]],
  533. b_terminations=interfaces[4:8],
  534. )
  535. cable5.clean()
  536. cable5.save()
  537. path1 = self.assertPathExists(
  538. (interfaces[0], cable1, front_ports[0], rear_ports[0], cable5, interfaces[4]),
  539. is_complete=True,
  540. is_active=True
  541. )
  542. path2 = self.assertPathExists(
  543. (interfaces[1], cable2, front_ports[1], rear_ports[0], cable5, interfaces[5]),
  544. is_complete=True,
  545. is_active=True
  546. )
  547. path3 = self.assertPathExists(
  548. (interfaces[2], cable3, front_ports[2], rear_ports[0], cable5, interfaces[6]),
  549. is_complete=True,
  550. is_active=True
  551. )
  552. path4 = self.assertPathExists(
  553. (interfaces[3], cable4, front_ports[3], rear_ports[0], cable5, interfaces[7]),
  554. is_complete=True,
  555. is_active=True
  556. )
  557. path5 = self.assertPathExists(
  558. (interfaces[4], cable5, rear_ports[0], front_ports[0], cable1, interfaces[0]),
  559. is_complete=True,
  560. is_active=True
  561. )
  562. path6 = self.assertPathExists(
  563. (interfaces[5], cable5, rear_ports[0], front_ports[1], cable2, interfaces[1]),
  564. is_complete=True,
  565. is_active=True
  566. )
  567. path7 = self.assertPathExists(
  568. (interfaces[6], cable5, rear_ports[0], front_ports[2], cable3, interfaces[2]),
  569. is_complete=True,
  570. is_active=True
  571. )
  572. path8 = self.assertPathExists(
  573. (interfaces[7], cable5, rear_ports[0], front_ports[3], cable4, interfaces[3]),
  574. is_complete=True,
  575. is_active=True
  576. )
  577. self.assertEqual(CablePath.objects.count(), 8)
  578. for interface in interfaces:
  579. interface.refresh_from_db()
  580. self.assertPathIsSet(interfaces[0], path1)
  581. self.assertPathIsSet(interfaces[1], path2)
  582. self.assertPathIsSet(interfaces[2], path3)
  583. self.assertPathIsSet(interfaces[3], path4)
  584. self.assertPathIsSet(interfaces[4], path5)
  585. self.assertPathIsSet(interfaces[5], path6)
  586. self.assertPathIsSet(interfaces[6], path7)
  587. self.assertPathIsSet(interfaces[7], path8)
  588. self.assertEqual(interfaces[4].cable_connector, 1)
  589. self.assertEqual(interfaces[4].cable_positions, [1])
  590. self.assertEqual(interfaces[5].cable_connector, 2)
  591. self.assertEqual(interfaces[5].cable_positions, [1])
  592. self.assertEqual(interfaces[6].cable_connector, 3)
  593. self.assertEqual(interfaces[6].cable_positions, [1])
  594. self.assertEqual(interfaces[7].cable_connector, 4)
  595. self.assertEqual(interfaces[7].cable_positions, [1])
  596. rear_ports[0].refresh_from_db()
  597. self.assertEqual(rear_ports[0].cable_connector, 1)
  598. self.assertEqual(rear_ports[0].cable_positions, [1, 2, 3, 4])
  599. # Test SVG generation
  600. CableTraceSVG(interfaces[0]).render()
  601. def test_106_cable_profile_shuffle(self):
  602. """
  603. [IF1] --C1-- [FP1][RP1] --C17-- [RP3][FP9] --C9-- [IF9]
  604. [IF2] --C2-- [FP2] [FP10] --C10-- [IF10]
  605. [IF3] --C3-- [FP3] [FP11] --C11-- [IF11]
  606. [IF4] --C4-- [FP4] [FP12] --C12-- [IF12]
  607. [IF5] --C5-- [FP5][RP2] [RP4][FP13] --C13-- [IF9]
  608. [IF6] --C6-- [FP6] [FP14] --C14-- [IF10]
  609. [IF7] --C7-- [FP7] [FP15] --C15-- [IF11]
  610. [IF8] --C8-- [FP8] [FP16] --C16-- [IF12]
  611. Cable profile: Shuffle (2x2 MPO8)
  612. """
  613. interfaces = [
  614. # A side
  615. Interface.objects.create(device=self.device, name='Interface 1:1'),
  616. Interface.objects.create(device=self.device, name='Interface 1:2'),
  617. Interface.objects.create(device=self.device, name='Interface 1:3'),
  618. Interface.objects.create(device=self.device, name='Interface 1:4'),
  619. Interface.objects.create(device=self.device, name='Interface 2:1'),
  620. Interface.objects.create(device=self.device, name='Interface 2:2'),
  621. Interface.objects.create(device=self.device, name='Interface 2:3'),
  622. Interface.objects.create(device=self.device, name='Interface 2:4'),
  623. # B side
  624. Interface.objects.create(device=self.device, name='Interface 3:1'),
  625. Interface.objects.create(device=self.device, name='Interface 3:2'),
  626. Interface.objects.create(device=self.device, name='Interface 3:3'),
  627. Interface.objects.create(device=self.device, name='Interface 3:4'),
  628. Interface.objects.create(device=self.device, name='Interface 4:1'),
  629. Interface.objects.create(device=self.device, name='Interface 4:2'),
  630. Interface.objects.create(device=self.device, name='Interface 4:3'),
  631. Interface.objects.create(device=self.device, name='Interface 4:4'),
  632. ]
  633. rear_ports = [
  634. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=4),
  635. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=4),
  636. RearPort.objects.create(device=self.device, name='Rear Port 3', positions=4),
  637. RearPort.objects.create(device=self.device, name='Rear Port 4', positions=4),
  638. ]
  639. front_ports = [
  640. FrontPort.objects.create(device=self.device, name='Front Port 1'),
  641. FrontPort.objects.create(device=self.device, name='Front Port 2'),
  642. FrontPort.objects.create(device=self.device, name='Front Port 3'),
  643. FrontPort.objects.create(device=self.device, name='Front Port 4'),
  644. FrontPort.objects.create(device=self.device, name='Front Port 5'),
  645. FrontPort.objects.create(device=self.device, name='Front Port 6'),
  646. FrontPort.objects.create(device=self.device, name='Front Port 7'),
  647. FrontPort.objects.create(device=self.device, name='Front Port 8'),
  648. FrontPort.objects.create(device=self.device, name='Front Port 9'),
  649. FrontPort.objects.create(device=self.device, name='Front Port 10'),
  650. FrontPort.objects.create(device=self.device, name='Front Port 11'),
  651. FrontPort.objects.create(device=self.device, name='Front Port 12'),
  652. FrontPort.objects.create(device=self.device, name='Front Port 13'),
  653. FrontPort.objects.create(device=self.device, name='Front Port 14'),
  654. FrontPort.objects.create(device=self.device, name='Front Port 15'),
  655. FrontPort.objects.create(device=self.device, name='Front Port 16'),
  656. ]
  657. port_mappings = []
  658. for i, front_port in enumerate(front_ports):
  659. port_mappings.append(
  660. PortMapping(
  661. device=self.device,
  662. front_port=front_ports[i],
  663. front_port_position=1,
  664. rear_port=rear_ports[int(i / 4)],
  665. rear_port_position=(i % 4) + 1,
  666. ),
  667. )
  668. PortMapping.objects.bulk_create(port_mappings)
  669. # Create cables
  670. cables = []
  671. for interface, front_port in zip(interfaces, front_ports):
  672. cable = Cable(a_terminations=[interface], b_terminations=[front_port])
  673. cable.clean()
  674. cable.save()
  675. cables.append(cable)
  676. shuffle_cable = Cable(
  677. profile=CableProfileChoices.TRUNK_2C4P_SHUFFLE,
  678. a_terminations=rear_ports[0:2],
  679. b_terminations=rear_ports[2:4],
  680. )
  681. shuffle_cable.clean()
  682. shuffle_cable.save()
  683. paths = [
  684. # A-to-B paths
  685. self.assertPathExists(
  686. (
  687. interfaces[0], cables[0], front_ports[0], rear_ports[0], shuffle_cable, rear_ports[2],
  688. front_ports[8], cables[8], interfaces[8],
  689. ),
  690. is_complete=True,
  691. is_active=True
  692. ),
  693. self.assertPathExists(
  694. (
  695. interfaces[1], cables[1], front_ports[1], rear_ports[0], shuffle_cable, rear_ports[2],
  696. front_ports[9], cables[9], interfaces[9],
  697. ),
  698. is_complete=True,
  699. is_active=True
  700. ),
  701. self.assertPathExists(
  702. (
  703. interfaces[2], cables[2], front_ports[2], rear_ports[0], shuffle_cable, rear_ports[3],
  704. front_ports[12], cables[12], interfaces[12],
  705. ),
  706. is_complete=True,
  707. is_active=True
  708. ),
  709. self.assertPathExists(
  710. (
  711. interfaces[3], cables[3], front_ports[3], rear_ports[0], shuffle_cable, rear_ports[3],
  712. front_ports[13], cables[13], interfaces[13],
  713. ),
  714. is_complete=True,
  715. is_active=True
  716. ),
  717. self.assertPathExists(
  718. (
  719. interfaces[4], cables[4], front_ports[4], rear_ports[1], shuffle_cable, rear_ports[2],
  720. front_ports[10], cables[10], interfaces[10],
  721. ),
  722. is_complete=True,
  723. is_active=True
  724. ),
  725. self.assertPathExists(
  726. (
  727. interfaces[5], cables[5], front_ports[5], rear_ports[1], shuffle_cable, rear_ports[2],
  728. front_ports[11], cables[11], interfaces[11],
  729. ),
  730. is_complete=True,
  731. is_active=True
  732. ),
  733. self.assertPathExists(
  734. (
  735. interfaces[6], cables[6], front_ports[6], rear_ports[1], shuffle_cable, rear_ports[3],
  736. front_ports[14], cables[14], interfaces[14],
  737. ),
  738. is_complete=True,
  739. is_active=True
  740. ),
  741. self.assertPathExists(
  742. (
  743. interfaces[7], cables[7], front_ports[7], rear_ports[1], shuffle_cable, rear_ports[3],
  744. front_ports[15], cables[15], interfaces[15],
  745. ),
  746. is_complete=True,
  747. is_active=True
  748. ),
  749. ]
  750. self.assertEqual(CablePath.objects.count(), len(paths) * 2)
  751. for i, (interface, path) in enumerate(zip(interfaces, paths)):
  752. interface.refresh_from_db()
  753. self.assertPathIsSet(interface, path)
  754. for i, rear_port in enumerate(rear_ports):
  755. rear_port.refresh_from_db()
  756. self.assertEqual(rear_port.cable_connector, (i % 2) + 1)
  757. self.assertEqual(rear_port.cable_positions, [1, 2, 3, 4])
  758. # Test SVG generation
  759. CableTraceSVG(interfaces[0]).render()
  760. def test_107_duplex_interface_profiled_patch_through_trunk_with_splices(self):
  761. """
  762. Tests that a duplex interface (cable_positions=[1,2]) traces both positions through
  763. profiled cables and splice pass-throughs, producing a single CablePath with both
  764. strands visible.
  765. [IF1] -C1(1C2P)- [FP1(p=2)][RP1(p=2)] -C2(1C2P)- [RP2(p=2)]
  766. [FP2] -C3- [FP4][RP3(p=2)] -C4(1C2P)- [RP4(p=2)][FP6(p=2)]
  767. -C5(1C2P)- [IF2] / [FP3] -C6- [FP5]
  768. Cable profiles: C1=1C2P, C2=1C2P, C3/C6=unprofiled splices, C4=1C2P, C5=1C2P
  769. """
  770. interfaces = [
  771. Interface.objects.create(device=self.device, name='Interface 1'),
  772. Interface.objects.create(device=self.device, name='Interface 2'),
  773. ]
  774. rear_ports = [
  775. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2),
  776. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2),
  777. RearPort.objects.create(device=self.device, name='Rear Port 3', positions=2),
  778. RearPort.objects.create(device=self.device, name='Rear Port 4', positions=2),
  779. ]
  780. front_ports = [
  781. FrontPort.objects.create(device=self.device, name='Front Port 1', positions=2), # Panel A duplex
  782. FrontPort.objects.create(device=self.device, name='Front Port 2'), # Splice A strand 1
  783. FrontPort.objects.create(device=self.device, name='Front Port 3'), # Splice A strand 2
  784. FrontPort.objects.create(device=self.device, name='Front Port 4'), # Splice B strand 1
  785. FrontPort.objects.create(device=self.device, name='Front Port 5'), # Splice B strand 2
  786. FrontPort.objects.create(device=self.device, name='Front Port 6', positions=2), # Panel B duplex
  787. ]
  788. PortMapping.objects.bulk_create([
  789. # Panel A: duplex FP1(pos=2) -> RP1(pos=2)
  790. PortMapping(
  791. device=self.device, front_port=front_ports[0], front_port_position=1,
  792. rear_port=rear_ports[0], rear_port_position=1,
  793. ),
  794. PortMapping(
  795. device=self.device, front_port=front_ports[0], front_port_position=2,
  796. rear_port=rear_ports[0], rear_port_position=2,
  797. ),
  798. # Splice A: FP2, FP3 -> RP2(pos=2)
  799. PortMapping(
  800. device=self.device, front_port=front_ports[1], front_port_position=1,
  801. rear_port=rear_ports[1], rear_port_position=1,
  802. ),
  803. PortMapping(
  804. device=self.device, front_port=front_ports[2], front_port_position=1,
  805. rear_port=rear_ports[1], rear_port_position=2,
  806. ),
  807. # Splice B: FP4, FP5 -> RP3(pos=2)
  808. PortMapping(
  809. device=self.device, front_port=front_ports[3], front_port_position=1,
  810. rear_port=rear_ports[2], rear_port_position=1,
  811. ),
  812. PortMapping(
  813. device=self.device, front_port=front_ports[4], front_port_position=1,
  814. rear_port=rear_ports[2], rear_port_position=2,
  815. ),
  816. # Panel B: duplex FP6(pos=2) -> RP4(pos=2)
  817. PortMapping(
  818. device=self.device, front_port=front_ports[5], front_port_position=1,
  819. rear_port=rear_ports[3], rear_port_position=1,
  820. ),
  821. PortMapping(
  822. device=self.device, front_port=front_ports[5], front_port_position=2,
  823. rear_port=rear_ports[3], rear_port_position=2,
  824. ),
  825. ])
  826. # Create cables
  827. cable1 = Cable(
  828. profile=CableProfileChoices.SINGLE_1C2P,
  829. a_terminations=[interfaces[0]],
  830. b_terminations=[front_ports[0]],
  831. )
  832. cable1.clean()
  833. cable1.save()
  834. cable2 = Cable(
  835. profile=CableProfileChoices.SINGLE_1C2P,
  836. a_terminations=[rear_ports[0]],
  837. b_terminations=[rear_ports[1]],
  838. )
  839. cable2.clean()
  840. cable2.save()
  841. cable3 = Cable(
  842. a_terminations=[front_ports[1]],
  843. b_terminations=[front_ports[3]],
  844. )
  845. cable3.clean()
  846. cable3.save()
  847. cable4 = Cable(
  848. profile=CableProfileChoices.SINGLE_1C2P,
  849. a_terminations=[rear_ports[2]],
  850. b_terminations=[rear_ports[3]],
  851. )
  852. cable4.clean()
  853. cable4.save()
  854. cable5 = Cable(
  855. profile=CableProfileChoices.SINGLE_1C2P,
  856. a_terminations=[front_ports[5]],
  857. b_terminations=[interfaces[1]],
  858. )
  859. cable5.clean()
  860. cable5.save()
  861. cable6 = Cable(
  862. a_terminations=[front_ports[2]],
  863. b_terminations=[front_ports[4]],
  864. )
  865. cable6.clean()
  866. cable6.save()
  867. # Verify forward path: IF1 -> IF2 (both strands through splice)
  868. self.assertPathExists(
  869. (
  870. interfaces[0], cable1, front_ports[0],
  871. rear_ports[0], cable2, rear_ports[1],
  872. [front_ports[1], front_ports[2]], [cable3, cable6], [front_ports[3], front_ports[4]],
  873. rear_ports[2], cable4, rear_ports[3],
  874. front_ports[5], cable5, interfaces[1],
  875. ),
  876. is_complete=True,
  877. is_active=True
  878. )
  879. # Verify reverse path: IF2 -> IF1
  880. self.assertPathExists(
  881. (
  882. interfaces[1], cable5, front_ports[5],
  883. rear_ports[3], cable4, rear_ports[2],
  884. [front_ports[3], front_ports[4]], [cable3, cable6], [front_ports[1], front_ports[2]],
  885. rear_ports[1], cable2, rear_ports[0],
  886. front_ports[0], cable1, interfaces[0],
  887. ),
  888. is_complete=True,
  889. is_active=True
  890. )
  891. self.assertEqual(CablePath.objects.count(), 2)
  892. # Verify cable positions on interfaces
  893. for iface in interfaces:
  894. iface.refresh_from_db()
  895. self.assertEqual(interfaces[0].cable_connector, 1)
  896. self.assertEqual(interfaces[0].cable_positions, [1, 2])
  897. self.assertEqual(interfaces[1].cable_connector, 1)
  898. self.assertEqual(interfaces[1].cable_positions, [1, 2])
  899. # Test SVG generation
  900. CableTraceSVG(interfaces[0]).render()
  901. def test_108_single_interface_two_frontports_unprofiled_through_trunk_with_splices(self):
  902. """
  903. Tests that positions seeded by PortMapping (not cable_positions) are preserved
  904. when crossing profiled cables.
  905. [IF1] -C1- [FP1,FP2][RP1(p=2)] -C2(1C2P)- [RP2(p=2)]
  906. [FP3] -C3- [FP5][RP3(p=2)] -C4(1C2P)- [RP4(p=2)]
  907. [FP7,FP8] -C5- [IF2] / [FP4] -C6- [FP6]
  908. PortMappings: FP1->RP1p1, FP2->RP1p2, FP3->RP2p1, FP4->RP2p2,
  909. FP5->RP3p1, FP6->RP3p2, FP7->RP4p1, FP8->RP4p2
  910. C1 is unprofiled (1 IF -> 2 FPs), C2/C4 are 1C2P trunks,
  911. C3/C6 are unprofiled splices, C5 is unprofiled (2 FPs -> 1 IF).
  912. """
  913. interfaces = [
  914. Interface.objects.create(device=self.device, name='Interface 1'),
  915. Interface.objects.create(device=self.device, name='Interface 2'),
  916. ]
  917. rear_ports = [
  918. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2),
  919. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2),
  920. RearPort.objects.create(device=self.device, name='Rear Port 3', positions=2),
  921. RearPort.objects.create(device=self.device, name='Rear Port 4', positions=2),
  922. ]
  923. front_ports = [
  924. FrontPort.objects.create(device=self.device, name='Front Port 1'), # Panel A strand 1
  925. FrontPort.objects.create(device=self.device, name='Front Port 2'), # Panel A strand 2
  926. FrontPort.objects.create(device=self.device, name='Front Port 3'), # Splice A strand 1
  927. FrontPort.objects.create(device=self.device, name='Front Port 4'), # Splice A strand 2
  928. FrontPort.objects.create(device=self.device, name='Front Port 5'), # Splice B strand 1
  929. FrontPort.objects.create(device=self.device, name='Front Port 6'), # Splice B strand 2
  930. FrontPort.objects.create(device=self.device, name='Front Port 7'), # Panel B strand 1
  931. FrontPort.objects.create(device=self.device, name='Front Port 8'), # Panel B strand 2
  932. ]
  933. PortMapping.objects.bulk_create([
  934. # Panel A: FP1, FP2 -> RP1(pos=2)
  935. PortMapping(
  936. device=self.device, front_port=front_ports[0], front_port_position=1,
  937. rear_port=rear_ports[0], rear_port_position=1,
  938. ),
  939. PortMapping(
  940. device=self.device, front_port=front_ports[1], front_port_position=1,
  941. rear_port=rear_ports[0], rear_port_position=2,
  942. ),
  943. # Splice A: FP3, FP4 -> RP2(pos=2)
  944. PortMapping(
  945. device=self.device, front_port=front_ports[2], front_port_position=1,
  946. rear_port=rear_ports[1], rear_port_position=1,
  947. ),
  948. PortMapping(
  949. device=self.device, front_port=front_ports[3], front_port_position=1,
  950. rear_port=rear_ports[1], rear_port_position=2,
  951. ),
  952. # Splice B: FP5, FP6 -> RP3(pos=2)
  953. PortMapping(
  954. device=self.device, front_port=front_ports[4], front_port_position=1,
  955. rear_port=rear_ports[2], rear_port_position=1,
  956. ),
  957. PortMapping(
  958. device=self.device, front_port=front_ports[5], front_port_position=1,
  959. rear_port=rear_ports[2], rear_port_position=2,
  960. ),
  961. # Panel B: FP7, FP8 -> RP4(pos=2)
  962. PortMapping(
  963. device=self.device, front_port=front_ports[6], front_port_position=1,
  964. rear_port=rear_ports[3], rear_port_position=1,
  965. ),
  966. PortMapping(
  967. device=self.device, front_port=front_ports[7], front_port_position=1,
  968. rear_port=rear_ports[3], rear_port_position=2,
  969. ),
  970. ])
  971. # Create cables
  972. cable1 = Cable(
  973. a_terminations=[interfaces[0]],
  974. b_terminations=[front_ports[0], front_ports[1]],
  975. )
  976. cable1.clean()
  977. cable1.save()
  978. cable2 = Cable(
  979. profile=CableProfileChoices.SINGLE_1C2P,
  980. a_terminations=[rear_ports[0]],
  981. b_terminations=[rear_ports[1]],
  982. )
  983. cable2.clean()
  984. cable2.save()
  985. cable3 = Cable(
  986. a_terminations=[front_ports[2]],
  987. b_terminations=[front_ports[4]],
  988. )
  989. cable3.clean()
  990. cable3.save()
  991. cable4 = Cable(
  992. profile=CableProfileChoices.SINGLE_1C2P,
  993. a_terminations=[rear_ports[2]],
  994. b_terminations=[rear_ports[3]],
  995. )
  996. cable4.clean()
  997. cable4.save()
  998. cable5 = Cable(
  999. a_terminations=[front_ports[6], front_ports[7]],
  1000. b_terminations=[interfaces[1]],
  1001. )
  1002. cable5.clean()
  1003. cable5.save()
  1004. cable6 = Cable(
  1005. a_terminations=[front_ports[3]],
  1006. b_terminations=[front_ports[5]],
  1007. )
  1008. cable6.clean()
  1009. cable6.save()
  1010. # Verify forward path: IF1 -> IF2 (both strands through splice)
  1011. self.assertPathExists(
  1012. (
  1013. interfaces[0], cable1, [front_ports[0], front_ports[1]],
  1014. rear_ports[0], cable2, rear_ports[1],
  1015. [front_ports[2], front_ports[3]], [cable3, cable6], [front_ports[4], front_ports[5]],
  1016. rear_ports[2], cable4, rear_ports[3],
  1017. [front_ports[6], front_ports[7]], cable5, interfaces[1],
  1018. ),
  1019. is_complete=True,
  1020. is_active=True
  1021. )
  1022. # Verify reverse path: IF2 -> IF1
  1023. self.assertPathExists(
  1024. (
  1025. interfaces[1], cable5, [front_ports[6], front_ports[7]],
  1026. rear_ports[3], cable4, rear_ports[2],
  1027. [front_ports[4], front_ports[5]], [cable3, cable6], [front_ports[2], front_ports[3]],
  1028. rear_ports[1], cable2, rear_ports[0],
  1029. [front_ports[0], front_ports[1]], cable1, interfaces[0],
  1030. ),
  1031. is_complete=True,
  1032. is_active=True
  1033. )
  1034. self.assertEqual(CablePath.objects.count(), 2)
  1035. # Verify cable positions are not set (unprofiled patch cables)
  1036. for iface in interfaces:
  1037. iface.refresh_from_db()
  1038. self.assertIsNone(interfaces[0].cable_connector)
  1039. self.assertIsNone(interfaces[0].cable_positions)
  1040. self.assertIsNone(interfaces[1].cable_connector)
  1041. self.assertIsNone(interfaces[1].cable_positions)
  1042. def test_109_multiconnector_trunk_through_patch_panel(self):
  1043. """
  1044. Tests that a 4-position interface traces correctly through a patch panel
  1045. that fans out to both connectors of a Trunk2C2P cable.
  1046. [IF1] --C1(1C4P)-- [FP1(p=4)][RP1(p=2)] --C3(Trunk2C2P)-- [RP3(p=2)][FP5(p=4)] --C5(1C4P)-- [IF2]
  1047. [RP2(p=2)] [RP4(p=2)]
  1048. PortMappings (Panel A): FP1p1->RP1p1, FP1p2->RP1p2, FP1p3->RP2p1, FP1p4->RP2p2
  1049. PortMappings (Panel B): FP5p1->RP3p1, FP5p2->RP3p2, FP5p3->RP4p1, FP5p4->RP4p2
  1050. """
  1051. interfaces = [
  1052. Interface.objects.create(device=self.device, name='Interface 1'),
  1053. Interface.objects.create(device=self.device, name='Interface 2'),
  1054. ]
  1055. rear_ports = [
  1056. RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2),
  1057. RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2),
  1058. RearPort.objects.create(device=self.device, name='Rear Port 3', positions=2),
  1059. RearPort.objects.create(device=self.device, name='Rear Port 4', positions=2),
  1060. ]
  1061. front_ports = [
  1062. FrontPort.objects.create(device=self.device, name='Front Port 1', positions=4),
  1063. FrontPort.objects.create(device=self.device, name='Front Port 5', positions=4),
  1064. ]
  1065. PortMapping.objects.bulk_create([
  1066. # Panel A: FP1(p=4) -> RP1(p=2) and RP2(p=2)
  1067. PortMapping(
  1068. device=self.device, front_port=front_ports[0], front_port_position=1,
  1069. rear_port=rear_ports[0], rear_port_position=1,
  1070. ),
  1071. PortMapping(
  1072. device=self.device, front_port=front_ports[0], front_port_position=2,
  1073. rear_port=rear_ports[0], rear_port_position=2,
  1074. ),
  1075. PortMapping(
  1076. device=self.device, front_port=front_ports[0], front_port_position=3,
  1077. rear_port=rear_ports[1], rear_port_position=1,
  1078. ),
  1079. PortMapping(
  1080. device=self.device, front_port=front_ports[0], front_port_position=4,
  1081. rear_port=rear_ports[1], rear_port_position=2,
  1082. ),
  1083. # Panel B: FP5(p=4) -> RP3(p=2) and RP4(p=2)
  1084. PortMapping(
  1085. device=self.device, front_port=front_ports[1], front_port_position=1,
  1086. rear_port=rear_ports[2], rear_port_position=1,
  1087. ),
  1088. PortMapping(
  1089. device=self.device, front_port=front_ports[1], front_port_position=2,
  1090. rear_port=rear_ports[2], rear_port_position=2,
  1091. ),
  1092. PortMapping(
  1093. device=self.device, front_port=front_ports[1], front_port_position=3,
  1094. rear_port=rear_ports[3], rear_port_position=1,
  1095. ),
  1096. PortMapping(
  1097. device=self.device, front_port=front_ports[1], front_port_position=4,
  1098. rear_port=rear_ports[3], rear_port_position=2,
  1099. ),
  1100. ])
  1101. # Create cables
  1102. cable1 = Cable(
  1103. profile=CableProfileChoices.SINGLE_1C4P,
  1104. a_terminations=[interfaces[0]],
  1105. b_terminations=[front_ports[0]],
  1106. )
  1107. cable1.clean()
  1108. cable1.save()
  1109. cable3 = Cable(
  1110. profile=CableProfileChoices.TRUNK_2C2P,
  1111. a_terminations=[rear_ports[0], rear_ports[1]],
  1112. b_terminations=[rear_ports[2], rear_ports[3]],
  1113. )
  1114. cable3.clean()
  1115. cable3.save()
  1116. cable5 = Cable(
  1117. profile=CableProfileChoices.SINGLE_1C4P,
  1118. a_terminations=[front_ports[1]],
  1119. b_terminations=[interfaces[1]],
  1120. )
  1121. cable5.clean()
  1122. cable5.save()
  1123. # Verify forward path: IF1 -> IF2 (all 4 positions through trunk)
  1124. self.assertPathExists(
  1125. (
  1126. interfaces[0], cable1, front_ports[0],
  1127. [rear_ports[0], rear_ports[1]], cable3, [rear_ports[2], rear_ports[3]],
  1128. front_ports[1], cable5, interfaces[1],
  1129. ),
  1130. is_complete=True,
  1131. is_active=True
  1132. )
  1133. # Verify reverse path: IF2 -> IF1
  1134. self.assertPathExists(
  1135. (
  1136. interfaces[1], cable5, front_ports[1],
  1137. [rear_ports[2], rear_ports[3]], cable3, [rear_ports[0], rear_ports[1]],
  1138. front_ports[0], cable1, interfaces[0],
  1139. ),
  1140. is_complete=True,
  1141. is_active=True
  1142. )
  1143. self.assertEqual(CablePath.objects.count(), 2)
  1144. # Verify cable positions
  1145. for iface in interfaces:
  1146. iface.refresh_from_db()
  1147. self.assertEqual(interfaces[0].cable_connector, 1)
  1148. self.assertEqual(interfaces[0].cable_positions, [1, 2, 3, 4])
  1149. self.assertEqual(interfaces[1].cable_connector, 1)
  1150. self.assertEqual(interfaces[1].cable_positions, [1, 2, 3, 4])
  1151. # Verify rear port connector assignments
  1152. for rp in rear_ports:
  1153. rp.refresh_from_db()
  1154. self.assertEqual(rear_ports[0].cable_connector, 1)
  1155. self.assertEqual(rear_ports[0].cable_positions, [1, 2])
  1156. self.assertEqual(rear_ports[1].cable_connector, 2)
  1157. self.assertEqual(rear_ports[1].cable_positions, [1, 2])
  1158. self.assertEqual(rear_ports[2].cable_connector, 1)
  1159. self.assertEqual(rear_ports[2].cable_positions, [1, 2])
  1160. self.assertEqual(rear_ports[3].cable_connector, 2)
  1161. self.assertEqual(rear_ports[3].cable_positions, [1, 2])
  1162. # Test SVG generation
  1163. CableTraceSVG(interfaces[0]).render()
  1164. def test_110_partial_termination_profiled_trunk(self):
  1165. """
  1166. Tests that tracing through a partially terminated profiled cable
  1167. produces a complete path for the connected pair and an incomplete
  1168. path for the unconnected pair, without errors. Also verifies that
  1169. attaching the missing termination completes the previously incomplete path.
  1170. [IF1] --C1-- [IF3]
  1171. [IF2] (empty)
  1172. Cable profile: Trunk 2C1P with only one B-side termination.
  1173. """
  1174. interfaces = [
  1175. Interface.objects.create(device=self.device, name='Interface 1'),
  1176. Interface.objects.create(device=self.device, name='Interface 2'),
  1177. Interface.objects.create(device=self.device, name='Interface 3'),
  1178. ]
  1179. # Create a 2-connector trunk cable with both A-side connectors
  1180. # populated but only one B-side connector terminated.
  1181. cable1 = Cable(
  1182. profile=CableProfileChoices.TRUNK_2C1P,
  1183. a_terminations=[interfaces[0], interfaces[1]],
  1184. b_terminations=[interfaces[2]],
  1185. )
  1186. cable1.clean()
  1187. cable1.save()
  1188. # IF1 (connector 1) → IF3 (connector 1): complete path
  1189. path1 = self.assertPathExists(
  1190. (interfaces[0], cable1, interfaces[2]),
  1191. is_complete=True,
  1192. is_active=True,
  1193. )
  1194. # IF3 (connector 1) → IF1 (connector 1): complete path (reverse)
  1195. path2 = self.assertPathExists(
  1196. (interfaces[2], cable1, interfaces[0]),
  1197. is_complete=True,
  1198. is_active=True,
  1199. )
  1200. # IF2 (connector 2) has no B-side peer.
  1201. # Tracing should stop at this segment, and the resulting path
  1202. # should remain incomplete.
  1203. # Verify via the origin's _path reference rather than matching
  1204. # the exact path shape directly.
  1205. interfaces[1].refresh_from_db()
  1206. self.assertIsNotNone(interfaces[1]._path_id)
  1207. path3 = CablePath.objects.get(pk=interfaces[1]._path_id)
  1208. self.assertFalse(path3.is_complete)
  1209. self.assertTrue(path3.is_active)
  1210. for iface in interfaces:
  1211. iface.refresh_from_db()
  1212. self.assertPathIsSet(interfaces[0], path1)
  1213. self.assertPathIsSet(interfaces[2], path2)
  1214. self.assertPathIsSet(interfaces[1], path3)
  1215. # Verify connector/position assignments
  1216. self.assertEqual(interfaces[0].cable_connector, 1)
  1217. self.assertEqual(interfaces[0].cable_positions, [1])
  1218. self.assertEqual(interfaces[1].cable_connector, 2)
  1219. self.assertEqual(interfaces[1].cable_positions, [1])
  1220. self.assertEqual(interfaces[2].cable_connector, 1)
  1221. self.assertEqual(interfaces[2].cable_positions, [1])
  1222. # Now attach the missing B-side termination and verify the
  1223. # previously incomplete path becomes complete.
  1224. interface4 = Interface.objects.create(device=self.device, name='Interface 4')
  1225. cable1.b_terminations = [interfaces[2], interface4]
  1226. cable1.clean()
  1227. cable1.save()
  1228. path4 = self.assertPathExists(
  1229. (interfaces[1], cable1, interface4),
  1230. is_complete=True,
  1231. is_active=True,
  1232. )
  1233. path5 = self.assertPathExists(
  1234. (interface4, cable1, interfaces[1]),
  1235. is_complete=True,
  1236. is_active=True,
  1237. )
  1238. interfaces[1].refresh_from_db()
  1239. interface4.refresh_from_db()
  1240. self.assertPathIsSet(interfaces[1], path4)
  1241. self.assertPathIsSet(interface4, path5)
  1242. self.assertEqual(interface4.cable_connector, 2)
  1243. self.assertEqual(interface4.cable_positions, [1])
  1244. def test_202_single_path_via_pass_through_with_breakouts(self):
  1245. """
  1246. [IF1] --C1-- [FP1] [RP1] --C2-- [IF3]
  1247. [IF2] [IF4]
  1248. """
  1249. interfaces = [
  1250. Interface.objects.create(device=self.device, name='Interface 1'),
  1251. Interface.objects.create(device=self.device, name='Interface 2'),
  1252. Interface.objects.create(device=self.device, name='Interface 3'),
  1253. Interface.objects.create(device=self.device, name='Interface 4'),
  1254. ]
  1255. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1', positions=4)
  1256. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1', positions=4)
  1257. PortMapping.objects.bulk_create([
  1258. PortMapping(
  1259. device=self.device,
  1260. front_port=frontport1,
  1261. front_port_position=1,
  1262. rear_port=rearport1,
  1263. rear_port_position=1,
  1264. ),
  1265. PortMapping(
  1266. device=self.device,
  1267. front_port=frontport1,
  1268. front_port_position=2,
  1269. rear_port=rearport1,
  1270. rear_port_position=2,
  1271. ),
  1272. PortMapping(
  1273. device=self.device,
  1274. front_port=frontport1,
  1275. front_port_position=3,
  1276. rear_port=rearport1,
  1277. rear_port_position=3,
  1278. ),
  1279. PortMapping(
  1280. device=self.device,
  1281. front_port=frontport1,
  1282. front_port_position=4,
  1283. rear_port=rearport1,
  1284. rear_port_position=4,
  1285. ),
  1286. ])
  1287. # Create cables
  1288. cable1 = Cable(
  1289. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1290. a_terminations=[frontport1],
  1291. b_terminations=[interfaces[0], interfaces[1]],
  1292. )
  1293. cable1.clean()
  1294. cable1.save()
  1295. cable2 = Cable(
  1296. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1297. a_terminations=[rearport1],
  1298. b_terminations=[interfaces[2], interfaces[3]]
  1299. )
  1300. cable2.clean()
  1301. cable2.save()
  1302. paths = [
  1303. self.assertPathExists(
  1304. (interfaces[0], cable1, frontport1, rearport1, cable2, interfaces[2]),
  1305. is_complete=True,
  1306. is_active=True
  1307. ),
  1308. self.assertPathExists(
  1309. (interfaces[1], cable1, frontport1, rearport1, cable2, interfaces[3]),
  1310. is_complete=True,
  1311. is_active=True
  1312. ),
  1313. self.assertPathExists(
  1314. (interfaces[2], cable2, rearport1, frontport1, cable1, interfaces[0]),
  1315. is_complete=True,
  1316. is_active=True
  1317. ),
  1318. self.assertPathExists(
  1319. (interfaces[3], cable2, rearport1, frontport1, cable1, interfaces[1]),
  1320. is_complete=True,
  1321. is_active=True
  1322. ),
  1323. ]
  1324. self.assertEqual(CablePath.objects.count(), 4)
  1325. for interface in interfaces:
  1326. interface.refresh_from_db()
  1327. self.assertPathIsSet(interfaces[0], paths[0])
  1328. self.assertPathIsSet(interfaces[1], paths[1])
  1329. self.assertPathIsSet(interfaces[2], paths[2])
  1330. self.assertPathIsSet(interfaces[3], paths[3])
  1331. # Test SVG generation
  1332. CableTraceSVG(interfaces[0]).render()
  1333. def test_204_multiple_paths_via_pass_through_with_breakouts(self):
  1334. """
  1335. [IF1] --C1-- [FP1] [RP1] --C3-- [RP2] [FP3] --C4-- [IF5]
  1336. [IF2] [IF6]
  1337. [IF3] --C2-- [FP2] [FP4] --C5-- [IF7]
  1338. [IF4] [IF8]
  1339. """
  1340. interfaces = [
  1341. Interface.objects.create(device=self.device, name='Interface 1'),
  1342. Interface.objects.create(device=self.device, name='Interface 2'),
  1343. Interface.objects.create(device=self.device, name='Interface 3'),
  1344. Interface.objects.create(device=self.device, name='Interface 4'),
  1345. Interface.objects.create(device=self.device, name='Interface 5'),
  1346. Interface.objects.create(device=self.device, name='Interface 6'),
  1347. Interface.objects.create(device=self.device, name='Interface 7'),
  1348. Interface.objects.create(device=self.device, name='Interface 8'),
  1349. ]
  1350. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1', positions=8)
  1351. rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2', positions=8)
  1352. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1:1', positions=4)
  1353. frontport2 = FrontPort.objects.create(device=self.device, name='Front Port 1:2', positions=4)
  1354. frontport3 = FrontPort.objects.create(device=self.device, name='Front Port 2:1', positions=4)
  1355. frontport4 = FrontPort.objects.create(device=self.device, name='Front Port 2:2', positions=4)
  1356. PortMapping.objects.bulk_create([
  1357. PortMapping(
  1358. device=self.device,
  1359. front_port=frontport1,
  1360. front_port_position=1,
  1361. rear_port=rearport1,
  1362. rear_port_position=1,
  1363. ),
  1364. PortMapping(
  1365. device=self.device,
  1366. front_port=frontport1,
  1367. front_port_position=2,
  1368. rear_port=rearport1,
  1369. rear_port_position=2,
  1370. ),
  1371. PortMapping(
  1372. device=self.device,
  1373. front_port=frontport2,
  1374. front_port_position=1,
  1375. rear_port=rearport1,
  1376. rear_port_position=5,
  1377. ),
  1378. PortMapping(
  1379. device=self.device,
  1380. front_port=frontport2,
  1381. front_port_position=2,
  1382. rear_port=rearport1,
  1383. rear_port_position=6,
  1384. ),
  1385. PortMapping(
  1386. device=self.device,
  1387. front_port=frontport3,
  1388. front_port_position=1,
  1389. rear_port=rearport2,
  1390. rear_port_position=1,
  1391. ),
  1392. PortMapping(
  1393. device=self.device,
  1394. front_port=frontport3,
  1395. front_port_position=2,
  1396. rear_port=rearport2,
  1397. rear_port_position=2,
  1398. ),
  1399. PortMapping(
  1400. device=self.device,
  1401. front_port=frontport4,
  1402. front_port_position=1,
  1403. rear_port=rearport2,
  1404. rear_port_position=5,
  1405. ),
  1406. PortMapping(
  1407. device=self.device,
  1408. front_port=frontport4,
  1409. front_port_position=2,
  1410. rear_port=rearport2,
  1411. rear_port_position=6,
  1412. ),
  1413. ])
  1414. # Create cables
  1415. cable1 = Cable(
  1416. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1417. a_terminations=[frontport1],
  1418. b_terminations=[interfaces[0], interfaces[1]],
  1419. )
  1420. cable1.clean()
  1421. cable1.save()
  1422. cable2 = Cable(
  1423. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1424. a_terminations=[frontport2],
  1425. b_terminations=[interfaces[2], interfaces[3]],
  1426. )
  1427. cable2.clean()
  1428. cable2.save()
  1429. cable3 = Cable(
  1430. profile=CableProfileChoices.SINGLE_1C8P,
  1431. a_terminations=[rearport1],
  1432. b_terminations=[rearport2]
  1433. )
  1434. cable3.clean()
  1435. cable3.save()
  1436. cable4 = Cable(
  1437. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1438. a_terminations=[frontport3],
  1439. b_terminations=[interfaces[4], interfaces[5]],
  1440. )
  1441. cable4.clean()
  1442. cable4.save()
  1443. cable5 = Cable(
  1444. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1445. a_terminations=[frontport4],
  1446. b_terminations=[interfaces[6], interfaces[7]],
  1447. )
  1448. cable5.clean()
  1449. cable5.save()
  1450. paths = [
  1451. self.assertPathExists(
  1452. (
  1453. interfaces[0], cable1, frontport1, rearport1, cable3, rearport2, frontport3, cable4,
  1454. interfaces[4],
  1455. ),
  1456. is_complete=True,
  1457. is_active=True,
  1458. ),
  1459. self.assertPathExists(
  1460. (
  1461. interfaces[1], cable1, frontport1, rearport1, cable3, rearport2, frontport3, cable4,
  1462. interfaces[5],
  1463. ),
  1464. is_complete=True,
  1465. is_active=True,
  1466. ),
  1467. self.assertPathExists(
  1468. (
  1469. interfaces[2], cable2, frontport2, rearport1, cable3, rearport2, frontport4, cable5,
  1470. interfaces[6],
  1471. ),
  1472. is_complete=True,
  1473. is_active=True,
  1474. ),
  1475. self.assertPathExists(
  1476. (
  1477. interfaces[3], cable2, frontport2, rearport1, cable3, rearport2, frontport4, cable5,
  1478. interfaces[7],
  1479. ),
  1480. is_complete=True,
  1481. is_active=True,
  1482. ),
  1483. self.assertPathExists(
  1484. (
  1485. interfaces[4], cable4, frontport3, rearport2, cable3, rearport1, frontport1, cable1,
  1486. interfaces[0],
  1487. ),
  1488. is_complete=True,
  1489. is_active=True,
  1490. ),
  1491. self.assertPathExists(
  1492. (
  1493. interfaces[5], cable4, frontport3, rearport2, cable3, rearport1, frontport1, cable1,
  1494. interfaces[1],
  1495. ),
  1496. is_complete=True,
  1497. is_active=True,
  1498. ),
  1499. self.assertPathExists(
  1500. (
  1501. interfaces[6], cable5, frontport4, rearport2, cable3, rearport1, frontport2, cable2,
  1502. interfaces[2],
  1503. ),
  1504. is_complete=True,
  1505. is_active=True,
  1506. ),
  1507. self.assertPathExists(
  1508. (
  1509. interfaces[7], cable5, frontport4, rearport2, cable3, rearport1, frontport2, cable2,
  1510. interfaces[3],
  1511. ),
  1512. is_complete=True,
  1513. is_active=True,
  1514. ),
  1515. ]
  1516. self.assertEqual(CablePath.objects.count(), 8)
  1517. for interface in interfaces:
  1518. interface.refresh_from_db()
  1519. self.assertPathIsSet(interfaces[0], paths[0])
  1520. self.assertPathIsSet(interfaces[1], paths[1])
  1521. self.assertPathIsSet(interfaces[2], paths[2])
  1522. self.assertPathIsSet(interfaces[3], paths[3])
  1523. self.assertPathIsSet(interfaces[4], paths[4])
  1524. self.assertPathIsSet(interfaces[5], paths[5])
  1525. self.assertPathIsSet(interfaces[6], paths[6])
  1526. self.assertPathIsSet(interfaces[7], paths[7])
  1527. # Test SVG generation
  1528. CableTraceSVG(interfaces[0]).render()
  1529. def test_212_interface_to_interface_via_circuit_with_breakouts(self):
  1530. """
  1531. [IF1] --C1-- [CT1] [CT2] --C2-- [IF3]
  1532. [IF2] [IF4]
  1533. """
  1534. interfaces = [
  1535. Interface.objects.create(device=self.device, name='Interface 1'),
  1536. Interface.objects.create(device=self.device, name='Interface 2'),
  1537. Interface.objects.create(device=self.device, name='Interface 3'),
  1538. Interface.objects.create(device=self.device, name='Interface 4'),
  1539. ]
  1540. circuittermination1 = CircuitTermination.objects.create(
  1541. circuit=self.circuit,
  1542. termination=self.site,
  1543. term_side='A'
  1544. )
  1545. circuittermination2 = CircuitTermination.objects.create(
  1546. circuit=self.circuit,
  1547. termination=self.site,
  1548. term_side='Z'
  1549. )
  1550. # Create cables
  1551. cable1 = Cable(
  1552. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1553. a_terminations=[circuittermination1],
  1554. b_terminations=[interfaces[0], interfaces[1]],
  1555. )
  1556. cable1.clean()
  1557. cable1.save()
  1558. cable2 = Cable(
  1559. profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
  1560. a_terminations=[circuittermination2],
  1561. b_terminations=[interfaces[2], interfaces[3]]
  1562. )
  1563. cable2.clean()
  1564. cable2.save()
  1565. # Check for two complete paths in either direction
  1566. paths = [
  1567. self.assertPathExists(
  1568. (interfaces[0], cable1, circuittermination1, circuittermination2, cable2, interfaces[2]),
  1569. is_complete=True,
  1570. is_active=True,
  1571. ),
  1572. self.assertPathExists(
  1573. (interfaces[1], cable1, circuittermination1, circuittermination2, cable2, interfaces[3]),
  1574. is_complete=True,
  1575. is_active=True,
  1576. ),
  1577. self.assertPathExists(
  1578. (interfaces[2], cable2, circuittermination2, circuittermination1, cable1, interfaces[0]),
  1579. is_complete=True,
  1580. is_active=True,
  1581. ),
  1582. self.assertPathExists(
  1583. (interfaces[3], cable2, circuittermination2, circuittermination1, cable1, interfaces[1]),
  1584. is_complete=True,
  1585. is_active=True,
  1586. ),
  1587. ]
  1588. self.assertEqual(CablePath.objects.count(), 4)
  1589. for interface in interfaces:
  1590. interface.refresh_from_db()
  1591. self.assertPathIsSet(interfaces[0], paths[0])
  1592. self.assertPathIsSet(interfaces[1], paths[1])
  1593. self.assertPathIsSet(interfaces[2], paths[2])
  1594. self.assertPathIsSet(interfaces[3], paths[3])
  1595. # Test SVG generation
  1596. CableTraceSVG(interfaces[0]).render()
  1597. # TBD: Is this a topology we want to support?
  1598. @skip("Test applicability TBD")
  1599. def test_217_interface_to_interface_via_rear_ports(self):
  1600. """
  1601. [IF1] --C1-- [FP1] [RP1] --C2-- [RP3] [FP3] --C3-- [IF2]
  1602. [FP2] [RP2] [RP4] [FP4]
  1603. """
  1604. interfaces = [
  1605. Interface.objects.create(device=self.device, name='Interface 1'),
  1606. Interface.objects.create(device=self.device, name='Interface 2'),
  1607. ]
  1608. rear_ports = [
  1609. RearPort.objects.create(device=self.device, name='Rear Port 1'),
  1610. RearPort.objects.create(device=self.device, name='Rear Port 2'),
  1611. RearPort.objects.create(device=self.device, name='Rear Port 3'),
  1612. RearPort.objects.create(device=self.device, name='Rear Port 4'),
  1613. ]
  1614. front_ports = [
  1615. FrontPort.objects.create(device=self.device, name='Front Port 1'),
  1616. FrontPort.objects.create(device=self.device, name='Front Port 2'),
  1617. FrontPort.objects.create(device=self.device, name='Front Port 3'),
  1618. FrontPort.objects.create(device=self.device, name='Front Port 4'),
  1619. ]
  1620. PortMapping.objects.bulk_create([
  1621. PortMapping(
  1622. device=self.device,
  1623. front_port=front_ports[0],
  1624. front_port_position=1,
  1625. rear_port=rear_ports[0],
  1626. rear_port_position=1,
  1627. ),
  1628. PortMapping(
  1629. device=self.device,
  1630. front_port=front_ports[1],
  1631. front_port_position=1,
  1632. rear_port=rear_ports[1],
  1633. rear_port_position=1,
  1634. ),
  1635. PortMapping(
  1636. device=self.device,
  1637. front_port=front_ports[2],
  1638. front_port_position=1,
  1639. rear_port=rear_ports[2],
  1640. rear_port_position=1,
  1641. ),
  1642. PortMapping(
  1643. device=self.device,
  1644. front_port=front_ports[3],
  1645. front_port_position=1,
  1646. rear_port=rear_ports[3],
  1647. rear_port_position=1,
  1648. ),
  1649. ])
  1650. # Create cables
  1651. cable1 = Cable(
  1652. profile=CableProfileChoices.SINGLE_2C1P,
  1653. a_terminations=[interfaces[0]],
  1654. b_terminations=[front_ports[0], front_ports[1]]
  1655. )
  1656. cable1.clean()
  1657. cable1.save()
  1658. cable2 = Cable(
  1659. a_terminations=[rear_ports[0], rear_ports[1]],
  1660. b_terminations=[rear_ports[2], rear_ports[3]]
  1661. )
  1662. cable2.clean()
  1663. cable2.save()
  1664. cable3 = Cable(
  1665. profile=CableProfileChoices.SINGLE_2C1P,
  1666. a_terminations=[interfaces[1]],
  1667. b_terminations=[front_ports[2], front_ports[3]]
  1668. )
  1669. cable3.clean()
  1670. cable3.save()
  1671. # Check for one complete path in either direction
  1672. paths = [
  1673. self.assertPathExists(
  1674. (
  1675. interfaces[0], cable1, (front_ports[0], front_ports[1]), (rear_ports[0], rear_ports[1]), cable2,
  1676. (rear_ports[2], rear_ports[3]), (front_ports[2], front_ports[3]), cable3, interfaces[1]
  1677. ),
  1678. is_complete=True
  1679. ),
  1680. self.assertPathExists(
  1681. (
  1682. interfaces[1], cable3, (front_ports[2], front_ports[3]), (rear_ports[2], rear_ports[3]), cable2,
  1683. (rear_ports[0], rear_ports[1]), (front_ports[0], front_ports[1]), cable1, interfaces[0]
  1684. ),
  1685. is_complete=True
  1686. ),
  1687. ]
  1688. self.assertEqual(CablePath.objects.count(), 2)
  1689. for interface in interfaces:
  1690. interface.refresh_from_db()
  1691. self.assertPathIsSet(interfaces[0], paths[0])
  1692. self.assertPathIsSet(interfaces[1], paths[1])
  1693. # Test SVG generation
  1694. CableTraceSVG(interfaces[0]).render()
  1695. def test_223_single_path_via_multiple_pass_throughs_with_breakouts(self):
  1696. """
  1697. [IF1] --C1-- [FP1] [RP1] --C2-- [IF3]
  1698. [IF2] [FP2] [RP2] [IF4]
  1699. """
  1700. interfaces = [
  1701. Interface.objects.create(device=self.device, name='Interface 1'),
  1702. Interface.objects.create(device=self.device, name='Interface 2'),
  1703. Interface.objects.create(device=self.device, name='Interface 3'),
  1704. Interface.objects.create(device=self.device, name='Interface 4'),
  1705. ]
  1706. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1')
  1707. rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2')
  1708. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1')
  1709. frontport2 = FrontPort.objects.create(device=self.device, name='Front Port 2')
  1710. PortMapping.objects.bulk_create([
  1711. PortMapping(
  1712. device=self.device,
  1713. front_port=frontport1,
  1714. front_port_position=1,
  1715. rear_port=rearport1,
  1716. rear_port_position=1,
  1717. ),
  1718. PortMapping(
  1719. device=self.device,
  1720. front_port=frontport2,
  1721. front_port_position=1,
  1722. rear_port=rearport2,
  1723. rear_port_position=1,
  1724. ),
  1725. ])
  1726. # Create cables
  1727. cable1 = Cable(
  1728. profile=CableProfileChoices.TRUNK_2C2P,
  1729. a_terminations=[interfaces[0], interfaces[1]],
  1730. b_terminations=[frontport1, frontport2]
  1731. )
  1732. cable1.clean()
  1733. cable1.save()
  1734. cable2 = Cable(
  1735. profile=CableProfileChoices.TRUNK_2C2P,
  1736. a_terminations=[rearport1, rearport2],
  1737. b_terminations=[interfaces[2], interfaces[3]]
  1738. )
  1739. cable2.clean()
  1740. cable2.save()
  1741. # Validate paths
  1742. self.assertPathExists(
  1743. (interfaces[0], cable1, frontport1, rearport1, cable2, interfaces[2]),
  1744. is_complete=True,
  1745. is_active=True
  1746. )
  1747. self.assertPathExists(
  1748. (interfaces[1], cable1, frontport2, rearport2, cable2, interfaces[3]),
  1749. is_complete=True,
  1750. is_active=True
  1751. )
  1752. self.assertPathExists(
  1753. (interfaces[2], cable2, rearport1, frontport1, cable1, interfaces[0]),
  1754. is_complete=True,
  1755. is_active=True
  1756. )
  1757. self.assertPathExists(
  1758. (interfaces[3], cable2, rearport2, frontport2, cable1, interfaces[1]),
  1759. is_complete=True,
  1760. is_active=True
  1761. )
  1762. self.assertEqual(CablePath.objects.count(), 4)
  1763. def test_304_add_port_mapping_between_connected_ports(self):
  1764. """
  1765. [IF1] --C1-- [FP1] [RP1] --C2-- [IF2]
  1766. """
  1767. interface1 = Interface.objects.create(device=self.device, name='Interface 1')
  1768. interface2 = Interface.objects.create(device=self.device, name='Interface 2')
  1769. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1')
  1770. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1')
  1771. cable1 = Cable(
  1772. a_terminations=[interface1],
  1773. b_terminations=[frontport1]
  1774. )
  1775. cable1.save()
  1776. cable2 = Cable(
  1777. a_terminations=[interface2],
  1778. b_terminations=[rearport1]
  1779. )
  1780. cable2.save()
  1781. # Check for incomplete paths
  1782. self.assertPathExists(
  1783. (interface1, cable1, frontport1),
  1784. is_complete=False,
  1785. is_active=True
  1786. )
  1787. self.assertPathExists(
  1788. (interface2, cable2, rearport1),
  1789. is_complete=False,
  1790. is_active=True
  1791. )
  1792. # Create a PortMapping between frontport1 and rearport1
  1793. PortMapping.objects.create(
  1794. device=self.device,
  1795. front_port=frontport1,
  1796. front_port_position=1,
  1797. rear_port=rearport1,
  1798. rear_port_position=1,
  1799. )
  1800. # Check that paths are now complete
  1801. self.assertPathExists(
  1802. (interface1, cable1, frontport1, rearport1, cable2, interface2),
  1803. is_complete=True,
  1804. is_active=True
  1805. )
  1806. self.assertPathExists(
  1807. (interface2, cable2, rearport1, frontport1, cable1, interface1),
  1808. is_complete=True,
  1809. is_active=True
  1810. )
  1811. def test_305_delete_port_mapping_between_connected_ports(self):
  1812. """
  1813. [IF1] --C1-- [FP1] [RP1] --C2-- [IF2]
  1814. """
  1815. interface1 = Interface.objects.create(device=self.device, name='Interface 1')
  1816. interface2 = Interface.objects.create(device=self.device, name='Interface 2')
  1817. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1')
  1818. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1')
  1819. cable1 = Cable(
  1820. a_terminations=[interface1],
  1821. b_terminations=[frontport1]
  1822. )
  1823. cable1.save()
  1824. cable2 = Cable(
  1825. a_terminations=[interface2],
  1826. b_terminations=[rearport1]
  1827. )
  1828. cable2.save()
  1829. portmapping1 = PortMapping.objects.create(
  1830. device=self.device,
  1831. front_port=frontport1,
  1832. front_port_position=1,
  1833. rear_port=rearport1,
  1834. rear_port_position=1,
  1835. )
  1836. # Check for complete paths
  1837. self.assertPathExists(
  1838. (interface1, cable1, frontport1, rearport1, cable2, interface2),
  1839. is_complete=True,
  1840. is_active=True
  1841. )
  1842. self.assertPathExists(
  1843. (interface2, cable2, rearport1, frontport1, cable1, interface1),
  1844. is_complete=True,
  1845. is_active=True
  1846. )
  1847. # Delete the PortMapping between frontport1 and rearport1
  1848. portmapping1.delete()
  1849. # Check that paths are no longer complete
  1850. self.assertPathExists(
  1851. (interface1, cable1, frontport1),
  1852. is_complete=False,
  1853. is_active=True
  1854. )
  1855. self.assertPathExists(
  1856. (interface2, cable2, rearport1),
  1857. is_complete=False,
  1858. is_active=True
  1859. )
  1860. def test_306_change_port_mapping_between_connected_ports(self):
  1861. """
  1862. [IF1] --C1-- [FP1] [RP1] --C3-- [IF3]
  1863. [IF2] --C2-- [FP2] [RP3] --C4-- [IF4]
  1864. """
  1865. interface1 = Interface.objects.create(device=self.device, name='Interface 1')
  1866. interface2 = Interface.objects.create(device=self.device, name='Interface 2')
  1867. interface3 = Interface.objects.create(device=self.device, name='Interface 3')
  1868. interface4 = Interface.objects.create(device=self.device, name='Interface 4')
  1869. frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1')
  1870. frontport2 = FrontPort.objects.create(device=self.device, name='Front Port 2')
  1871. rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1')
  1872. rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2')
  1873. cable1 = Cable(
  1874. a_terminations=[interface1],
  1875. b_terminations=[frontport1]
  1876. )
  1877. cable1.save()
  1878. cable2 = Cable(
  1879. a_terminations=[interface2],
  1880. b_terminations=[frontport2]
  1881. )
  1882. cable2.save()
  1883. cable3 = Cable(
  1884. a_terminations=[interface3],
  1885. b_terminations=[rearport1]
  1886. )
  1887. cable3.save()
  1888. cable4 = Cable(
  1889. a_terminations=[interface4],
  1890. b_terminations=[rearport2]
  1891. )
  1892. cable4.save()
  1893. portmapping1 = PortMapping.objects.create(
  1894. device=self.device,
  1895. front_port=frontport1,
  1896. front_port_position=1,
  1897. rear_port=rearport1,
  1898. rear_port_position=1,
  1899. )
  1900. # Verify expected initial paths
  1901. self.assertPathExists(
  1902. (interface1, cable1, frontport1, rearport1, cable3, interface3),
  1903. is_complete=True,
  1904. is_active=True
  1905. )
  1906. self.assertPathExists(
  1907. (interface3, cable3, rearport1, frontport1, cable1, interface1),
  1908. is_complete=True,
  1909. is_active=True
  1910. )
  1911. # Delete and replace the PortMapping to connect interface1 to interface4
  1912. portmapping1.delete()
  1913. portmapping2 = PortMapping.objects.create(
  1914. device=self.device,
  1915. front_port=frontport1,
  1916. front_port_position=1,
  1917. rear_port=rearport2,
  1918. rear_port_position=1,
  1919. )
  1920. # Verify expected new paths
  1921. self.assertPathExists(
  1922. (interface1, cable1, frontport1, rearport2, cable4, interface4),
  1923. is_complete=True,
  1924. is_active=True
  1925. )
  1926. self.assertPathExists(
  1927. (interface4, cable4, rearport2, frontport1, cable1, interface1),
  1928. is_complete=True,
  1929. is_active=True
  1930. )
  1931. # Delete and replace the PortMapping to connect interface2 to interface4
  1932. portmapping2.delete()
  1933. PortMapping.objects.create(
  1934. device=self.device,
  1935. front_port=frontport2,
  1936. front_port_position=1,
  1937. rear_port=rearport2,
  1938. rear_port_position=1,
  1939. )
  1940. # Verify expected new paths
  1941. self.assertPathExists(
  1942. (interface2, cable2, frontport2, rearport2, cable4, interface4),
  1943. is_complete=True,
  1944. is_active=True
  1945. )
  1946. self.assertPathExists(
  1947. (interface4, cable4, rearport2, frontport2, cable2, interface2),
  1948. is_complete=True,
  1949. is_active=True
  1950. )