test_views.py 133 KB

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