test_views.py 129 KB

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