test_views.py 131 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629
  1. import json
  2. from decimal import Decimal
  3. from zoneinfo import ZoneInfo
  4. import yaml
  5. from django.test import override_settings
  6. from django.urls import reverse
  7. from netaddr import EUI
  8. from dcim.choices import *
  9. from dcim.constants import *
  10. from dcim.models import *
  11. from ipam.models import ASN, RIR, VLAN, VRF
  12. from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices
  13. from tenancy.models import Tenant
  14. from users.models import User
  15. from utilities.testing import ViewTestCases, create_tags, create_test_device, post_data
  16. from wireless.models import WirelessLAN
  17. class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  18. model = Region
  19. @classmethod
  20. def setUpTestData(cls):
  21. # Create three Regions
  22. regions = (
  23. Region(name='Region 1', slug='region-1', comments=''),
  24. Region(
  25. name='Region 2', slug='region-2', comments="It's going to take a lot to drag me away from you"
  26. ),
  27. Region(name='Region 3', slug='region-3'),
  28. )
  29. for region in regions:
  30. region.save()
  31. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  32. cls.form_data = {
  33. 'name': 'Region X',
  34. 'slug': 'region-x',
  35. 'parent': regions[2].pk,
  36. 'description': 'A new region',
  37. 'tags': [t.pk for t in tags],
  38. 'comments': 'This comment is really exciting!',
  39. }
  40. cls.csv_data = (
  41. "name,slug,description,comments",
  42. "Region 4,region-4,Fourth region,",
  43. "Region 5,region-5,Fifth region,hi guys",
  44. "Region 6,region-6,Sixth region,bye guys",
  45. )
  46. cls.csv_update_data = (
  47. "id,name,description",
  48. f"{regions[0].pk},Region 7,Fourth region7",
  49. f"{regions[1].pk},Region 8,Fifth region8",
  50. f"{regions[2].pk},Region 0,Sixth region9",
  51. )
  52. cls.bulk_edit_data = {
  53. 'description': 'New description',
  54. 'comments': 'This comment is super exciting!!!',
  55. }
  56. class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  57. model = SiteGroup
  58. @classmethod
  59. def setUpTestData(cls):
  60. # Create three SiteGroups
  61. sitegroups = (
  62. SiteGroup(name='Site Group 1', slug='site-group-1', comments='Still here'),
  63. SiteGroup(name='Site Group 2', slug='site-group-2'),
  64. SiteGroup(name='Site Group 3', slug='site-group-3'),
  65. )
  66. for sitegroup in sitegroups:
  67. sitegroup.save()
  68. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  69. cls.form_data = {
  70. 'name': 'Site Group X',
  71. 'slug': 'site-group-x',
  72. 'parent': sitegroups[2].pk,
  73. 'description': 'A new site group',
  74. 'tags': [t.pk for t in tags],
  75. 'comments': 'still here',
  76. }
  77. cls.csv_data = (
  78. "name,slug,description,comments",
  79. "Site Group 4,site-group-4,Fourth site group,",
  80. "Site Group 5,site-group-5,Fifth site group,still hear",
  81. "Site Group 6,site-group-6,Sixth site group,"
  82. )
  83. cls.csv_update_data = (
  84. "id,name,description,comments",
  85. f"{sitegroups[0].pk},Site Group 7,Fourth site group7,",
  86. f"{sitegroups[1].pk},Site Group 8,Fifth site group8,when will it end",
  87. f"{sitegroups[2].pk},Site Group 0,Sixth site group9,",
  88. )
  89. cls.bulk_edit_data = {
  90. 'description': 'New description',
  91. 'comments': 'the end',
  92. }
  93. class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  94. model = Site
  95. @classmethod
  96. def setUpTestData(cls):
  97. regions = (
  98. Region(name='Region 1', slug='region-1'),
  99. Region(name='Region 2', slug='region-2'),
  100. )
  101. for region in regions:
  102. region.save()
  103. groups = (
  104. SiteGroup(name='Site Group 1', slug='site-group-1'),
  105. SiteGroup(name='Site Group 2', slug='site-group-2'),
  106. )
  107. for group in groups:
  108. group.save()
  109. rir = RIR.objects.create(name='RFC 6996', is_private=True)
  110. asns = [
  111. ASN(asn=65000 + i, rir=rir) for i in range(8)
  112. ]
  113. ASN.objects.bulk_create(asns)
  114. sites = Site.objects.bulk_create([
  115. Site(name='Site 1', slug='site-1', region=regions[0], group=groups[1]),
  116. Site(name='Site 2', slug='site-2', region=regions[0], group=groups[1]),
  117. Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
  118. ])
  119. sites[0].asns.set([asns[0], asns[1]])
  120. sites[1].asns.set([asns[2], asns[3]])
  121. sites[2].asns.set([asns[4], asns[5]])
  122. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  123. cls.form_data = {
  124. 'name': 'Site X',
  125. 'slug': 'site-x',
  126. 'status': SiteStatusChoices.STATUS_PLANNED,
  127. 'region': regions[1].pk,
  128. 'group': groups[1].pk,
  129. 'tenant': None,
  130. 'facility': 'Facility X',
  131. 'asns': [asns[6].pk, asns[7].pk],
  132. 'time_zone': ZoneInfo('UTC'),
  133. 'description': 'Site description',
  134. 'physical_address': '742 Evergreen Terrace, Springfield, USA',
  135. 'shipping_address': '742 Evergreen Terrace, Springfield, USA',
  136. 'latitude': Decimal('35.780000'),
  137. 'longitude': Decimal('-78.642000'),
  138. 'comments': 'Test site',
  139. 'tags': [t.pk for t in tags],
  140. }
  141. cls.csv_data = (
  142. "name,slug,status",
  143. "Site 4,site-4,planned",
  144. "Site 5,site-5,active",
  145. "Site 6,site-6,staging",
  146. )
  147. cls.csv_update_data = (
  148. "id,name,status",
  149. f"{sites[0].pk},Site 7,staging",
  150. f"{sites[1].pk},Site 8,planned",
  151. f"{sites[2].pk},Site 9,active",
  152. )
  153. cls.bulk_edit_data = {
  154. 'status': SiteStatusChoices.STATUS_PLANNED,
  155. 'region': regions[1].pk,
  156. 'group': groups[1].pk,
  157. 'tenant': None,
  158. 'time_zone': ZoneInfo('US/Eastern'),
  159. 'description': 'New description',
  160. }
  161. class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  162. model = Location
  163. @classmethod
  164. def setUpTestData(cls):
  165. site = Site.objects.create(name='Site 1', slug='site-1')
  166. tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1')
  167. locations = (
  168. Location(
  169. name='Location 1',
  170. slug='location-1',
  171. site=site,
  172. status=LocationStatusChoices.STATUS_ACTIVE,
  173. tenant=tenant,
  174. comments='',
  175. ),
  176. Location(
  177. name='Location 2',
  178. slug='location-2',
  179. site=site,
  180. status=LocationStatusChoices.STATUS_ACTIVE,
  181. tenant=tenant,
  182. comments='First comment!',
  183. ),
  184. Location(
  185. name='Location 3',
  186. slug='location-3',
  187. site=site,
  188. status=LocationStatusChoices.STATUS_ACTIVE,
  189. tenant=tenant,
  190. comments='_This_ is a **bold comment**',
  191. ),
  192. )
  193. for location in locations:
  194. location.save()
  195. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  196. cls.form_data = {
  197. 'name': 'Location X',
  198. 'slug': 'location-x',
  199. 'site': site.pk,
  200. 'status': LocationStatusChoices.STATUS_PLANNED,
  201. 'facility': 'Facility X',
  202. 'tenant': tenant.pk,
  203. 'description': 'A new location',
  204. 'tags': [t.pk for t in tags],
  205. 'comments': 'This comment is really boring',
  206. }
  207. cls.csv_data = (
  208. "site,tenant,name,slug,status,description,comments",
  209. "Site 1,Tenant 1,Location 4,location-4,planned,Fourth location,",
  210. "Site 1,Tenant 1,Location 5,location-5,planned,Fifth location,",
  211. "Site 1,Tenant 1,Location 6,location-6,planned,Sixth location,hi!",
  212. )
  213. cls.csv_update_data = (
  214. "id,name,description,comments",
  215. f"{locations[0].pk},Location 7,Fourth location7,Useful comment",
  216. f"{locations[1].pk},Location 8,Fifth location8,unuseful comment",
  217. f"{locations[2].pk},Location 0,Sixth location9,",
  218. )
  219. cls.bulk_edit_data = {
  220. 'description': 'New description',
  221. 'comments': 'This comment is also really boring',
  222. }
  223. class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  224. model = RackRole
  225. @classmethod
  226. def setUpTestData(cls):
  227. rack_roles = (
  228. RackRole(name='Rack Role 1', slug='rack-role-1'),
  229. RackRole(name='Rack Role 2', slug='rack-role-2'),
  230. RackRole(name='Rack Role 3', slug='rack-role-3'),
  231. )
  232. RackRole.objects.bulk_create(rack_roles)
  233. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  234. cls.form_data = {
  235. 'name': 'Rack Role X',
  236. 'slug': 'rack-role-x',
  237. 'color': 'c0c0c0',
  238. 'description': 'New role',
  239. 'tags': [t.pk for t in tags],
  240. }
  241. cls.csv_data = (
  242. "name,slug,color",
  243. "Rack Role 4,rack-role-4,ff0000",
  244. "Rack Role 5,rack-role-5,00ff00",
  245. "Rack Role 6,rack-role-6,0000ff",
  246. )
  247. cls.csv_update_data = (
  248. "id,name,description",
  249. f"{rack_roles[0].pk},Rack Role 7,New description7",
  250. f"{rack_roles[1].pk},Rack Role 8,New description8",
  251. f"{rack_roles[2].pk},Rack Role 9,New description9",
  252. )
  253. cls.bulk_edit_data = {
  254. 'color': '00ff00',
  255. 'description': 'New description',
  256. }
  257. class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  258. model = RackReservation
  259. @classmethod
  260. def setUpTestData(cls):
  261. user2 = User.objects.create_user(username='testuser2')
  262. user3 = User.objects.create_user(username='testuser3')
  263. site = Site.objects.create(name='Site 1', slug='site-1')
  264. location = Location(name='Location 1', slug='location-1', site=site)
  265. location.save()
  266. rack = Rack(name='Rack 1', site=site, location=location)
  267. rack.save()
  268. rack_reservations = (
  269. RackReservation(rack=rack, user=user2, units=[1, 2, 3], description='Reservation 1'),
  270. RackReservation(rack=rack, user=user2, units=[4, 5, 6], description='Reservation 2'),
  271. RackReservation(rack=rack, user=user2, units=[7, 8, 9], description='Reservation 3'),
  272. )
  273. RackReservation.objects.bulk_create(rack_reservations)
  274. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  275. cls.form_data = {
  276. 'rack': rack.pk,
  277. 'units': "10,11,12",
  278. 'user': user3.pk,
  279. 'tenant': None,
  280. 'description': 'Rack reservation',
  281. 'tags': [t.pk for t in tags],
  282. }
  283. cls.csv_data = (
  284. 'site,location,rack,units,description',
  285. 'Site 1,Location 1,Rack 1,"10,11,12",Reservation 1',
  286. 'Site 1,Location 1,Rack 1,"13,14,15",Reservation 2',
  287. 'Site 1,Location 1,Rack 1,"16,17,18",Reservation 3',
  288. )
  289. cls.csv_update_data = (
  290. 'id,description',
  291. f'{rack_reservations[0].pk},New description1',
  292. f'{rack_reservations[1].pk},New description2',
  293. f'{rack_reservations[2].pk},New description3',
  294. )
  295. cls.bulk_edit_data = {
  296. 'user': user3.pk,
  297. 'tenant': None,
  298. 'description': 'New description',
  299. }
  300. class RackTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  301. model = RackType
  302. @classmethod
  303. def setUpTestData(cls):
  304. manufacturers = (
  305. Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
  306. Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
  307. )
  308. Manufacturer.objects.bulk_create(manufacturers)
  309. rack_types = (
  310. RackType(
  311. manufacturer=manufacturers[0],
  312. model='RackType 1',
  313. slug='rack-type-1',
  314. form_factor=RackFormFactorChoices.TYPE_CABINET,
  315. ),
  316. RackType(
  317. manufacturer=manufacturers[0],
  318. model='RackType 2',
  319. slug='rack-type-2',
  320. form_factor=RackFormFactorChoices.TYPE_CABINET,
  321. ),
  322. RackType(
  323. manufacturer=manufacturers[0],
  324. model='RackType 3',
  325. slug='rack-type-3',
  326. form_factor=RackFormFactorChoices.TYPE_CABINET,
  327. ),
  328. )
  329. RackType.objects.bulk_create(rack_types)
  330. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  331. cls.form_data = {
  332. 'manufacturer': manufacturers[1].pk,
  333. 'model': 'RackType X',
  334. 'slug': 'rack-type-x',
  335. 'type': RackFormFactorChoices.TYPE_CABINET,
  336. 'width': RackWidthChoices.WIDTH_19IN,
  337. 'u_height': 48,
  338. 'desc_units': False,
  339. 'outer_width': 500,
  340. 'outer_depth': 500,
  341. 'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
  342. 'starting_unit': 1,
  343. 'weight': 100,
  344. 'max_weight': 2000,
  345. 'weight_unit': WeightUnitChoices.UNIT_POUND,
  346. 'form_factor': RackFormFactorChoices.TYPE_CABINET,
  347. 'comments': 'Some comments',
  348. 'tags': [t.pk for t in tags],
  349. }
  350. cls.csv_data = (
  351. "manufacturer,model,slug,width,u_height,weight,max_weight,weight_unit",
  352. "Manufacturer 1,RackType 4,rack-type-4,19,42,100,2000,kg",
  353. "Manufacturer 1,RackType 5,rack-type-5,19,42,100,2000,kg",
  354. "Manufacturer 1,RackType 6,rack-type-6,19,42,100,2000,kg",
  355. )
  356. cls.csv_update_data = (
  357. "id,model",
  358. f"{rack_types[0].pk},RackType 7",
  359. f"{rack_types[1].pk},RackType 8",
  360. f"{rack_types[2].pk},RackType 9",
  361. )
  362. cls.bulk_edit_data = {
  363. 'manufacturer': manufacturers[1].pk,
  364. 'type': RackFormFactorChoices.TYPE_4POST,
  365. 'width': RackWidthChoices.WIDTH_23IN,
  366. 'u_height': 49,
  367. 'desc_units': True,
  368. 'outer_width': 30,
  369. 'outer_depth': 30,
  370. 'outer_unit': RackDimensionUnitChoices.UNIT_INCH,
  371. 'weight': 200,
  372. 'max_weight': 4000,
  373. 'weight_unit': WeightUnitChoices.UNIT_POUND,
  374. 'comments': 'New comments',
  375. }
  376. class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  377. model = Rack
  378. @classmethod
  379. def setUpTestData(cls):
  380. sites = (
  381. Site(name='Site 1', slug='site-1'),
  382. Site(name='Site 2', slug='site-2'),
  383. )
  384. Site.objects.bulk_create(sites)
  385. locations = (
  386. Location(name='Location 1', slug='location-1', site=sites[0]),
  387. Location(name='Location 2', slug='location-2', site=sites[1])
  388. )
  389. for location in locations:
  390. location.save()
  391. rackroles = (
  392. RackRole(name='Rack Role 1', slug='rack-role-1'),
  393. RackRole(name='Rack Role 2', slug='rack-role-2'),
  394. )
  395. RackRole.objects.bulk_create(rackroles)
  396. racks = (
  397. Rack(name='Rack 1', site=sites[0]),
  398. Rack(name='Rack 2', site=sites[0]),
  399. Rack(name='Rack 3', site=sites[0]),
  400. )
  401. Rack.objects.bulk_create(racks)
  402. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  403. cls.form_data = {
  404. 'name': 'Rack X',
  405. 'facility_id': 'Facility X',
  406. 'site': sites[1].pk,
  407. 'location': locations[1].pk,
  408. 'tenant': None,
  409. 'status': RackStatusChoices.STATUS_PLANNED,
  410. 'role': rackroles[1].pk,
  411. 'serial': '123456',
  412. 'asset_tag': 'ABCDEF',
  413. 'form_factor': RackFormFactorChoices.TYPE_CABINET,
  414. 'width': RackWidthChoices.WIDTH_19IN,
  415. 'u_height': 48,
  416. 'desc_units': False,
  417. 'outer_width': 500,
  418. 'outer_depth': 500,
  419. 'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
  420. 'starting_unit': 1,
  421. 'weight': 100,
  422. 'max_weight': 2000,
  423. 'weight_unit': WeightUnitChoices.UNIT_POUND,
  424. 'comments': 'Some comments',
  425. 'tags': [t.pk for t in tags],
  426. }
  427. cls.csv_data = (
  428. "site,location,name,status,width,u_height,weight,max_weight,weight_unit",
  429. "Site 1,,Rack 4,active,19,42,100,2000,kg",
  430. "Site 1,Location 1,Rack 5,active,19,42,100,2000,kg",
  431. "Site 2,Location 2,Rack 6,active,19,42,100,2000,kg",
  432. )
  433. cls.csv_update_data = (
  434. "id,name,status",
  435. f"{racks[0].pk},Rack 7,{RackStatusChoices.STATUS_DEPRECATED}",
  436. f"{racks[1].pk},Rack 8,{RackStatusChoices.STATUS_DEPRECATED}",
  437. f"{racks[2].pk},Rack 9,{RackStatusChoices.STATUS_DEPRECATED}",
  438. )
  439. cls.bulk_edit_data = {
  440. 'site': sites[1].pk,
  441. 'location': locations[1].pk,
  442. 'tenant': None,
  443. 'status': RackStatusChoices.STATUS_DEPRECATED,
  444. 'role': rackroles[1].pk,
  445. 'serial': '654321',
  446. 'form_factor': RackFormFactorChoices.TYPE_4POST,
  447. 'width': RackWidthChoices.WIDTH_23IN,
  448. 'u_height': 49,
  449. 'desc_units': True,
  450. 'outer_width': 30,
  451. 'outer_depth': 30,
  452. 'outer_unit': RackDimensionUnitChoices.UNIT_INCH,
  453. 'weight': 200,
  454. 'max_weight': 4000,
  455. 'weight_unit': WeightUnitChoices.UNIT_POUND,
  456. 'comments': 'New comments',
  457. }
  458. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  459. def test_list_rack_elevations(self):
  460. """
  461. Test viewing the list of rack elevations.
  462. """
  463. response = self.client.get(reverse('dcim:rack_elevation_list'))
  464. self.assertHttpStatus(response, 200)
  465. class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  466. model = Manufacturer
  467. @classmethod
  468. def setUpTestData(cls):
  469. manufacturers = (
  470. Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
  471. Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
  472. Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
  473. )
  474. Manufacturer.objects.bulk_create(manufacturers)
  475. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  476. cls.form_data = {
  477. 'name': 'Manufacturer X',
  478. 'slug': 'manufacturer-x',
  479. 'description': 'A new manufacturer',
  480. 'tags': [t.pk for t in tags],
  481. }
  482. cls.csv_data = (
  483. "name,slug,description",
  484. "Manufacturer 4,manufacturer-4,Fourth manufacturer",
  485. "Manufacturer 5,manufacturer-5,Fifth manufacturer",
  486. "Manufacturer 6,manufacturer-6,Sixth manufacturer",
  487. )
  488. cls.csv_update_data = (
  489. "id,name,description",
  490. f"{manufacturers[0].pk},Manufacturer 7,Fourth manufacturer7",
  491. f"{manufacturers[1].pk},Manufacturer 8,Fifth manufacturer8",
  492. f"{manufacturers[2].pk},Manufacturer 9,Sixth manufacturer9",
  493. )
  494. cls.bulk_edit_data = {
  495. 'description': 'New description',
  496. }
  497. # TODO: Change base class to PrimaryObjectViewTestCase
  498. # Blocked by absence of bulk import view for DeviceTypes
  499. class DeviceTypeTestCase(
  500. ViewTestCases.GetObjectViewTestCase,
  501. ViewTestCases.GetObjectChangelogViewTestCase,
  502. ViewTestCases.CreateObjectViewTestCase,
  503. ViewTestCases.EditObjectViewTestCase,
  504. ViewTestCases.DeleteObjectViewTestCase,
  505. ViewTestCases.ListObjectsViewTestCase,
  506. ViewTestCases.BulkEditObjectsViewTestCase,
  507. ViewTestCases.BulkDeleteObjectsViewTestCase
  508. ):
  509. model = DeviceType
  510. @classmethod
  511. def setUpTestData(cls):
  512. manufacturers = (
  513. Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
  514. Manufacturer(name='Manufacturer 2', slug='manufacturer-2')
  515. )
  516. Manufacturer.objects.bulk_create(manufacturers)
  517. platforms = (
  518. Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0]),
  519. Platform(name='Platform 2', slug='platform-3', manufacturer=manufacturers[1]),
  520. )
  521. Platform.objects.bulk_create(platforms)
  522. DeviceType.objects.bulk_create([
  523. DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturers[0]),
  524. DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturers[0]),
  525. DeviceType(model='Device Type 3', slug='device-type-3', manufacturer=manufacturers[0]),
  526. ])
  527. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  528. cls.form_data = {
  529. 'manufacturer': manufacturers[1].pk,
  530. 'default_platform': platforms[0].pk,
  531. 'model': 'Device Type X',
  532. 'slug': 'device-type-x',
  533. 'part_number': '123ABC',
  534. 'u_height': 2,
  535. 'is_full_depth': True,
  536. 'subdevice_role': None,
  537. 'comments': 'Some comments',
  538. 'tags': [t.pk for t in tags],
  539. }
  540. cls.bulk_edit_data = {
  541. 'manufacturer': manufacturers[1].pk,
  542. 'default_platform': platforms[1].pk,
  543. 'u_height': 3,
  544. 'is_full_depth': False,
  545. }
  546. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  547. def test_devicetype_consoleports(self):
  548. devicetype = DeviceType.objects.first()
  549. console_ports = (
  550. ConsolePortTemplate(device_type=devicetype, name='Console Port 1'),
  551. ConsolePortTemplate(device_type=devicetype, name='Console Port 2'),
  552. ConsolePortTemplate(device_type=devicetype, name='Console Port 3'),
  553. )
  554. ConsolePortTemplate.objects.bulk_create(console_ports)
  555. url = reverse('dcim:devicetype_consoleports', kwargs={'pk': devicetype.pk})
  556. self.assertHttpStatus(self.client.get(url), 200)
  557. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  558. def test_devicetype_consoleserverports(self):
  559. devicetype = DeviceType.objects.first()
  560. console_server_ports = (
  561. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port 1'),
  562. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port 2'),
  563. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port 3'),
  564. )
  565. ConsoleServerPortTemplate.objects.bulk_create(console_server_ports)
  566. url = reverse('dcim:devicetype_consoleserverports', kwargs={'pk': devicetype.pk})
  567. self.assertHttpStatus(self.client.get(url), 200)
  568. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  569. def test_devicetype_powerports(self):
  570. devicetype = DeviceType.objects.first()
  571. power_ports = (
  572. PowerPortTemplate(device_type=devicetype, name='Power Port 1'),
  573. PowerPortTemplate(device_type=devicetype, name='Power Port 2'),
  574. PowerPortTemplate(device_type=devicetype, name='Power Port 3'),
  575. )
  576. PowerPortTemplate.objects.bulk_create(power_ports)
  577. url = reverse('dcim:devicetype_powerports', kwargs={'pk': devicetype.pk})
  578. self.assertHttpStatus(self.client.get(url), 200)
  579. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  580. def test_devicetype_poweroutlets(self):
  581. devicetype = DeviceType.objects.first()
  582. power_outlets = (
  583. PowerOutletTemplate(device_type=devicetype, name='Power Outlet 1'),
  584. PowerOutletTemplate(device_type=devicetype, name='Power Outlet 2'),
  585. PowerOutletTemplate(device_type=devicetype, name='Power Outlet 3'),
  586. )
  587. PowerOutletTemplate.objects.bulk_create(power_outlets)
  588. url = reverse('dcim:devicetype_poweroutlets', kwargs={'pk': devicetype.pk})
  589. self.assertHttpStatus(self.client.get(url), 200)
  590. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  591. def test_devicetype_interfaces(self):
  592. devicetype = DeviceType.objects.first()
  593. interfaces = (
  594. InterfaceTemplate(device_type=devicetype, name='Interface 1'),
  595. InterfaceTemplate(device_type=devicetype, name='Interface 2'),
  596. InterfaceTemplate(device_type=devicetype, name='Interface 3'),
  597. )
  598. InterfaceTemplate.objects.bulk_create(interfaces)
  599. url = reverse('dcim:devicetype_interfaces', kwargs={'pk': devicetype.pk})
  600. self.assertHttpStatus(self.client.get(url), 200)
  601. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  602. def test_devicetype_rearports(self):
  603. devicetype = DeviceType.objects.first()
  604. rear_ports = (
  605. RearPortTemplate(device_type=devicetype, name='Rear Port 1'),
  606. RearPortTemplate(device_type=devicetype, name='Rear Port 2'),
  607. RearPortTemplate(device_type=devicetype, name='Rear Port 3'),
  608. )
  609. RearPortTemplate.objects.bulk_create(rear_ports)
  610. url = reverse('dcim:devicetype_rearports', kwargs={'pk': devicetype.pk})
  611. self.assertHttpStatus(self.client.get(url), 200)
  612. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  613. def test_devicetype_frontports(self):
  614. devicetype = DeviceType.objects.first()
  615. rear_ports = (
  616. RearPortTemplate(device_type=devicetype, name='Rear Port 1'),
  617. RearPortTemplate(device_type=devicetype, name='Rear Port 2'),
  618. RearPortTemplate(device_type=devicetype, name='Rear Port 3'),
  619. )
  620. RearPortTemplate.objects.bulk_create(rear_ports)
  621. front_ports = (
  622. FrontPortTemplate(
  623. device_type=devicetype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1
  624. ),
  625. FrontPortTemplate(
  626. device_type=devicetype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1
  627. ),
  628. FrontPortTemplate(
  629. device_type=devicetype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1
  630. ),
  631. )
  632. FrontPortTemplate.objects.bulk_create(front_ports)
  633. url = reverse('dcim:devicetype_frontports', kwargs={'pk': devicetype.pk})
  634. self.assertHttpStatus(self.client.get(url), 200)
  635. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  636. def test_devicetype_modulebays(self):
  637. devicetype = DeviceType.objects.first()
  638. module_bays = (
  639. ModuleBayTemplate(device_type=devicetype, name='Module Bay 1'),
  640. ModuleBayTemplate(device_type=devicetype, name='Module Bay 2'),
  641. ModuleBayTemplate(device_type=devicetype, name='Module Bay 3'),
  642. )
  643. ModuleBayTemplate.objects.bulk_create(module_bays)
  644. url = reverse('dcim:devicetype_modulebays', kwargs={'pk': devicetype.pk})
  645. self.assertHttpStatus(self.client.get(url), 200)
  646. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  647. def test_devicetype_devicebays(self):
  648. devicetype = DeviceType.objects.first()
  649. device_bays = (
  650. DeviceBayTemplate(device_type=devicetype, name='Device Bay 1'),
  651. DeviceBayTemplate(device_type=devicetype, name='Device Bay 2'),
  652. DeviceBayTemplate(device_type=devicetype, name='Device Bay 3'),
  653. )
  654. DeviceBayTemplate.objects.bulk_create(device_bays)
  655. url = reverse('dcim:devicetype_devicebays', kwargs={'pk': devicetype.pk})
  656. self.assertHttpStatus(self.client.get(url), 200)
  657. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  658. def test_devicetype_inventoryitems(self):
  659. devicetype = DeviceType.objects.first()
  660. inventory_items = (
  661. DeviceBayTemplate(device_type=devicetype, name='Device Bay 1'),
  662. DeviceBayTemplate(device_type=devicetype, name='Device Bay 2'),
  663. DeviceBayTemplate(device_type=devicetype, name='Device Bay 3'),
  664. )
  665. for inventory_item in inventory_items:
  666. inventory_item.save()
  667. url = reverse('dcim:devicetype_inventoryitems', kwargs={'pk': devicetype.pk})
  668. self.assertHttpStatus(self.client.get(url), 200)
  669. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  670. def test_import_objects(self):
  671. """
  672. Custom import test for YAML-based imports (versus CSV)
  673. """
  674. IMPORT_DATA = """
  675. manufacturer: Generic
  676. model: TEST-1000
  677. slug: test-1000
  678. default_platform: Platform
  679. u_height: 2
  680. is_full_depth: false
  681. airflow: front-to-rear
  682. subdevice_role: parent
  683. weight: 10
  684. weight_unit: kg
  685. comments: Test comment
  686. console-ports:
  687. - name: Console Port 1
  688. type: de-9
  689. - name: Console Port 2
  690. type: de-9
  691. - name: Console Port 3
  692. type: de-9
  693. console-server-ports:
  694. - name: Console Server Port 1
  695. type: rj-45
  696. - name: Console Server Port 2
  697. type: rj-45
  698. - name: Console Server Port 3
  699. type: rj-45
  700. power-ports:
  701. - name: Power Port 1
  702. type: iec-60320-c14
  703. - name: Power Port 2
  704. type: iec-60320-c14
  705. - name: Power Port 3
  706. type: iec-60320-c14
  707. power-outlets:
  708. - name: Power Outlet 1
  709. type: iec-60320-c13
  710. power_port: Power Port 1
  711. feed_leg: A
  712. - name: Power Outlet 2
  713. type: iec-60320-c13
  714. power_port: Power Port 1
  715. feed_leg: A
  716. - name: Power Outlet 3
  717. type: iec-60320-c13
  718. power_port: Power Port 1
  719. feed_leg: A
  720. interfaces:
  721. - name: Interface 1
  722. type: 1000base-t
  723. mgmt_only: true
  724. - name: Interface 2
  725. type: 1000base-t
  726. enabled: false
  727. - name: Interface 3
  728. type: 1000base-t
  729. rear-ports:
  730. - name: Rear Port 1
  731. type: 8p8c
  732. - name: Rear Port 2
  733. type: 8p8c
  734. - name: Rear Port 3
  735. type: 8p8c
  736. front-ports:
  737. - name: Front Port 1
  738. type: 8p8c
  739. rear_port: Rear Port 1
  740. - name: Front Port 2
  741. type: 8p8c
  742. rear_port: Rear Port 2
  743. - name: Front Port 3
  744. type: 8p8c
  745. rear_port: Rear Port 3
  746. module-bays:
  747. - name: Module Bay 1
  748. - name: Module Bay 2
  749. - name: Module Bay 3
  750. device-bays:
  751. - name: Device Bay 1
  752. - name: Device Bay 2
  753. - name: Device Bay 3
  754. inventory-items:
  755. - name: Inventory Item 1
  756. manufacturer: Generic
  757. - name: Inventory Item 2
  758. manufacturer: Generic
  759. - name: Inventory Item 3
  760. manufacturer: Generic
  761. """
  762. # Create the manufacturer and platform
  763. manufacturer = Manufacturer(name='Generic', slug='generic')
  764. manufacturer.save()
  765. platform = Platform(name='Platform', slug='test-platform', manufacturer=manufacturer)
  766. platform.save()
  767. # Add all required permissions to the test user
  768. self.add_permissions(
  769. 'dcim.view_devicetype',
  770. 'dcim.add_devicetype',
  771. 'dcim.add_consoleporttemplate',
  772. 'dcim.add_consoleserverporttemplate',
  773. 'dcim.add_powerporttemplate',
  774. 'dcim.add_poweroutlettemplate',
  775. 'dcim.add_interfacetemplate',
  776. 'dcim.add_frontporttemplate',
  777. 'dcim.add_rearporttemplate',
  778. 'dcim.add_modulebaytemplate',
  779. 'dcim.add_devicebaytemplate',
  780. 'dcim.add_inventoryitemtemplate',
  781. )
  782. form_data = {
  783. 'data': IMPORT_DATA,
  784. 'format': 'yaml'
  785. }
  786. response = self.client.post(reverse('dcim:devicetype_bulk_import'), data=form_data, follow=True)
  787. self.assertHttpStatus(response, 200)
  788. device_type = DeviceType.objects.get(model='TEST-1000')
  789. self.assertEqual(device_type.manufacturer.pk, manufacturer.pk)
  790. self.assertEqual(device_type.default_platform.pk, platform.pk)
  791. self.assertEqual(device_type.slug, 'test-1000')
  792. self.assertEqual(device_type.u_height, 2)
  793. self.assertFalse(device_type.is_full_depth)
  794. self.assertEqual(device_type.airflow, DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR)
  795. self.assertEqual(device_type.subdevice_role, SubdeviceRoleChoices.ROLE_PARENT)
  796. self.assertEqual(device_type.weight, 10)
  797. self.assertEqual(device_type.weight_unit, WeightUnitChoices.UNIT_KILOGRAM)
  798. self.assertEqual(device_type.comments, 'Test comment')
  799. # Verify all of the components were created
  800. self.assertEqual(device_type.consoleporttemplates.count(), 3)
  801. cp1 = ConsolePortTemplate.objects.first()
  802. self.assertEqual(cp1.name, 'Console Port 1')
  803. self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9)
  804. self.assertEqual(device_type.consoleserverporttemplates.count(), 3)
  805. csp1 = ConsoleServerPortTemplate.objects.first()
  806. self.assertEqual(csp1.name, 'Console Server Port 1')
  807. self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45)
  808. self.assertEqual(device_type.powerporttemplates.count(), 3)
  809. pp1 = PowerPortTemplate.objects.first()
  810. self.assertEqual(pp1.name, 'Power Port 1')
  811. self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14)
  812. self.assertEqual(device_type.poweroutlettemplates.count(), 3)
  813. po1 = PowerOutletTemplate.objects.first()
  814. self.assertEqual(po1.name, 'Power Outlet 1')
  815. self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13)
  816. self.assertEqual(po1.power_port, pp1)
  817. self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A)
  818. self.assertEqual(device_type.interfacetemplates.count(), 3)
  819. iface1 = InterfaceTemplate.objects.first()
  820. self.assertEqual(iface1.name, 'Interface 1')
  821. self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
  822. self.assertTrue(iface1.mgmt_only)
  823. self.assertTrue(iface1.enabled)
  824. iface2 = InterfaceTemplate.objects.filter(name="Interface 2").first()
  825. self.assertFalse(iface2.enabled)
  826. self.assertEqual(device_type.rearporttemplates.count(), 3)
  827. rp1 = RearPortTemplate.objects.first()
  828. self.assertEqual(rp1.name, 'Rear Port 1')
  829. self.assertEqual(device_type.frontporttemplates.count(), 3)
  830. fp1 = FrontPortTemplate.objects.first()
  831. self.assertEqual(fp1.name, 'Front Port 1')
  832. self.assertEqual(fp1.rear_port, rp1)
  833. self.assertEqual(fp1.rear_port_position, 1)
  834. self.assertEqual(device_type.modulebaytemplates.count(), 3)
  835. mb1 = ModuleBayTemplate.objects.first()
  836. self.assertEqual(mb1.name, 'Module Bay 1')
  837. self.assertEqual(device_type.devicebaytemplates.count(), 3)
  838. db1 = DeviceBayTemplate.objects.first()
  839. self.assertEqual(db1.name, 'Device Bay 1')
  840. self.assertEqual(device_type.inventoryitemtemplates.count(), 3)
  841. ii1 = InventoryItemTemplate.objects.first()
  842. self.assertEqual(ii1.name, 'Inventory Item 1')
  843. def test_export_objects(self):
  844. url = reverse('dcim:devicetype_list')
  845. self.add_permissions('dcim.view_devicetype')
  846. # Test default YAML export
  847. response = self.client.get(f'{url}?export')
  848. self.assertEqual(response.status_code, 200)
  849. data = list(yaml.load_all(response.content, Loader=yaml.SafeLoader))
  850. self.assertEqual(len(data), 3)
  851. self.assertEqual(data[0]['manufacturer'], 'Manufacturer 1')
  852. self.assertEqual(data[0]['model'], 'Device Type 1')
  853. # Test table-based export
  854. response = self.client.get(f'{url}?export=table')
  855. self.assertHttpStatus(response, 200)
  856. self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8')
  857. # TODO: Change base class to PrimaryObjectViewTestCase
  858. # Blocked by absence of bulk import view for ModuleTypes
  859. class ModuleTypeTestCase(
  860. ViewTestCases.GetObjectViewTestCase,
  861. ViewTestCases.GetObjectChangelogViewTestCase,
  862. ViewTestCases.CreateObjectViewTestCase,
  863. ViewTestCases.EditObjectViewTestCase,
  864. ViewTestCases.DeleteObjectViewTestCase,
  865. ViewTestCases.ListObjectsViewTestCase,
  866. ViewTestCases.BulkEditObjectsViewTestCase,
  867. ViewTestCases.BulkDeleteObjectsViewTestCase
  868. ):
  869. model = ModuleType
  870. @classmethod
  871. def setUpTestData(cls):
  872. manufacturers = (
  873. Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
  874. Manufacturer(name='Manufacturer 2', slug='manufacturer-2')
  875. )
  876. Manufacturer.objects.bulk_create(manufacturers)
  877. ModuleType.objects.bulk_create([
  878. ModuleType(model='Module Type 1', manufacturer=manufacturers[0]),
  879. ModuleType(model='Module Type 2', manufacturer=manufacturers[0]),
  880. ModuleType(model='Module Type 3', manufacturer=manufacturers[0]),
  881. ])
  882. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  883. cls.form_data = {
  884. 'manufacturer': manufacturers[1].pk,
  885. 'model': 'Device Type X',
  886. 'part_number': '123ABC',
  887. 'comments': 'Some comments',
  888. 'tags': [t.pk for t in tags],
  889. }
  890. cls.bulk_edit_data = {
  891. 'manufacturer': manufacturers[1].pk,
  892. 'part_number': '456DEF',
  893. }
  894. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  895. def test_moduletype_consoleports(self):
  896. moduletype = ModuleType.objects.first()
  897. console_ports = (
  898. ConsolePortTemplate(module_type=moduletype, name='Console Port 1'),
  899. ConsolePortTemplate(module_type=moduletype, name='Console Port 2'),
  900. ConsolePortTemplate(module_type=moduletype, name='Console Port 3'),
  901. )
  902. ConsolePortTemplate.objects.bulk_create(console_ports)
  903. url = reverse('dcim:moduletype_consoleports', kwargs={'pk': moduletype.pk})
  904. self.assertHttpStatus(self.client.get(url), 200)
  905. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  906. def test_moduletype_consoleserverports(self):
  907. moduletype = ModuleType.objects.first()
  908. console_server_ports = (
  909. ConsoleServerPortTemplate(module_type=moduletype, name='Console Server Port 1'),
  910. ConsoleServerPortTemplate(module_type=moduletype, name='Console Server Port 2'),
  911. ConsoleServerPortTemplate(module_type=moduletype, name='Console Server Port 3'),
  912. )
  913. ConsoleServerPortTemplate.objects.bulk_create(console_server_ports)
  914. url = reverse('dcim:moduletype_consoleserverports', kwargs={'pk': moduletype.pk})
  915. self.assertHttpStatus(self.client.get(url), 200)
  916. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  917. def test_moduletype_powerports(self):
  918. moduletype = ModuleType.objects.first()
  919. power_ports = (
  920. PowerPortTemplate(module_type=moduletype, name='Power Port 1'),
  921. PowerPortTemplate(module_type=moduletype, name='Power Port 2'),
  922. PowerPortTemplate(module_type=moduletype, name='Power Port 3'),
  923. )
  924. PowerPortTemplate.objects.bulk_create(power_ports)
  925. url = reverse('dcim:moduletype_powerports', kwargs={'pk': moduletype.pk})
  926. self.assertHttpStatus(self.client.get(url), 200)
  927. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  928. def test_moduletype_poweroutlets(self):
  929. moduletype = ModuleType.objects.first()
  930. power_outlets = (
  931. PowerOutletTemplate(module_type=moduletype, name='Power Outlet 1'),
  932. PowerOutletTemplate(module_type=moduletype, name='Power Outlet 2'),
  933. PowerOutletTemplate(module_type=moduletype, name='Power Outlet 3'),
  934. )
  935. PowerOutletTemplate.objects.bulk_create(power_outlets)
  936. url = reverse('dcim:moduletype_poweroutlets', kwargs={'pk': moduletype.pk})
  937. self.assertHttpStatus(self.client.get(url), 200)
  938. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  939. def test_moduletype_interfaces(self):
  940. moduletype = ModuleType.objects.first()
  941. interfaces = (
  942. InterfaceTemplate(module_type=moduletype, name='Interface 1'),
  943. InterfaceTemplate(module_type=moduletype, name='Interface 2'),
  944. InterfaceTemplate(module_type=moduletype, name='Interface 3'),
  945. )
  946. InterfaceTemplate.objects.bulk_create(interfaces)
  947. url = reverse('dcim:moduletype_interfaces', kwargs={'pk': moduletype.pk})
  948. self.assertHttpStatus(self.client.get(url), 200)
  949. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  950. def test_moduletype_rearports(self):
  951. moduletype = ModuleType.objects.first()
  952. rear_ports = (
  953. RearPortTemplate(module_type=moduletype, name='Rear Port 1'),
  954. RearPortTemplate(module_type=moduletype, name='Rear Port 2'),
  955. RearPortTemplate(module_type=moduletype, name='Rear Port 3'),
  956. )
  957. RearPortTemplate.objects.bulk_create(rear_ports)
  958. url = reverse('dcim:moduletype_rearports', kwargs={'pk': moduletype.pk})
  959. self.assertHttpStatus(self.client.get(url), 200)
  960. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  961. def test_moduletype_frontports(self):
  962. moduletype = ModuleType.objects.first()
  963. rear_ports = (
  964. RearPortTemplate(module_type=moduletype, name='Rear Port 1'),
  965. RearPortTemplate(module_type=moduletype, name='Rear Port 2'),
  966. RearPortTemplate(module_type=moduletype, name='Rear Port 3'),
  967. )
  968. RearPortTemplate.objects.bulk_create(rear_ports)
  969. front_ports = (
  970. FrontPortTemplate(
  971. module_type=moduletype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1
  972. ),
  973. FrontPortTemplate(
  974. module_type=moduletype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1
  975. ),
  976. FrontPortTemplate(
  977. module_type=moduletype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1
  978. ),
  979. )
  980. FrontPortTemplate.objects.bulk_create(front_ports)
  981. url = reverse('dcim:moduletype_frontports', kwargs={'pk': moduletype.pk})
  982. self.assertHttpStatus(self.client.get(url), 200)
  983. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  984. def test_import_objects(self):
  985. """
  986. Custom import test for YAML-based imports (versus CSV)
  987. """
  988. IMPORT_DATA = """
  989. manufacturer: Generic
  990. model: TEST-1000
  991. weight: 10
  992. weight_unit: lb
  993. comments: Test comment
  994. console-ports:
  995. - name: Console Port 1
  996. type: de-9
  997. - name: Console Port 2
  998. type: de-9
  999. - name: Console Port 3
  1000. type: de-9
  1001. console-server-ports:
  1002. - name: Console Server Port 1
  1003. type: rj-45
  1004. - name: Console Server Port 2
  1005. type: rj-45
  1006. - name: Console Server Port 3
  1007. type: rj-45
  1008. power-ports:
  1009. - name: Power Port 1
  1010. type: iec-60320-c14
  1011. - name: Power Port 2
  1012. type: iec-60320-c14
  1013. - name: Power Port 3
  1014. type: iec-60320-c14
  1015. power-outlets:
  1016. - name: Power Outlet 1
  1017. type: iec-60320-c13
  1018. power_port: Power Port 1
  1019. feed_leg: A
  1020. - name: Power Outlet 2
  1021. type: iec-60320-c13
  1022. power_port: Power Port 1
  1023. feed_leg: A
  1024. - name: Power Outlet 3
  1025. type: iec-60320-c13
  1026. power_port: Power Port 1
  1027. feed_leg: A
  1028. interfaces:
  1029. - name: Interface 1
  1030. type: 1000base-t
  1031. mgmt_only: true
  1032. - name: Interface 2
  1033. type: 1000base-t
  1034. - name: Interface 3
  1035. type: 1000base-t
  1036. rear-ports:
  1037. - name: Rear Port 1
  1038. type: 8p8c
  1039. - name: Rear Port 2
  1040. type: 8p8c
  1041. - name: Rear Port 3
  1042. type: 8p8c
  1043. front-ports:
  1044. - name: Front Port 1
  1045. type: 8p8c
  1046. rear_port: Rear Port 1
  1047. - name: Front Port 2
  1048. type: 8p8c
  1049. rear_port: Rear Port 2
  1050. - name: Front Port 3
  1051. type: 8p8c
  1052. rear_port: Rear Port 3
  1053. module-bays:
  1054. - name: Module Bay 1
  1055. position: 1
  1056. - name: Module Bay 2
  1057. position: 2
  1058. - name: Module Bay 3
  1059. position: 3
  1060. """
  1061. # Create the manufacturer
  1062. manufacturer = Manufacturer(name='Generic', slug='generic')
  1063. manufacturer.save()
  1064. # Add all required permissions to the test user
  1065. self.add_permissions(
  1066. 'dcim.view_moduletype',
  1067. 'dcim.add_moduletype',
  1068. 'dcim.add_consoleporttemplate',
  1069. 'dcim.add_consoleserverporttemplate',
  1070. 'dcim.add_powerporttemplate',
  1071. 'dcim.add_poweroutlettemplate',
  1072. 'dcim.add_interfacetemplate',
  1073. 'dcim.add_frontporttemplate',
  1074. 'dcim.add_rearporttemplate',
  1075. 'dcim.add_modulebaytemplate',
  1076. )
  1077. form_data = {
  1078. 'data': IMPORT_DATA,
  1079. 'format': 'yaml'
  1080. }
  1081. response = self.client.post(reverse('dcim:moduletype_bulk_import'), data=form_data, follow=True)
  1082. self.assertHttpStatus(response, 200)
  1083. module_type = ModuleType.objects.get(model='TEST-1000')
  1084. self.assertEqual(module_type.manufacturer.pk, manufacturer.pk)
  1085. self.assertEqual(module_type.weight, 10)
  1086. self.assertEqual(module_type.weight_unit, WeightUnitChoices.UNIT_POUND)
  1087. self.assertEqual(module_type.comments, 'Test comment')
  1088. # Verify all the components were created
  1089. self.assertEqual(module_type.consoleporttemplates.count(), 3)
  1090. cp1 = ConsolePortTemplate.objects.first()
  1091. self.assertEqual(cp1.name, 'Console Port 1')
  1092. self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9)
  1093. self.assertEqual(module_type.consoleserverporttemplates.count(), 3)
  1094. csp1 = ConsoleServerPortTemplate.objects.first()
  1095. self.assertEqual(csp1.name, 'Console Server Port 1')
  1096. self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45)
  1097. self.assertEqual(module_type.powerporttemplates.count(), 3)
  1098. pp1 = PowerPortTemplate.objects.first()
  1099. self.assertEqual(pp1.name, 'Power Port 1')
  1100. self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14)
  1101. self.assertEqual(module_type.poweroutlettemplates.count(), 3)
  1102. po1 = PowerOutletTemplate.objects.first()
  1103. self.assertEqual(po1.name, 'Power Outlet 1')
  1104. self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13)
  1105. self.assertEqual(po1.power_port, pp1)
  1106. self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A)
  1107. self.assertEqual(module_type.interfacetemplates.count(), 3)
  1108. iface1 = InterfaceTemplate.objects.first()
  1109. self.assertEqual(iface1.name, 'Interface 1')
  1110. self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
  1111. self.assertTrue(iface1.mgmt_only)
  1112. self.assertEqual(module_type.rearporttemplates.count(), 3)
  1113. rp1 = RearPortTemplate.objects.first()
  1114. self.assertEqual(rp1.name, 'Rear Port 1')
  1115. self.assertEqual(module_type.frontporttemplates.count(), 3)
  1116. fp1 = FrontPortTemplate.objects.first()
  1117. self.assertEqual(fp1.name, 'Front Port 1')
  1118. self.assertEqual(fp1.rear_port, rp1)
  1119. self.assertEqual(fp1.rear_port_position, 1)
  1120. self.assertEqual(module_type.modulebaytemplates.count(), 3)
  1121. mb1 = ModuleBayTemplate.objects.first()
  1122. self.assertEqual(mb1.name, 'Module Bay 1')
  1123. self.assertEqual(mb1.position, '1')
  1124. def test_export_objects(self):
  1125. url = reverse('dcim:moduletype_list')
  1126. self.add_permissions('dcim.view_moduletype')
  1127. # Test default YAML export
  1128. response = self.client.get(f'{url}?export')
  1129. self.assertEqual(response.status_code, 200)
  1130. data = list(yaml.load_all(response.content, Loader=yaml.SafeLoader))
  1131. self.assertEqual(len(data), 3)
  1132. self.assertEqual(data[0]['manufacturer'], 'Manufacturer 1')
  1133. self.assertEqual(data[0]['model'], 'Module Type 1')
  1134. # Test table-based export
  1135. response = self.client.get(f'{url}?export=table')
  1136. self.assertHttpStatus(response, 200)
  1137. self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8')
  1138. class ModuleTypeProfileTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  1139. model = ModuleTypeProfile
  1140. SCHEMAS = [
  1141. {
  1142. "properties": {
  1143. "foo": {
  1144. "type": "string"
  1145. }
  1146. }
  1147. },
  1148. {
  1149. "properties": {
  1150. "foo": {
  1151. "type": "integer"
  1152. }
  1153. }
  1154. },
  1155. {
  1156. "properties": {
  1157. "foo": {
  1158. "type": "boolean"
  1159. }
  1160. }
  1161. },
  1162. ]
  1163. @classmethod
  1164. def setUpTestData(cls):
  1165. module_type_profiles = (
  1166. ModuleTypeProfile(
  1167. name='Module Type Profile 1',
  1168. schema=cls.SCHEMAS[0]
  1169. ),
  1170. ModuleTypeProfile(
  1171. name='Module Type Profile 2',
  1172. schema=cls.SCHEMAS[1]
  1173. ),
  1174. ModuleTypeProfile(
  1175. name='Module Type Profile 3',
  1176. schema=cls.SCHEMAS[2]
  1177. ),
  1178. )
  1179. ModuleTypeProfile.objects.bulk_create(module_type_profiles)
  1180. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  1181. cls.form_data = {
  1182. 'name': 'Module Type Profile X',
  1183. 'description': 'A new profile',
  1184. 'schema': json.dumps(cls.SCHEMAS[0]),
  1185. 'tags': [t.pk for t in tags],
  1186. }
  1187. cls.csv_data = (
  1188. "name,schema",
  1189. f"Module Type Profile 4,{json.dumps(cls.SCHEMAS[0])}",
  1190. f"Module Type Profile 5,{json.dumps(cls.SCHEMAS[1])}",
  1191. f"Module Type Profile 6,{json.dumps(cls.SCHEMAS[2])}",
  1192. )
  1193. cls.csv_update_data = (
  1194. "id,description",
  1195. f"{module_type_profiles[0].pk},New description",
  1196. f"{module_type_profiles[1].pk},New description",
  1197. f"{module_type_profiles[2].pk},New description",
  1198. )
  1199. cls.bulk_edit_data = {
  1200. 'description': 'New description',
  1201. }
  1202. #
  1203. # DeviceType components
  1204. #
  1205. class ConsolePortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1206. model = ConsolePortTemplate
  1207. validation_excluded_fields = ('name', 'label')
  1208. @classmethod
  1209. def setUpTestData(cls):
  1210. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1211. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1212. ConsolePortTemplate.objects.bulk_create((
  1213. ConsolePortTemplate(device_type=devicetype, name='Console Port Template 1'),
  1214. ConsolePortTemplate(device_type=devicetype, name='Console Port Template 2'),
  1215. ConsolePortTemplate(device_type=devicetype, name='Console Port Template 3'),
  1216. ))
  1217. cls.form_data = {
  1218. 'device_type': devicetype.pk,
  1219. 'name': 'Console Port Template X',
  1220. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1221. }
  1222. cls.bulk_create_data = {
  1223. 'device_type': devicetype.pk,
  1224. 'name': 'Console Port Template [4-6]',
  1225. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1226. }
  1227. cls.bulk_edit_data = {
  1228. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1229. }
  1230. class ConsoleServerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1231. model = ConsoleServerPortTemplate
  1232. validation_excluded_fields = ('name', 'label')
  1233. @classmethod
  1234. def setUpTestData(cls):
  1235. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1236. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1237. ConsoleServerPortTemplate.objects.bulk_create((
  1238. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port Template 1'),
  1239. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port Template 2'),
  1240. ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port Template 3'),
  1241. ))
  1242. cls.form_data = {
  1243. 'device_type': devicetype.pk,
  1244. 'name': 'Console Server Port Template X',
  1245. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1246. }
  1247. cls.bulk_create_data = {
  1248. 'device_type': devicetype.pk,
  1249. 'name': 'Console Server Port Template [4-6]',
  1250. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1251. }
  1252. cls.bulk_edit_data = {
  1253. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  1254. }
  1255. class PowerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1256. model = PowerPortTemplate
  1257. validation_excluded_fields = ('name', 'label')
  1258. @classmethod
  1259. def setUpTestData(cls):
  1260. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1261. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1262. PowerPortTemplate.objects.bulk_create((
  1263. PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
  1264. PowerPortTemplate(device_type=devicetype, name='Power Port Template 2'),
  1265. PowerPortTemplate(device_type=devicetype, name='Power Port Template 3'),
  1266. ))
  1267. cls.form_data = {
  1268. 'device_type': devicetype.pk,
  1269. 'name': 'Power Port Template X',
  1270. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  1271. 'maximum_draw': 100,
  1272. 'allocated_draw': 50,
  1273. }
  1274. cls.bulk_create_data = {
  1275. 'device_type': devicetype.pk,
  1276. 'name': 'Power Port Template [4-6]',
  1277. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  1278. 'maximum_draw': 100,
  1279. 'allocated_draw': 50,
  1280. }
  1281. cls.bulk_edit_data = {
  1282. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  1283. 'maximum_draw': 100,
  1284. 'allocated_draw': 50,
  1285. }
  1286. class PowerOutletTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1287. model = PowerOutletTemplate
  1288. validation_excluded_fields = ('name', 'label')
  1289. @classmethod
  1290. def setUpTestData(cls):
  1291. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1292. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1293. PowerOutletTemplate.objects.bulk_create((
  1294. PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 1'),
  1295. PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 2'),
  1296. PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 3'),
  1297. ))
  1298. powerports = (
  1299. PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
  1300. )
  1301. PowerPortTemplate.objects.bulk_create(powerports)
  1302. cls.form_data = {
  1303. 'device_type': devicetype.pk,
  1304. 'name': 'Power Outlet Template X',
  1305. 'type': PowerOutletTypeChoices.TYPE_IEC_C13,
  1306. 'power_port': powerports[0].pk,
  1307. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  1308. }
  1309. cls.bulk_create_data = {
  1310. 'device_type': devicetype.pk,
  1311. 'name': 'Power Outlet Template [4-6]',
  1312. 'type': PowerOutletTypeChoices.TYPE_IEC_C13,
  1313. 'power_port': powerports[0].pk,
  1314. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  1315. }
  1316. cls.bulk_edit_data = {
  1317. 'type': PowerOutletTypeChoices.TYPE_IEC_C13,
  1318. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  1319. }
  1320. class InterfaceTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1321. model = InterfaceTemplate
  1322. validation_excluded_fields = ('name', 'label')
  1323. @classmethod
  1324. def setUpTestData(cls):
  1325. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1326. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1327. InterfaceTemplate.objects.bulk_create((
  1328. InterfaceTemplate(device_type=devicetype, name='Interface Template 1'),
  1329. InterfaceTemplate(device_type=devicetype, name='Interface Template 2'),
  1330. InterfaceTemplate(device_type=devicetype, name='Interface Template 3'),
  1331. ))
  1332. cls.form_data = {
  1333. 'device_type': devicetype.pk,
  1334. 'name': 'Interface Template X',
  1335. 'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
  1336. 'mgmt_only': True,
  1337. }
  1338. cls.bulk_create_data = {
  1339. 'device_type': devicetype.pk,
  1340. 'name': 'Interface Template [4-6]',
  1341. # Test that a label can be applied to each generated interface templates
  1342. 'label': 'Interface Template Label [3-5]',
  1343. 'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
  1344. 'mgmt_only': True,
  1345. }
  1346. cls.bulk_edit_data = {
  1347. 'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
  1348. 'mgmt_only': True,
  1349. }
  1350. class FrontPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1351. model = FrontPortTemplate
  1352. validation_excluded_fields = ('name', 'label', 'rear_port')
  1353. @classmethod
  1354. def setUpTestData(cls):
  1355. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1356. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1357. rearports = (
  1358. RearPortTemplate(device_type=devicetype, name='Rear Port Template 1'),
  1359. RearPortTemplate(device_type=devicetype, name='Rear Port Template 2'),
  1360. RearPortTemplate(device_type=devicetype, name='Rear Port Template 3'),
  1361. RearPortTemplate(device_type=devicetype, name='Rear Port Template 4'),
  1362. RearPortTemplate(device_type=devicetype, name='Rear Port Template 5'),
  1363. RearPortTemplate(device_type=devicetype, name='Rear Port Template 6'),
  1364. )
  1365. RearPortTemplate.objects.bulk_create(rearports)
  1366. FrontPortTemplate.objects.bulk_create(
  1367. (
  1368. FrontPortTemplate(
  1369. device_type=devicetype, name='Front Port Template 1', rear_port=rearports[0], rear_port_position=1
  1370. ),
  1371. FrontPortTemplate(
  1372. device_type=devicetype, name='Front Port Template 2', rear_port=rearports[1], rear_port_position=1
  1373. ),
  1374. FrontPortTemplate(
  1375. device_type=devicetype, name='Front Port Template 3', rear_port=rearports[2], rear_port_position=1
  1376. ),
  1377. )
  1378. )
  1379. cls.form_data = {
  1380. 'device_type': devicetype.pk,
  1381. 'name': 'Front Port X',
  1382. 'type': PortTypeChoices.TYPE_8P8C,
  1383. 'rear_port': rearports[3].pk,
  1384. 'rear_port_position': 1,
  1385. }
  1386. cls.bulk_create_data = {
  1387. 'device_type': devicetype.pk,
  1388. 'name': 'Front Port [4-6]',
  1389. 'type': PortTypeChoices.TYPE_8P8C,
  1390. 'rear_port': [f'{rp.pk}:1' for rp in rearports[3:6]],
  1391. }
  1392. cls.bulk_edit_data = {
  1393. 'type': PortTypeChoices.TYPE_8P8C,
  1394. }
  1395. class RearPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1396. model = RearPortTemplate
  1397. validation_excluded_fields = ('name', 'label')
  1398. @classmethod
  1399. def setUpTestData(cls):
  1400. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1401. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1402. RearPortTemplate.objects.bulk_create((
  1403. RearPortTemplate(device_type=devicetype, name='Rear Port Template 1'),
  1404. RearPortTemplate(device_type=devicetype, name='Rear Port Template 2'),
  1405. RearPortTemplate(device_type=devicetype, name='Rear Port Template 3'),
  1406. ))
  1407. cls.form_data = {
  1408. 'device_type': devicetype.pk,
  1409. 'name': 'Rear Port Template X',
  1410. 'type': PortTypeChoices.TYPE_8P8C,
  1411. 'positions': 2,
  1412. }
  1413. cls.bulk_create_data = {
  1414. 'device_type': devicetype.pk,
  1415. 'name': 'Rear Port Template [4-6]',
  1416. 'type': PortTypeChoices.TYPE_8P8C,
  1417. 'positions': 2,
  1418. }
  1419. cls.bulk_edit_data = {
  1420. 'type': PortTypeChoices.TYPE_8P8C,
  1421. }
  1422. class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1423. model = ModuleBayTemplate
  1424. validation_excluded_fields = ('name', 'label')
  1425. @classmethod
  1426. def setUpTestData(cls):
  1427. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1428. devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
  1429. ModuleBayTemplate.objects.bulk_create((
  1430. ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 1'),
  1431. ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 2'),
  1432. ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 3'),
  1433. ))
  1434. cls.form_data = {
  1435. 'device_type': devicetype.pk,
  1436. 'name': 'Module Bay Template X',
  1437. }
  1438. cls.bulk_create_data = {
  1439. 'device_type': devicetype.pk,
  1440. 'name': 'Module Bay Template [4-6]',
  1441. }
  1442. cls.bulk_edit_data = {
  1443. 'description': 'Foo bar',
  1444. }
  1445. class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1446. model = DeviceBayTemplate
  1447. validation_excluded_fields = ('name', 'label')
  1448. @classmethod
  1449. def setUpTestData(cls):
  1450. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1451. devicetype = DeviceType.objects.create(
  1452. manufacturer=manufacturer,
  1453. model='Device Type 1',
  1454. slug='device-type-1',
  1455. subdevice_role=SubdeviceRoleChoices.ROLE_PARENT
  1456. )
  1457. DeviceBayTemplate.objects.bulk_create((
  1458. DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 1'),
  1459. DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 2'),
  1460. DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 3'),
  1461. ))
  1462. cls.form_data = {
  1463. 'device_type': devicetype.pk,
  1464. 'name': 'Device Bay Template X',
  1465. }
  1466. cls.bulk_create_data = {
  1467. 'device_type': devicetype.pk,
  1468. 'name': 'Device Bay Template [4-6]',
  1469. }
  1470. cls.bulk_edit_data = {
  1471. 'description': 'Foo bar',
  1472. }
  1473. class InventoryItemTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
  1474. model = InventoryItemTemplate
  1475. validation_excluded_fields = ('name', 'label')
  1476. @classmethod
  1477. def setUpTestData(cls):
  1478. manufacturers = (
  1479. Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
  1480. Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
  1481. )
  1482. Manufacturer.objects.bulk_create(manufacturers)
  1483. devicetype = DeviceType.objects.create(
  1484. manufacturer=manufacturers[0], model='Device Type 1', slug='device-type-1'
  1485. )
  1486. inventory_item_templates = (
  1487. InventoryItemTemplate(
  1488. device_type=devicetype, name='Inventory Item Template 1', manufacturer=manufacturers[0]
  1489. ),
  1490. InventoryItemTemplate(
  1491. device_type=devicetype, name='Inventory Item Template 2', manufacturer=manufacturers[0]
  1492. ),
  1493. InventoryItemTemplate(
  1494. device_type=devicetype, name='Inventory Item Template 3', manufacturer=manufacturers[0]
  1495. ),
  1496. )
  1497. for item in inventory_item_templates:
  1498. item.save()
  1499. cls.form_data = {
  1500. 'device_type': devicetype.pk,
  1501. 'name': 'Inventory Item Template X',
  1502. 'manufacturer': manufacturers[1].pk,
  1503. }
  1504. cls.bulk_create_data = {
  1505. 'device_type': devicetype.pk,
  1506. 'name': 'Inventory Item Template [4-6]',
  1507. 'manufacturer': manufacturers[1].pk,
  1508. }
  1509. cls.bulk_edit_data = {
  1510. 'description': 'Foo bar',
  1511. }
  1512. class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  1513. model = DeviceRole
  1514. @classmethod
  1515. def setUpTestData(cls):
  1516. roles = [
  1517. DeviceRole(name='Device Role 1', slug='device-role-1'),
  1518. DeviceRole(name='Device Role 2', slug='device-role-2'),
  1519. DeviceRole(name='Device Role 3', slug='device-role-3'),
  1520. DeviceRole(name='Device Role 4', slug='device-role-4'),
  1521. ]
  1522. for role in roles:
  1523. role.save()
  1524. roles.append(DeviceRole.objects.create(name='Device Role 5', slug='device-role-5', parent=roles[3]))
  1525. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  1526. cls.form_data = {
  1527. 'name': 'Device Role X',
  1528. 'slug': 'device-role-x',
  1529. 'color': 'c0c0c0',
  1530. 'vm_role': False,
  1531. 'description': 'New device role',
  1532. 'tags': [t.pk for t in tags],
  1533. }
  1534. cls.csv_data = (
  1535. "name,slug,color",
  1536. "Device Role 6,device-role-6,ff0000",
  1537. "Device Role 7,device-role-7,00ff00",
  1538. "Device Role 8,device-role-8,0000ff",
  1539. )
  1540. cls.csv_update_data = (
  1541. "id,name,description",
  1542. f"{roles[0].pk},Device Role 7,New description7",
  1543. f"{roles[1].pk},Device Role 8,New description8",
  1544. f"{roles[2].pk},Device Role 9,New description9",
  1545. f"{roles[4].pk},Device Role 10,New description10",
  1546. )
  1547. cls.bulk_edit_data = {
  1548. 'color': '00ff00',
  1549. 'description': 'New description',
  1550. }
  1551. class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  1552. model = Platform
  1553. @classmethod
  1554. def setUpTestData(cls):
  1555. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1556. platforms = (
  1557. Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturer),
  1558. Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturer),
  1559. Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturer),
  1560. )
  1561. Platform.objects.bulk_create(platforms)
  1562. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  1563. cls.form_data = {
  1564. 'name': 'Platform X',
  1565. 'slug': 'platform-x',
  1566. 'manufacturer': manufacturer.pk,
  1567. 'description': 'A new platform',
  1568. 'tags': [t.pk for t in tags],
  1569. }
  1570. cls.csv_data = (
  1571. "name,slug,description",
  1572. "Platform 4,platform-4,Fourth platform",
  1573. "Platform 5,platform-5,Fifth platform",
  1574. "Platform 6,platform-6,Sixth platform",
  1575. )
  1576. cls.csv_update_data = (
  1577. "id,name,description",
  1578. f"{platforms[0].pk},Platform 7,Fourth platform7",
  1579. f"{platforms[1].pk},Platform 8,Fifth platform8",
  1580. f"{platforms[2].pk},Platform 9,Sixth platform9",
  1581. )
  1582. cls.bulk_edit_data = {
  1583. 'description': 'New description',
  1584. }
  1585. class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  1586. model = Device
  1587. @classmethod
  1588. def setUpTestData(cls):
  1589. sites = (
  1590. Site(name='Site 1', slug='site-1'),
  1591. Site(name='Site 2', slug='site-2'),
  1592. )
  1593. Site.objects.bulk_create(sites)
  1594. location = Location(site=sites[0], name='Location 1', slug='location-1')
  1595. location.save()
  1596. racks = (
  1597. Rack(name='Rack 1', site=sites[0], location=location),
  1598. Rack(name='Rack 2', site=sites[1]),
  1599. )
  1600. Rack.objects.bulk_create(racks)
  1601. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  1602. devicetypes = (
  1603. DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturer),
  1604. DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturer),
  1605. )
  1606. DeviceType.objects.bulk_create(devicetypes)
  1607. roles = (
  1608. DeviceRole(name='Device Role 1', slug='device-role-1'),
  1609. DeviceRole(name='Device Role 2', slug='device-role-2'),
  1610. )
  1611. for role in roles:
  1612. role.save()
  1613. platforms = (
  1614. Platform(name='Platform 1', slug='platform-1'),
  1615. Platform(name='Platform 2', slug='platform-2'),
  1616. )
  1617. Platform.objects.bulk_create(platforms)
  1618. devices = (
  1619. Device(
  1620. name='Device 1',
  1621. site=sites[0],
  1622. rack=racks[0],
  1623. device_type=devicetypes[0],
  1624. role=roles[0],
  1625. platform=platforms[0],
  1626. ),
  1627. Device(
  1628. name='Device 2',
  1629. site=sites[0],
  1630. rack=racks[0],
  1631. device_type=devicetypes[0],
  1632. role=roles[0],
  1633. platform=platforms[0],
  1634. ),
  1635. Device(
  1636. name='Device 3',
  1637. site=sites[0],
  1638. rack=racks[0],
  1639. device_type=devicetypes[0],
  1640. role=roles[0],
  1641. platform=platforms[0],
  1642. ),
  1643. )
  1644. Device.objects.bulk_create(devices)
  1645. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  1646. VirtualChassis.objects.create(name='Virtual Chassis 1')
  1647. cls.form_data = {
  1648. 'device_type': devicetypes[1].pk,
  1649. 'role': roles[1].pk,
  1650. 'tenant': None,
  1651. 'platform': platforms[1].pk,
  1652. 'name': 'Device X',
  1653. 'serial': '123456',
  1654. 'asset_tag': 'ABCDEF',
  1655. 'site': sites[1].pk,
  1656. 'rack': racks[1].pk,
  1657. 'position': 1,
  1658. 'face': DeviceFaceChoices.FACE_FRONT,
  1659. 'latitude': Decimal('35.780000'),
  1660. 'longitude': Decimal('-78.642000'),
  1661. 'status': DeviceStatusChoices.STATUS_PLANNED,
  1662. 'primary_ip4': None,
  1663. 'primary_ip6': None,
  1664. 'cluster': None,
  1665. 'virtual_chassis': None,
  1666. 'vc_position': None,
  1667. 'vc_priority': None,
  1668. 'comments': 'A new device',
  1669. 'tags': [t.pk for t in tags],
  1670. 'local_context_data': None,
  1671. }
  1672. cls.csv_data = (
  1673. (
  1674. "role,manufacturer,device_type,status,name,site,location,rack,position,face,virtual_chassis,"
  1675. "vc_position,vc_priority"
  1676. ),
  1677. (
  1678. "Device Role 1,Manufacturer 1,Device Type 1,active,Device 4,Site 1,Location 1,Rack 1,10,front,"
  1679. "Virtual Chassis 1,1,10"
  1680. ),
  1681. (
  1682. "Device Role 1,Manufacturer 1,Device Type 1,active,Device 5,Site 1,Location 1,Rack 1,20,front,"
  1683. "Virtual Chassis 1,2,20"
  1684. ),
  1685. (
  1686. "Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Location 1,Rack 1,30,front,"
  1687. "Virtual Chassis 1,3,30"
  1688. ),
  1689. )
  1690. cls.csv_update_data = (
  1691. "id,status",
  1692. f"{devices[0].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
  1693. f"{devices[1].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
  1694. f"{devices[2].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
  1695. )
  1696. cls.bulk_edit_data = {
  1697. 'device_type': devicetypes[1].pk,
  1698. 'role': roles[1].pk,
  1699. 'tenant': None,
  1700. 'platform': platforms[1].pk,
  1701. 'serial': '123456',
  1702. 'status': DeviceStatusChoices.STATUS_DECOMMISSIONING,
  1703. }
  1704. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1705. def test_device_consoleports(self):
  1706. device = Device.objects.first()
  1707. console_ports = (
  1708. ConsolePort(device=device, name='Console Port 1'),
  1709. ConsolePort(device=device, name='Console Port 2'),
  1710. ConsolePort(device=device, name='Console Port 3'),
  1711. )
  1712. ConsolePort.objects.bulk_create(console_ports)
  1713. url = reverse('dcim:device_consoleports', kwargs={'pk': device.pk})
  1714. self.assertHttpStatus(self.client.get(url), 200)
  1715. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1716. def test_device_consoleserverports(self):
  1717. device = Device.objects.first()
  1718. console_server_ports = (
  1719. ConsoleServerPort(device=device, name='Console Server Port 1'),
  1720. ConsoleServerPort(device=device, name='Console Server Port 2'),
  1721. ConsoleServerPort(device=device, name='Console Server Port 3'),
  1722. )
  1723. ConsoleServerPort.objects.bulk_create(console_server_ports)
  1724. url = reverse('dcim:device_consoleserverports', kwargs={'pk': device.pk})
  1725. self.assertHttpStatus(self.client.get(url), 200)
  1726. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1727. def test_device_powerports(self):
  1728. device = Device.objects.first()
  1729. power_ports = (
  1730. PowerPort(device=device, name='Power Port 1'),
  1731. PowerPort(device=device, name='Power Port 2'),
  1732. PowerPort(device=device, name='Power Port 3'),
  1733. )
  1734. PowerPort.objects.bulk_create(power_ports)
  1735. url = reverse('dcim:device_powerports', kwargs={'pk': device.pk})
  1736. self.assertHttpStatus(self.client.get(url), 200)
  1737. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1738. def test_device_poweroutlets(self):
  1739. device = Device.objects.first()
  1740. power_outlets = (
  1741. PowerOutlet(device=device, name='Power Outlet 1'),
  1742. PowerOutlet(device=device, name='Power Outlet 2'),
  1743. PowerOutlet(device=device, name='Power Outlet 3'),
  1744. )
  1745. PowerOutlet.objects.bulk_create(power_outlets)
  1746. url = reverse('dcim:device_poweroutlets', kwargs={'pk': device.pk})
  1747. self.assertHttpStatus(self.client.get(url), 200)
  1748. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1749. def test_device_interfaces(self):
  1750. device = Device.objects.first()
  1751. interfaces = (
  1752. Interface(device=device, name='Interface 1'),
  1753. Interface(device=device, name='Interface 2'),
  1754. Interface(device=device, name='Interface 3'),
  1755. )
  1756. Interface.objects.bulk_create(interfaces)
  1757. url = reverse('dcim:device_interfaces', kwargs={'pk': device.pk})
  1758. self.assertHttpStatus(self.client.get(url), 200)
  1759. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1760. def test_device_rearports(self):
  1761. device = Device.objects.first()
  1762. rear_ports = (
  1763. RearPort(device=device, name='Rear Port 1'),
  1764. RearPort(device=device, name='Rear Port 2'),
  1765. RearPort(device=device, name='Rear Port 3'),
  1766. )
  1767. RearPort.objects.bulk_create(rear_ports)
  1768. url = reverse('dcim:device_rearports', kwargs={'pk': device.pk})
  1769. self.assertHttpStatus(self.client.get(url), 200)
  1770. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1771. def test_device_frontports(self):
  1772. device = Device.objects.first()
  1773. rear_ports = (
  1774. RearPort(device=device, name='Rear Port 1'),
  1775. RearPort(device=device, name='Rear Port 2'),
  1776. RearPort(device=device, name='Rear Port 3'),
  1777. )
  1778. RearPort.objects.bulk_create(rear_ports)
  1779. front_ports = (
  1780. FrontPort(device=device, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1),
  1781. FrontPort(device=device, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1),
  1782. FrontPort(device=device, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1),
  1783. )
  1784. FrontPort.objects.bulk_create(front_ports)
  1785. url = reverse('dcim:device_frontports', kwargs={'pk': device.pk})
  1786. self.assertHttpStatus(self.client.get(url), 200)
  1787. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1788. def test_device_modulebays(self):
  1789. device = Device.objects.first()
  1790. ModuleBay.objects.create(device=device, name='Module Bay 1')
  1791. ModuleBay.objects.create(device=device, name='Module Bay 2')
  1792. ModuleBay.objects.create(device=device, name='Module Bay 3')
  1793. url = reverse('dcim:device_modulebays', kwargs={'pk': device.pk})
  1794. self.assertHttpStatus(self.client.get(url), 200)
  1795. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1796. def test_device_devicebays(self):
  1797. device = Device.objects.first()
  1798. device_bays = (
  1799. DeviceBay(device=device, name='Device Bay 1'),
  1800. DeviceBay(device=device, name='Device Bay 2'),
  1801. DeviceBay(device=device, name='Device Bay 3'),
  1802. )
  1803. DeviceBay.objects.bulk_create(device_bays)
  1804. url = reverse('dcim:device_devicebays', kwargs={'pk': device.pk})
  1805. self.assertHttpStatus(self.client.get(url), 200)
  1806. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1807. def test_device_inventory(self):
  1808. device = Device.objects.first()
  1809. inventory_items = (
  1810. InventoryItem(device=device, name='Inventory Item 1'),
  1811. InventoryItem(device=device, name='Inventory Item 2'),
  1812. InventoryItem(device=device, name='Inventory Item 3'),
  1813. )
  1814. for item in inventory_items:
  1815. item.save()
  1816. url = reverse('dcim:device_inventory', kwargs={'pk': device.pk})
  1817. self.assertHttpStatus(self.client.get(url), 200)
  1818. class ModuleTestCase(
  1819. # Module does not support bulk renaming (no name field) or
  1820. # bulk creation (need to specify module bays)
  1821. ViewTestCases.GetObjectViewTestCase,
  1822. ViewTestCases.GetObjectChangelogViewTestCase,
  1823. ViewTestCases.CreateObjectViewTestCase,
  1824. ViewTestCases.EditObjectViewTestCase,
  1825. ViewTestCases.DeleteObjectViewTestCase,
  1826. ViewTestCases.ListObjectsViewTestCase,
  1827. ViewTestCases.BulkImportObjectsViewTestCase,
  1828. ViewTestCases.BulkEditObjectsViewTestCase,
  1829. ViewTestCases.BulkDeleteObjectsViewTestCase,
  1830. ):
  1831. model = Module
  1832. @classmethod
  1833. def setUpTestData(cls):
  1834. manufacturer = Manufacturer.objects.create(name='Generic', slug='generic')
  1835. devices = (
  1836. create_test_device('Device 1'),
  1837. create_test_device('Device 2'),
  1838. )
  1839. module_types = (
  1840. ModuleType(manufacturer=manufacturer, model='Module Type 1'),
  1841. ModuleType(manufacturer=manufacturer, model='Module Type 2'),
  1842. ModuleType(manufacturer=manufacturer, model='Module Type 3'),
  1843. ModuleType(manufacturer=manufacturer, model='Module Type 4'),
  1844. )
  1845. ModuleType.objects.bulk_create(module_types)
  1846. module_bays = (
  1847. ModuleBay(device=devices[0], name='Module Bay 1'),
  1848. ModuleBay(device=devices[0], name='Module Bay 2'),
  1849. ModuleBay(device=devices[0], name='Module Bay 3'),
  1850. ModuleBay(device=devices[0], name='Module Bay 4'),
  1851. ModuleBay(device=devices[0], name='Module Bay 5'),
  1852. ModuleBay(device=devices[1], name='Module Bay 1'),
  1853. ModuleBay(device=devices[1], name='Module Bay 2'),
  1854. ModuleBay(device=devices[1], name='Module Bay 3'),
  1855. ModuleBay(device=devices[1], name='Module Bay 4'),
  1856. ModuleBay(device=devices[1], name='Module Bay 5'),
  1857. )
  1858. for module_bay in module_bays:
  1859. module_bay.save()
  1860. modules = (
  1861. Module(device=devices[0], module_bay=module_bays[0], module_type=module_types[0]),
  1862. Module(device=devices[0], module_bay=module_bays[1], module_type=module_types[1]),
  1863. Module(device=devices[0], module_bay=module_bays[2], module_type=module_types[2]),
  1864. )
  1865. Module.objects.bulk_create(modules)
  1866. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  1867. cls.form_data = {
  1868. 'device': devices[0].pk,
  1869. 'module_bay': module_bays[3].pk,
  1870. 'module_type': module_types[0].pk,
  1871. 'status': ModuleStatusChoices.STATUS_ACTIVE,
  1872. 'serial': 'A',
  1873. 'tags': [t.pk for t in tags],
  1874. }
  1875. cls.bulk_edit_data = {
  1876. 'module_type': module_types[3].pk,
  1877. 'status': ModuleStatusChoices.STATUS_PLANNED,
  1878. }
  1879. cls.csv_data = (
  1880. "device,module_bay,module_type,status,serial,asset_tag",
  1881. "Device 2,Module Bay 1,Module Type 1,active,A,A",
  1882. "Device 2,Module Bay 2,Module Type 2,planned,B,B",
  1883. "Device 2,Module Bay 3,Module Type 3,failed,C,C",
  1884. )
  1885. cls.csv_update_data = (
  1886. "id,status,serial",
  1887. f"{modules[0].pk},offline,Serial 2",
  1888. f"{modules[1].pk},offline,Serial 3",
  1889. f"{modules[2].pk},offline,Serial 1",
  1890. )
  1891. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1892. def test_module_component_replication(self):
  1893. self.add_permissions('dcim.add_module')
  1894. # Add 5 InterfaceTemplates to a ModuleType
  1895. module_type = ModuleType.objects.first()
  1896. interface_templates = [
  1897. InterfaceTemplate(module_type=module_type, name=f'Interface {i}') for i in range(1, 6)
  1898. ]
  1899. InterfaceTemplate.objects.bulk_create(interface_templates)
  1900. form_data = self.form_data.copy()
  1901. device = Device.objects.get(pk=form_data['device'])
  1902. # Create a module *without* replicating components
  1903. form_data['replicate_components'] = False
  1904. request = {
  1905. 'path': self._get_url('add'),
  1906. 'data': post_data(form_data),
  1907. }
  1908. self.assertHttpStatus(self.client.post(**request), 302)
  1909. self.assertEqual(Interface.objects.filter(device=device).count(), 0)
  1910. # Create a second module (in the next bay) with replicated components
  1911. form_data['module_bay'] += 1
  1912. form_data['replicate_components'] = True
  1913. request = {
  1914. 'path': self._get_url('add'),
  1915. 'data': post_data(form_data),
  1916. }
  1917. self.assertHttpStatus(self.client.post(**request), 302)
  1918. self.assertEqual(Interface.objects.filter(device=device).count(), 5)
  1919. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1920. def test_module_bulk_replication(self):
  1921. self.add_permissions('dcim.add_module')
  1922. # Add 5 InterfaceTemplates to a ModuleType
  1923. module_type = ModuleType.objects.first()
  1924. interface_templates = [
  1925. InterfaceTemplate(module_type=module_type, name=f'Interface {i}')
  1926. for i in range(1, 6)
  1927. ]
  1928. InterfaceTemplate.objects.bulk_create(interface_templates)
  1929. # Create a module *without* replicating components
  1930. device = Device.objects.get(name='Device 2')
  1931. module_bay = ModuleBay.objects.get(device=device, name='Module Bay 4')
  1932. csv_data = [
  1933. "device,module_bay,module_type,status,replicate_components",
  1934. f"{device.name},{module_bay.name},{module_type.model},active,false"
  1935. ]
  1936. request = {
  1937. 'path': self._get_url('bulk_import'),
  1938. 'data': {
  1939. 'data': '\n'.join(csv_data),
  1940. 'format': ImportFormatChoices.CSV,
  1941. 'csv_delimiter': CSVDelimiterChoices.AUTO,
  1942. }
  1943. }
  1944. initial_count = Module.objects.count()
  1945. self.assertHttpStatus(self.client.post(**request), 302)
  1946. self.assertEqual(Module.objects.count(), initial_count + len(csv_data) - 1)
  1947. self.assertEqual(Interface.objects.filter(device=device).count(), 0)
  1948. # Create a second module (in the next bay) with replicated components
  1949. module_bay = ModuleBay.objects.get(device=device, name='Module Bay 5')
  1950. csv_data[1] = f"{device.name},{module_bay.name},{module_type.model},active,true"
  1951. request = {
  1952. 'path': self._get_url('bulk_import'),
  1953. 'data': {
  1954. 'data': '\n'.join(csv_data),
  1955. 'format': ImportFormatChoices.CSV,
  1956. 'csv_delimiter': CSVDelimiterChoices.AUTO,
  1957. }
  1958. }
  1959. initial_count = Module.objects.count()
  1960. self.assertHttpStatus(self.client.post(**request), 302)
  1961. self.assertEqual(Module.objects.count(), initial_count + len(csv_data) - 1)
  1962. self.assertEqual(Interface.objects.filter(device=device).count(), 5)
  1963. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1964. def test_module_component_adoption(self):
  1965. self.add_permissions('dcim.add_module')
  1966. interface_name = "Interface-1"
  1967. # Add an interface to the ModuleType
  1968. module_type = ModuleType.objects.first()
  1969. InterfaceTemplate(module_type=module_type, name=interface_name).save()
  1970. form_data = self.form_data.copy()
  1971. device = Device.objects.get(pk=form_data['device'])
  1972. # Create an interface to be adopted
  1973. interface = Interface(device=device, name=interface_name, type=InterfaceTypeChoices.TYPE_10GE_FIXED)
  1974. interface.save()
  1975. # Ensure that interface is created with no module
  1976. self.assertIsNone(interface.module)
  1977. # Create a module with adopted components
  1978. form_data['module_type'] = module_type
  1979. form_data['replicate_components'] = False
  1980. form_data['adopt_components'] = True
  1981. request = {
  1982. 'path': self._get_url('add'),
  1983. 'data': post_data(form_data),
  1984. }
  1985. self.assertHttpStatus(self.client.post(**request), 302)
  1986. # Re-retrieve interface to get new module id
  1987. interface.refresh_from_db()
  1988. # Check that the Interface now has a module
  1989. self.assertIsNotNone(interface.module)
  1990. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  1991. def test_module_bulk_adoption(self):
  1992. self.add_permissions('dcim.add_module')
  1993. interface_name = "Interface-1"
  1994. # Add an interface to the ModuleType
  1995. module_type = ModuleType.objects.first()
  1996. InterfaceTemplate(module_type=module_type, name=interface_name).save()
  1997. form_data = self.form_data.copy()
  1998. device = Device.objects.get(pk=form_data['device'])
  1999. # Create an interface to be adopted
  2000. interface = Interface(device=device, name=interface_name, type=InterfaceTypeChoices.TYPE_10GE_FIXED)
  2001. interface.save()
  2002. # Ensure that interface is created with no module
  2003. self.assertIsNone(interface.module)
  2004. # Create a module with adopted components
  2005. module_bay = ModuleBay.objects.get(device=device, name='Module Bay 4')
  2006. csv_data = [
  2007. "device,module_bay,module_type,status,replicate_components,adopt_components",
  2008. f"{device.name},{module_bay.name},{module_type.model},active,false,true"
  2009. ]
  2010. request = {
  2011. 'path': self._get_url('bulk_import'),
  2012. 'data': {
  2013. 'data': '\n'.join(csv_data),
  2014. 'format': ImportFormatChoices.CSV,
  2015. 'csv_delimiter': CSVDelimiterChoices.AUTO,
  2016. }
  2017. }
  2018. initial_count = self._get_queryset().count()
  2019. self.assertHttpStatus(self.client.post(**request), 302)
  2020. self.assertEqual(self._get_queryset().count(), initial_count + len(csv_data) - 1)
  2021. # Re-retrieve interface to get new module id
  2022. interface.refresh_from_db()
  2023. # Check that the Interface now has a module
  2024. self.assertIsNotNone(interface.module)
  2025. class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2026. model = ConsolePort
  2027. validation_excluded_fields = ('name', 'label')
  2028. @classmethod
  2029. def setUpTestData(cls):
  2030. device = create_test_device('Device 1')
  2031. console_ports = (
  2032. ConsolePort(device=device, name='Console Port 1'),
  2033. ConsolePort(device=device, name='Console Port 2'),
  2034. ConsolePort(device=device, name='Console Port 3'),
  2035. )
  2036. ConsolePort.objects.bulk_create(console_ports)
  2037. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2038. cls.form_data = {
  2039. 'device': device.pk,
  2040. 'name': 'Console Port X',
  2041. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  2042. 'description': 'A console port',
  2043. 'tags': sorted([t.pk for t in tags]),
  2044. }
  2045. cls.bulk_create_data = {
  2046. 'device': device.pk,
  2047. 'name': 'Console Port [4-6]',
  2048. # Test that a label can be applied to each generated console ports
  2049. 'label': 'Serial[3-5]',
  2050. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  2051. 'description': 'A console port',
  2052. 'tags': sorted([t.pk for t in tags]),
  2053. }
  2054. cls.bulk_edit_data = {
  2055. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  2056. 'description': 'New description',
  2057. }
  2058. cls.csv_data = (
  2059. "device,name",
  2060. "Device 1,Console Port 4",
  2061. "Device 1,Console Port 5",
  2062. "Device 1,Console Port 6",
  2063. )
  2064. cls.csv_update_data = (
  2065. "id,name,description",
  2066. f"{console_ports[0].pk},Console Port 7,New description7",
  2067. f"{console_ports[1].pk},Console Port 8,New description8",
  2068. f"{console_ports[2].pk},Console Port 9,New description9",
  2069. )
  2070. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2071. def test_trace(self):
  2072. consoleport = ConsolePort.objects.first()
  2073. consoleserverport = ConsoleServerPort.objects.create(
  2074. device=consoleport.device,
  2075. name='Console Server Port 1'
  2076. )
  2077. Cable(a_terminations=[consoleport], b_terminations=[consoleserverport]).save()
  2078. response = self.client.get(reverse('dcim:consoleport_trace', kwargs={'pk': consoleport.pk}))
  2079. self.assertHttpStatus(response, 200)
  2080. class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2081. model = ConsoleServerPort
  2082. validation_excluded_fields = ('name', 'label')
  2083. @classmethod
  2084. def setUpTestData(cls):
  2085. device = create_test_device('Device 1')
  2086. console_server_ports = (
  2087. ConsoleServerPort(device=device, name='Console Server Port 1'),
  2088. ConsoleServerPort(device=device, name='Console Server Port 2'),
  2089. ConsoleServerPort(device=device, name='Console Server Port 3'),
  2090. )
  2091. ConsoleServerPort.objects.bulk_create(console_server_ports)
  2092. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2093. cls.form_data = {
  2094. 'device': device.pk,
  2095. 'name': 'Console Server Port X',
  2096. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  2097. 'description': 'A console server port',
  2098. 'tags': [t.pk for t in tags],
  2099. }
  2100. cls.bulk_create_data = {
  2101. 'device': device.pk,
  2102. 'name': 'Console Server Port [4-6]',
  2103. 'type': ConsolePortTypeChoices.TYPE_RJ45,
  2104. 'description': 'A console server port',
  2105. 'tags': [t.pk for t in tags],
  2106. }
  2107. cls.bulk_edit_data = {
  2108. 'type': ConsolePortTypeChoices.TYPE_RJ11,
  2109. 'description': 'New description',
  2110. }
  2111. cls.csv_data = (
  2112. "device,name",
  2113. "Device 1,Console Server Port 4",
  2114. "Device 1,Console Server Port 5",
  2115. "Device 1,Console Server Port 6",
  2116. )
  2117. cls.csv_update_data = (
  2118. "id,name,description",
  2119. f"{console_server_ports[0].pk},Console Server Port 7,New description 7",
  2120. f"{console_server_ports[1].pk},Console Server Port 8,New description 8",
  2121. f"{console_server_ports[2].pk},Console Server Port 9,New description 9",
  2122. )
  2123. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2124. def test_trace(self):
  2125. consoleserverport = ConsoleServerPort.objects.first()
  2126. consoleport = ConsolePort.objects.create(
  2127. device=consoleserverport.device,
  2128. name='Console Port 1'
  2129. )
  2130. Cable(a_terminations=[consoleserverport], b_terminations=[consoleport]).save()
  2131. response = self.client.get(reverse('dcim:consoleserverport_trace', kwargs={'pk': consoleserverport.pk}))
  2132. self.assertHttpStatus(response, 200)
  2133. class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2134. model = PowerPort
  2135. validation_excluded_fields = ('name', 'label')
  2136. @classmethod
  2137. def setUpTestData(cls):
  2138. device = create_test_device('Device 1')
  2139. power_ports = (
  2140. PowerPort(device=device, name='Power Port 1'),
  2141. PowerPort(device=device, name='Power Port 2'),
  2142. PowerPort(device=device, name='Power Port 3'),
  2143. )
  2144. PowerPort.objects.bulk_create(power_ports)
  2145. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2146. cls.form_data = {
  2147. 'device': device.pk,
  2148. 'name': 'Power Port X',
  2149. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  2150. 'maximum_draw': 100,
  2151. 'allocated_draw': 50,
  2152. 'description': 'A power port',
  2153. 'tags': [t.pk for t in tags],
  2154. }
  2155. cls.bulk_create_data = {
  2156. 'device': device.pk,
  2157. 'name': 'Power Port [4-6]]',
  2158. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  2159. 'maximum_draw': 100,
  2160. 'allocated_draw': 50,
  2161. 'description': 'A power port',
  2162. 'tags': [t.pk for t in tags],
  2163. }
  2164. cls.bulk_edit_data = {
  2165. 'type': PowerPortTypeChoices.TYPE_IEC_C14,
  2166. 'maximum_draw': 100,
  2167. 'allocated_draw': 50,
  2168. 'description': 'New description',
  2169. }
  2170. cls.csv_data = (
  2171. "device,name",
  2172. "Device 1,Power Port 4",
  2173. "Device 1,Power Port 5",
  2174. "Device 1,Power Port 6",
  2175. )
  2176. cls.csv_update_data = (
  2177. "id,name,description",
  2178. f"{power_ports[0].pk},Power Port 7,New description7",
  2179. f"{power_ports[1].pk},Power Port 8,New description8",
  2180. f"{power_ports[2].pk},Power Port 9,New description9",
  2181. )
  2182. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2183. def test_trace(self):
  2184. powerport = PowerPort.objects.first()
  2185. poweroutlet = PowerOutlet.objects.create(
  2186. device=powerport.device,
  2187. name='Power Outlet 1'
  2188. )
  2189. Cable(a_terminations=[powerport], b_terminations=[poweroutlet]).save()
  2190. response = self.client.get(reverse('dcim:powerport_trace', kwargs={'pk': powerport.pk}))
  2191. self.assertHttpStatus(response, 200)
  2192. class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2193. model = PowerOutlet
  2194. validation_excluded_fields = ('name', 'label')
  2195. @classmethod
  2196. def setUpTestData(cls):
  2197. device = create_test_device('Device 1')
  2198. powerports = (
  2199. PowerPort(device=device, name='Power Port 1'),
  2200. PowerPort(device=device, name='Power Port 2'),
  2201. )
  2202. PowerPort.objects.bulk_create(powerports)
  2203. power_outlets = (
  2204. PowerOutlet(device=device, name='Power Outlet 1', power_port=powerports[0]),
  2205. PowerOutlet(device=device, name='Power Outlet 2', power_port=powerports[0]),
  2206. PowerOutlet(device=device, name='Power Outlet 3', power_port=powerports[0]),
  2207. )
  2208. PowerOutlet.objects.bulk_create(power_outlets)
  2209. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2210. cls.form_data = {
  2211. 'device': device.pk,
  2212. 'name': 'Power Outlet X',
  2213. 'type': PowerOutletTypeChoices.TYPE_IEC_C13,
  2214. 'status': PowerOutletStatusChoices.STATUS_ENABLED,
  2215. 'power_port': powerports[1].pk,
  2216. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  2217. 'description': 'A power outlet',
  2218. 'tags': [t.pk for t in tags],
  2219. }
  2220. cls.bulk_create_data = {
  2221. 'device': device.pk,
  2222. 'name': 'Power Outlet [4-6]',
  2223. 'type': PowerOutletTypeChoices.TYPE_IEC_C13,
  2224. 'status': PowerOutletStatusChoices.STATUS_ENABLED,
  2225. 'power_port': powerports[1].pk,
  2226. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  2227. 'description': 'A power outlet',
  2228. 'tags': [t.pk for t in tags],
  2229. }
  2230. cls.bulk_edit_data = {
  2231. 'type': PowerOutletTypeChoices.TYPE_IEC_C15,
  2232. 'status': PowerOutletStatusChoices.STATUS_ENABLED,
  2233. 'power_port': powerports[1].pk,
  2234. 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
  2235. 'description': 'New description',
  2236. }
  2237. cls.csv_data = (
  2238. "device,name",
  2239. "Device 1,Power Outlet 4",
  2240. "Device 1,Power Outlet 5",
  2241. "Device 1,Power Outlet 6",
  2242. )
  2243. cls.csv_update_data = (
  2244. "id,name,description",
  2245. f"{power_outlets[0].pk},Power Outlet 7,New description7",
  2246. f"{power_outlets[1].pk},Power Outlet 8,New description8",
  2247. f"{power_outlets[2].pk},Power Outlet 9,New description9",
  2248. )
  2249. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2250. def test_trace(self):
  2251. poweroutlet = PowerOutlet.objects.first()
  2252. powerport = PowerPort.objects.first()
  2253. Cable(a_terminations=[poweroutlet], b_terminations=[powerport]).save()
  2254. response = self.client.get(reverse('dcim:poweroutlet_trace', kwargs={'pk': poweroutlet.pk}))
  2255. self.assertHttpStatus(response, 200)
  2256. class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2257. model = Interface
  2258. validation_excluded_fields = ('name', 'label')
  2259. @classmethod
  2260. def setUpTestData(cls):
  2261. device = create_test_device('Device 1')
  2262. interfaces = (
  2263. Interface(device=device, name='Interface 1'),
  2264. Interface(device=device, name='Interface 2'),
  2265. Interface(device=device, name='Interface 3'),
  2266. Interface(device=device, name='LAG', type=InterfaceTypeChoices.TYPE_LAG),
  2267. Interface(device=device, name='_BRIDGE', type=InterfaceTypeChoices.TYPE_VIRTUAL), # Must be ordered last
  2268. )
  2269. Interface.objects.bulk_create(interfaces)
  2270. vlans = (
  2271. VLAN(vid=1, name='VLAN1', site=device.site),
  2272. VLAN(vid=101, name='VLAN101', site=device.site),
  2273. VLAN(vid=102, name='VLAN102', site=device.site),
  2274. VLAN(vid=103, name='VLAN103', site=device.site),
  2275. )
  2276. VLAN.objects.bulk_create(vlans)
  2277. wireless_lans = (
  2278. WirelessLAN(ssid='WLAN1'),
  2279. WirelessLAN(ssid='WLAN2'),
  2280. )
  2281. WirelessLAN.objects.bulk_create(wireless_lans)
  2282. vrfs = (
  2283. VRF(name='VRF 1'),
  2284. VRF(name='VRF 2'),
  2285. VRF(name='VRF 3'),
  2286. )
  2287. VRF.objects.bulk_create(vrfs)
  2288. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2289. cls.form_data = {
  2290. 'device': device.pk,
  2291. 'name': 'Interface X',
  2292. 'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
  2293. 'enabled': False,
  2294. 'bridge': interfaces[4].pk,
  2295. 'lag': interfaces[3].pk,
  2296. 'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
  2297. 'mtu': 65000,
  2298. 'speed': 1000000,
  2299. 'duplex': 'full',
  2300. 'mgmt_only': True,
  2301. 'description': 'A front port',
  2302. 'mode': InterfaceModeChoices.MODE_TAGGED,
  2303. 'tx_power': 10,
  2304. 'poe_mode': InterfacePoEModeChoices.MODE_PSE,
  2305. 'poe_type': InterfacePoETypeChoices.TYPE_1_8023AF,
  2306. 'untagged_vlan': vlans[0].pk,
  2307. 'tagged_vlans': [v.pk for v in vlans[1:4]],
  2308. 'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
  2309. 'vrf': vrfs[0].pk,
  2310. 'tags': [t.pk for t in tags],
  2311. }
  2312. cls.bulk_create_data = {
  2313. 'device': device.pk,
  2314. 'name': 'Interface [4-6]',
  2315. 'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
  2316. 'enabled': False,
  2317. 'bridge': interfaces[4].pk,
  2318. 'lag': interfaces[3].pk,
  2319. 'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
  2320. 'mtu': 2000,
  2321. 'speed': 100000,
  2322. 'duplex': 'half',
  2323. 'mgmt_only': True,
  2324. 'description': 'A front port',
  2325. 'poe_mode': InterfacePoEModeChoices.MODE_PSE,
  2326. 'poe_type': InterfacePoETypeChoices.TYPE_1_8023AF,
  2327. 'mode': InterfaceModeChoices.MODE_TAGGED,
  2328. 'untagged_vlan': vlans[0].pk,
  2329. 'tagged_vlans': [v.pk for v in vlans[1:4]],
  2330. 'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
  2331. 'vrf': vrfs[0].pk,
  2332. 'tags': [t.pk for t in tags],
  2333. }
  2334. cls.bulk_edit_data = {
  2335. 'type': InterfaceTypeChoices.TYPE_1GE_FIXED,
  2336. 'enabled': True,
  2337. 'lag': interfaces[3].pk,
  2338. 'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
  2339. 'mtu': 2000,
  2340. 'speed': 1000000,
  2341. 'duplex': 'full',
  2342. 'mgmt_only': True,
  2343. 'description': 'New description',
  2344. 'poe_mode': InterfacePoEModeChoices.MODE_PD,
  2345. 'poe_type': InterfacePoETypeChoices.TYPE_2_8023AT,
  2346. 'mode': InterfaceModeChoices.MODE_TAGGED,
  2347. 'tx_power': 10,
  2348. 'untagged_vlan': vlans[0].pk,
  2349. 'tagged_vlans': [v.pk for v in vlans[1:4]],
  2350. 'vrf': vrfs[1].pk,
  2351. }
  2352. cls.csv_data = (
  2353. "device,name,type,vrf.pk,poe_mode,poe_type",
  2354. f"Device 1,Interface 4,1000base-t,{vrfs[0].pk},pse,type1-ieee802.3af",
  2355. f"Device 1,Interface 5,1000base-t,{vrfs[0].pk},pse,type1-ieee802.3af",
  2356. f"Device 1,Interface 6,1000base-t,{vrfs[0].pk},pse,type1-ieee802.3af",
  2357. )
  2358. cls.csv_update_data = (
  2359. "id,name,description",
  2360. f"{interfaces[0].pk},Interface 7,New description7",
  2361. f"{interfaces[1].pk},Interface 8,New description8",
  2362. f"{interfaces[2].pk},Interface 9,New description9",
  2363. )
  2364. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2365. def test_trace(self):
  2366. interface1, interface2 = Interface.objects.all()[:2]
  2367. Cable(a_terminations=[interface1], b_terminations=[interface2]).save()
  2368. response = self.client.get(reverse('dcim:interface_trace', kwargs={'pk': interface1.pk}))
  2369. self.assertHttpStatus(response, 200)
  2370. def test_bulk_delete_child_interfaces(self):
  2371. interface1 = Interface.objects.get(name='Interface 1')
  2372. device = interface1.device
  2373. self.add_permissions('dcim.delete_interface')
  2374. # Create a child interface
  2375. child = Interface.objects.create(
  2376. device=device,
  2377. name='Interface 1A',
  2378. type=InterfaceTypeChoices.TYPE_VIRTUAL,
  2379. parent=interface1
  2380. )
  2381. self.assertEqual(device.interfaces.count(), 6)
  2382. # Attempt to delete only the parent interface
  2383. data = {
  2384. 'confirm': True,
  2385. }
  2386. self.client.post(self._get_url('delete', interface1), data)
  2387. self.assertEqual(device.interfaces.count(), 6) # Parent was not deleted
  2388. # Attempt to bulk delete parent & child together
  2389. data = {
  2390. 'pk': [interface1.pk, child.pk],
  2391. 'confirm': True,
  2392. '_confirm': True, # Form button
  2393. }
  2394. self.client.post(self._get_url('bulk_delete'), data)
  2395. self.assertEqual(device.interfaces.count(), 4) # Child & parent were both deleted
  2396. class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2397. model = FrontPort
  2398. validation_excluded_fields = ('name', 'label', 'rear_port')
  2399. @classmethod
  2400. def setUpTestData(cls):
  2401. device = create_test_device('Device 1')
  2402. rearports = (
  2403. RearPort(device=device, name='Rear Port 1'),
  2404. RearPort(device=device, name='Rear Port 2'),
  2405. RearPort(device=device, name='Rear Port 3'),
  2406. RearPort(device=device, name='Rear Port 4'),
  2407. RearPort(device=device, name='Rear Port 5'),
  2408. RearPort(device=device, name='Rear Port 6'),
  2409. )
  2410. RearPort.objects.bulk_create(rearports)
  2411. front_ports = (
  2412. FrontPort(device=device, name='Front Port 1', rear_port=rearports[0]),
  2413. FrontPort(device=device, name='Front Port 2', rear_port=rearports[1]),
  2414. FrontPort(device=device, name='Front Port 3', rear_port=rearports[2]),
  2415. )
  2416. FrontPort.objects.bulk_create(front_ports)
  2417. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2418. cls.form_data = {
  2419. 'device': device.pk,
  2420. 'name': 'Front Port X',
  2421. 'type': PortTypeChoices.TYPE_8P8C,
  2422. 'rear_port': rearports[3].pk,
  2423. 'rear_port_position': 1,
  2424. 'description': 'New description',
  2425. 'tags': [t.pk for t in tags],
  2426. }
  2427. cls.bulk_create_data = {
  2428. 'device': device.pk,
  2429. 'name': 'Front Port [4-6]',
  2430. 'type': PortTypeChoices.TYPE_8P8C,
  2431. 'rear_port': [f'{rp.pk}:1' for rp in rearports[3:6]],
  2432. 'description': 'New description',
  2433. 'tags': [t.pk for t in tags],
  2434. }
  2435. cls.bulk_edit_data = {
  2436. 'type': PortTypeChoices.TYPE_8P8C,
  2437. 'description': 'New description',
  2438. }
  2439. cls.csv_data = (
  2440. "device,name,type,rear_port,rear_port_position",
  2441. "Device 1,Front Port 4,8p8c,Rear Port 4,1",
  2442. "Device 1,Front Port 5,8p8c,Rear Port 5,1",
  2443. "Device 1,Front Port 6,8p8c,Rear Port 6,1",
  2444. )
  2445. cls.csv_update_data = (
  2446. "id,name,description",
  2447. f"{front_ports[0].pk},Front Port 7,New description7",
  2448. f"{front_ports[1].pk},Front Port 8,New description8",
  2449. f"{front_ports[2].pk},Front Port 9,New description9",
  2450. )
  2451. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2452. def test_trace(self):
  2453. frontport = FrontPort.objects.first()
  2454. interface = Interface.objects.create(
  2455. device=frontport.device,
  2456. name='Interface 1'
  2457. )
  2458. Cable(a_terminations=[frontport], b_terminations=[interface]).save()
  2459. response = self.client.get(reverse('dcim:frontport_trace', kwargs={'pk': frontport.pk}))
  2460. self.assertHttpStatus(response, 200)
  2461. class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2462. model = RearPort
  2463. validation_excluded_fields = ('name', 'label')
  2464. @classmethod
  2465. def setUpTestData(cls):
  2466. device = create_test_device('Device 1')
  2467. rear_ports = (
  2468. RearPort(device=device, name='Rear Port 1'),
  2469. RearPort(device=device, name='Rear Port 2'),
  2470. RearPort(device=device, name='Rear Port 3'),
  2471. )
  2472. RearPort.objects.bulk_create(rear_ports)
  2473. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2474. cls.form_data = {
  2475. 'device': device.pk,
  2476. 'name': 'Rear Port X',
  2477. 'type': PortTypeChoices.TYPE_8P8C,
  2478. 'positions': 3,
  2479. 'description': 'A rear port',
  2480. 'tags': [t.pk for t in tags],
  2481. }
  2482. cls.bulk_create_data = {
  2483. 'device': device.pk,
  2484. 'name': 'Rear Port [4-6]',
  2485. 'type': PortTypeChoices.TYPE_8P8C,
  2486. 'positions': 3,
  2487. 'description': 'A rear port',
  2488. 'tags': [t.pk for t in tags],
  2489. }
  2490. cls.bulk_edit_data = {
  2491. 'type': PortTypeChoices.TYPE_8P8C,
  2492. 'description': 'New description',
  2493. }
  2494. cls.csv_data = (
  2495. "device,name,type,positions",
  2496. "Device 1,Rear Port 4,8p8c,1",
  2497. "Device 1,Rear Port 5,8p8c,1",
  2498. "Device 1,Rear Port 6,8p8c,1",
  2499. )
  2500. cls.csv_update_data = (
  2501. "id,name,description",
  2502. f"{rear_ports[0].pk},Rear Port 7,New description7",
  2503. f"{rear_ports[1].pk},Rear Port 8,New description8",
  2504. f"{rear_ports[2].pk},Rear Port 9,New description9",
  2505. )
  2506. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2507. def test_trace(self):
  2508. rearport = RearPort.objects.first()
  2509. interface = Interface.objects.create(
  2510. device=rearport.device,
  2511. name='Interface 1'
  2512. )
  2513. Cable(a_terminations=[rearport], b_terminations=[interface]).save()
  2514. response = self.client.get(reverse('dcim:rearport_trace', kwargs={'pk': rearport.pk}))
  2515. self.assertHttpStatus(response, 200)
  2516. class ModuleBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2517. model = ModuleBay
  2518. validation_excluded_fields = ('name', 'label')
  2519. @classmethod
  2520. def setUpTestData(cls):
  2521. device = create_test_device('Device 1')
  2522. module_bays = (
  2523. ModuleBay(device=device, name='Module Bay 1'),
  2524. ModuleBay(device=device, name='Module Bay 2'),
  2525. ModuleBay(device=device, name='Module Bay 3'),
  2526. )
  2527. for module_bay in module_bays:
  2528. module_bay.save()
  2529. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2530. cls.form_data = {
  2531. 'device': device.pk,
  2532. 'name': 'Module Bay X',
  2533. 'description': 'A device bay',
  2534. 'tags': [t.pk for t in tags],
  2535. }
  2536. cls.bulk_create_data = {
  2537. 'device': device.pk,
  2538. 'name': 'Module Bay [4-6]',
  2539. 'description': 'A module bay',
  2540. 'tags': [t.pk for t in tags],
  2541. }
  2542. cls.bulk_edit_data = {
  2543. 'description': 'New description',
  2544. }
  2545. cls.csv_data = (
  2546. "device,name",
  2547. "Device 1,Module Bay 4",
  2548. "Device 1,Module Bay 5",
  2549. "Device 1,Module Bay 6",
  2550. )
  2551. cls.csv_update_data = (
  2552. "id,name,description",
  2553. f"{module_bays[0].pk},Module Bay 7,New description7",
  2554. f"{module_bays[1].pk},Module Bay 8,New description8",
  2555. f"{module_bays[2].pk},Module Bay 9,New description9",
  2556. )
  2557. class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2558. model = DeviceBay
  2559. validation_excluded_fields = ('name', 'label')
  2560. @classmethod
  2561. def setUpTestData(cls):
  2562. device = create_test_device('Device 1')
  2563. # Update the DeviceType subdevice role to allow adding DeviceBays
  2564. DeviceType.objects.update(subdevice_role=SubdeviceRoleChoices.ROLE_PARENT)
  2565. device_bays = (
  2566. DeviceBay(device=device, name='Device Bay 1'),
  2567. DeviceBay(device=device, name='Device Bay 2'),
  2568. DeviceBay(device=device, name='Device Bay 3'),
  2569. )
  2570. DeviceBay.objects.bulk_create(device_bays)
  2571. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2572. cls.form_data = {
  2573. 'device': device.pk,
  2574. 'name': 'Device Bay X',
  2575. 'description': 'A device bay',
  2576. 'tags': [t.pk for t in tags],
  2577. }
  2578. cls.bulk_create_data = {
  2579. 'device': device.pk,
  2580. 'name': 'Device Bay [4-6]',
  2581. 'description': 'A device bay',
  2582. 'tags': [t.pk for t in tags],
  2583. }
  2584. cls.bulk_edit_data = {
  2585. 'description': 'New description',
  2586. }
  2587. cls.csv_data = (
  2588. "device,name",
  2589. "Device 1,Device Bay 4",
  2590. "Device 1,Device Bay 5",
  2591. "Device 1,Device Bay 6",
  2592. )
  2593. cls.csv_update_data = (
  2594. "id,name,description",
  2595. f"{device_bays[0].pk},Device Bay 7,New description7",
  2596. f"{device_bays[1].pk},Device Bay 8,New description8",
  2597. f"{device_bays[2].pk},Device Bay 9,New description9",
  2598. )
  2599. class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
  2600. model = InventoryItem
  2601. validation_excluded_fields = ('name', 'label')
  2602. @classmethod
  2603. def setUpTestData(cls):
  2604. device = create_test_device('Device 1')
  2605. manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
  2606. roles = (
  2607. InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
  2608. InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
  2609. )
  2610. InventoryItemRole.objects.bulk_create(roles)
  2611. inventory_item1 = InventoryItem.objects.create(
  2612. device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer
  2613. )
  2614. inventory_item2 = InventoryItem.objects.create(
  2615. device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer
  2616. )
  2617. inventory_item3 = InventoryItem.objects.create(
  2618. device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer
  2619. )
  2620. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2621. cls.form_data = {
  2622. 'device': device.pk,
  2623. 'role': roles[1].pk,
  2624. 'manufacturer': manufacturer.pk,
  2625. 'name': 'Inventory Item X',
  2626. 'parent': None,
  2627. 'discovered': False,
  2628. 'part_id': '123456',
  2629. 'serial': '123ABC',
  2630. 'asset_tag': 'ABC123',
  2631. 'status': InventoryItemStatusChoices.STATUS_ACTIVE,
  2632. 'description': 'An inventory item',
  2633. 'tags': [t.pk for t in tags],
  2634. }
  2635. cls.bulk_create_data = {
  2636. 'device': device.pk,
  2637. 'name': 'Inventory Item [4-6]',
  2638. 'role': roles[1].pk,
  2639. 'manufacturer': manufacturer.pk,
  2640. 'parent': None,
  2641. 'discovered': False,
  2642. 'part_id': '123456',
  2643. 'serial': '123ABC',
  2644. 'status': InventoryItemStatusChoices.STATUS_ACTIVE,
  2645. 'description': 'An inventory item',
  2646. 'tags': [t.pk for t in tags],
  2647. }
  2648. cls.bulk_edit_data = {
  2649. 'role': roles[1].pk,
  2650. 'part_id': '123456',
  2651. 'description': 'New description',
  2652. }
  2653. cls.csv_data = (
  2654. "device,name,parent,status",
  2655. "Device 1,Inventory Item 4,Inventory Item 1,active",
  2656. "Device 1,Inventory Item 5,Inventory Item 2,planned",
  2657. "Device 1,Inventory Item 6,Inventory Item 3,failed",
  2658. )
  2659. cls.csv_update_data = (
  2660. "id,name,description",
  2661. f"{inventory_item1.pk},Inventory Item 7,New description7",
  2662. f"{inventory_item2.pk},Inventory Item 8,New description8",
  2663. f"{inventory_item3.pk},Inventory Item 9,New description9",
  2664. )
  2665. class InventoryItemRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
  2666. model = InventoryItemRole
  2667. @classmethod
  2668. def setUpTestData(cls):
  2669. inventory_item_roles = (
  2670. InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
  2671. InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
  2672. InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3'),
  2673. )
  2674. InventoryItemRole.objects.bulk_create(inventory_item_roles)
  2675. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2676. cls.form_data = {
  2677. 'name': 'Inventory Item Role X',
  2678. 'slug': 'inventory-item-role-x',
  2679. 'color': 'c0c0c0',
  2680. 'description': 'New inventory item role',
  2681. 'tags': [t.pk for t in tags],
  2682. }
  2683. cls.csv_data = (
  2684. "name,slug,color",
  2685. "Inventory Item Role 4,inventory-item-role-4,ff0000",
  2686. "Inventory Item Role 5,inventory-item-role-5,00ff00",
  2687. "Inventory Item Role 6,inventory-item-role-6,0000ff",
  2688. )
  2689. cls.csv_update_data = (
  2690. "id,name,description",
  2691. f"{inventory_item_roles[0].pk},Inventory Item Role 7,New description7",
  2692. f"{inventory_item_roles[1].pk},Inventory Item Role 8,New description8",
  2693. f"{inventory_item_roles[2].pk},Inventory Item Role 9,New description9",
  2694. )
  2695. cls.bulk_edit_data = {
  2696. 'color': '00ff00',
  2697. 'description': 'New description',
  2698. }
  2699. # TODO: Change base class to PrimaryObjectViewTestCase
  2700. # Blocked by lack of common creation view for cables (termination A must be initialized)
  2701. class CableTestCase(
  2702. ViewTestCases.GetObjectViewTestCase,
  2703. ViewTestCases.GetObjectChangelogViewTestCase,
  2704. ViewTestCases.EditObjectViewTestCase,
  2705. ViewTestCases.DeleteObjectViewTestCase,
  2706. ViewTestCases.ListObjectsViewTestCase,
  2707. ViewTestCases.BulkImportObjectsViewTestCase,
  2708. ViewTestCases.BulkEditObjectsViewTestCase,
  2709. ViewTestCases.BulkDeleteObjectsViewTestCase
  2710. ):
  2711. model = Cable
  2712. @classmethod
  2713. def setUpTestData(cls):
  2714. site = Site.objects.create(name='Site 1', slug='site-1')
  2715. manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
  2716. devicetype = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer)
  2717. role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
  2718. vc = VirtualChassis.objects.create(name='Virtual Chassis')
  2719. devices = (
  2720. Device(name='Device 1', site=site, device_type=devicetype, role=role),
  2721. Device(name='Device 2', site=site, device_type=devicetype, role=role),
  2722. Device(name='Device 3', site=site, device_type=devicetype, role=role),
  2723. Device(name='Device 4', site=site, device_type=devicetype, role=role),
  2724. )
  2725. Device.objects.bulk_create(devices)
  2726. vc.members.set((devices[0], devices[1], devices[2]))
  2727. vc.master = devices[0]
  2728. vc.save()
  2729. interfaces = (
  2730. Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2731. Interface(device=devices[0], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2732. Interface(device=devices[0], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2733. Interface(device=devices[1], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2734. Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2735. Interface(device=devices[1], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2736. Interface(device=devices[2], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2737. Interface(device=devices[2], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2738. Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2739. Interface(device=devices[3], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2740. Interface(device=devices[3], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2741. Interface(device=devices[3], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2742. Interface(device=devices[1], name='Device 2 Interface', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2743. Interface(device=devices[2], name='Device 3 Interface', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2744. Interface(device=devices[3], name='Interface 4', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2745. Interface(device=devices[3], name='Interface 5', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
  2746. )
  2747. Interface.objects.bulk_create(interfaces)
  2748. cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]], type=CableTypeChoices.TYPE_CAT6)
  2749. cable1.save()
  2750. cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]], type=CableTypeChoices.TYPE_CAT6)
  2751. cable2.save()
  2752. cable3 = Cable(a_terminations=[interfaces[2]], b_terminations=[interfaces[5]], type=CableTypeChoices.TYPE_CAT6)
  2753. cable3.save()
  2754. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2755. cls.form_data = {
  2756. # TODO: Revisit this limitation
  2757. # Changing terminations not supported when editing an existing Cable
  2758. 'a_terminations': [interfaces[0].pk],
  2759. 'b_terminations': [interfaces[3].pk],
  2760. 'type': CableTypeChoices.TYPE_CAT6,
  2761. 'status': LinkStatusChoices.STATUS_PLANNED,
  2762. 'label': 'Label',
  2763. 'color': 'c0c0c0',
  2764. 'length': 100,
  2765. 'length_unit': CableLengthUnitChoices.UNIT_FOOT,
  2766. 'tags': [t.pk for t in tags],
  2767. }
  2768. cls.csv_data = (
  2769. "side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name",
  2770. "Device 3,dcim.interface,Interface 1,Device 4,dcim.interface,Interface 1",
  2771. "Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2",
  2772. "Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
  2773. "Device 1,dcim.interface,Device 2 Interface,Device 4,dcim.interface,Interface 4",
  2774. "Device 1,dcim.interface,Device 3 Interface,Device 4,dcim.interface,Interface 5",
  2775. )
  2776. cls.csv_update_data = (
  2777. "id,label,color",
  2778. f"{cable1.pk},New label7,00ff00",
  2779. f"{cable2.pk},New label8,00ff00",
  2780. f"{cable3.pk},New label9,00ff00",
  2781. )
  2782. cls.bulk_edit_data = {
  2783. 'type': CableTypeChoices.TYPE_CAT5E,
  2784. 'status': LinkStatusChoices.STATUS_CONNECTED,
  2785. 'label': 'New label',
  2786. 'color': '00ff00',
  2787. 'length': 50,
  2788. 'length_unit': CableLengthUnitChoices.UNIT_METER,
  2789. }
  2790. def model_to_dict(self, *args, **kwargs):
  2791. data = super().model_to_dict(*args, **kwargs)
  2792. # Serialize termination objects
  2793. if 'a_terminations' in data:
  2794. data['a_terminations'] = [obj.pk for obj in data['a_terminations']]
  2795. if 'b_terminations' in data:
  2796. data['b_terminations'] = [obj.pk for obj in data['b_terminations']]
  2797. return data
  2798. class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  2799. model = VirtualChassis
  2800. @classmethod
  2801. def setUpTestData(cls):
  2802. site = Site.objects.create(name='Site 1', slug='site-1')
  2803. manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
  2804. device_type = DeviceType.objects.create(
  2805. manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
  2806. )
  2807. role = DeviceRole.objects.create(
  2808. name='Device Role', slug='device-role-1'
  2809. )
  2810. devices = (
  2811. Device(device_type=device_type, role=role, name='Device 1', site=site),
  2812. Device(device_type=device_type, role=role, name='Device 2', site=site),
  2813. Device(device_type=device_type, role=role, name='Device 3', site=site),
  2814. Device(device_type=device_type, role=role, name='Device 4', site=site),
  2815. Device(device_type=device_type, role=role, name='Device 5', site=site),
  2816. Device(device_type=device_type, role=role, name='Device 6', site=site),
  2817. Device(device_type=device_type, role=role, name='Device 7', site=site),
  2818. Device(device_type=device_type, role=role, name='Device 8', site=site),
  2819. Device(device_type=device_type, role=role, name='Device 9', site=site),
  2820. Device(device_type=device_type, role=role, name='Device 10', site=site),
  2821. Device(device_type=device_type, role=role, name='Device 11', site=site),
  2822. Device(device_type=device_type, role=role, name='Device 12', site=site),
  2823. )
  2824. Device.objects.bulk_create(devices)
  2825. # Create three VirtualChassis with three members each
  2826. vc1 = VirtualChassis.objects.create(name='VC1', master=devices[0], domain='domain-1')
  2827. Device.objects.filter(pk=devices[0].pk).update(virtual_chassis=vc1, vc_position=1)
  2828. Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=vc1, vc_position=2)
  2829. Device.objects.filter(pk=devices[2].pk).update(virtual_chassis=vc1, vc_position=3)
  2830. vc2 = VirtualChassis.objects.create(name='VC2', master=devices[3], domain='domain-2')
  2831. Device.objects.filter(pk=devices[3].pk).update(virtual_chassis=vc2, vc_position=1)
  2832. Device.objects.filter(pk=devices[4].pk).update(virtual_chassis=vc2, vc_position=2)
  2833. Device.objects.filter(pk=devices[5].pk).update(virtual_chassis=vc2, vc_position=3)
  2834. vc3 = VirtualChassis.objects.create(name='VC3', master=devices[6], domain='domain-3')
  2835. Device.objects.filter(pk=devices[6].pk).update(virtual_chassis=vc3, vc_position=1)
  2836. Device.objects.filter(pk=devices[7].pk).update(virtual_chassis=vc3, vc_position=2)
  2837. Device.objects.filter(pk=devices[8].pk).update(virtual_chassis=vc3, vc_position=3)
  2838. cls.form_data = {
  2839. 'name': 'VC4',
  2840. 'domain': 'domain-4',
  2841. # Management form data for VC members
  2842. 'form-TOTAL_FORMS': 0,
  2843. 'form-INITIAL_FORMS': 3,
  2844. 'form-MIN_NUM_FORMS': 0,
  2845. 'form-MAX_NUM_FORMS': 1000,
  2846. }
  2847. cls.csv_data = (
  2848. "name,domain,master",
  2849. "VC4,Domain 4,Device 10",
  2850. "VC5,Domain 5,Device 11",
  2851. "VC6,Domain 6,Device 12",
  2852. )
  2853. cls.csv_update_data = (
  2854. "id,name,domain",
  2855. f"{vc1.pk},VC7,Domain 7",
  2856. f"{vc2.pk},VC8,Domain 8",
  2857. f"{vc3.pk},VC9,Domain 9",
  2858. )
  2859. cls.bulk_edit_data = {
  2860. 'domain': 'domain-x',
  2861. }
  2862. class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  2863. model = PowerPanel
  2864. @classmethod
  2865. def setUpTestData(cls):
  2866. sites = (
  2867. Site(name='Site 1', slug='site-1'),
  2868. Site(name='Site 2', slug='site-2'),
  2869. )
  2870. Site.objects.bulk_create(sites)
  2871. locations = (
  2872. Location(name='Location 1', slug='location-1', site=sites[0]),
  2873. Location(name='Location 2', slug='location-2', site=sites[1]),
  2874. )
  2875. for location in locations:
  2876. location.save()
  2877. power_panels = (
  2878. PowerPanel(site=sites[0], location=locations[0], name='Power Panel 1'),
  2879. PowerPanel(site=sites[0], location=locations[0], name='Power Panel 2'),
  2880. PowerPanel(site=sites[0], location=locations[0], name='Power Panel 3'),
  2881. )
  2882. PowerPanel.objects.bulk_create(power_panels)
  2883. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2884. cls.form_data = {
  2885. 'site': sites[1].pk,
  2886. 'location': locations[1].pk,
  2887. 'name': 'Power Panel X',
  2888. 'tags': [t.pk for t in tags],
  2889. }
  2890. cls.csv_data = (
  2891. "site,location,name",
  2892. "Site 1,Location 1,Power Panel 4",
  2893. "Site 1,Location 1,Power Panel 5",
  2894. "Site 1,Location 1,Power Panel 6",
  2895. )
  2896. cls.csv_update_data = (
  2897. "id,name",
  2898. f"{power_panels[0].pk},Power Panel 7",
  2899. f"{power_panels[1].pk},Power Panel 8",
  2900. f"{power_panels[2].pk},Power Panel 9",
  2901. )
  2902. cls.bulk_edit_data = {
  2903. 'site': sites[1].pk,
  2904. 'location': locations[1].pk,
  2905. }
  2906. class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  2907. model = PowerFeed
  2908. @classmethod
  2909. def setUpTestData(cls):
  2910. site = Site.objects.create(name='Site 1', slug='site-1')
  2911. powerpanels = (
  2912. PowerPanel(site=site, name='Power Panel 1'),
  2913. PowerPanel(site=site, name='Power Panel 2'),
  2914. )
  2915. PowerPanel.objects.bulk_create(powerpanels)
  2916. racks = (
  2917. Rack(site=site, name='Rack 1'),
  2918. Rack(site=site, name='Rack 2'),
  2919. )
  2920. Rack.objects.bulk_create(racks)
  2921. power_feeds = (
  2922. PowerFeed(name='Power Feed 1', power_panel=powerpanels[0], rack=racks[0]),
  2923. PowerFeed(name='Power Feed 2', power_panel=powerpanels[0], rack=racks[0]),
  2924. PowerFeed(name='Power Feed 3', power_panel=powerpanels[0], rack=racks[0]),
  2925. )
  2926. PowerFeed.objects.bulk_create(power_feeds)
  2927. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2928. cls.form_data = {
  2929. 'name': 'Power Feed X',
  2930. 'power_panel': powerpanels[1].pk,
  2931. 'rack': racks[1].pk,
  2932. 'status': PowerFeedStatusChoices.STATUS_PLANNED,
  2933. 'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
  2934. 'supply': PowerFeedSupplyChoices.SUPPLY_DC,
  2935. 'phase': PowerFeedPhaseChoices.PHASE_3PHASE,
  2936. 'voltage': 100,
  2937. 'amperage': 100,
  2938. 'max_utilization': 50,
  2939. 'comments': 'New comments',
  2940. 'tags': [t.pk for t in tags],
  2941. }
  2942. cls.csv_data = (
  2943. "site,power_panel,name,status,type,supply,phase,voltage,amperage,max_utilization",
  2944. "Site 1,Power Panel 1,Power Feed 4,active,primary,ac,single-phase,120,20,80",
  2945. "Site 1,Power Panel 1,Power Feed 5,active,primary,ac,single-phase,120,20,80",
  2946. "Site 1,Power Panel 1,Power Feed 6,active,primary,ac,single-phase,120,20,80",
  2947. )
  2948. cls.csv_update_data = (
  2949. "id,name,status",
  2950. f"{power_feeds[0].pk},Power Feed 7,{PowerFeedStatusChoices.STATUS_PLANNED}",
  2951. f"{power_feeds[1].pk},Power Feed 8,{PowerFeedStatusChoices.STATUS_PLANNED}",
  2952. f"{power_feeds[2].pk},Power Feed 9,{PowerFeedStatusChoices.STATUS_PLANNED}",
  2953. )
  2954. cls.bulk_edit_data = {
  2955. 'power_panel': powerpanels[1].pk,
  2956. 'rack': racks[1].pk,
  2957. 'status': PowerFeedStatusChoices.STATUS_PLANNED,
  2958. 'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
  2959. 'supply': PowerFeedSupplyChoices.SUPPLY_DC,
  2960. 'phase': PowerFeedPhaseChoices.PHASE_3PHASE,
  2961. 'voltage': 100,
  2962. 'amperage': 100,
  2963. 'max_utilization': 50,
  2964. 'comments': 'New comments',
  2965. }
  2966. @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
  2967. def test_trace(self):
  2968. manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
  2969. device_type = DeviceType.objects.create(
  2970. manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
  2971. )
  2972. role = DeviceRole.objects.create(
  2973. name='Device Role', slug='device-role-1'
  2974. )
  2975. device = Device.objects.create(
  2976. site=Site.objects.first(), device_type=device_type, role=role
  2977. )
  2978. powerfeed = PowerFeed.objects.first()
  2979. powerport = PowerPort.objects.create(
  2980. device=device,
  2981. name='Power Port 1'
  2982. )
  2983. Cable(a_terminations=[powerfeed], b_terminations=[powerport]).save()
  2984. response = self.client.get(reverse('dcim:powerfeed_trace', kwargs={'pk': powerfeed.pk}))
  2985. self.assertHttpStatus(response, 200)
  2986. class VirtualDeviceContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  2987. model = VirtualDeviceContext
  2988. @classmethod
  2989. def setUpTestData(cls):
  2990. devices = [create_test_device(name='Device 1')]
  2991. vdcs = (
  2992. VirtualDeviceContext(name='VDC 1', identifier=1, device=devices[0], status='active'),
  2993. VirtualDeviceContext(name='VDC 2', identifier=2, device=devices[0], status='active'),
  2994. VirtualDeviceContext(name='VDC 3', identifier=3, device=devices[0], status='active'),
  2995. )
  2996. VirtualDeviceContext.objects.bulk_create(vdcs)
  2997. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  2998. cls.form_data = {
  2999. 'device': devices[0].pk,
  3000. 'status': 'active',
  3001. 'name': 'VDC 4',
  3002. 'identifier': 4,
  3003. 'primary_ip4': None,
  3004. 'primary_ip6': None,
  3005. 'tags': [t.pk for t in tags],
  3006. }
  3007. cls.csv_data = (
  3008. "device,status,name,identifier",
  3009. "Device 1,active,VDC 5,5",
  3010. "Device 1,active,VDC 6,6",
  3011. "Device 1,active,VDC 7,7",
  3012. )
  3013. cls.csv_update_data = (
  3014. "id,status",
  3015. f"{vdcs[0].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
  3016. f"{vdcs[1].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
  3017. f"{vdcs[2].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
  3018. )
  3019. cls.bulk_edit_data = {
  3020. 'status': VirtualDeviceContextStatusChoices.STATUS_OFFLINE,
  3021. }
  3022. class MACAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
  3023. model = MACAddress
  3024. @classmethod
  3025. def setUpTestData(cls):
  3026. device = create_test_device(name='Device 1')
  3027. interfaces = (
  3028. Interface(device=device, name='Interface 1', type='1000base-t'),
  3029. Interface(device=device, name='Interface 2', type='1000base-t'),
  3030. Interface(device=device, name='Interface 3', type='1000base-t'),
  3031. Interface(device=device, name='Interface 4', type='1000base-t'),
  3032. Interface(device=device, name='Interface 5', type='1000base-t'),
  3033. Interface(device=device, name='Interface 6', type='1000base-t'),
  3034. )
  3035. Interface.objects.bulk_create(interfaces)
  3036. mac_addresses = (
  3037. MACAddress(mac_address='00:00:00:00:00:01', assigned_object=interfaces[0]),
  3038. MACAddress(mac_address='00:00:00:00:00:02', assigned_object=interfaces[1]),
  3039. MACAddress(mac_address='00:00:00:00:00:03', assigned_object=interfaces[2]),
  3040. )
  3041. MACAddress.objects.bulk_create(mac_addresses)
  3042. tags = create_tags('Alpha', 'Bravo', 'Charlie')
  3043. cls.form_data = {
  3044. 'mac_address': EUI('00:00:00:00:00:04'),
  3045. 'description': 'New MAC address',
  3046. 'interface_id': interfaces[3].pk,
  3047. 'tags': [t.pk for t in tags],
  3048. }
  3049. cls.csv_data = (
  3050. "mac_address,device,interface",
  3051. "00:00:00:00:00:04,Device 1,Interface 4",
  3052. "00:00:00:00:00:05,Device 1,Interface 5",
  3053. "00:00:00:00:00:06,Device 1,Interface 6",
  3054. )
  3055. cls.csv_update_data = (
  3056. "id,mac_address",
  3057. f"{mac_addresses[0].pk},00:00:00:00:00:0a",
  3058. f"{mac_addresses[1].pk},00:00:00:00:00:0b",
  3059. f"{mac_addresses[2].pk},00:00:00:00:00:0c",
  3060. )
  3061. cls.bulk_edit_data = {
  3062. 'description': 'New description',
  3063. }