diff --git a/.travis.yml b/.travis.yml
index 94bfa0dab..33abc8425 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,7 @@ python:
- "3.5"
install:
- pip install -r requirements.txt
- - pip install pep8
+ - pip install pycodestyle
before_script:
- psql --version
- psql -U postgres -c 'SELECT version();'
diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py
index 9b75bc184..479c21add 100644
--- a/netbox/circuits/api/views.py
+++ b/netbox/circuits/api/views.py
@@ -19,6 +19,7 @@ from . import serializers
class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
fields = (
+ (Circuit, ['status']),
(CircuitTermination, ['term_side']),
)
diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py
index 13f68639f..ce8b4c349 100644
--- a/netbox/dcim/api/views.py
+++ b/netbox/dcim/api/views.py
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from collections import OrderedDict
from django.conf import settings
-from django.db import transaction
from django.http import HttpResponseBadRequest, HttpResponseForbidden
from django.shortcuts import get_object_or_404
from drf_yasg import openapi
@@ -37,11 +36,12 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
fields = (
(Device, ['face', 'status']),
(ConsolePort, ['connection_status']),
- (Interface, ['form_factor']),
+ (Interface, ['form_factor', 'mode']),
(InterfaceConnection, ['connection_status']),
(InterfaceTemplate, ['form_factor']),
(PowerPort, ['connection_status']),
(Rack, ['type', 'width']),
+ (Site, ['status']),
)
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
index da8dc0457..593bef113 100644
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -34,7 +34,7 @@ from .models import (
RackRole, Region, Site, VirtualChassis
)
-DEVICE_BY_PK_RE = '{\d+\}'
+DEVICE_BY_PK_RE = r'{\d+\}'
INTERFACE_MODE_HELP_TEXT = """
Access: One untagged VLAN
diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py
index 39bd4ad3d..14c9ef393 100644
--- a/netbox/dcim/models.py
+++ b/netbox/dcim/models.py
@@ -963,6 +963,12 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
'face': "Must specify rack face when defining rack position.",
})
+ # Prevent 0U devices from being assigned to a specific position
+ if self.position and self.device_type.u_height == 0:
+ raise ValidationError({
+ 'position': "A U0 device type ({}) cannot be assigned to a rack position.".format(self.device_type)
+ })
+
if self.rack:
try:
@@ -1205,8 +1211,8 @@ class ConsoleServerPortManager(models.Manager):
def get_queryset(self):
# Pad any trailing digits to effect natural sorting
return super(ConsoleServerPortManager, self).get_queryset().extra(select={
- 'name_padded': "CONCAT(REGEXP_REPLACE(dcim_consoleserverport.name, '\d+$', ''), "
- "LPAD(SUBSTRING(dcim_consoleserverport.name FROM '\d+$'), 8, '0'))",
+ 'name_padded': r"CONCAT(REGEXP_REPLACE(dcim_consoleserverport.name, '\d+$', ''), "
+ r"LPAD(SUBSTRING(dcim_consoleserverport.name FROM '\d+$'), 8, '0'))",
}).order_by('device', 'name_padded')
@@ -1287,8 +1293,8 @@ class PowerOutletManager(models.Manager):
def get_queryset(self):
# Pad any trailing digits to effect natural sorting
return super(PowerOutletManager, self).get_queryset().extra(select={
- 'name_padded': "CONCAT(REGEXP_REPLACE(dcim_poweroutlet.name, '\d+$', ''), "
- "LPAD(SUBSTRING(dcim_poweroutlet.name FROM '\d+$'), 8, '0'))",
+ 'name_padded': r"CONCAT(REGEXP_REPLACE(dcim_poweroutlet.name, '\d+$', ''), "
+ r"LPAD(SUBSTRING(dcim_poweroutlet.name FROM '\d+$'), 8, '0'))",
}).order_by('device', 'name_padded')
diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py
index e71395ebc..427687ef9 100644
--- a/netbox/dcim/tables.py
+++ b/netbox/dcim/tables.py
@@ -7,9 +7,9 @@ from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, ToggleColumn
from .models import (
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
- DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceTemplate, InventoryItem, Manufacturer, Platform,
- PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, Region, Site,
- VirtualChassis,
+ DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate, InventoryItem,
+ Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
+ RackReservation, Region, Site, VirtualChassis,
)
REGION_LINK = """
@@ -594,7 +594,7 @@ class InterfaceConnectionTable(BaseTable):
interface_b = tables.Column(verbose_name='Interface B')
class Meta(BaseTable.Meta):
- model = Interface
+ model = InterfaceConnection
fields = ('device_a', 'interface_a', 'device_b', 'interface_b')
diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py
index 252c2d12c..f39629fa0 100644
--- a/netbox/extras/api/views.py
+++ b/netbox/extras/api/views.py
@@ -99,7 +99,7 @@ class TopologyMapViewSet(ModelViewSet):
try:
data = tmap.render(img_format=img_format)
- except:
+ except Exception:
return HttpResponse(
"There was an error generating the requested graph. Ensure that the GraphViz executables have been "
"installed correctly."
diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py
index a923ae596..55f8435d6 100644
--- a/netbox/extras/forms.py
+++ b/netbox/extras/forms.py
@@ -4,6 +4,7 @@ from collections import OrderedDict
from django import forms
from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ObjectDoesNotExist
from utilities.forms import BootstrapMixin, BulkEditForm, LaxURLField
from .constants import CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL
@@ -53,7 +54,14 @@ def get_custom_fields_for_model(content_type, filterable_only=False, bulk_edit=F
choices = [(cfc.pk, cfc) for cfc in cf.choices.all()]
if not cf.required or bulk_edit or filterable_only:
choices = [(None, '---------')] + choices
- field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required)
+ # Check for a default choice
+ default_choice = None
+ if initial:
+ try:
+ default_choice = cf.choices.get(value=initial).pk
+ except ObjectDoesNotExist:
+ pass
+ field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required, initial=default_choice)
# URL
elif cf.type == CF_TYPE_URL:
diff --git a/netbox/extras/migrations/0007_unicode_literals.py b/netbox/extras/migrations/0007_unicode_literals.py
index c9a624510..cda07583f 100644
--- a/netbox/extras/migrations/0007_unicode_literals.py
+++ b/netbox/extras/migrations/0007_unicode_literals.py
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='customfield',
name='default',
- field=models.CharField(blank=True, help_text='Default value for the field. Use "true" or "false" for booleans. N/A for selection fields.', max_length=100),
+ field=models.CharField(blank=True, help_text='Default value for the field. Use "true" or "false" for booleans.', max_length=100),
),
migrations.AlterField(
model_name='customfield',
diff --git a/netbox/extras/migrations/0008_reports.py b/netbox/extras/migrations/0008_reports.py
index 7f3eccc32..fbfde2cba 100644
--- a/netbox/extras/migrations/0008_reports.py
+++ b/netbox/extras/migrations/0008_reports.py
@@ -19,7 +19,7 @@ def verify_postgresql_version(apps, schema_editor):
with connection.cursor() as cursor:
cursor.execute("SELECT VERSION()")
row = cursor.fetchone()
- pg_version = re.match('^PostgreSQL (\d+\.\d+(\.\d+)?)', row[0]).group(1)
+ pg_version = re.match(r'^PostgreSQL (\d+\.\d+(\.\d+)?)', row[0]).group(1)
if StrictVersion(pg_version) < StrictVersion('9.4.0'):
raise Exception("PostgreSQL 9.4.0 or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(pg_version))
diff --git a/netbox/extras/models.py b/netbox/extras/models.py
index 75945adcd..cb68f0e0d 100644
--- a/netbox/extras/models.py
+++ b/netbox/extras/models.py
@@ -91,7 +91,7 @@ class CustomField(models.Model):
default = models.CharField(
max_length=100,
blank=True,
- help_text='Default value for the field. Use "true" or "false" for booleans. N/A for selection fields.'
+ help_text='Default value for the field. Use "true" or "false" for booleans.'
)
weight = models.PositiveSmallIntegerField(
default=100,
diff --git a/netbox/extras/rpc.py b/netbox/extras/rpc.py
index 2a547ca45..552f592c7 100644
--- a/netbox/extras/rpc.py
+++ b/netbox/extras/rpc.py
@@ -163,8 +163,8 @@ class IOSSSH(SSHClient):
sh_ver = self._send('show version').split('\r\n')
return {
- 'serial': parse(sh_ver, 'Processor board ID ([^\s]+)'),
- 'description': parse(sh_ver, 'cisco ([^\s]+)')
+ 'serial': parse(sh_ver, r'Processor board ID ([^\s]+)'),
+ 'description': parse(sh_ver, r'cisco ([^\s]+)')
}
def items(chassis_serial=None):
@@ -172,9 +172,9 @@ class IOSSSH(SSHClient):
for i in cmd:
i_fmt = i.replace('\r\n', ' ')
try:
- m_name = re.search('NAME: "([^"]+)"', i_fmt).group(1)
- m_pid = re.search('PID: ([^\s]+)', i_fmt).group(1)
- m_serial = re.search('SN: ([^\s]+)', i_fmt).group(1)
+ m_name = re.search(r'NAME: "([^"]+)"', i_fmt).group(1)
+ m_pid = re.search(r'PID: ([^\s]+)', i_fmt).group(1)
+ m_serial = re.search(r'SN: ([^\s]+)', i_fmt).group(1)
# Omit built-in items and those with no PID
if m_serial != chassis_serial and m_pid.lower() != 'unspecified':
yield {
@@ -208,7 +208,7 @@ class OpengearSSH(SSHClient):
try:
stdin, stdout, stderr = self.ssh.exec_command("showserial")
serial = stdout.readlines()[0].strip()
- except:
+ except Exception:
raise RuntimeError("Failed to glean chassis serial from device.")
# Older models don't provide serial info
if serial == "No serial number information available":
@@ -217,7 +217,7 @@ class OpengearSSH(SSHClient):
try:
stdin, stdout, stderr = self.ssh.exec_command("config -g config.system.model")
description = stdout.readlines()[0].split(' ', 1)[1].strip()
- except:
+ except Exception:
raise RuntimeError("Failed to glean chassis description from device.")
return {
diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py
index f6a55b618..31e899afd 100644
--- a/netbox/ipam/api/views.py
+++ b/netbox/ipam/api/views.py
@@ -4,7 +4,7 @@ from django.conf import settings
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.decorators import detail_route
-from rest_framework.exceptions import PermissionDenied
+from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.response import Response
from extras.api.views import CustomFieldModelViewSet
@@ -98,7 +98,31 @@ class PrefixViewSet(CustomFieldModelViewSet):
requested_prefixes = request.data if isinstance(request.data, list) else [request.data]
# Allocate prefixes to the requested objects based on availability within the parent
- for requested_prefix in requested_prefixes:
+ for i, requested_prefix in enumerate(requested_prefixes):
+
+ # Validate requested prefix size
+ error_msg = None
+ if 'prefix_length' not in requested_prefix:
+ error_msg = "Item {}: prefix_length field missing".format(i)
+ elif not isinstance(requested_prefix['prefix_length'], int):
+ error_msg = "Item {}: Invalid prefix length ({})".format(
+ i, requested_prefix['prefix_length']
+ )
+ elif prefix.family == 4 and requested_prefix['prefix_length'] > 32:
+ error_msg = "Item {}: Invalid prefix length ({}) for IPv4".format(
+ i, requested_prefix['prefix_length']
+ )
+ elif prefix.family == 6 and requested_prefix['prefix_length'] > 128:
+ error_msg = "Item {}: Invalid prefix length ({}) for IPv6".format(
+ i, requested_prefix['prefix_length']
+ )
+ if error_msg:
+ return Response(
+ {
+ "detail": error_msg
+ },
+ status=status.HTTP_400_BAD_REQUEST
+ )
# Find the first available prefix equal to or larger than the requested size
for available_prefix in available_prefixes.iter_cidrs():
@@ -160,8 +184,8 @@ class PrefixViewSet(CustomFieldModelViewSet):
requested_ips = request.data if isinstance(request.data, list) else [request.data]
# Determine if the requested number of IPs is available
- available_ips = list(prefix.get_available_ips())
- if len(available_ips) < len(requested_ips):
+ available_ips = prefix.get_available_ips()
+ if available_ips.size < len(requested_ips):
return Response(
{
"detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
@@ -171,8 +195,9 @@ class PrefixViewSet(CustomFieldModelViewSet):
)
# Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix
+ available_ips = iter(available_ips)
for requested_ip in requested_ips:
- requested_ip['address'] = available_ips.pop(0)
+ requested_ip['address'] = next(available_ips)
requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None
# Initialize the serializer with a list or a single object depending on what was requested
diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py
index 005d44a84..f21cc299d 100644
--- a/netbox/ipam/filters.py
+++ b/netbox/ipam/filters.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import django_filters
+from django.core.exceptions import ValidationError
from django.db.models import Q
import netaddr
from netaddr.core import AddrFormatError
@@ -233,6 +234,10 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
method='search_by_parent',
label='Parent prefix',
)
+ address = django_filters.CharFilter(
+ method='filter_address',
+ label='Address',
+ )
mask_length = django_filters.NumberFilter(
method='filter_mask_length',
label='Mask length',
@@ -313,6 +318,17 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
except (AddrFormatError, ValueError):
return queryset.none()
+ def filter_address(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ try:
+ # Match address and subnet mask
+ if '/' in value:
+ return queryset.filter(address=value)
+ return queryset.filter(address__net_host=value)
+ except ValidationError:
+ return queryset.none()
+
def filter_mask_length(self, queryset, name, value):
if not value:
return queryset
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
index 4d507bb46..5e4e8c76f 100644
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -22,7 +22,7 @@ if sys.version_info[0] < 3:
DeprecationWarning
)
-VERSION = '2.3.4'
+VERSION = '2.3.5'
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -268,7 +268,15 @@ SWAGGER_SETTINGS = {
'utilities.custom_inspectors.NullablePaginatorInspector',
'drf_yasg.inspectors.DjangoRestResponsePagination',
'drf_yasg.inspectors.CoreAPICompatInspector',
- ]
+ ],
+ 'SECURITY_DEFINITIONS': {
+ 'Bearer': {
+ 'type': 'apiKey',
+ 'name': 'Authorization',
+ 'in': 'header',
+ }
+ },
+ 'VALIDATOR_URL': None,
}
@@ -281,5 +289,5 @@ INTERNAL_IPS = (
try:
HOSTNAME = socket.gethostname()
-except:
+except Exception:
HOSTNAME = 'localhost'
diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py
index 5f7b26a71..4907555ed 100644
--- a/netbox/netbox/urls.py
+++ b/netbox/netbox/urls.py
@@ -52,9 +52,9 @@ _patterns = [
url(r'^api/secrets/', include('secrets.api.urls')),
url(r'^api/tenancy/', include('tenancy.api.urls')),
url(r'^api/virtualization/', include('virtualization.api.urls')),
- url(r'^api/docs/$', schema_view.with_ui('swagger', cache_timeout=None), name='api_docs'),
- url(r'^api/redoc/$', schema_view.with_ui('redoc', cache_timeout=None), name='api_redocs'),
- url(r'^api/swagger(?P.json|.yaml)$', schema_view.without_ui(cache_timeout=None), name='schema_swagger'),
+ url(r'^api/docs/$', schema_view.with_ui('swagger'), name='api_docs'),
+ url(r'^api/redoc/$', schema_view.with_ui('redoc'), name='api_redocs'),
+ url(r'^api/swagger(?P.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'),
# Serving static media in Django to pipe it through LoginRequiredMiddleware
url(r'^media/(?P.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py
index 8f8107805..8e7fa3b16 100644
--- a/netbox/secrets/forms.py
+++ b/netbox/secrets/forms.py
@@ -26,7 +26,7 @@ def validate_rsa_key(key, is_secret=True):
raise forms.ValidationError("This looks like a private key. Please provide your public RSA key.")
try:
PKCS1_OAEP.new(key)
- except:
+ except Exception:
raise forms.ValidationError("Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.")
diff --git a/netbox/secrets/models.py b/netbox/secrets/models.py
index e1f367d03..4bd644564 100644
--- a/netbox/secrets/models.py
+++ b/netbox/secrets/models.py
@@ -87,7 +87,7 @@ class UserKey(CreatedUpdatedModel):
raise ValidationError({
'public_key': "Invalid RSA key format."
})
- except:
+ except Exception:
raise ValidationError("Something went wrong while trying to save your key. Please ensure that you're "
"uploading a valid RSA public key in PEM format (no SSH/PGP).")
diff --git a/netbox/templates/dcim/inc/rack_elevation.html b/netbox/templates/dcim/inc/rack_elevation.html
index e6b721db4..46fe01c8d 100644
--- a/netbox/templates/dcim/inc/rack_elevation.html
+++ b/netbox/templates/dcim/inc/rack_elevation.html
@@ -26,7 +26,7 @@
{% ifequal u.device.face face_id %}
+ data-content="{{ u.device.device_role }}
{{ u.device.device_type.full_name }} ({{ u.device.device_type.u_height }}U){% if u.device.asset_tag %}
{{ u.device.asset_tag }}{% endif %}{% if u.device.serial %}
{{ u.device.serial }}{% endif %}">
{{ u.device.name|default:u.device.device_role }}
{% if u.device.devicebay_count %}
({{ u.device.get_children.count }}/{{ u.device.devicebay_count }})
diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py
index 69b102f5c..1cea0b0da 100644
--- a/netbox/utilities/forms.py
+++ b/netbox/utilities/forms.py
@@ -38,10 +38,10 @@ COLOR_CHOICES = (
('607d8b', 'Dark grey'),
('111111', 'Black'),
)
-NUMERIC_EXPANSION_PATTERN = '\[((?:\d+[?:,-])+\d+)\]'
-ALPHANUMERIC_EXPANSION_PATTERN = '\[((?:[a-zA-Z0-9]+[?:,-])+[a-zA-Z0-9]+)\]'
-IP4_EXPANSION_PATTERN = '\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]'
-IP6_EXPANSION_PATTERN = '\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]'
+NUMERIC_EXPANSION_PATTERN = r'\[((?:\d+[?:,-])+\d+)\]'
+ALPHANUMERIC_EXPANSION_PATTERN = r'\[((?:[a-zA-Z0-9]+[?:,-])+[a-zA-Z0-9]+)\]'
+IP4_EXPANSION_PATTERN = r'\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]'
+IP6_EXPANSION_PATTERN = r'\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]'
def parse_numeric_range(string, base=10):
@@ -407,7 +407,7 @@ class FlexibleModelChoiceField(forms.ModelChoiceField):
try:
if not self.to_field_name:
key = 'pk'
- elif re.match('^\{\d+\}$', value):
+ elif re.match(r'^\{\d+\}$', value):
key = 'pk'
value = value.strip('{}')
else:
diff --git a/netbox/utilities/managers.py b/netbox/utilities/managers.py
index dc882d462..33b4356d4 100644
--- a/netbox/utilities/managers.py
+++ b/netbox/utilities/managers.py
@@ -23,9 +23,9 @@ class NaturalOrderByManager(Manager):
id3 = '_{}_{}3'.format(db_table, primary_field)
queryset = super(NaturalOrderByManager, self).get_queryset().extra(select={
- id1: "CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)".format(db_table, primary_field),
- id2: "SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, primary_field),
- id3: "CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)".format(db_table, primary_field),
+ id1: r"CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)".format(db_table, primary_field),
+ id2: r"SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, primary_field),
+ id3: r"CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)".format(db_table, primary_field),
})
ordering = fields[0:-1] + (id1, id2, id3)
diff --git a/netbox/utilities/validators.py b/netbox/utilities/validators.py
index 8eb990486..dcdb9bc6d 100644
--- a/netbox/utilities/validators.py
+++ b/netbox/utilities/validators.py
@@ -15,7 +15,7 @@ class EnhancedURLValidator(URLValidator):
A fake URL list which "contains" all scheme names abiding by the syntax defined in RFC 3986 section 3.1
"""
def __contains__(self, item):
- if not item or not re.match('^[a-z][0-9a-z+\-.]*$', item.lower()):
+ if not item or not re.match(r'^[a-z][0-9a-z+\-.]*$', item.lower()):
return False
return True
diff --git a/scripts/cibuild.sh b/scripts/cibuild.sh
index 4f4fe1ca3..c0a49b5ef 100755
--- a/scripts/cibuild.sh
+++ b/scripts/cibuild.sh
@@ -23,8 +23,11 @@ fi
# Check all python source files for PEP 8 compliance, but explicitly
# ignore:
+# - W504: line break after binary operator
# - E501: line greater than 80 characters in length
-pep8 --ignore=E501 netbox/
+pycodestyle \
+ --ignore=W504,E501 \
+ netbox/
RC=$?
if [[ $RC != 0 ]]; then
echo -e "\n$(info) one or more PEP 8 errors detected, failing build."