1
0
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:
Jens L
2017-01-23 22:44:29 +01:00
committed by Jeremy Stretch
parent 1bddd038fe
commit 80439c495e
17 changed files with 152 additions and 97 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
*.pyc *.pyc
/netbox/netbox/configuration.py /netbox/netbox/configuration.py
/netbox/netbox/ldap_config.py
/netbox/static /netbox/static
.idea .idea
/*.sh /*.sh

View File

@ -9,6 +9,9 @@ env:
language: python language: python
python: python:
- "2.7" - "2.7"
- "3.4"
- "3.5"
- "3.6"
install: install:
- pip install -r requirements.txt - pip install -r requirements.txt
- pip install pep8 - pip install pep8

View File

@ -3,14 +3,14 @@
**Debian/Ubuntu** **Debian/Ubuntu**
```no-highlight ```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** **CentOS/RHEL**
```no-highlight ```no-highlight
# yum install -y epel-release # 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. You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.

View File

@ -1,6 +1,7 @@
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from dcim.fields import ASNField from dcim.fields import ASNField
from extras.models import CustomFieldModel, CustomFieldValue from extras.models import CustomFieldModel, CustomFieldValue
@ -33,6 +34,7 @@ def humanize_speed(speed):
return '{} Kbps'.format(speed) return '{} Kbps'.format(speed)
@python_2_unicode_compatible
class Provider(CreatedUpdatedModel, CustomFieldModel): class Provider(CreatedUpdatedModel, CustomFieldModel):
""" """
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
@ -67,6 +69,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
]) ])
@python_2_unicode_compatible
class CircuitType(models.Model): class CircuitType(models.Model):
""" """
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return "{}?type={}".format(reverse('circuits:circuit_list'), self.slug) return "{}?type={}".format(reverse('circuits:circuit_list'), self.slug)
@python_2_unicode_compatible
class Circuit(CreatedUpdatedModel, CustomFieldModel): class Circuit(CreatedUpdatedModel, CustomFieldModel):
""" """
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple 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'] ordering = ['provider', 'cid']
unique_together = ['provider', 'cid'] unique_together = ['provider', 'cid']
def __unicode__(self): def __str__(self):
return u'{} {}'.format(self.provider, self.cid) return u'{} {}'.format(self.provider, self.cid)
def get_absolute_url(self): def get_absolute_url(self):
@ -141,6 +145,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
commit_rate_human.admin_order_field = 'commit_rate' commit_rate_human.admin_order_field = 'commit_rate'
@python_2_unicode_compatible
class CircuitTermination(models.Model): class CircuitTermination(models.Model):
circuit = models.ForeignKey('Circuit', related_name='terminations', on_delete=models.CASCADE) 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') 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'] ordering = ['circuit', 'term_side']
unique_together = ['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()) return u'{} (Side {})'.format(self.circuit, self.get_term_side_display())
def get_peer_termination(self): def get_peer_termination(self):

View File

@ -13,7 +13,7 @@ from utilities.forms import (
SlugField, SlugField,
) )
from formfields import MACAddressFormField from .formfields import MACAddressFormField
from .models import ( from .models import (
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED, DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED,
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType,

View File

@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Count, Q, ObjectDoesNotExist from django.db.models import Count, Q, ObjectDoesNotExist
from django.utils.encoding import python_2_unicode_compatible
from circuits.models import Circuit from circuits.models import Circuit
from extras.models import CustomFieldModel, CustomField, CustomFieldValue from extras.models import CustomFieldModel, CustomField, CustomFieldValue
@ -199,6 +200,7 @@ class SiteManager(NaturalOrderByManager):
return self.natural_order_by('name') return self.natural_order_by('name')
@python_2_unicode_compatible
class Site(CreatedUpdatedModel, CustomFieldModel): class Site(CreatedUpdatedModel, CustomFieldModel):
""" """
A Site represents a geographic location within a network; typically a building or campus. The optional facility 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
@ -265,6 +267,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
# Racks # Racks
# #
@python_2_unicode_compatible
class RackGroup(models.Model): 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 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'], ['site', 'slug'],
] ]
def __unicode__(self): def __str__(self):
return u'{} - {}'.format(self.site.name, self.name) return u'{} - {}'.format(self.site.name, self.name)
def get_absolute_url(self): def get_absolute_url(self):
return "{}?group_id={}".format(reverse('dcim:rack_list'), self.pk) return "{}?group_id={}".format(reverse('dcim:rack_list'), self.pk)
@python_2_unicode_compatible
class RackRole(models.Model): class RackRole(models.Model):
""" """
Racks can be organized by functional role, similar to Devices. Racks can be organized by functional role, similar to Devices.
@ -300,7 +304,7 @@ class RackRole(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
@ -313,6 +317,7 @@ class RackManager(NaturalOrderByManager):
return self.natural_order_by('site__name', 'name') return self.natural_order_by('site__name', 'name')
@python_2_unicode_compatible
class Rack(CreatedUpdatedModel, CustomFieldModel): 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. 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'], ['site', 'facility_id'],
] ]
def __unicode__(self): def __str__(self):
return self.display_name return self.display_name
def get_absolute_url(self): 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) devices = self.devices.select_related('device_type').filter(position__gte=1).exclude(pk__in=exclude)
# Initialize the rack unit skeleton # 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 # Remove units consumed by installed devices
for d in devices: for d in devices:
@ -477,6 +482,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
# Device Types # Device Types
# #
@python_2_unicode_compatible
class Manufacturer(models.Model): class Manufacturer(models.Model):
""" """
A Manufacturer represents a company which produces hardware devices; for example, Juniper or Dell. A Manufacturer represents a company which produces hardware devices; for example, Juniper or Dell.
@ -487,13 +493,14 @@ class Manufacturer(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return "{}?manufacturer={}".format(reverse('dcim:devicetype_list'), self.slug) return "{}?manufacturer={}".format(reverse('dcim:devicetype_list'), self.slug)
@python_2_unicode_compatible
class DeviceType(models.Model, CustomFieldModel): class DeviceType(models.Model, CustomFieldModel):
""" """
A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as 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'], ['manufacturer', 'slug'],
] ]
def __unicode__(self): def __str__(self):
return self.model return self.model
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -608,6 +615,7 @@ class DeviceType(models.Model, CustomFieldModel):
return bool(self.subdevice_role is False) return bool(self.subdevice_role is False)
@python_2_unicode_compatible
class ConsolePortTemplate(models.Model): class ConsolePortTemplate(models.Model):
""" """
A template for a ConsolePort to be created for a new Device. A template for a ConsolePort to be created for a new Device.
@ -619,10 +627,11 @@ class ConsolePortTemplate(models.Model):
ordering = ['device_type', 'name'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class ConsoleServerPortTemplate(models.Model): class ConsoleServerPortTemplate(models.Model):
""" """
A template for a ConsoleServerPort to be created for a new Device. A template for a ConsoleServerPort to be created for a new Device.
@ -634,10 +643,11 @@ class ConsoleServerPortTemplate(models.Model):
ordering = ['device_type', 'name'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class PowerPortTemplate(models.Model): class PowerPortTemplate(models.Model):
""" """
A template for a PowerPort to be created for a new Device. A template for a PowerPort to be created for a new Device.
@ -649,10 +659,11 @@ class PowerPortTemplate(models.Model):
ordering = ['device_type', 'name'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class PowerOutletTemplate(models.Model): class PowerOutletTemplate(models.Model):
""" """
A template for a PowerOutlet to be created for a new Device. A template for a PowerOutlet to be created for a new Device.
@ -664,7 +675,7 @@ class PowerOutletTemplate(models.Model):
ordering = ['device_type', 'name'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@ -706,6 +717,7 @@ class InterfaceManager(models.Manager):
}).order_by(*ordering) }).order_by(*ordering)
@python_2_unicode_compatible
class InterfaceTemplate(models.Model): class InterfaceTemplate(models.Model):
""" """
A template for a physical data interface on a new Device. A template for a physical data interface on a new Device.
@ -721,10 +733,11 @@ class InterfaceTemplate(models.Model):
ordering = ['device_type', 'name'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class DeviceBayTemplate(models.Model): class DeviceBayTemplate(models.Model):
""" """
A template for a DeviceBay to be created for a new parent Device. 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'] ordering = ['device_type', 'name']
unique_together = ['device_type', 'name'] unique_together = ['device_type', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@ -744,6 +757,7 @@ class DeviceBayTemplate(models.Model):
# Devices # Devices
# #
@python_2_unicode_compatible
class DeviceRole(models.Model): class DeviceRole(models.Model):
""" """
Devices are organized by functional role; for example, "Core Switch" or "File Server". Each DeviceRole is assigned a 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return "{}?role={}".format(reverse('dcim:device_list'), self.slug) return "{}?role={}".format(reverse('dcim:device_list'), self.slug)
@python_2_unicode_compatible
class Platform(models.Model): class Platform(models.Model):
""" """
Platform refers to the software or firmware running on a Device; for example, "Cisco IOS-XR" or "Juniper Junos". 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
@ -789,6 +804,7 @@ class DeviceManager(NaturalOrderByManager):
return self.natural_order_by('name') return self.natural_order_by('name')
@python_2_unicode_compatible
class Device(CreatedUpdatedModel, CustomFieldModel): class Device(CreatedUpdatedModel, CustomFieldModel):
""" """
A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, 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'] ordering = ['name']
unique_together = ['rack', 'position', 'face'] unique_together = ['rack', 'position', 'face']
def __unicode__(self): def __str__(self):
return self.display_name return self.display_name
def get_absolute_url(self): def get_absolute_url(self):
@ -968,6 +984,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
return RPC_CLIENTS.get(self.platform.rpc_client) return RPC_CLIENTS.get(self.platform.rpc_client)
@python_2_unicode_compatible
class ConsolePort(models.Model): class ConsolePort(models.Model):
""" """
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts. A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
@ -982,7 +999,7 @@ class ConsolePort(models.Model):
ordering = ['device', 'name'] ordering = ['device', 'name']
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
# Used for connections export # Used for connections export
@ -1011,6 +1028,7 @@ class ConsoleServerPortManager(models.Manager):
}).order_by('device', 'name_as_integer') }).order_by('device', 'name_as_integer')
@python_2_unicode_compatible
class ConsoleServerPort(models.Model): class ConsoleServerPort(models.Model):
""" """
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts. 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: class Meta:
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class PowerPort(models.Model): class PowerPort(models.Model):
""" """
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets. A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
@ -1041,7 +1060,7 @@ class PowerPort(models.Model):
ordering = ['device', 'name'] ordering = ['device', 'name']
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
# Used for connections export # Used for connections export
@ -1064,6 +1083,7 @@ class PowerOutletManager(models.Manager):
}).order_by('device', 'name_padded') }).order_by('device', 'name_padded')
@python_2_unicode_compatible
class PowerOutlet(models.Model): class PowerOutlet(models.Model):
""" """
A physical power outlet (output) within a Device which provides power to a PowerPort. A physical power outlet (output) within a Device which provides power to a PowerPort.
@ -1076,10 +1096,11 @@ class PowerOutlet(models.Model):
class Meta: class Meta:
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible
class Interface(models.Model): class Interface(models.Model):
""" """
A physical data interface within a Device. An Interface can connect to exactly one other Interface via the creation 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'] ordering = ['device', 'name']
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def clean(self): def clean(self):
@ -1176,6 +1197,7 @@ class InterfaceConnection(models.Model):
]) ])
@python_2_unicode_compatible
class DeviceBay(models.Model): class DeviceBay(models.Model):
""" """
An empty space within a Device which can house a child device An empty space within a Device which can house a child device
@ -1189,7 +1211,7 @@ class DeviceBay(models.Model):
ordering = ['device', 'name'] ordering = ['device', 'name']
unique_together = ['device', 'name'] unique_together = ['device', 'name']
def __unicode__(self): def __str__(self):
return u'{} - {}'.format(self.device.name, self.name) return u'{} - {}'.format(self.device.name, self.name)
def clean(self): def clean(self):
@ -1205,6 +1227,7 @@ class DeviceBay(models.Model):
raise ValidationError("Cannot install a device into itself.") raise ValidationError("Cannot install a device into itself.")
@python_2_unicode_compatible
class Module(models.Model): 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 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'] ordering = ['device__id', 'parent__id', 'name']
unique_together = ['device', 'parent', 'name'] unique_together = ['device', 'parent', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name

View File

@ -65,7 +65,7 @@ class SiteTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/sites/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/sites/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -75,7 +75,7 @@ class SiteTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/sites/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/sites/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), 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)): def test_get_site_list_rack(self, endpoint='/{}api/dcim/sites/1/racks/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(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( self.assertEqual(
sorted(i.keys()), sorted(i.keys()),
sorted(self.rack_fields), 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)): def test_get_site_list_graphs(self, endpoint='/{}api/dcim/sites/1/graphs/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(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( self.assertEqual(
sorted(i.keys()), sorted(i.keys()),
sorted(self.graph_fields), sorted(self.graph_fields),
@ -159,7 +159,7 @@ class RackTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/racks/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/racks/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -173,7 +173,7 @@ class RackTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/racks/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/racks/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),
@ -202,7 +202,7 @@ class ManufacturersTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/manufacturers/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/manufacturers/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -212,7 +212,7 @@ class ManufacturersTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/manufacturers/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/manufacturers/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),
@ -250,7 +250,7 @@ class DeviceTypeTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/device-types/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/device-types/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -261,7 +261,7 @@ class DeviceTypeTest(APITestCase):
def test_detail_list(self, endpoint='/{}api/dcim/device-types/1/'.format(settings.BASE_PATH)): def test_detail_list(self, endpoint='/{}api/dcim/device-types/1/'.format(settings.BASE_PATH)):
# TODO: details returns list view. # TODO: details returns list view.
# response = self.client.get(endpoint) # 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(response.status_code, status.HTTP_200_OK)
# self.assertEqual( # self.assertEqual(
# sorted(content.keys()), # sorted(content.keys()),
@ -284,7 +284,7 @@ class DeviceRolesTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/device-roles/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/device-roles/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -294,7 +294,7 @@ class DeviceRolesTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/device-roles/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/device-roles/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),
@ -312,7 +312,7 @@ class PlatformsTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/platforms/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/platforms/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -322,7 +322,7 @@ class PlatformsTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/platforms/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/platforms/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),
@ -360,7 +360,7 @@ class DeviceTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/devices/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/devices/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for device in content: for device in content:
self.assertEqual( self.assertEqual(
@ -425,7 +425,7 @@ class DeviceTest(APITestCase):
] ]
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
device = content[0] device = content[0]
self.assertEqual( self.assertEqual(
@ -435,7 +435,7 @@ class DeviceTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/devices/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/devices/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), 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)): def test_get_list(self, endpoint='/{}api/dcim/devices/9/console-server-ports/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for console_port in content: for console_port in content:
self.assertEqual( 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)): def test_get_list(self, endpoint='/{}api/dcim/devices/1/console-ports/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for console_port in content: for console_port in content:
self.assertEqual( self.assertEqual(
@ -493,7 +493,7 @@ class ConsolePortsTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/console-ports/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/console-ports/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), 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)): def test_get_list(self, endpoint='/{}api/dcim/devices/1/power-ports/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -528,7 +528,7 @@ class PowerPortsTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/power-ports/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/power-ports/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), 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)): def test_get_list(self, endpoint='/{}api/dcim/devices/11/power-outlets/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -599,7 +599,7 @@ class InterfaceTest(APITestCase):
def test_get_list(self, endpoint='/{}api/dcim/devices/1/interfaces/'.format(settings.BASE_PATH)): def test_get_list(self, endpoint='/{}api/dcim/devices/1/interfaces/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -613,7 +613,7 @@ class InterfaceTest(APITestCase):
def test_get_detail(self, endpoint='/{}api/dcim/interfaces/1/'.format(settings.BASE_PATH)): def test_get_detail(self, endpoint='/{}api/dcim/interfaces/1/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),
@ -626,7 +626,7 @@ class InterfaceTest(APITestCase):
def test_get_graph_list(self, endpoint='/{}api/dcim/interfaces/1/graphs/'.format(settings.BASE_PATH)): def test_get_graph_list(self, endpoint='/{}api/dcim/interfaces/1/graphs/'.format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
for i in content: for i in content:
self.assertEqual( self.assertEqual(
@ -637,7 +637,7 @@ class InterfaceTest(APITestCase):
def test_get_interface_connections(self, endpoint='/{}api/dcim/interface-connections/4/' def test_get_interface_connections(self, endpoint='/{}api/dcim/interface-connections/4/'
.format(settings.BASE_PATH)): .format(settings.BASE_PATH)):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), 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' def test_get_list(self, endpoint=('/{}api/dcim/related-connections/?peer-device=test1-edge1&peer-interface=xe-0/0/3'
.format(settings.BASE_PATH))): .format(settings.BASE_PATH))):
response = self.client.get(endpoint) 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(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
sorted(content.keys()), sorted(content.keys()),

View File

@ -50,7 +50,7 @@ class FlatJSONRenderer(renderers.BaseRenderer):
def render(self, data, media_type=None, renderer_context=None): def render(self, data, media_type=None, renderer_context=None):
def flatten(entry): def flatten(entry):
for key, val in entry.iteritems(): for key, val in entry.items():
if isinstance(val, dict): if isinstance(val, dict):
for child_key, child_val in flatten(val): for child_key, child_val in flatten(val):
yield "{}_{}".format(key, child_key), child_val yield "{}_{}".format(key, child_key), child_val

View File

@ -8,6 +8,7 @@ from django.core.validators import ValidationError
from django.db import models from django.db import models
from django.http import HttpResponse from django.http import HttpResponse
from django.template import Template, Context from django.template import Template, Context
from django.utils.encoding import python_2_unicode_compatible
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -93,6 +94,7 @@ class CustomFieldModel(object):
return OrderedDict([(field, None) for field in fields]) return OrderedDict([(field, None) for field in fields])
@python_2_unicode_compatible
class CustomField(models.Model): class CustomField(models.Model):
obj_type = models.ManyToManyField(ContentType, related_name='custom_fields', verbose_name='Object(s)', obj_type = models.ManyToManyField(ContentType, related_name='custom_fields', verbose_name='Object(s)',
limit_choices_to={'model__in': CUSTOMFIELD_MODELS}, limit_choices_to={'model__in': CUSTOMFIELD_MODELS},
@ -114,7 +116,7 @@ class CustomField(models.Model):
class Meta: class Meta:
ordering = ['weight', 'name'] ordering = ['weight', 'name']
def __unicode__(self): def __str__(self):
return self.label or self.name.replace('_', ' ').capitalize() return self.label or self.name.replace('_', ' ').capitalize()
def serialize_value(self, value): def serialize_value(self, value):
@ -153,6 +155,7 @@ class CustomField(models.Model):
return serialized_value return serialized_value
@python_2_unicode_compatible
class CustomFieldValue(models.Model): class CustomFieldValue(models.Model):
field = models.ForeignKey('CustomField', related_name='values') field = models.ForeignKey('CustomField', related_name='values')
obj_type = models.ForeignKey(ContentType, related_name='+', on_delete=models.PROTECT) obj_type = models.ForeignKey(ContentType, related_name='+', on_delete=models.PROTECT)
@ -164,7 +167,7 @@ class CustomFieldValue(models.Model):
ordering = ['obj_type', 'obj_id'] ordering = ['obj_type', 'obj_id']
unique_together = ['field', 'obj_type', 'obj_id'] unique_together = ['field', 'obj_type', 'obj_id']
def __unicode__(self): def __str__(self):
return u'{} {}'.format(self.obj, self.field) return u'{} {}'.format(self.obj, self.field)
@property @property
@ -183,6 +186,7 @@ class CustomFieldValue(models.Model):
super(CustomFieldValue, self).save(*args, **kwargs) super(CustomFieldValue, self).save(*args, **kwargs)
@python_2_unicode_compatible
class CustomFieldChoice(models.Model): class CustomFieldChoice(models.Model):
field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT}, field = models.ForeignKey('CustomField', related_name='choices', limit_choices_to={'type': CF_TYPE_SELECT},
on_delete=models.CASCADE) on_delete=models.CASCADE)
@ -193,7 +197,7 @@ class CustomFieldChoice(models.Model):
ordering = ['field', 'weight', 'value'] ordering = ['field', 'weight', 'value']
unique_together = ['field', 'value'] unique_together = ['field', 'value']
def __unicode__(self): def __str__(self):
return self.value return self.value
def clean(self): def clean(self):
@ -207,6 +211,7 @@ class CustomFieldChoice(models.Model):
CustomFieldValue.objects.filter(field__type=CF_TYPE_SELECT, serialized_value=str(pk)).delete() CustomFieldValue.objects.filter(field__type=CF_TYPE_SELECT, serialized_value=str(pk)).delete()
@python_2_unicode_compatible
class Graph(models.Model): class Graph(models.Model):
type = models.PositiveSmallIntegerField(choices=GRAPH_TYPE_CHOICES) type = models.PositiveSmallIntegerField(choices=GRAPH_TYPE_CHOICES)
weight = models.PositiveSmallIntegerField(default=1000) weight = models.PositiveSmallIntegerField(default=1000)
@ -217,7 +222,7 @@ class Graph(models.Model):
class Meta: class Meta:
ordering = ['type', 'weight', 'name'] ordering = ['type', 'weight', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def embed_url(self, obj): def embed_url(self, obj):
@ -231,6 +236,7 @@ class Graph(models.Model):
return template.render(Context({'obj': obj})) return template.render(Context({'obj': obj}))
@python_2_unicode_compatible
class ExportTemplate(models.Model): class ExportTemplate(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS}) content_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS})
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
@ -245,7 +251,7 @@ class ExportTemplate(models.Model):
['content_type', 'name'] ['content_type', 'name']
] ]
def __unicode__(self): def __str__(self):
return u'{}: {}'.format(self.content_type, self.name) return u'{}: {}'.format(self.content_type, self.name)
def to_response(self, context_dict, filename): def to_response(self, context_dict, filename):
@ -264,6 +270,7 @@ class ExportTemplate(models.Model):
return response return response
@python_2_unicode_compatible
class TopologyMap(models.Model): class TopologyMap(models.Model):
name = models.CharField(max_length=50, unique=True) name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True) slug = models.SlugField(unique=True)
@ -278,7 +285,7 @@ class TopologyMap(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@property @property
@ -328,6 +335,7 @@ class UserActionManager(models.Manager):
self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message) self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message)
@python_2_unicode_compatible
class UserAction(models.Model): class UserAction(models.Model):
""" """
A record of an action (add, edit, or delete) performed on an object by a User. 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: class Meta:
ordering = ['-time'] ordering = ['-time']
def __unicode__(self): def __str__(self):
if self.message: if self.message:
return u'{} {}'.format(self.user, self.message) return u'{} {}'.format(self.user, self.message)
return u'{} {} {}'.format(self.user, self.get_action_display(), self.content_type) return u'{} {} {}'.format(self.user, self.get_action_display(), self.content_type)

View File

@ -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. # This script will generate a random 50-character string suitable for use as a SECRET_KEY.
import os import os
import random import random
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(-_=+)' charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(-_=+)'
random.seed = (os.urandom(2048)) 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)))

View File

@ -7,6 +7,7 @@ from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from django.utils.encoding import python_2_unicode_compatible
from dcim.models import Interface from dcim.models import Interface
from extras.models import CustomFieldModel, CustomFieldValue from extras.models import CustomFieldModel, CustomFieldValue
@ -72,6 +73,7 @@ IP_PROTOCOL_CHOICES = (
) )
@python_2_unicode_compatible
class VRF(CreatedUpdatedModel, CustomFieldModel): class VRF(CreatedUpdatedModel, CustomFieldModel):
""" """
A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing 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 = 'VRF'
verbose_name_plural = 'VRFs' verbose_name_plural = 'VRFs'
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
@ -107,6 +109,7 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
]) ])
@python_2_unicode_compatible
class RIR(models.Model): class RIR(models.Model):
""" """
A Regional Internet Registry (RIR) is responsible for the allocation of a large portion of the global IP address 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 = 'RIR'
verbose_name_plural = 'RIRs' verbose_name_plural = 'RIRs'
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return "{}?rir={}".format(reverse('ipam:aggregate_list'), self.slug) return "{}?rir={}".format(reverse('ipam:aggregate_list'), self.slug)
@python_2_unicode_compatible
class Aggregate(CreatedUpdatedModel, CustomFieldModel): class Aggregate(CreatedUpdatedModel, CustomFieldModel):
""" """
An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize 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: class Meta:
ordering = ['family', 'prefix'] ordering = ['family', 'prefix']
def __unicode__(self): def __str__(self):
return str(self.prefix) return str(self.prefix)
def get_absolute_url(self): def get_absolute_url(self):
@ -206,6 +210,7 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
return int(children_size / self.prefix.size * 100) return int(children_size / self.prefix.size * 100)
@python_2_unicode_compatible
class Role(models.Model): class Role(models.Model):
""" """
A Role represents the functional role of a Prefix or VLAN; for example, "Customer," "Infrastructure," or 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: class Meta:
ordering = ['weight', 'name'] ordering = ['weight', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
@property @property
@ -265,6 +270,7 @@ class PrefixQuerySet(NullsFirstQuerySet):
return filter(lambda p: p.depth <= limit, queryset) return filter(lambda p: p.depth <= limit, queryset)
@python_2_unicode_compatible
class Prefix(CreatedUpdatedModel, CustomFieldModel): class Prefix(CreatedUpdatedModel, CustomFieldModel):
""" """
A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and 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'] ordering = ['vrf', 'family', 'prefix']
verbose_name_plural = 'prefixes' verbose_name_plural = 'prefixes'
def __unicode__(self): def __str__(self):
return str(self.prefix) return str(self.prefix)
def get_absolute_url(self): 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') return qs.annotate(host=RawSQL('INET(HOST(ipam_ipaddress.address))', [])).order_by('family', 'host')
@python_2_unicode_compatible
class IPAddress(CreatedUpdatedModel, CustomFieldModel): class IPAddress(CreatedUpdatedModel, CustomFieldModel):
""" """
An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is 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 = 'IP address'
verbose_name_plural = 'IP addresses' verbose_name_plural = 'IP addresses'
def __unicode__(self): def __str__(self):
return str(self.address) return str(self.address)
def get_absolute_url(self): def get_absolute_url(self):
@ -471,6 +478,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
return STATUS_CHOICE_CLASSES[self.status] return STATUS_CHOICE_CLASSES[self.status]
@python_2_unicode_compatible
class VLANGroup(models.Model): class VLANGroup(models.Model):
""" """
A VLAN group is an arbitrary collection of VLANs within which VLAN IDs and names must be unique. 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 = 'VLAN group'
verbose_name_plural = 'VLAN groups' verbose_name_plural = 'VLAN groups'
def __unicode__(self): def __str__(self):
return u'{} - {}'.format(self.site.name, self.name) return u'{} - {}'.format(self.site.name, self.name)
def get_absolute_url(self): def get_absolute_url(self):
return "{}?group_id={}".format(reverse('ipam:vlan_list'), self.pk) return "{}?group_id={}".format(reverse('ipam:vlan_list'), self.pk)
@python_2_unicode_compatible
class VLAN(CreatedUpdatedModel, CustomFieldModel): 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 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 = 'VLAN'
verbose_name_plural = 'VLANs' verbose_name_plural = 'VLANs'
def __unicode__(self): def __str__(self):
return self.display_name return self.display_name
def get_absolute_url(self): def get_absolute_url(self):
@ -560,6 +569,7 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
return STATUS_CHOICE_CLASSES[self.status] return STATUS_CHOICE_CLASSES[self.status]
@python_2_unicode_compatible
class Service(CreatedUpdatedModel): 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 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'] ordering = ['device', 'protocol', 'port']
unique_together = ['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()) return u'{} ({}/{})'.format(self.name, self.port, self.get_protocol_display())

View File

@ -6,7 +6,7 @@ from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
try: try:
import configuration from netbox import configuration
except ImportError: except ImportError:
raise ImproperlyConfigured("Configuration file is not present. Please define netbox/netbox/configuration.py per " raise ImproperlyConfigured("Configuration file is not present. Please define netbox/netbox/configuration.py per "
"the documentation.") "the documentation.")
@ -50,7 +50,7 @@ CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
# Attempt to import LDAP configuration if it has been defined # Attempt to import LDAP configuration if it has been defined
LDAP_IGNORE_CERT_ERRORS = False LDAP_IGNORE_CERT_ERRORS = False
try: try:
from ldap_config import * from netbox.ldap_config import *
LDAP_CONFIGURED = True LDAP_CONFIGURED = True
except ImportError: except ImportError:
LDAP_CONFIGURED = False LDAP_CONFIGURED = False

View File

@ -2,7 +2,7 @@ from django.conf import settings
from django.conf.urls import include, url from django.conf.urls import include, url
from django.contrib import admin 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 from users.views import login, logout

View File

@ -8,7 +8,7 @@ from django.contrib.auth.models import Group, User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models 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 dcim.models import Device
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
@ -51,6 +51,7 @@ class UserKeyQuerySet(models.QuerySet):
raise Exception("Bulk deletion has been disabled.") raise Exception("Bulk deletion has been disabled.")
@python_2_unicode_compatible
class UserKey(CreatedUpdatedModel): class UserKey(CreatedUpdatedModel):
""" """
A UserKey stores a user's personal RSA (public) encryption key, which is used to generate their unique encrypted 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_public_key = self.public_key
self.__initial_master_key_cipher = self.master_key_cipher self.__initial_master_key_cipher = self.master_key_cipher
def __unicode__(self): def __str__(self):
return self.user.username return self.user.username
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
@ -170,6 +171,7 @@ class UserKey(CreatedUpdatedModel):
self.save() self.save()
@python_2_unicode_compatible
class SecretRole(models.Model): class SecretRole(models.Model):
""" """
A SecretRole represents an arbitrary functional classification of Secrets. For example, a user might define roles 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: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): 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() return user in self.users.all() or user.groups.filter(pk__in=self.groups.all()).exists()
@python_2_unicode_compatible
class Secret(CreatedUpdatedModel): class Secret(CreatedUpdatedModel):
""" """
A Secret stores an AES256-encrypted copy of sensitive data, such as passwords or secret keys. An irreversible 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) self.plaintext = kwargs.pop('plaintext', None)
super(Secret, self).__init__(*args, **kwargs) super(Secret, self).__init__(*args, **kwargs)
def __unicode__(self): def __str__(self):
if self.role and self.device: if self.role and self.device:
return u'{} for {}'.format(self.role, self.device) return u'{} for {}'.format(self.role, self.device)
return u'Secret' return u'Secret'

View File

@ -1 +0,0 @@
from test_models import *

View File

@ -1,12 +1,14 @@
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from extras.models import CustomFieldModel, CustomFieldValue from extras.models import CustomFieldModel, CustomFieldValue
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
from utilities.utils import csv_format from utilities.utils import csv_format
@python_2_unicode_compatible
class TenantGroup(models.Model): class TenantGroup(models.Model):
""" """
An arbitrary collection of Tenants. An arbitrary collection of Tenants.
@ -17,13 +19,14 @@ class TenantGroup(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return "{}?group={}".format(reverse('tenancy:tenant_list'), self.slug) return "{}?group={}".format(reverse('tenancy:tenant_list'), self.slug)
@python_2_unicode_compatible
class Tenant(CreatedUpdatedModel, CustomFieldModel): class Tenant(CreatedUpdatedModel, CustomFieldModel):
""" """
A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal 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: class Meta:
ordering = ['group', 'name'] ordering = ['group', 'name']
def __unicode__(self): def __str__(self):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):

View File

@ -10,7 +10,7 @@ from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
) )
from models import Tenant, TenantGroup from .models import Tenant, TenantGroup
from . import filters, forms, tables from . import filters, forms, tables