test_views.py 129 KB

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