1from decimal import Decimal
2
3import pytz
4import yaml
5from django.contrib.auth.models import User
6from django.contrib.contenttypes.models import ContentType
7from django.test import override_settings
8from django.urls import reverse
9from netaddr import EUI
10
11from dcim.choices import *
12from dcim.constants import *
13from dcim.models import *
14from ipam.models import VLAN
15from utilities.testing import ViewTestCases, create_tags, create_test_device
16
17
18class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
19    model = Region
20
21    @classmethod
22    def setUpTestData(cls):
23
24        # Create three Regions
25        regions = (
26            Region(name='Region 1', slug='region-1'),
27            Region(name='Region 2', slug='region-2'),
28            Region(name='Region 3', slug='region-3'),
29        )
30        for region in regions:
31            region.save()
32
33        cls.form_data = {
34            'name': 'Region X',
35            'slug': 'region-x',
36            'parent': regions[2].pk,
37            'description': 'A new region',
38        }
39
40        cls.csv_data = (
41            "name,slug,description",
42            "Region 4,region-4,Fourth region",
43            "Region 5,region-5,Fifth region",
44            "Region 6,region-6,Sixth region",
45        )
46
47        cls.bulk_edit_data = {
48            'description': 'New description',
49        }
50
51
52class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
53    model = SiteGroup
54
55    @classmethod
56    def setUpTestData(cls):
57
58        # Create three SiteGroups
59        sitegroups = (
60            SiteGroup(name='Site Group 1', slug='site-group-1'),
61            SiteGroup(name='Site Group 2', slug='site-group-2'),
62            SiteGroup(name='Site Group 3', slug='site-group-3'),
63        )
64        for sitegroup in sitegroups:
65            sitegroup.save()
66
67        cls.form_data = {
68            'name': 'Site Group X',
69            'slug': 'site-group-x',
70            'parent': sitegroups[2].pk,
71            'description': 'A new site group',
72        }
73
74        cls.csv_data = (
75            "name,slug,description",
76            "Site Group 4,site-group-4,Fourth site group",
77            "Site Group 5,site-group-5,Fifth site group",
78            "Site Group 6,site-group-6,Sixth site group",
79        )
80
81        cls.bulk_edit_data = {
82            'description': 'New description',
83        }
84
85
86class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
87    model = Site
88
89    @classmethod
90    def setUpTestData(cls):
91
92        regions = (
93            Region(name='Region 1', slug='region-1'),
94            Region(name='Region 2', slug='region-2'),
95        )
96        for region in regions:
97            region.save()
98
99        groups = (
100            SiteGroup(name='Site Group 1', slug='site-group-1'),
101            SiteGroup(name='Site Group 2', slug='site-group-2'),
102        )
103        for group in groups:
104            group.save()
105
106        Site.objects.bulk_create([
107            Site(name='Site 1', slug='site-1', region=regions[0], group=groups[1]),
108            Site(name='Site 2', slug='site-2', region=regions[0], group=groups[1]),
109            Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
110        ])
111
112        tags = create_tags('Alpha', 'Bravo', 'Charlie')
113
114        cls.form_data = {
115            'name': 'Site X',
116            'slug': 'site-x',
117            'status': SiteStatusChoices.STATUS_PLANNED,
118            'region': regions[1].pk,
119            'group': groups[1].pk,
120            'tenant': None,
121            'facility': 'Facility X',
122            'asn': 65001,
123            'time_zone': pytz.UTC,
124            'description': 'Site description',
125            'physical_address': '742 Evergreen Terrace, Springfield, USA',
126            'shipping_address': '742 Evergreen Terrace, Springfield, USA',
127            'latitude': Decimal('35.780000'),
128            'longitude': Decimal('-78.642000'),
129            'contact_name': 'Hank Hill',
130            'contact_phone': '123-555-9999',
131            'contact_email': 'hank@stricklandpropane.com',
132            'comments': 'Test site',
133            'tags': [t.pk for t in tags],
134        }
135
136        cls.csv_data = (
137            "name,slug,status",
138            "Site 4,site-4,planned",
139            "Site 5,site-5,active",
140            "Site 6,site-6,staging",
141        )
142
143        cls.bulk_edit_data = {
144            'status': SiteStatusChoices.STATUS_PLANNED,
145            'region': regions[1].pk,
146            'group': groups[1].pk,
147            'tenant': None,
148            'asn': 65009,
149            'time_zone': pytz.timezone('US/Eastern'),
150            'description': 'New description',
151        }
152
153
154class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
155    model = Location
156
157    @classmethod
158    def setUpTestData(cls):
159
160        site = Site(name='Site 1', slug='site-1')
161        site.save()
162
163        locations = (
164            Location(name='Location 1', slug='location-1', site=site),
165            Location(name='Location 2', slug='location-2', site=site),
166            Location(name='Location 3', slug='location-3', site=site),
167        )
168        for location in locations:
169            location.save()
170
171        cls.form_data = {
172            'name': 'Location X',
173            'slug': 'location-x',
174            'site': site.pk,
175            'description': 'A new location',
176        }
177
178        cls.csv_data = (
179            "site,name,slug,description",
180            "Site 1,Location 4,location-4,Fourth location",
181            "Site 1,Location 5,location-5,Fifth location",
182            "Site 1,Location 6,location-6,Sixth location",
183        )
184
185        cls.bulk_edit_data = {
186            'description': 'New description',
187        }
188
189
190class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
191    model = RackRole
192
193    @classmethod
194    def setUpTestData(cls):
195
196        RackRole.objects.bulk_create([
197            RackRole(name='Rack Role 1', slug='rack-role-1'),
198            RackRole(name='Rack Role 2', slug='rack-role-2'),
199            RackRole(name='Rack Role 3', slug='rack-role-3'),
200        ])
201
202        cls.form_data = {
203            'name': 'Rack Role X',
204            'slug': 'rack-role-x',
205            'color': 'c0c0c0',
206            'description': 'New role',
207        }
208
209        cls.csv_data = (
210            "name,slug,color",
211            "Rack Role 4,rack-role-4,ff0000",
212            "Rack Role 5,rack-role-5,00ff00",
213            "Rack Role 6,rack-role-6,0000ff",
214        )
215
216        cls.bulk_edit_data = {
217            'color': '00ff00',
218            'description': 'New description',
219        }
220
221
222class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
223    model = RackReservation
224
225    @classmethod
226    def setUpTestData(cls):
227
228        user2 = User.objects.create_user(username='testuser2')
229        user3 = User.objects.create_user(username='testuser3')
230
231        site = Site.objects.create(name='Site 1', slug='site-1')
232
233        location = Location(name='Location 1', slug='location-1', site=site)
234        location.save()
235
236        rack = Rack(name='Rack 1', site=site, location=location)
237        rack.save()
238
239        RackReservation.objects.bulk_create([
240            RackReservation(rack=rack, user=user2, units=[1, 2, 3], description='Reservation 1'),
241            RackReservation(rack=rack, user=user2, units=[4, 5, 6], description='Reservation 2'),
242            RackReservation(rack=rack, user=user2, units=[7, 8, 9], description='Reservation 3'),
243        ])
244
245        tags = create_tags('Alpha', 'Bravo', 'Charlie')
246
247        cls.form_data = {
248            'rack': rack.pk,
249            'units': "10,11,12",
250            'user': user3.pk,
251            'tenant': None,
252            'description': 'Rack reservation',
253            'tags': [t.pk for t in tags],
254        }
255
256        cls.csv_data = (
257            'site,location,rack,units,description',
258            'Site 1,Location 1,Rack 1,"10,11,12",Reservation 1',
259            'Site 1,Location 1,Rack 1,"13,14,15",Reservation 2',
260            'Site 1,Location 1,Rack 1,"16,17,18",Reservation 3',
261        )
262
263        cls.bulk_edit_data = {
264            'user': user3.pk,
265            'tenant': None,
266            'description': 'New description',
267        }
268
269
270class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
271    model = Rack
272
273    @classmethod
274    def setUpTestData(cls):
275
276        sites = (
277            Site(name='Site 1', slug='site-1'),
278            Site(name='Site 2', slug='site-2'),
279        )
280        Site.objects.bulk_create(sites)
281
282        locations = (
283            Location(name='Location 1', slug='location-1', site=sites[0]),
284            Location(name='Location 2', slug='location-2', site=sites[1])
285        )
286        for location in locations:
287            location.save()
288
289        rackroles = (
290            RackRole(name='Rack Role 1', slug='rack-role-1'),
291            RackRole(name='Rack Role 2', slug='rack-role-2'),
292        )
293        RackRole.objects.bulk_create(rackroles)
294
295        Rack.objects.bulk_create((
296            Rack(name='Rack 1', site=sites[0]),
297            Rack(name='Rack 2', site=sites[0]),
298            Rack(name='Rack 3', site=sites[0]),
299        ))
300
301        tags = create_tags('Alpha', 'Bravo', 'Charlie')
302
303        cls.form_data = {
304            'name': 'Rack X',
305            'facility_id': 'Facility X',
306            'site': sites[1].pk,
307            'location': locations[1].pk,
308            'tenant': None,
309            'status': RackStatusChoices.STATUS_PLANNED,
310            'role': rackroles[1].pk,
311            'serial': '123456',
312            'asset_tag': 'ABCDEF',
313            'type': RackTypeChoices.TYPE_CABINET,
314            'width': RackWidthChoices.WIDTH_19IN,
315            'u_height': 48,
316            'desc_units': False,
317            'outer_width': 500,
318            'outer_depth': 500,
319            'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
320            'comments': 'Some comments',
321            'tags': [t.pk for t in tags],
322        }
323
324        cls.csv_data = (
325            "site,location,name,status,width,u_height",
326            "Site 1,,Rack 4,active,19,42",
327            "Site 1,Location 1,Rack 5,active,19,42",
328            "Site 2,Location 2,Rack 6,active,19,42",
329        )
330
331        cls.bulk_edit_data = {
332            'site': sites[1].pk,
333            'location': locations[1].pk,
334            'tenant': None,
335            'status': RackStatusChoices.STATUS_DEPRECATED,
336            'role': rackroles[1].pk,
337            'serial': '654321',
338            'type': RackTypeChoices.TYPE_4POST,
339            'width': RackWidthChoices.WIDTH_23IN,
340            'u_height': 49,
341            'desc_units': True,
342            'outer_width': 30,
343            'outer_depth': 30,
344            'outer_unit': RackDimensionUnitChoices.UNIT_INCH,
345            'comments': 'New comments',
346        }
347
348    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
349    def test_list_rack_elevations(self):
350        """
351        Test viewing the list of rack elevations.
352        """
353        response = self.client.get(reverse('dcim:rack_elevation_list'))
354        self.assertHttpStatus(response, 200)
355
356
357class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
358    model = Manufacturer
359
360    @classmethod
361    def setUpTestData(cls):
362
363        Manufacturer.objects.bulk_create([
364            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
365            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
366            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
367        ])
368
369        cls.form_data = {
370            'name': 'Manufacturer X',
371            'slug': 'manufacturer-x',
372            'description': 'A new manufacturer',
373        }
374
375        cls.csv_data = (
376            "name,slug,description",
377            "Manufacturer 4,manufacturer-4,Fourth manufacturer",
378            "Manufacturer 5,manufacturer-5,Fifth manufacturer",
379            "Manufacturer 6,manufacturer-6,Sixth manufacturer",
380        )
381
382        cls.bulk_edit_data = {
383            'description': 'New description',
384        }
385
386
387# TODO: Change base class to PrimaryObjectViewTestCase
388# Blocked by absence of bulk import view for DeviceTypes
389class DeviceTypeTestCase(
390    ViewTestCases.GetObjectViewTestCase,
391    ViewTestCases.GetObjectChangelogViewTestCase,
392    ViewTestCases.CreateObjectViewTestCase,
393    ViewTestCases.EditObjectViewTestCase,
394    ViewTestCases.DeleteObjectViewTestCase,
395    ViewTestCases.ListObjectsViewTestCase,
396    ViewTestCases.BulkEditObjectsViewTestCase,
397    ViewTestCases.BulkDeleteObjectsViewTestCase
398):
399    model = DeviceType
400
401    @classmethod
402    def setUpTestData(cls):
403
404        manufacturers = (
405            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
406            Manufacturer(name='Manufacturer 2', slug='manufacturer-2')
407        )
408        Manufacturer.objects.bulk_create(manufacturers)
409
410        DeviceType.objects.bulk_create([
411            DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturers[0]),
412            DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturers[0]),
413            DeviceType(model='Device Type 3', slug='device-type-3', manufacturer=manufacturers[0]),
414        ])
415
416        tags = create_tags('Alpha', 'Bravo', 'Charlie')
417
418        cls.form_data = {
419            'manufacturer': manufacturers[1].pk,
420            'model': 'Device Type X',
421            'slug': 'device-type-x',
422            'part_number': '123ABC',
423            'u_height': 2,
424            'is_full_depth': True,
425            'subdevice_role': '',  # CharField
426            'comments': 'Some comments',
427            'tags': [t.pk for t in tags],
428        }
429
430        cls.bulk_edit_data = {
431            'manufacturer': manufacturers[1].pk,
432            'u_height': 3,
433            'is_full_depth': False,
434        }
435
436    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
437    def test_import_objects(self):
438        """
439        Custom import test for YAML-based imports (versus CSV)
440        """
441        IMPORT_DATA = """
442manufacturer: Generic
443model: TEST-1000
444slug: test-1000
445u_height: 2
446subdevice_role: parent
447comments: test comment
448console-ports:
449  - name: Console Port 1
450    type: de-9
451  - name: Console Port 2
452    type: de-9
453  - name: Console Port 3
454    type: de-9
455console-server-ports:
456  - name: Console Server Port 1
457    type: rj-45
458  - name: Console Server Port 2
459    type: rj-45
460  - name: Console Server Port 3
461    type: rj-45
462power-ports:
463  - name: Power Port 1
464    type: iec-60320-c14
465  - name: Power Port 2
466    type: iec-60320-c14
467  - name: Power Port 3
468    type: iec-60320-c14
469power-outlets:
470  - name: Power Outlet 1
471    type: iec-60320-c13
472    power_port: Power Port 1
473    feed_leg: A
474  - name: Power Outlet 2
475    type: iec-60320-c13
476    power_port: Power Port 1
477    feed_leg: A
478  - name: Power Outlet 3
479    type: iec-60320-c13
480    power_port: Power Port 1
481    feed_leg: A
482interfaces:
483  - name: Interface 1
484    type: 1000base-t
485    mgmt_only: true
486  - name: Interface 2
487    type: 1000base-t
488  - name: Interface 3
489    type: 1000base-t
490rear-ports:
491  - name: Rear Port 1
492    type: 8p8c
493  - name: Rear Port 2
494    type: 8p8c
495  - name: Rear Port 3
496    type: 8p8c
497front-ports:
498  - name: Front Port 1
499    type: 8p8c
500    rear_port: Rear Port 1
501  - name: Front Port 2
502    type: 8p8c
503    rear_port: Rear Port 2
504  - name: Front Port 3
505    type: 8p8c
506    rear_port: Rear Port 3
507device-bays:
508  - name: Device Bay 1
509  - name: Device Bay 2
510  - name: Device Bay 3
511"""
512
513        # Create the manufacturer
514        Manufacturer(name='Generic', slug='generic').save()
515
516        # Add all required permissions to the test user
517        self.add_permissions(
518            'dcim.view_devicetype',
519            'dcim.add_devicetype',
520            'dcim.add_consoleporttemplate',
521            'dcim.add_consoleserverporttemplate',
522            'dcim.add_powerporttemplate',
523            'dcim.add_poweroutlettemplate',
524            'dcim.add_interfacetemplate',
525            'dcim.add_frontporttemplate',
526            'dcim.add_rearporttemplate',
527            'dcim.add_devicebaytemplate',
528        )
529
530        form_data = {
531            'data': IMPORT_DATA,
532            'format': 'yaml'
533        }
534        response = self.client.post(reverse('dcim:devicetype_import'), data=form_data, follow=True)
535        self.assertHttpStatus(response, 200)
536
537        dt = DeviceType.objects.get(model='TEST-1000')
538        self.assertEqual(dt.comments, 'test comment')
539
540        # Verify all of the components were created
541        self.assertEqual(dt.consoleporttemplates.count(), 3)
542        cp1 = ConsolePortTemplate.objects.first()
543        self.assertEqual(cp1.name, 'Console Port 1')
544        self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9)
545
546        self.assertEqual(dt.consoleserverporttemplates.count(), 3)
547        csp1 = ConsoleServerPortTemplate.objects.first()
548        self.assertEqual(csp1.name, 'Console Server Port 1')
549        self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45)
550
551        self.assertEqual(dt.powerporttemplates.count(), 3)
552        pp1 = PowerPortTemplate.objects.first()
553        self.assertEqual(pp1.name, 'Power Port 1')
554        self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14)
555
556        self.assertEqual(dt.poweroutlettemplates.count(), 3)
557        po1 = PowerOutletTemplate.objects.first()
558        self.assertEqual(po1.name, 'Power Outlet 1')
559        self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13)
560        self.assertEqual(po1.power_port, pp1)
561        self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A)
562
563        self.assertEqual(dt.interfacetemplates.count(), 3)
564        iface1 = InterfaceTemplate.objects.first()
565        self.assertEqual(iface1.name, 'Interface 1')
566        self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
567        self.assertTrue(iface1.mgmt_only)
568
569        self.assertEqual(dt.rearporttemplates.count(), 3)
570        rp1 = RearPortTemplate.objects.first()
571        self.assertEqual(rp1.name, 'Rear Port 1')
572
573        self.assertEqual(dt.frontporttemplates.count(), 3)
574        fp1 = FrontPortTemplate.objects.first()
575        self.assertEqual(fp1.name, 'Front Port 1')
576        self.assertEqual(fp1.rear_port, rp1)
577        self.assertEqual(fp1.rear_port_position, 1)
578
579        self.assertEqual(dt.devicebaytemplates.count(), 3)
580        db1 = DeviceBayTemplate.objects.first()
581        self.assertEqual(db1.name, 'Device Bay 1')
582
583    def test_export_objects(self):
584        url = reverse('dcim:devicetype_list')
585        self.add_permissions('dcim.view_devicetype')
586
587        # Test default YAML export
588        response = self.client.get('{}?export'.format(url))
589        self.assertEqual(response.status_code, 200)
590        data = list(yaml.load_all(response.content, Loader=yaml.SafeLoader))
591        self.assertEqual(len(data), 3)
592        self.assertEqual(data[0]['manufacturer'], 'Manufacturer 1')
593        self.assertEqual(data[0]['model'], 'Device Type 1')
594
595        # Test table-based export
596        response = self.client.get(f'{url}?export=table')
597        self.assertHttpStatus(response, 200)
598        self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8')
599
600
601#
602# DeviceType components
603#
604
605class ConsolePortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
606    model = ConsolePortTemplate
607
608    @classmethod
609    def setUpTestData(cls):
610        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
611        devicetypes = (
612            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
613            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
614        )
615        DeviceType.objects.bulk_create(devicetypes)
616
617        ConsolePortTemplate.objects.bulk_create((
618            ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 1'),
619            ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 2'),
620            ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 3'),
621        ))
622
623        cls.form_data = {
624            'device_type': devicetypes[1].pk,
625            'name': 'Console Port Template X',
626            'type': ConsolePortTypeChoices.TYPE_RJ45,
627        }
628
629        cls.bulk_create_data = {
630            'device_type': devicetypes[1].pk,
631            'name_pattern': 'Console Port Template [4-6]',
632            'type': ConsolePortTypeChoices.TYPE_RJ45,
633        }
634
635        cls.bulk_edit_data = {
636            'type': ConsolePortTypeChoices.TYPE_RJ45,
637        }
638
639
640class ConsoleServerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
641    model = ConsoleServerPortTemplate
642
643    @classmethod
644    def setUpTestData(cls):
645        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
646        devicetypes = (
647            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
648            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
649        )
650        DeviceType.objects.bulk_create(devicetypes)
651
652        ConsoleServerPortTemplate.objects.bulk_create((
653            ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 1'),
654            ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 2'),
655            ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 3'),
656        ))
657
658        cls.form_data = {
659            'device_type': devicetypes[1].pk,
660            'name': 'Console Server Port Template X',
661            'type': ConsolePortTypeChoices.TYPE_RJ45,
662        }
663
664        cls.bulk_create_data = {
665            'device_type': devicetypes[1].pk,
666            'name_pattern': 'Console Server Port Template [4-6]',
667            'type': ConsolePortTypeChoices.TYPE_RJ45,
668        }
669
670        cls.bulk_edit_data = {
671            'type': ConsolePortTypeChoices.TYPE_RJ45,
672        }
673
674
675class PowerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
676    model = PowerPortTemplate
677
678    @classmethod
679    def setUpTestData(cls):
680        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
681        devicetypes = (
682            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
683            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
684        )
685        DeviceType.objects.bulk_create(devicetypes)
686
687        PowerPortTemplate.objects.bulk_create((
688            PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 1'),
689            PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 2'),
690            PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 3'),
691        ))
692
693        cls.form_data = {
694            'device_type': devicetypes[1].pk,
695            'name': 'Power Port Template X',
696            'type': PowerPortTypeChoices.TYPE_IEC_C14,
697            'maximum_draw': 100,
698            'allocated_draw': 50,
699        }
700
701        cls.bulk_create_data = {
702            'device_type': devicetypes[1].pk,
703            'name_pattern': 'Power Port Template [4-6]',
704            'type': PowerPortTypeChoices.TYPE_IEC_C14,
705            'maximum_draw': 100,
706            'allocated_draw': 50,
707        }
708
709        cls.bulk_edit_data = {
710            'type': PowerPortTypeChoices.TYPE_IEC_C14,
711            'maximum_draw': 100,
712            'allocated_draw': 50,
713        }
714
715
716class PowerOutletTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
717    model = PowerOutletTemplate
718
719    @classmethod
720    def setUpTestData(cls):
721        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
722        devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
723
724        PowerOutletTemplate.objects.bulk_create((
725            PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 1'),
726            PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 2'),
727            PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 3'),
728        ))
729
730        powerports = (
731            PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
732        )
733        PowerPortTemplate.objects.bulk_create(powerports)
734
735        cls.form_data = {
736            'device_type': devicetype.pk,
737            'name': 'Power Outlet Template X',
738            'type': PowerOutletTypeChoices.TYPE_IEC_C13,
739            'power_port': powerports[0].pk,
740            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
741        }
742
743        cls.bulk_create_data = {
744            'device_type': devicetype.pk,
745            'name_pattern': 'Power Outlet Template [4-6]',
746            'type': PowerOutletTypeChoices.TYPE_IEC_C13,
747            'power_port': powerports[0].pk,
748            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
749        }
750
751        cls.bulk_edit_data = {
752            'type': PowerOutletTypeChoices.TYPE_IEC_C13,
753            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
754        }
755
756
757class InterfaceTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
758    model = InterfaceTemplate
759
760    @classmethod
761    def setUpTestData(cls):
762        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
763        devicetypes = (
764            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
765            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
766        )
767        DeviceType.objects.bulk_create(devicetypes)
768
769        InterfaceTemplate.objects.bulk_create((
770            InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 1'),
771            InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 2'),
772            InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 3'),
773        ))
774
775        cls.form_data = {
776            'device_type': devicetypes[1].pk,
777            'name': 'Interface Template X',
778            'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
779            'mgmt_only': True,
780        }
781
782        cls.bulk_create_data = {
783            'device_type': devicetypes[1].pk,
784            'name_pattern': 'Interface Template [4-6]',
785            # Test that a label can be applied to each generated interface templates
786            'label_pattern': 'Interface Template Label [3-5]',
787            'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
788            'mgmt_only': True,
789        }
790
791        cls.bulk_edit_data = {
792            'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
793            'mgmt_only': True,
794        }
795
796
797class FrontPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
798    model = FrontPortTemplate
799
800    @classmethod
801    def setUpTestData(cls):
802        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
803        devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
804
805        rearports = (
806            RearPortTemplate(device_type=devicetype, name='Rear Port Template 1'),
807            RearPortTemplate(device_type=devicetype, name='Rear Port Template 2'),
808            RearPortTemplate(device_type=devicetype, name='Rear Port Template 3'),
809            RearPortTemplate(device_type=devicetype, name='Rear Port Template 4'),
810            RearPortTemplate(device_type=devicetype, name='Rear Port Template 5'),
811            RearPortTemplate(device_type=devicetype, name='Rear Port Template 6'),
812        )
813        RearPortTemplate.objects.bulk_create(rearports)
814
815        FrontPortTemplate.objects.bulk_create((
816            FrontPortTemplate(device_type=devicetype, name='Front Port Template 1', rear_port=rearports[0], rear_port_position=1),
817            FrontPortTemplate(device_type=devicetype, name='Front Port Template 2', rear_port=rearports[1], rear_port_position=1),
818            FrontPortTemplate(device_type=devicetype, name='Front Port Template 3', rear_port=rearports[2], rear_port_position=1),
819        ))
820
821        cls.form_data = {
822            'device_type': devicetype.pk,
823            'name': 'Front Port X',
824            'type': PortTypeChoices.TYPE_8P8C,
825            'rear_port': rearports[3].pk,
826            'rear_port_position': 1,
827        }
828
829        cls.bulk_create_data = {
830            'device_type': devicetype.pk,
831            'name_pattern': 'Front Port [4-6]',
832            'type': PortTypeChoices.TYPE_8P8C,
833            'rear_port_set': [
834                '{}:1'.format(rp.pk) for rp in rearports[3:6]
835            ],
836        }
837
838        cls.bulk_edit_data = {
839            'type': PortTypeChoices.TYPE_8P8C,
840        }
841
842
843class RearPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
844    model = RearPortTemplate
845
846    @classmethod
847    def setUpTestData(cls):
848        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
849        devicetypes = (
850            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
851            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
852        )
853        DeviceType.objects.bulk_create(devicetypes)
854
855        RearPortTemplate.objects.bulk_create((
856            RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 1'),
857            RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 2'),
858            RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 3'),
859        ))
860
861        cls.form_data = {
862            'device_type': devicetypes[1].pk,
863            'name': 'Rear Port Template X',
864            'type': PortTypeChoices.TYPE_8P8C,
865            'positions': 2,
866        }
867
868        cls.bulk_create_data = {
869            'device_type': devicetypes[1].pk,
870            'name_pattern': 'Rear Port Template [4-6]',
871            'type': PortTypeChoices.TYPE_8P8C,
872            'positions': 2,
873        }
874
875        cls.bulk_edit_data = {
876            'type': PortTypeChoices.TYPE_8P8C,
877        }
878
879
880class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
881    model = DeviceBayTemplate
882
883    @classmethod
884    def setUpTestData(cls):
885        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
886        devicetypes = (
887            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1', subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
888            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2', subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
889        )
890        DeviceType.objects.bulk_create(devicetypes)
891
892        DeviceBayTemplate.objects.bulk_create((
893            DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 1'),
894            DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 2'),
895            DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 3'),
896        ))
897
898        cls.form_data = {
899            'device_type': devicetypes[1].pk,
900            'name': 'Device Bay Template X',
901        }
902
903        cls.bulk_create_data = {
904            'device_type': devicetypes[1].pk,
905            'name_pattern': 'Device Bay Template [4-6]',
906        }
907
908        cls.bulk_edit_data = {
909            'description': 'Foo bar',
910        }
911
912
913class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
914    model = DeviceRole
915
916    @classmethod
917    def setUpTestData(cls):
918
919        DeviceRole.objects.bulk_create([
920            DeviceRole(name='Device Role 1', slug='device-role-1'),
921            DeviceRole(name='Device Role 2', slug='device-role-2'),
922            DeviceRole(name='Device Role 3', slug='device-role-3'),
923        ])
924
925        cls.form_data = {
926            'name': 'Devie Role X',
927            'slug': 'device-role-x',
928            'color': 'c0c0c0',
929            'vm_role': False,
930            'description': 'New device role',
931        }
932
933        cls.csv_data = (
934            "name,slug,color",
935            "Device Role 4,device-role-4,ff0000",
936            "Device Role 5,device-role-5,00ff00",
937            "Device Role 6,device-role-6,0000ff",
938        )
939
940        cls.bulk_edit_data = {
941            'color': '00ff00',
942            'description': 'New description',
943        }
944
945
946class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
947    model = Platform
948
949    @classmethod
950    def setUpTestData(cls):
951
952        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
953
954        Platform.objects.bulk_create([
955            Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturer),
956            Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturer),
957            Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturer),
958        ])
959
960        cls.form_data = {
961            'name': 'Platform X',
962            'slug': 'platform-x',
963            'manufacturer': manufacturer.pk,
964            'napalm_driver': 'junos',
965            'napalm_args': None,
966            'description': 'A new platform',
967        }
968
969        cls.csv_data = (
970            "name,slug,description",
971            "Platform 4,platform-4,Fourth platform",
972            "Platform 5,platform-5,Fifth platform",
973            "Platform 6,platform-6,Sixth platform",
974        )
975
976        cls.bulk_edit_data = {
977            'napalm_driver': 'ios',
978            'description': 'New description',
979        }
980
981
982class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
983    model = Device
984
985    @classmethod
986    def setUpTestData(cls):
987
988        sites = (
989            Site(name='Site 1', slug='site-1'),
990            Site(name='Site 2', slug='site-2'),
991        )
992        Site.objects.bulk_create(sites)
993
994        location = Location(site=sites[0], name='Location 1', slug='location-1')
995        location.save()
996
997        racks = (
998            Rack(name='Rack 1', site=sites[0], location=location),
999            Rack(name='Rack 2', site=sites[1]),
1000        )
1001        Rack.objects.bulk_create(racks)
1002
1003        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
1004
1005        devicetypes = (
1006            DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturer),
1007            DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturer),
1008        )
1009        DeviceType.objects.bulk_create(devicetypes)
1010
1011        deviceroles = (
1012            DeviceRole(name='Device Role 1', slug='device-role-1'),
1013            DeviceRole(name='Device Role 2', slug='device-role-2'),
1014        )
1015        DeviceRole.objects.bulk_create(deviceroles)
1016
1017        platforms = (
1018            Platform(name='Platform 1', slug='platform-1'),
1019            Platform(name='Platform 2', slug='platform-2'),
1020        )
1021        Platform.objects.bulk_create(platforms)
1022
1023        Device.objects.bulk_create([
1024            Device(name='Device 1', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
1025            Device(name='Device 2', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
1026            Device(name='Device 3', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
1027        ])
1028
1029        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1030
1031        VirtualChassis.objects.create(name='Virtual Chassis 1')
1032
1033        cls.form_data = {
1034            'device_type': devicetypes[1].pk,
1035            'device_role': deviceroles[1].pk,
1036            'tenant': None,
1037            'platform': platforms[1].pk,
1038            'name': 'Device X',
1039            'serial': '123456',
1040            'asset_tag': 'ABCDEF',
1041            'site': sites[1].pk,
1042            'rack': racks[1].pk,
1043            'position': 1,
1044            'face': DeviceFaceChoices.FACE_FRONT,
1045            'status': DeviceStatusChoices.STATUS_PLANNED,
1046            'primary_ip4': None,
1047            'primary_ip6': None,
1048            'cluster': None,
1049            'virtual_chassis': None,
1050            'vc_position': None,
1051            'vc_priority': None,
1052            'comments': 'A new device',
1053            'tags': [t.pk for t in tags],
1054            'local_context_data': None,
1055        }
1056
1057        cls.csv_data = (
1058            "device_role,manufacturer,device_type,status,name,site,location,rack,position,face,virtual_chassis,vc_position,vc_priority",
1059            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 4,Site 1,Location 1,Rack 1,10,front,Virtual Chassis 1,1,10",
1060            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 5,Site 1,Location 1,Rack 1,20,front,Virtual Chassis 1,2,20",
1061            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Location 1,Rack 1,30,front,Virtual Chassis 1,3,30",
1062        )
1063
1064        cls.bulk_edit_data = {
1065            'device_type': devicetypes[1].pk,
1066            'device_role': deviceroles[1].pk,
1067            'tenant': None,
1068            'platform': platforms[1].pk,
1069            'serial': '123456',
1070            'status': DeviceStatusChoices.STATUS_DECOMMISSIONING,
1071        }
1072
1073    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1074    def test_device_consoleports(self):
1075        device = Device.objects.first()
1076        console_ports = (
1077            ConsolePort(device=device, name='Console Port 1'),
1078            ConsolePort(device=device, name='Console Port 2'),
1079            ConsolePort(device=device, name='Console Port 3'),
1080        )
1081        ConsolePort.objects.bulk_create(console_ports)
1082
1083        url = reverse('dcim:device_consoleports', kwargs={'pk': device.pk})
1084        self.assertHttpStatus(self.client.get(url), 200)
1085
1086    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1087    def test_device_consoleserverports(self):
1088        device = Device.objects.first()
1089        console_server_ports = (
1090            ConsoleServerPort(device=device, name='Console Server Port 1'),
1091            ConsoleServerPort(device=device, name='Console Server Port 2'),
1092            ConsoleServerPort(device=device, name='Console Server Port 3'),
1093        )
1094        ConsoleServerPort.objects.bulk_create(console_server_ports)
1095
1096        url = reverse('dcim:device_consoleserverports', kwargs={'pk': device.pk})
1097        self.assertHttpStatus(self.client.get(url), 200)
1098
1099    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1100    def test_device_powerports(self):
1101        device = Device.objects.first()
1102        power_ports = (
1103            PowerPort(device=device, name='Power Port 1'),
1104            PowerPort(device=device, name='Power Port 2'),
1105            PowerPort(device=device, name='Power Port 3'),
1106        )
1107        PowerPort.objects.bulk_create(power_ports)
1108
1109        url = reverse('dcim:device_powerports', kwargs={'pk': device.pk})
1110        self.assertHttpStatus(self.client.get(url), 200)
1111
1112    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1113    def test_device_poweroutlets(self):
1114        device = Device.objects.first()
1115        power_outlets = (
1116            PowerOutlet(device=device, name='Power Outlet 1'),
1117            PowerOutlet(device=device, name='Power Outlet 2'),
1118            PowerOutlet(device=device, name='Power Outlet 3'),
1119        )
1120        PowerOutlet.objects.bulk_create(power_outlets)
1121
1122        url = reverse('dcim:device_poweroutlets', kwargs={'pk': device.pk})
1123        self.assertHttpStatus(self.client.get(url), 200)
1124
1125    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1126    def test_device_interfaces(self):
1127        device = Device.objects.first()
1128        interfaces = (
1129            Interface(device=device, name='Interface 1'),
1130            Interface(device=device, name='Interface 2'),
1131            Interface(device=device, name='Interface 3'),
1132        )
1133        Interface.objects.bulk_create(interfaces)
1134
1135        url = reverse('dcim:device_interfaces', kwargs={'pk': device.pk})
1136        self.assertHttpStatus(self.client.get(url), 200)
1137
1138    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1139    def test_device_rearports(self):
1140        device = Device.objects.first()
1141        rear_ports = (
1142            RearPort(device=device, name='Rear Port 1'),
1143            RearPort(device=device, name='Rear Port 2'),
1144            RearPort(device=device, name='Rear Port 3'),
1145        )
1146        RearPort.objects.bulk_create(rear_ports)
1147
1148        url = reverse('dcim:device_rearports', kwargs={'pk': device.pk})
1149        self.assertHttpStatus(self.client.get(url), 200)
1150
1151    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1152    def test_device_frontports(self):
1153        device = Device.objects.first()
1154        rear_ports = (
1155            RearPort(device=device, name='Rear Port 1'),
1156            RearPort(device=device, name='Rear Port 2'),
1157            RearPort(device=device, name='Rear Port 3'),
1158        )
1159        RearPort.objects.bulk_create(rear_ports)
1160        front_ports = (
1161            FrontPort(device=device, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1),
1162            FrontPort(device=device, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1),
1163            FrontPort(device=device, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1),
1164        )
1165        FrontPort.objects.bulk_create(front_ports)
1166
1167        url = reverse('dcim:device_frontports', kwargs={'pk': device.pk})
1168        self.assertHttpStatus(self.client.get(url), 200)
1169
1170    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1171    def test_device_devicebays(self):
1172        device = Device.objects.first()
1173        device_bays = (
1174            DeviceBay(device=device, name='Device Bay 1'),
1175            DeviceBay(device=device, name='Device Bay 2'),
1176            DeviceBay(device=device, name='Device Bay 3'),
1177        )
1178        DeviceBay.objects.bulk_create(device_bays)
1179
1180        url = reverse('dcim:device_devicebays', kwargs={'pk': device.pk})
1181        self.assertHttpStatus(self.client.get(url), 200)
1182
1183    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1184    def test_device_inventory(self):
1185        device = Device.objects.first()
1186        inventory_items = (
1187            InventoryItem(device=device, name='Inventory Item 1'),
1188            InventoryItem(device=device, name='Inventory Item 2'),
1189            InventoryItem(device=device, name='Inventory Item 3'),
1190        )
1191        for item in inventory_items:
1192            item.save()
1193
1194        url = reverse('dcim:device_inventory', kwargs={'pk': device.pk})
1195        self.assertHttpStatus(self.client.get(url), 200)
1196
1197
1198class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1199    model = ConsolePort
1200
1201    @classmethod
1202    def setUpTestData(cls):
1203        device = create_test_device('Device 1')
1204
1205        ConsolePort.objects.bulk_create([
1206            ConsolePort(device=device, name='Console Port 1'),
1207            ConsolePort(device=device, name='Console Port 2'),
1208            ConsolePort(device=device, name='Console Port 3'),
1209        ])
1210
1211        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1212
1213        cls.form_data = {
1214            'device': device.pk,
1215            'name': 'Console Port X',
1216            'type': ConsolePortTypeChoices.TYPE_RJ45,
1217            'description': 'A console port',
1218            'tags': sorted([t.pk for t in tags]),
1219        }
1220
1221        cls.bulk_create_data = {
1222            'device': device.pk,
1223            'name_pattern': 'Console Port [4-6]',
1224            # Test that a label can be applied to each generated console ports
1225            'label_pattern': 'Serial[3-5]',
1226            'type': ConsolePortTypeChoices.TYPE_RJ45,
1227            'description': 'A console port',
1228            'tags': sorted([t.pk for t in tags]),
1229        }
1230
1231        cls.bulk_edit_data = {
1232            'type': ConsolePortTypeChoices.TYPE_RJ45,
1233            'description': 'New description',
1234        }
1235
1236        cls.csv_data = (
1237            "device,name",
1238            "Device 1,Console Port 4",
1239            "Device 1,Console Port 5",
1240            "Device 1,Console Port 6",
1241        )
1242
1243    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1244    def test_trace(self):
1245        consoleport = ConsolePort.objects.first()
1246        consoleserverport = ConsoleServerPort.objects.create(
1247            device=consoleport.device,
1248            name='Console Server Port 1'
1249        )
1250        Cable(termination_a=consoleport, termination_b=consoleserverport).save()
1251
1252        response = self.client.get(reverse('dcim:consoleport_trace', kwargs={'pk': consoleport.pk}))
1253        self.assertHttpStatus(response, 200)
1254
1255
1256class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1257    model = ConsoleServerPort
1258
1259    @classmethod
1260    def setUpTestData(cls):
1261        device = create_test_device('Device 1')
1262
1263        ConsoleServerPort.objects.bulk_create([
1264            ConsoleServerPort(device=device, name='Console Server Port 1'),
1265            ConsoleServerPort(device=device, name='Console Server Port 2'),
1266            ConsoleServerPort(device=device, name='Console Server Port 3'),
1267        ])
1268
1269        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1270
1271        cls.form_data = {
1272            'device': device.pk,
1273            'name': 'Console Server Port X',
1274            'type': ConsolePortTypeChoices.TYPE_RJ45,
1275            'description': 'A console server port',
1276            'tags': [t.pk for t in tags],
1277        }
1278
1279        cls.bulk_create_data = {
1280            'device': device.pk,
1281            'name_pattern': 'Console Server Port [4-6]',
1282            'type': ConsolePortTypeChoices.TYPE_RJ45,
1283            'description': 'A console server port',
1284            'tags': [t.pk for t in tags],
1285        }
1286
1287        cls.bulk_edit_data = {
1288            'type': ConsolePortTypeChoices.TYPE_RJ11,
1289            'description': 'New description',
1290        }
1291
1292        cls.csv_data = (
1293            "device,name",
1294            "Device 1,Console Server Port 4",
1295            "Device 1,Console Server Port 5",
1296            "Device 1,Console Server Port 6",
1297        )
1298
1299    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1300    def test_trace(self):
1301        consoleserverport = ConsoleServerPort.objects.first()
1302        consoleport = ConsolePort.objects.create(
1303            device=consoleserverport.device,
1304            name='Console Port 1'
1305        )
1306        Cable(termination_a=consoleserverport, termination_b=consoleport).save()
1307
1308        response = self.client.get(reverse('dcim:consoleserverport_trace', kwargs={'pk': consoleserverport.pk}))
1309        self.assertHttpStatus(response, 200)
1310
1311
1312class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1313    model = PowerPort
1314
1315    @classmethod
1316    def setUpTestData(cls):
1317        device = create_test_device('Device 1')
1318
1319        PowerPort.objects.bulk_create([
1320            PowerPort(device=device, name='Power Port 1'),
1321            PowerPort(device=device, name='Power Port 2'),
1322            PowerPort(device=device, name='Power Port 3'),
1323        ])
1324
1325        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1326
1327        cls.form_data = {
1328            'device': device.pk,
1329            'name': 'Power Port X',
1330            'type': PowerPortTypeChoices.TYPE_IEC_C14,
1331            'maximum_draw': 100,
1332            'allocated_draw': 50,
1333            'description': 'A power port',
1334            'tags': [t.pk for t in tags],
1335        }
1336
1337        cls.bulk_create_data = {
1338            'device': device.pk,
1339            'name_pattern': 'Power Port [4-6]]',
1340            'type': PowerPortTypeChoices.TYPE_IEC_C14,
1341            'maximum_draw': 100,
1342            'allocated_draw': 50,
1343            'description': 'A power port',
1344            'tags': [t.pk for t in tags],
1345        }
1346
1347        cls.bulk_edit_data = {
1348            'type': PowerPortTypeChoices.TYPE_IEC_C14,
1349            'maximum_draw': 100,
1350            'allocated_draw': 50,
1351            'description': 'New description',
1352        }
1353
1354        cls.csv_data = (
1355            "device,name",
1356            "Device 1,Power Port 4",
1357            "Device 1,Power Port 5",
1358            "Device 1,Power Port 6",
1359        )
1360
1361    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1362    def test_trace(self):
1363        powerport = PowerPort.objects.first()
1364        poweroutlet = PowerOutlet.objects.create(
1365            device=powerport.device,
1366            name='Power Outlet 1'
1367        )
1368        Cable(termination_a=powerport, termination_b=poweroutlet).save()
1369
1370        response = self.client.get(reverse('dcim:powerport_trace', kwargs={'pk': powerport.pk}))
1371        self.assertHttpStatus(response, 200)
1372
1373
1374class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
1375    model = PowerOutlet
1376
1377    @classmethod
1378    def setUpTestData(cls):
1379        device = create_test_device('Device 1')
1380
1381        powerports = (
1382            PowerPort(device=device, name='Power Port 1'),
1383            PowerPort(device=device, name='Power Port 2'),
1384        )
1385        PowerPort.objects.bulk_create(powerports)
1386
1387        PowerOutlet.objects.bulk_create([
1388            PowerOutlet(device=device, name='Power Outlet 1', power_port=powerports[0]),
1389            PowerOutlet(device=device, name='Power Outlet 2', power_port=powerports[0]),
1390            PowerOutlet(device=device, name='Power Outlet 3', power_port=powerports[0]),
1391        ])
1392
1393        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1394
1395        cls.form_data = {
1396            'device': device.pk,
1397            'name': 'Power Outlet X',
1398            'type': PowerOutletTypeChoices.TYPE_IEC_C13,
1399            'power_port': powerports[1].pk,
1400            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
1401            'description': 'A power outlet',
1402            'tags': [t.pk for t in tags],
1403        }
1404
1405        cls.bulk_create_data = {
1406            'device': device.pk,
1407            'name_pattern': 'Power Outlet [4-6]',
1408            'type': PowerOutletTypeChoices.TYPE_IEC_C13,
1409            'power_port': powerports[1].pk,
1410            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
1411            'description': 'A power outlet',
1412            'tags': [t.pk for t in tags],
1413        }
1414
1415        cls.bulk_edit_data = {
1416            'type': PowerOutletTypeChoices.TYPE_IEC_C15,
1417            'power_port': powerports[1].pk,
1418            'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
1419            'description': 'New description',
1420        }
1421
1422        cls.csv_data = (
1423            "device,name",
1424            "Device 1,Power Outlet 4",
1425            "Device 1,Power Outlet 5",
1426            "Device 1,Power Outlet 6",
1427        )
1428
1429    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1430    def test_trace(self):
1431        poweroutlet = PowerOutlet.objects.first()
1432        powerport = PowerPort.objects.first()
1433        Cable(termination_a=poweroutlet, termination_b=powerport).save()
1434
1435        response = self.client.get(reverse('dcim:poweroutlet_trace', kwargs={'pk': poweroutlet.pk}))
1436        self.assertHttpStatus(response, 200)
1437
1438
1439class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
1440    model = Interface
1441
1442    @classmethod
1443    def setUpTestData(cls):
1444        device = create_test_device('Device 1')
1445
1446        interfaces = (
1447            Interface(device=device, name='Interface 1'),
1448            Interface(device=device, name='Interface 2'),
1449            Interface(device=device, name='Interface 3'),
1450            Interface(device=device, name='LAG', type=InterfaceTypeChoices.TYPE_LAG),
1451        )
1452        Interface.objects.bulk_create(interfaces)
1453
1454        vlans = (
1455            VLAN(vid=1, name='VLAN1', site=device.site),
1456            VLAN(vid=101, name='VLAN101', site=device.site),
1457            VLAN(vid=102, name='VLAN102', site=device.site),
1458            VLAN(vid=103, name='VLAN103', site=device.site),
1459        )
1460        VLAN.objects.bulk_create(vlans)
1461
1462        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1463
1464        cls.form_data = {
1465            'device': device.pk,
1466            'virtual_machine': None,
1467            'name': 'Interface X',
1468            'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
1469            'enabled': False,
1470            'lag': interfaces[3].pk,
1471            'mac_address': EUI('01:02:03:04:05:06'),
1472            'mtu': 65000,
1473            'mgmt_only': True,
1474            'description': 'A front port',
1475            'mode': InterfaceModeChoices.MODE_TAGGED,
1476            'untagged_vlan': vlans[0].pk,
1477            'tagged_vlans': [v.pk for v in vlans[1:4]],
1478            'tags': [t.pk for t in tags],
1479        }
1480
1481        cls.bulk_create_data = {
1482            'device': device.pk,
1483            'name_pattern': 'Interface [4-6]',
1484            'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
1485            'enabled': False,
1486            'lag': interfaces[3].pk,
1487            'mac_address': EUI('01:02:03:04:05:06'),
1488            'mtu': 2000,
1489            'mgmt_only': True,
1490            'description': 'A front port',
1491            'mode': InterfaceModeChoices.MODE_TAGGED,
1492            'untagged_vlan': vlans[0].pk,
1493            'tagged_vlans': [v.pk for v in vlans[1:4]],
1494            'tags': [t.pk for t in tags],
1495        }
1496
1497        cls.bulk_edit_data = {
1498            'type': InterfaceTypeChoices.TYPE_1GE_FIXED,
1499            'enabled': True,
1500            'lag': interfaces[3].pk,
1501            'mac_address': EUI('01:02:03:04:05:06'),
1502            'mtu': 2000,
1503            'mgmt_only': True,
1504            'description': 'New description',
1505            'mode': InterfaceModeChoices.MODE_TAGGED,
1506            'untagged_vlan': vlans[0].pk,
1507            'tagged_vlans': [v.pk for v in vlans[1:4]],
1508        }
1509
1510        cls.csv_data = (
1511            "device,name,type",
1512            "Device 1,Interface 4,1000base-t",
1513            "Device 1,Interface 5,1000base-t",
1514            "Device 1,Interface 6,1000base-t",
1515        )
1516
1517    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1518    def test_trace(self):
1519        interface1, interface2 = Interface.objects.all()[:2]
1520        Cable(termination_a=interface1, termination_b=interface2).save()
1521
1522        response = self.client.get(reverse('dcim:interface_trace', kwargs={'pk': interface1.pk}))
1523        self.assertHttpStatus(response, 200)
1524
1525
1526class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1527    model = FrontPort
1528
1529    @classmethod
1530    def setUpTestData(cls):
1531        device = create_test_device('Device 1')
1532
1533        rearports = (
1534            RearPort(device=device, name='Rear Port 1'),
1535            RearPort(device=device, name='Rear Port 2'),
1536            RearPort(device=device, name='Rear Port 3'),
1537            RearPort(device=device, name='Rear Port 4'),
1538            RearPort(device=device, name='Rear Port 5'),
1539            RearPort(device=device, name='Rear Port 6'),
1540        )
1541        RearPort.objects.bulk_create(rearports)
1542
1543        FrontPort.objects.bulk_create([
1544            FrontPort(device=device, name='Front Port 1', rear_port=rearports[0]),
1545            FrontPort(device=device, name='Front Port 2', rear_port=rearports[1]),
1546            FrontPort(device=device, name='Front Port 3', rear_port=rearports[2]),
1547        ])
1548
1549        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1550
1551        cls.form_data = {
1552            'device': device.pk,
1553            'name': 'Front Port X',
1554            'type': PortTypeChoices.TYPE_8P8C,
1555            'rear_port': rearports[3].pk,
1556            'rear_port_position': 1,
1557            'description': 'New description',
1558            'tags': [t.pk for t in tags],
1559        }
1560
1561        cls.bulk_create_data = {
1562            'device': device.pk,
1563            'name_pattern': 'Front Port [4-6]',
1564            'type': PortTypeChoices.TYPE_8P8C,
1565            'rear_port_set': [
1566                '{}:1'.format(rp.pk) for rp in rearports[3:6]
1567            ],
1568            'description': 'New description',
1569            'tags': [t.pk for t in tags],
1570        }
1571
1572        cls.bulk_edit_data = {
1573            'type': PortTypeChoices.TYPE_8P8C,
1574            'description': 'New description',
1575        }
1576
1577        cls.csv_data = (
1578            "device,name,type,rear_port,rear_port_position",
1579            "Device 1,Front Port 4,8p8c,Rear Port 4,1",
1580            "Device 1,Front Port 5,8p8c,Rear Port 5,1",
1581            "Device 1,Front Port 6,8p8c,Rear Port 6,1",
1582        )
1583
1584    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1585    def test_trace(self):
1586        frontport = FrontPort.objects.first()
1587        interface = Interface.objects.create(
1588            device=frontport.device,
1589            name='Interface 1'
1590        )
1591        Cable(termination_a=frontport, termination_b=interface).save()
1592
1593        response = self.client.get(reverse('dcim:frontport_trace', kwargs={'pk': frontport.pk}))
1594        self.assertHttpStatus(response, 200)
1595
1596
1597class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
1598    model = RearPort
1599
1600    @classmethod
1601    def setUpTestData(cls):
1602        device = create_test_device('Device 1')
1603
1604        RearPort.objects.bulk_create([
1605            RearPort(device=device, name='Rear Port 1'),
1606            RearPort(device=device, name='Rear Port 2'),
1607            RearPort(device=device, name='Rear Port 3'),
1608        ])
1609
1610        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1611
1612        cls.form_data = {
1613            'device': device.pk,
1614            'name': 'Rear Port X',
1615            'type': PortTypeChoices.TYPE_8P8C,
1616            'positions': 3,
1617            'description': 'A rear port',
1618            'tags': [t.pk for t in tags],
1619        }
1620
1621        cls.bulk_create_data = {
1622            'device': device.pk,
1623            'name_pattern': 'Rear Port [4-6]',
1624            'type': PortTypeChoices.TYPE_8P8C,
1625            'positions': 3,
1626            'description': 'A rear port',
1627            'tags': [t.pk for t in tags],
1628        }
1629
1630        cls.bulk_edit_data = {
1631            'type': PortTypeChoices.TYPE_8P8C,
1632            'description': 'New description',
1633        }
1634
1635        cls.csv_data = (
1636            "device,name,type,positions",
1637            "Device 1,Rear Port 4,8p8c,1",
1638            "Device 1,Rear Port 5,8p8c,1",
1639            "Device 1,Rear Port 6,8p8c,1",
1640        )
1641
1642    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1643    def test_trace(self):
1644        rearport = RearPort.objects.first()
1645        interface = Interface.objects.create(
1646            device=rearport.device,
1647            name='Interface 1'
1648        )
1649        Cable(termination_a=rearport, termination_b=interface).save()
1650
1651        response = self.client.get(reverse('dcim:rearport_trace', kwargs={'pk': rearport.pk}))
1652        self.assertHttpStatus(response, 200)
1653
1654
1655class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
1656    model = DeviceBay
1657
1658    @classmethod
1659    def setUpTestData(cls):
1660        device = create_test_device('Device 1')
1661
1662        # Update the DeviceType subdevice role to allow adding DeviceBays
1663        DeviceType.objects.update(subdevice_role=SubdeviceRoleChoices.ROLE_PARENT)
1664
1665        DeviceBay.objects.bulk_create([
1666            DeviceBay(device=device, name='Device Bay 1'),
1667            DeviceBay(device=device, name='Device Bay 2'),
1668            DeviceBay(device=device, name='Device Bay 3'),
1669        ])
1670
1671        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1672
1673        cls.form_data = {
1674            'device': device.pk,
1675            'name': 'Device Bay X',
1676            'description': 'A device bay',
1677            'tags': [t.pk for t in tags],
1678        }
1679
1680        cls.bulk_create_data = {
1681            'device': device.pk,
1682            'name_pattern': 'Device Bay [4-6]',
1683            'description': 'A device bay',
1684            'tags': [t.pk for t in tags],
1685        }
1686
1687        cls.bulk_edit_data = {
1688            'description': 'New description',
1689        }
1690
1691        cls.csv_data = (
1692            "device,name",
1693            "Device 1,Device Bay 4",
1694            "Device 1,Device Bay 5",
1695            "Device 1,Device Bay 6",
1696        )
1697
1698
1699class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
1700    model = InventoryItem
1701
1702    @classmethod
1703    def setUpTestData(cls):
1704        device = create_test_device('Device 1')
1705        manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
1706
1707        InventoryItem.objects.create(device=device, name='Inventory Item 1')
1708        InventoryItem.objects.create(device=device, name='Inventory Item 2')
1709        InventoryItem.objects.create(device=device, name='Inventory Item 3')
1710
1711        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1712
1713        cls.form_data = {
1714            'device': device.pk,
1715            'manufacturer': manufacturer.pk,
1716            'name': 'Inventory Item X',
1717            'parent': None,
1718            'discovered': False,
1719            'part_id': '123456',
1720            'serial': '123ABC',
1721            'asset_tag': 'ABC123',
1722            'description': 'An inventory item',
1723            'tags': [t.pk for t in tags],
1724        }
1725
1726        cls.bulk_create_data = {
1727            'device': device.pk,
1728            'name_pattern': 'Inventory Item [4-6]',
1729            'manufacturer': manufacturer.pk,
1730            'parent': None,
1731            'discovered': False,
1732            'part_id': '123456',
1733            'serial': '123ABC',
1734            'description': 'An inventory item',
1735            'tags': [t.pk for t in tags],
1736        }
1737
1738        cls.bulk_edit_data = {
1739            'part_id': '123456',
1740            'description': 'New description',
1741        }
1742
1743        cls.csv_data = (
1744            "device,name,parent",
1745            "Device 1,Inventory Item 4,Inventory Item 1",
1746            "Device 1,Inventory Item 5,Inventory Item 2",
1747            "Device 1,Inventory Item 6,Inventory Item 3",
1748        )
1749
1750
1751# TODO: Change base class to PrimaryObjectViewTestCase
1752# Blocked by lack of common creation view for cables (termination A must be initialized)
1753class CableTestCase(
1754    ViewTestCases.GetObjectViewTestCase,
1755    ViewTestCases.GetObjectChangelogViewTestCase,
1756    ViewTestCases.EditObjectViewTestCase,
1757    ViewTestCases.DeleteObjectViewTestCase,
1758    ViewTestCases.ListObjectsViewTestCase,
1759    ViewTestCases.BulkImportObjectsViewTestCase,
1760    ViewTestCases.BulkEditObjectsViewTestCase,
1761    ViewTestCases.BulkDeleteObjectsViewTestCase
1762):
1763    model = Cable
1764
1765    @classmethod
1766    def setUpTestData(cls):
1767
1768        site = Site.objects.create(name='Site 1', slug='site-1')
1769        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
1770        devicetype = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer)
1771        devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
1772
1773        devices = (
1774            Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole),
1775            Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole),
1776            Device(name='Device 3', site=site, device_type=devicetype, device_role=devicerole),
1777            Device(name='Device 4', site=site, device_type=devicetype, device_role=devicerole),
1778        )
1779        Device.objects.bulk_create(devices)
1780
1781        interfaces = (
1782            Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1783            Interface(device=devices[0], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1784            Interface(device=devices[0], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1785            Interface(device=devices[1], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1786            Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1787            Interface(device=devices[1], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1788            Interface(device=devices[2], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1789            Interface(device=devices[2], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1790            Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1791            Interface(device=devices[3], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1792            Interface(device=devices[3], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1793            Interface(device=devices[3], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
1794        )
1795        Interface.objects.bulk_create(interfaces)
1796
1797        Cable(termination_a=interfaces[0], termination_b=interfaces[3], type=CableTypeChoices.TYPE_CAT6).save()
1798        Cable(termination_a=interfaces[1], termination_b=interfaces[4], type=CableTypeChoices.TYPE_CAT6).save()
1799        Cable(termination_a=interfaces[2], termination_b=interfaces[5], type=CableTypeChoices.TYPE_CAT6).save()
1800
1801        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1802
1803        interface_ct = ContentType.objects.get_for_model(Interface)
1804        cls.form_data = {
1805            # Changing terminations not supported when editing an existing Cable
1806            'termination_a_type': interface_ct.pk,
1807            'termination_a_id': interfaces[0].pk,
1808            'termination_b_type': interface_ct.pk,
1809            'termination_b_id': interfaces[3].pk,
1810            'type': CableTypeChoices.TYPE_CAT6,
1811            'status': CableStatusChoices.STATUS_PLANNED,
1812            'label': 'Label',
1813            'color': 'c0c0c0',
1814            'length': 100,
1815            'length_unit': CableLengthUnitChoices.UNIT_FOOT,
1816            'tags': [t.pk for t in tags],
1817        }
1818
1819        cls.csv_data = (
1820            "side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name",
1821            "Device 3,dcim.interface,Interface 1,Device 4,dcim.interface,Interface 1",
1822            "Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2",
1823            "Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
1824        )
1825
1826        cls.bulk_edit_data = {
1827            'type': CableTypeChoices.TYPE_CAT5E,
1828            'status': CableStatusChoices.STATUS_CONNECTED,
1829            'label': 'New label',
1830            'color': '00ff00',
1831            'length': 50,
1832            'length_unit': CableLengthUnitChoices.UNIT_METER,
1833        }
1834
1835
1836class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1837    model = VirtualChassis
1838
1839    @classmethod
1840    def setUpTestData(cls):
1841
1842        site = Site.objects.create(name='Site 1', slug='site-1')
1843        manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
1844        device_type = DeviceType.objects.create(
1845            manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
1846        )
1847        device_role = DeviceRole.objects.create(
1848            name='Device Role', slug='device-role-1'
1849        )
1850
1851        devices = (
1852            Device(device_type=device_type, device_role=device_role, name='Device 1', site=site),
1853            Device(device_type=device_type, device_role=device_role, name='Device 2', site=site),
1854            Device(device_type=device_type, device_role=device_role, name='Device 3', site=site),
1855            Device(device_type=device_type, device_role=device_role, name='Device 4', site=site),
1856            Device(device_type=device_type, device_role=device_role, name='Device 5', site=site),
1857            Device(device_type=device_type, device_role=device_role, name='Device 6', site=site),
1858            Device(device_type=device_type, device_role=device_role, name='Device 7', site=site),
1859            Device(device_type=device_type, device_role=device_role, name='Device 8', site=site),
1860            Device(device_type=device_type, device_role=device_role, name='Device 9', site=site),
1861            Device(device_type=device_type, device_role=device_role, name='Device 10', site=site),
1862            Device(device_type=device_type, device_role=device_role, name='Device 11', site=site),
1863            Device(device_type=device_type, device_role=device_role, name='Device 12', site=site),
1864        )
1865        Device.objects.bulk_create(devices)
1866
1867        # Create three VirtualChassis with three members each
1868        vc1 = VirtualChassis.objects.create(name='VC1', master=devices[0], domain='domain-1')
1869        Device.objects.filter(pk=devices[0].pk).update(virtual_chassis=vc1, vc_position=1)
1870        Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=vc1, vc_position=2)
1871        Device.objects.filter(pk=devices[2].pk).update(virtual_chassis=vc1, vc_position=3)
1872        vc2 = VirtualChassis.objects.create(name='VC2', master=devices[3], domain='domain-2')
1873        Device.objects.filter(pk=devices[3].pk).update(virtual_chassis=vc2, vc_position=1)
1874        Device.objects.filter(pk=devices[4].pk).update(virtual_chassis=vc2, vc_position=2)
1875        Device.objects.filter(pk=devices[5].pk).update(virtual_chassis=vc2, vc_position=3)
1876        vc3 = VirtualChassis.objects.create(name='VC3', master=devices[6], domain='domain-3')
1877        Device.objects.filter(pk=devices[6].pk).update(virtual_chassis=vc3, vc_position=1)
1878        Device.objects.filter(pk=devices[7].pk).update(virtual_chassis=vc3, vc_position=2)
1879        Device.objects.filter(pk=devices[8].pk).update(virtual_chassis=vc3, vc_position=3)
1880
1881        cls.form_data = {
1882            'name': 'VC4',
1883            'domain': 'domain-4',
1884            # Management form data for VC members
1885            'form-TOTAL_FORMS': 0,
1886            'form-INITIAL_FORMS': 3,
1887            'form-MIN_NUM_FORMS': 0,
1888            'form-MAX_NUM_FORMS': 1000,
1889        }
1890
1891        cls.csv_data = (
1892            "name,domain,master",
1893            "VC4,Domain 4,Device 10",
1894            "VC5,Domain 5,Device 11",
1895            "VC6,Domain 6,Device 12",
1896        )
1897
1898        cls.bulk_edit_data = {
1899            'domain': 'domain-x',
1900        }
1901
1902
1903class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1904    model = PowerPanel
1905
1906    @classmethod
1907    def setUpTestData(cls):
1908
1909        sites = (
1910            Site(name='Site 1', slug='site-1'),
1911            Site(name='Site 2', slug='site-2'),
1912        )
1913        Site.objects.bulk_create(sites)
1914
1915        locations = (
1916            Location(name='Location 1', slug='location-1', site=sites[0]),
1917            Location(name='Location 2', slug='location-2', site=sites[1]),
1918        )
1919        for location in locations:
1920            location.save()
1921
1922        PowerPanel.objects.bulk_create((
1923            PowerPanel(site=sites[0], location=locations[0], name='Power Panel 1'),
1924            PowerPanel(site=sites[0], location=locations[0], name='Power Panel 2'),
1925            PowerPanel(site=sites[0], location=locations[0], name='Power Panel 3'),
1926        ))
1927
1928        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1929
1930        cls.form_data = {
1931            'site': sites[1].pk,
1932            'location': locations[1].pk,
1933            'name': 'Power Panel X',
1934            'tags': [t.pk for t in tags],
1935        }
1936
1937        cls.csv_data = (
1938            "site,location,name",
1939            "Site 1,Location 1,Power Panel 4",
1940            "Site 1,Location 1,Power Panel 5",
1941            "Site 1,Location 1,Power Panel 6",
1942        )
1943
1944        cls.bulk_edit_data = {
1945            'site': sites[1].pk,
1946            'location': locations[1].pk,
1947        }
1948
1949
1950class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
1951    model = PowerFeed
1952
1953    @classmethod
1954    def setUpTestData(cls):
1955
1956        site = Site.objects.create(name='Site 1', slug='site-1')
1957
1958        powerpanels = (
1959            PowerPanel(site=site, name='Power Panel 1'),
1960            PowerPanel(site=site, name='Power Panel 2'),
1961        )
1962        PowerPanel.objects.bulk_create(powerpanels)
1963
1964        racks = (
1965            Rack(site=site, name='Rack 1'),
1966            Rack(site=site, name='Rack 2'),
1967        )
1968        Rack.objects.bulk_create(racks)
1969
1970        PowerFeed.objects.bulk_create((
1971            PowerFeed(name='Power Feed 1', power_panel=powerpanels[0], rack=racks[0]),
1972            PowerFeed(name='Power Feed 2', power_panel=powerpanels[0], rack=racks[0]),
1973            PowerFeed(name='Power Feed 3', power_panel=powerpanels[0], rack=racks[0]),
1974        ))
1975
1976        tags = create_tags('Alpha', 'Bravo', 'Charlie')
1977
1978        cls.form_data = {
1979            'name': 'Power Feed X',
1980            'power_panel': powerpanels[1].pk,
1981            'rack': racks[1].pk,
1982            'status': PowerFeedStatusChoices.STATUS_PLANNED,
1983            'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
1984            'supply': PowerFeedSupplyChoices.SUPPLY_DC,
1985            'phase': PowerFeedPhaseChoices.PHASE_3PHASE,
1986            'voltage': 100,
1987            'amperage': 100,
1988            'max_utilization': 50,
1989            'comments': 'New comments',
1990            'tags': [t.pk for t in tags],
1991        }
1992
1993        cls.csv_data = (
1994            "site,power_panel,name,status,type,supply,phase,voltage,amperage,max_utilization",
1995            "Site 1,Power Panel 1,Power Feed 4,active,primary,ac,single-phase,120,20,80",
1996            "Site 1,Power Panel 1,Power Feed 5,active,primary,ac,single-phase,120,20,80",
1997            "Site 1,Power Panel 1,Power Feed 6,active,primary,ac,single-phase,120,20,80",
1998        )
1999
2000        cls.bulk_edit_data = {
2001            'power_panel': powerpanels[1].pk,
2002            'rack': racks[1].pk,
2003            'status': PowerFeedStatusChoices.STATUS_PLANNED,
2004            'type': PowerFeedTypeChoices.TYPE_REDUNDANT,
2005            'supply': PowerFeedSupplyChoices.SUPPLY_DC,
2006            'phase': PowerFeedPhaseChoices.PHASE_3PHASE,
2007            'voltage': 100,
2008            'amperage': 100,
2009            'max_utilization': 50,
2010            'comments': 'New comments',
2011        }
2012
2013    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
2014    def test_trace(self):
2015        manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
2016        device_type = DeviceType.objects.create(
2017            manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
2018        )
2019        device_role = DeviceRole.objects.create(
2020            name='Device Role', slug='device-role-1'
2021        )
2022        device = Device.objects.create(
2023            site=Site.objects.first(), device_type=device_type, device_role=device_role
2024        )
2025
2026        powerfeed = PowerFeed.objects.first()
2027        powerport = PowerPort.objects.create(
2028            device=device,
2029            name='Power Port 1'
2030        )
2031        Cable(termination_a=powerfeed, termination_b=powerport).save()
2032
2033        response = self.client.get(reverse('dcim:powerfeed_trace', kwargs={'pk': powerfeed.pk}))
2034        self.assertHttpStatus(response, 200)
2035