mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Basic Support for Python 3 (#827)
* Rudimentary python3 support * update docs and trigger Travis * fix some of the tests * fix all python3 errors * change env calls to just python * add @python_2_unicode_compatible decorator to models for python2 compatibility * switch netbox.configuration to from netbox import configuration
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
*.pyc
|
||||
/netbox/netbox/configuration.py
|
||||
/netbox/netbox/ldap_config.py
|
||||
/netbox/static
|
||||
.idea
|
||||
/*.sh
|
||||
|
@ -9,6 +9,9 @@ env:
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- pip install pep8
|
||||
|
@ -3,14 +3,14 @@
|
||||
**Debian/Ubuntu**
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y python2.7 python-dev python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev
|
||||
# apt-get install -y python3 python3-dev python3-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev
|
||||
```
|
||||
|
||||
**CentOS/RHEL**
|
||||
|
||||
```no-highlight
|
||||
# yum install -y epel-release
|
||||
# yum install -y gcc python2 python-devel python-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel
|
||||
# yum install -y gcc python3 python3-devel python3-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel
|
||||
```
|
||||
|
||||
You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from dcim.fields import ASNField
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
@ -33,6 +34,7 @@ def humanize_speed(speed):
|
||||
return '{} Kbps'.format(speed)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Provider(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
|
||||
@ -51,7 +53,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -67,6 +69,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
|
||||
])
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CircuitType(models.Model):
|
||||
"""
|
||||
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
|
||||
@ -78,13 +81,14 @@ class CircuitType(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?type={}".format(reverse('circuits:circuit_list'), self.slug)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
|
||||
@ -105,7 +109,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
||||
ordering = ['provider', 'cid']
|
||||
unique_together = ['provider', 'cid']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} {}'.format(self.provider, self.cid)
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -141,6 +145,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
||||
commit_rate_human.admin_order_field = 'commit_rate'
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CircuitTermination(models.Model):
|
||||
circuit = models.ForeignKey('Circuit', related_name='terminations', on_delete=models.CASCADE)
|
||||
term_side = models.CharField(max_length=1, choices=TERM_SIDE_CHOICES, verbose_name='Termination')
|
||||
@ -156,7 +161,7 @@ class CircuitTermination(models.Model):
|
||||
ordering = ['circuit', 'term_side']
|
||||
unique_together = ['circuit', 'term_side']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} (Side {})'.format(self.circuit, self.get_term_side_display())
|
||||
|
||||
def get_peer_termination(self):
|
||||
|
@ -13,7 +13,7 @@ from utilities.forms import (
|
||||
SlugField,
|
||||
)
|
||||
|
||||
from formfields import MACAddressFormField
|
||||
from .formfields import MACAddressFormField
|
||||
from .models import (
|
||||
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED,
|
||||
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType,
|
||||
|
@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Count, Q, ObjectDoesNotExist
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from circuits.models import Circuit
|
||||
from extras.models import CustomFieldModel, CustomField, CustomFieldValue
|
||||
@ -199,6 +200,7 @@ class SiteManager(NaturalOrderByManager):
|
||||
return self.natural_order_by('name')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A Site represents a geographic location within a network; typically a building or campus. The optional facility
|
||||
@ -222,7 +224,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -265,6 +267,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
# Racks
|
||||
#
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class RackGroup(models.Model):
|
||||
"""
|
||||
Racks can be grouped as subsets within a Site. The scope of a group will depend on how Sites are defined. For
|
||||
@ -282,13 +285,14 @@ class RackGroup(models.Model):
|
||||
['site', 'slug'],
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} - {}'.format(self.site.name, self.name)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group_id={}".format(reverse('dcim:rack_list'), self.pk)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class RackRole(models.Model):
|
||||
"""
|
||||
Racks can be organized by functional role, similar to Devices.
|
||||
@ -300,7 +304,7 @@ class RackRole(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -313,6 +317,7 @@ class RackManager(NaturalOrderByManager):
|
||||
return self.natural_order_by('site__name', 'name')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
|
||||
@ -343,7 +348,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
['site', 'facility_id'],
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.display_name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -442,7 +447,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
devices = self.devices.select_related('device_type').filter(position__gte=1).exclude(pk__in=exclude)
|
||||
|
||||
# Initialize the rack unit skeleton
|
||||
units = range(1, self.u_height + 1)
|
||||
units = list(range(1, self.u_height + 1))
|
||||
|
||||
# Remove units consumed by installed devices
|
||||
for d in devices:
|
||||
@ -477,6 +482,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
# Device Types
|
||||
#
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Manufacturer(models.Model):
|
||||
"""
|
||||
A Manufacturer represents a company which produces hardware devices; for example, Juniper or Dell.
|
||||
@ -487,13 +493,14 @@ class Manufacturer(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?manufacturer={}".format(reverse('dcim:devicetype_list'), self.slug)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DeviceType(models.Model, CustomFieldModel):
|
||||
"""
|
||||
A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as
|
||||
@ -538,7 +545,7 @@ class DeviceType(models.Model, CustomFieldModel):
|
||||
['manufacturer', 'slug'],
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.model
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -608,6 +615,7 @@ class DeviceType(models.Model, CustomFieldModel):
|
||||
return bool(self.subdevice_role is False)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ConsolePortTemplate(models.Model):
|
||||
"""
|
||||
A template for a ConsolePort to be created for a new Device.
|
||||
@ -619,10 +627,11 @@ class ConsolePortTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ConsoleServerPortTemplate(models.Model):
|
||||
"""
|
||||
A template for a ConsoleServerPort to be created for a new Device.
|
||||
@ -634,10 +643,11 @@ class ConsoleServerPortTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PowerPortTemplate(models.Model):
|
||||
"""
|
||||
A template for a PowerPort to be created for a new Device.
|
||||
@ -649,10 +659,11 @@ class PowerPortTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PowerOutletTemplate(models.Model):
|
||||
"""
|
||||
A template for a PowerOutlet to be created for a new Device.
|
||||
@ -664,7 +675,7 @@ class PowerOutletTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@ -706,6 +717,7 @@ class InterfaceManager(models.Manager):
|
||||
}).order_by(*ordering)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class InterfaceTemplate(models.Model):
|
||||
"""
|
||||
A template for a physical data interface on a new Device.
|
||||
@ -721,10 +733,11 @@ class InterfaceTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DeviceBayTemplate(models.Model):
|
||||
"""
|
||||
A template for a DeviceBay to be created for a new parent Device.
|
||||
@ -736,7 +749,7 @@ class DeviceBayTemplate(models.Model):
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@ -744,6 +757,7 @@ class DeviceBayTemplate(models.Model):
|
||||
# Devices
|
||||
#
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DeviceRole(models.Model):
|
||||
"""
|
||||
Devices are organized by functional role; for example, "Core Switch" or "File Server". Each DeviceRole is assigned a
|
||||
@ -756,13 +770,14 @@ class DeviceRole(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?role={}".format(reverse('dcim:device_list'), self.slug)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Platform(models.Model):
|
||||
"""
|
||||
Platform refers to the software or firmware running on a Device; for example, "Cisco IOS-XR" or "Juniper Junos".
|
||||
@ -776,7 +791,7 @@ class Platform(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -789,6 +804,7 @@ class DeviceManager(NaturalOrderByManager):
|
||||
return self.natural_order_by('name')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
|
||||
@ -828,7 +844,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
ordering = ['name']
|
||||
unique_together = ['rack', 'position', 'face']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.display_name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -968,6 +984,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
return RPC_CLIENTS.get(self.platform.rpc_client)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ConsolePort(models.Model):
|
||||
"""
|
||||
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
||||
@ -982,7 +999,7 @@ class ConsolePort(models.Model):
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
# Used for connections export
|
||||
@ -1011,6 +1028,7 @@ class ConsoleServerPortManager(models.Manager):
|
||||
}).order_by('device', 'name_as_integer')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ConsoleServerPort(models.Model):
|
||||
"""
|
||||
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
||||
@ -1023,10 +1041,11 @@ class ConsoleServerPort(models.Model):
|
||||
class Meta:
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PowerPort(models.Model):
|
||||
"""
|
||||
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
||||
@ -1041,7 +1060,7 @@ class PowerPort(models.Model):
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
# Used for connections export
|
||||
@ -1064,6 +1083,7 @@ class PowerOutletManager(models.Manager):
|
||||
}).order_by('device', 'name_padded')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PowerOutlet(models.Model):
|
||||
"""
|
||||
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
||||
@ -1076,10 +1096,11 @@ class PowerOutlet(models.Model):
|
||||
class Meta:
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Interface(models.Model):
|
||||
"""
|
||||
A physical data interface within a Device. An Interface can connect to exactly one other Interface via the creation
|
||||
@ -1099,7 +1120,7 @@ class Interface(models.Model):
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
@ -1176,6 +1197,7 @@ class InterfaceConnection(models.Model):
|
||||
])
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DeviceBay(models.Model):
|
||||
"""
|
||||
An empty space within a Device which can house a child device
|
||||
@ -1189,7 +1211,7 @@ class DeviceBay(models.Model):
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} - {}'.format(self.device.name, self.name)
|
||||
|
||||
def clean(self):
|
||||
@ -1205,6 +1227,7 @@ class DeviceBay(models.Model):
|
||||
raise ValidationError("Cannot install a device into itself.")
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Module(models.Model):
|
||||
"""
|
||||
A Module represents a piece of hardware within a Device, such as a line card or power supply. Modules are used only
|
||||
@ -1223,5 +1246,5 @@ class Module(models.Model):
|
||||
ordering = ['device__id', 'parent__id', 'name']
|
||||
unique_together = ['device', 'parent', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -65,7 +65,7 @@ class SiteTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/sites/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -75,7 +75,7 @@ class SiteTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/sites/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -84,9 +84,9 @@ class SiteTest(APITestCase):
|
||||
|
||||
def test_get_site_list_rack(self, endpoint='/{}api/dcim/sites/1/racks/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in json.loads(response.content):
|
||||
for i in json.loads(response.content.decode('utf-8')):
|
||||
self.assertEqual(
|
||||
sorted(i.keys()),
|
||||
sorted(self.rack_fields),
|
||||
@ -99,9 +99,9 @@ class SiteTest(APITestCase):
|
||||
|
||||
def test_get_site_list_graphs(self, endpoint='/{}api/dcim/sites/1/graphs/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in json.loads(response.content):
|
||||
for i in json.loads(response.content.decode('utf-8')):
|
||||
self.assertEqual(
|
||||
sorted(i.keys()),
|
||||
sorted(self.graph_fields),
|
||||
@ -159,7 +159,7 @@ class RackTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/racks/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -173,7 +173,7 @@ class RackTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/racks/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -202,7 +202,7 @@ class ManufacturersTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/manufacturers/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -212,7 +212,7 @@ class ManufacturersTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/manufacturers/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -250,7 +250,7 @@ class DeviceTypeTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/device-types/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -261,7 +261,7 @@ class DeviceTypeTest(APITestCase):
|
||||
def test_detail_list(self, endpoint='/{}api/dcim/device-types/1/'.format(settings.BASE_PATH)):
|
||||
# TODO: details returns list view.
|
||||
# response = self.client.get(endpoint)
|
||||
# content = json.loads(response.content)
|
||||
# content = json.loads(response.content.decode('utf-8'))
|
||||
# self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
# self.assertEqual(
|
||||
# sorted(content.keys()),
|
||||
@ -284,7 +284,7 @@ class DeviceRolesTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/device-roles/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -294,7 +294,7 @@ class DeviceRolesTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/device-roles/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -312,7 +312,7 @@ class PlatformsTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/platforms/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -322,7 +322,7 @@ class PlatformsTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/platforms/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -360,7 +360,7 @@ class DeviceTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for device in content:
|
||||
self.assertEqual(
|
||||
@ -425,7 +425,7 @@ class DeviceTest(APITestCase):
|
||||
]
|
||||
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
device = content[0]
|
||||
self.assertEqual(
|
||||
@ -435,7 +435,7 @@ class DeviceTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/devices/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -453,7 +453,7 @@ class ConsoleServerPortsTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/9/console-server-ports/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for console_port in content:
|
||||
self.assertEqual(
|
||||
@ -475,7 +475,7 @@ class ConsolePortsTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/1/console-ports/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for console_port in content:
|
||||
self.assertEqual(
|
||||
@ -493,7 +493,7 @@ class ConsolePortsTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/console-ports/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -514,7 +514,7 @@ class PowerPortsTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/1/power-ports/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -528,7 +528,7 @@ class PowerPortsTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/power-ports/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -549,7 +549,7 @@ class PowerOutletsTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/11/power-outlets/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -599,7 +599,7 @@ class InterfaceTest(APITestCase):
|
||||
|
||||
def test_get_list(self, endpoint='/{}api/dcim/devices/1/interfaces/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
@ -613,7 +613,7 @@ class InterfaceTest(APITestCase):
|
||||
|
||||
def test_get_detail(self, endpoint='/{}api/dcim/interfaces/1/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -625,19 +625,19 @@ class InterfaceTest(APITestCase):
|
||||
)
|
||||
|
||||
def test_get_graph_list(self, endpoint='/{}api/dcim/interfaces/1/graphs/'.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
sorted(i.keys()),
|
||||
sorted(SiteTest.graph_fields),
|
||||
)
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for i in content:
|
||||
self.assertEqual(
|
||||
sorted(i.keys()),
|
||||
sorted(SiteTest.graph_fields),
|
||||
)
|
||||
|
||||
def test_get_interface_connections(self, endpoint='/{}api/dcim/interface-connections/4/'
|
||||
.format(settings.BASE_PATH)):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
@ -659,7 +659,7 @@ class RelatedConnectionsTest(APITestCase):
|
||||
def test_get_list(self, endpoint=('/{}api/dcim/related-connections/?peer-device=test1-edge1&peer-interface=xe-0/0/3'
|
||||
.format(settings.BASE_PATH))):
|
||||
response = self.client.get(endpoint)
|
||||
content = json.loads(response.content)
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
sorted(content.keys()),
|
||||
|
@ -50,7 +50,7 @@ class FlatJSONRenderer(renderers.BaseRenderer):
|
||||
def render(self, data, media_type=None, renderer_context=None):
|
||||
|
||||
def flatten(entry):
|
||||
for key, val in entry.iteritems():
|
||||
for key, val in entry.items():
|
||||
if isinstance(val, dict):
|
||||
for child_key, child_val in flatten(val):
|
||||
yield "{}_{}".format(key, child_key), child_val
|
||||
|
@ -8,6 +8,7 @@ from django.core.validators import ValidationError
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
from django.template import Template, Context
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
@ -93,6 +94,7 @@ class CustomFieldModel(object):
|
||||
return OrderedDict([(field, None) for field in fields])
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomField(models.Model):
|
||||
obj_type = models.ManyToManyField(ContentType, related_name='custom_fields', verbose_name='Object(s)',
|
||||
limit_choices_to={'model__in': CUSTOMFIELD_MODELS},
|
||||
@ -114,7 +116,7 @@ class CustomField(models.Model):
|
||||
class Meta:
|
||||
ordering = ['weight', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.label or self.name.replace('_', ' ').capitalize()
|
||||
|
||||
def serialize_value(self, value):
|
||||
@ -153,6 +155,7 @@ class CustomField(models.Model):
|
||||
return serialized_value
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomFieldValue(models.Model):
|
||||
field = models.ForeignKey('CustomField', related_name='values')
|
||||
obj_type = models.ForeignKey(ContentType, related_name='+', on_delete=models.PROTECT)
|
||||
@ -164,7 +167,7 @@ class CustomFieldValue(models.Model):
|
||||
ordering = ['obj_type', 'obj_id']
|
||||
unique_together = ['field', 'obj_type', 'obj_id']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} {}'.format(self.obj, self.field)
|
||||
|
||||
@property
|
||||
@ -183,6 +186,7 @@ class CustomFieldValue(models.Model):
|
||||
super(CustomFieldValue, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomFieldChoice(models.Model):
|
||||
field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT},
|
||||
on_delete=models.CASCADE)
|
||||
@ -193,7 +197,7 @@ class CustomFieldChoice(models.Model):
|
||||
ordering = ['field', 'weight', 'value']
|
||||
unique_together = ['field', 'value']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def clean(self):
|
||||
@ -207,6 +211,7 @@ class CustomFieldChoice(models.Model):
|
||||
CustomFieldValue.objects.filter(field__type=CF_TYPE_SELECT, serialized_value=str(pk)).delete()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Graph(models.Model):
|
||||
type = models.PositiveSmallIntegerField(choices=GRAPH_TYPE_CHOICES)
|
||||
weight = models.PositiveSmallIntegerField(default=1000)
|
||||
@ -217,7 +222,7 @@ class Graph(models.Model):
|
||||
class Meta:
|
||||
ordering = ['type', 'weight', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def embed_url(self, obj):
|
||||
@ -231,6 +236,7 @@ class Graph(models.Model):
|
||||
return template.render(Context({'obj': obj}))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ExportTemplate(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS})
|
||||
name = models.CharField(max_length=100)
|
||||
@ -245,7 +251,7 @@ class ExportTemplate(models.Model):
|
||||
['content_type', 'name']
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{}: {}'.format(self.content_type, self.name)
|
||||
|
||||
def to_response(self, context_dict, filename):
|
||||
@ -264,6 +270,7 @@ class ExportTemplate(models.Model):
|
||||
return response
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class TopologyMap(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
@ -278,7 +285,7 @@ class TopologyMap(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
@ -328,6 +335,7 @@ class UserActionManager(models.Manager):
|
||||
self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserAction(models.Model):
|
||||
"""
|
||||
A record of an action (add, edit, or delete) performed on an object by a User.
|
||||
@ -344,7 +352,7 @@ class UserAction(models.Model):
|
||||
class Meta:
|
||||
ordering = ['-time']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
if self.message:
|
||||
return u'{} {}'.format(self.user, self.message)
|
||||
return u'{} {} {}'.format(self.user, self.get_action_display(), self.content_type)
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
# This script will generate a random 50-character string suitable for use as a SECRET_KEY.
|
||||
import os
|
||||
import random
|
||||
|
||||
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(-_=+)'
|
||||
random.seed = (os.urandom(2048))
|
||||
print ''.join(random.choice(charset) for c in range(50))
|
||||
print(''.join(random.choice(charset) for c in range(50)))
|
||||
|
@ -7,6 +7,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models.expressions import RawSQL
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from dcim.models import Interface
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
@ -72,6 +73,7 @@ IP_PROTOCOL_CHOICES = (
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class VRF(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing
|
||||
@ -91,7 +93,7 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
|
||||
verbose_name = 'VRF'
|
||||
verbose_name_plural = 'VRFs'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -107,6 +109,7 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
|
||||
])
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class RIR(models.Model):
|
||||
"""
|
||||
A Regional Internet Registry (RIR) is responsible for the allocation of a large portion of the global IP address
|
||||
@ -122,13 +125,14 @@ class RIR(models.Model):
|
||||
verbose_name = 'RIR'
|
||||
verbose_name_plural = 'RIRs'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?rir={}".format(reverse('ipam:aggregate_list'), self.slug)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
|
||||
@ -144,7 +148,7 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
||||
class Meta:
|
||||
ordering = ['family', 'prefix']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return str(self.prefix)
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -206,6 +210,7 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
||||
return int(children_size / self.prefix.size * 100)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Role(models.Model):
|
||||
"""
|
||||
A Role represents the functional role of a Prefix or VLAN; for example, "Customer," "Infrastructure," or
|
||||
@ -218,7 +223,7 @@ class Role(models.Model):
|
||||
class Meta:
|
||||
ordering = ['weight', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
@ -265,6 +270,7 @@ class PrefixQuerySet(NullsFirstQuerySet):
|
||||
return filter(lambda p: p.depth <= limit, queryset)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Prefix(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and
|
||||
@ -294,7 +300,7 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
|
||||
ordering = ['vrf', 'family', 'prefix']
|
||||
verbose_name_plural = 'prefixes'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return str(self.prefix)
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -379,6 +385,7 @@ class IPAddressManager(models.Manager):
|
||||
return qs.annotate(host=RawSQL('INET(HOST(ipam_ipaddress.address))', [])).order_by('family', 'host')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
|
||||
@ -411,7 +418,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
||||
verbose_name = 'IP address'
|
||||
verbose_name_plural = 'IP addresses'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return str(self.address)
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -471,6 +478,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
||||
return STATUS_CHOICE_CLASSES[self.status]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class VLANGroup(models.Model):
|
||||
"""
|
||||
A VLAN group is an arbitrary collection of VLANs within which VLAN IDs and names must be unique.
|
||||
@ -488,13 +496,14 @@ class VLANGroup(models.Model):
|
||||
verbose_name = 'VLAN group'
|
||||
verbose_name_plural = 'VLAN groups'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} - {}'.format(self.site.name, self.name)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group_id={}".format(reverse('ipam:vlan_list'), self.pk)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned
|
||||
@ -526,7 +535,7 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
||||
verbose_name = 'VLAN'
|
||||
verbose_name_plural = 'VLANs'
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.display_name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -560,6 +569,7 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
||||
return STATUS_CHOICE_CLASSES[self.status]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Service(CreatedUpdatedModel):
|
||||
"""
|
||||
A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device. A Service may optionally be tied
|
||||
@ -578,5 +588,5 @@ class Service(CreatedUpdatedModel):
|
||||
ordering = ['device', 'protocol', 'port']
|
||||
unique_together = ['device', 'protocol', 'port']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return u'{} ({}/{})'.format(self.name, self.port, self.get_protocol_display())
|
||||
|
@ -6,7 +6,7 @@ from django.contrib.messages import constants as messages
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
try:
|
||||
import configuration
|
||||
from netbox import configuration
|
||||
except ImportError:
|
||||
raise ImproperlyConfigured("Configuration file is not present. Please define netbox/netbox/configuration.py per "
|
||||
"the documentation.")
|
||||
@ -50,7 +50,7 @@ CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
|
||||
# Attempt to import LDAP configuration if it has been defined
|
||||
LDAP_IGNORE_CERT_ERRORS = False
|
||||
try:
|
||||
from ldap_config import *
|
||||
from netbox.ldap_config import *
|
||||
LDAP_CONFIGURED = True
|
||||
except ImportError:
|
||||
LDAP_CONFIGURED = False
|
||||
|
@ -2,7 +2,7 @@ from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
|
||||
from views import home, handle_500, trigger_500
|
||||
from netbox.views import home, handle_500, trigger_500
|
||||
from users.views import login, logout
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ from django.contrib.auth.models import Group, User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.encoding import force_bytes, python_2_unicode_compatible
|
||||
|
||||
from dcim.models import Device
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
@ -51,6 +51,7 @@ class UserKeyQuerySet(models.QuerySet):
|
||||
raise Exception("Bulk deletion has been disabled.")
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserKey(CreatedUpdatedModel):
|
||||
"""
|
||||
A UserKey stores a user's personal RSA (public) encryption key, which is used to generate their unique encrypted
|
||||
@ -76,7 +77,7 @@ class UserKey(CreatedUpdatedModel):
|
||||
self.__initial_public_key = self.public_key
|
||||
self.__initial_master_key_cipher = self.master_key_cipher
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
@ -170,6 +171,7 @@ class UserKey(CreatedUpdatedModel):
|
||||
self.save()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class SecretRole(models.Model):
|
||||
"""
|
||||
A SecretRole represents an arbitrary functional classification of Secrets. For example, a user might define roles
|
||||
@ -186,7 +188,7 @@ class SecretRole(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -201,6 +203,7 @@ class SecretRole(models.Model):
|
||||
return user in self.users.all() or user.groups.filter(pk__in=self.groups.all()).exists()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Secret(CreatedUpdatedModel):
|
||||
"""
|
||||
A Secret stores an AES256-encrypted copy of sensitive data, such as passwords or secret keys. An irreversible
|
||||
@ -227,7 +230,7 @@ class Secret(CreatedUpdatedModel):
|
||||
self.plaintext = kwargs.pop('plaintext', None)
|
||||
super(Secret, self).__init__(*args, **kwargs)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
if self.role and self.device:
|
||||
return u'{} for {}'.format(self.role, self.device)
|
||||
return u'Secret'
|
||||
|
@ -1 +0,0 @@
|
||||
from test_models import *
|
||||
|
@ -1,12 +1,14 @@
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from extras.models import CustomFieldModel, CustomFieldValue
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
from utilities.utils import csv_format
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class TenantGroup(models.Model):
|
||||
"""
|
||||
An arbitrary collection of Tenants.
|
||||
@ -17,13 +19,14 @@ class TenantGroup(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group={}".format(reverse('tenancy:tenant_list'), self.slug)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Tenant(CreatedUpdatedModel, CustomFieldModel):
|
||||
"""
|
||||
A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal
|
||||
@ -39,7 +42,7 @@ class Tenant(CreatedUpdatedModel, CustomFieldModel):
|
||||
class Meta:
|
||||
ordering = ['group', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
|
@ -10,7 +10,7 @@ from utilities.views import (
|
||||
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
)
|
||||
|
||||
from models import Tenant, TenantGroup
|
||||
from .models import Tenant, TenantGroup
|
||||
from . import filters, forms, tables
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user