Compare commits

..

44 Commits

Author SHA1 Message Date
Jeremy Stretch
2ddb4b90c5 Merge pull request #85 from digitalocean/develop
Release v1.0.4
2016-06-28 12:17:12 -04:00
Jeremy Stretch
cce6c89810 Corrected static path in Apache config 2016-06-28 11:57:44 -04:00
Jeremy Stretch
b37503ed8f Corrected typos in the Apache config; cleaned up grammar 2016-06-28 11:50:25 -04:00
Jeremy Stretch
374702927b Fixes #80: Correct rack face (lowercase) to be consistent with export behavior (uppercase) 2016-06-28 11:38:09 -04:00
Jeremy Stretch
0eb8227044 Merge branch 'develop' of https://github.com/digitalocean/netbox into develop 2016-06-28 11:15:56 -04:00
Jeremy Stretch
98febf3979 Fixes #72: Check for re-used interfaces when importing interface connections 2016-06-28 11:11:53 -04:00
Jeremy Stretch
6a4a636794 Merge pull request #58 from digitalocean/travis-ci-pep-8
Add CI check for PEP 8 compliance
2016-06-28 10:56:42 -04:00
Matt Layher
f1857dd189 Add CI check for PEP 8 compliance 2016-06-28 10:43:38 -04:00
Jeremy Stretch
d22e4e7698 Merge pull request #59 from digitalocean/dcim-tests-pep-8
Fix PEP 8 error in DCIM tests
2016-06-28 10:42:38 -04:00
Jeremy Stretch
6848a3dc81 Fixes #67: Improved Aggregate validation; extended aggregate documentation 2016-06-28 10:04:03 -04:00
Jeremy Stretch
4dac43c1c9 Fixes #48: Set .container to auto with a max width 2016-06-28 09:50:00 -04:00
Jeremy Stretch
b392aa4a4a Fixes #45: Strip plus signs during slugification 2016-06-28 09:39:55 -04:00
Matt Layher
5181c97281 Fix PEP 8 error in DCIM tests 2016-06-28 00:25:12 -04:00
Jeremy Stretch
66a16dd06b Merge pull request #41 from digitalocean/travis-ci-tests
Run tests in CI
2016-06-28 00:10:19 -04:00
Matt Layher
c5d498ac14 Run tests in CI 2016-06-28 00:05:18 -04:00
Jeremy Stretch
2080abc6c3 Corrected SiteTest to account for earlier Graph model change 2016-06-27 23:56:39 -04:00
Jeremy Stretch
b379918295 Merge pull request #57 from digitalocean/develop
Release 1.0.3-r1
2016-06-27 23:24:01 -04:00
Jeremy Stretch
4e5f537cc5 When editing an object, cancel_url should point to its normal view; when adding, it should point to the object list 2016-06-27 23:18:26 -04:00
Jeremy Stretch
7918f85cdd Corrected rack height validation to exclude 0U devices 2016-06-27 23:08:30 -04:00
Jeremy Stretch
df1147d941 Merge pull request #56 from digitalocean/develop
Release 1.0.3
2016-06-27 22:55:09 -04:00
Jeremy Stretch
65bc91e9de Merge branch 'develop' of https://github.com/digitalocean/netbox into develop 2016-06-27 22:48:55 -04:00
Jeremy Stretch
9aa0972a8c Corrected regex to ignore shell files in root dir 2016-06-27 22:48:24 -04:00
Jeremy Stretch
4cd6f99cbd Merge pull request #52 from alexconrey/develop
added apache config information to getting-started.md
2016-06-27 22:39:58 -04:00
Jeremy Stretch
df01947c9e Corrected claim about RIRs being pre-populated 2016-06-27 22:35:07 -04:00
Jeremy Stretch
4dd31497e5 Fixes #26: Corrected rack validation to work when there are no devices within the rack 2016-06-27 22:27:40 -04:00
Jeremy Stretch
f958bc0580 Merge pull request #47 from digitalocean/readme-travis-badges
Add Travis build badges for both master and develop against python 2.7
2016-06-27 22:16:49 -04:00
Jeremy Stretch
0a22821209 Merge pull request #46 from digitalocean/contributing-prs
Add Submitting Pull Requests section to CONTRIBUTING
2016-06-27 22:16:13 -04:00
Alex Conrey
a4cbfd7d5b added apache config information to getting-started.md 2016-06-27 19:51:46 -05:00
Matt Layher
f0fb60734a Add Travis build badges for both master and develop against python 2.7 2016-06-27 19:55:17 -04:00
Matt Layher
e334c64a7c Add Submitting Pull Requests section to CONTRIBUTING 2016-06-27 19:49:57 -04:00
Jeremy Stretch
6e068770ea Merge pull request #38 from digitalocean/travis-ci
Add Travis CI build
2016-06-27 16:54:30 -04:00
Matt Layher
d5d4eb9fd5 Add Travis CI build 2016-06-27 16:48:54 -04:00
Jeremy Stretch
ab880e1053 Fixed IPAddress 'parent prefixes' display; added warning for duplicate IPs 2016-06-27 15:51:47 -04:00
Jeremy Stretch
1ea8f04c23 Added a note about the IRC chanel to the README 2016-06-27 15:23:06 -04:00
Jeremy Stretch
0b37d4f5e6 Merge pull request #31 from digitalocean/develop
Release 1.0.2
2016-06-27 14:53:27 -04:00
Jeremy Stretch
c6e66a073d Fixes #29: Corrected typo 2016-06-27 14:39:08 -04:00
Jeremy Stretch
eade3cbd6b Fixes #25: Recurse expand_pattern only if there are more ranges to unpack 2016-06-27 14:12:30 -04:00
Jeremy Stretch
7cf437e11b Fixes #26: Added rack height validation 2016-06-27 13:53:39 -04:00
Jeremy Stretch
19a302774a Changed gunicorn path to since that appears to be standard for Ubuntu now 2016-06-27 13:21:38 -04:00
Jeremy Stretch
a35d927235 Merge pull request #23 from digitalocean/develop
1.0.1
2016-06-27 12:35:48 -04:00
Jeremy Stretch
1cd20861f2 Added RackGroup slug filter 2016-06-27 12:30:25 -04:00
Jeremy Stretch
e78263637a Fixes #22: Corrected handling of RackGroup names on import of new Racks 2016-06-27 12:23:31 -04:00
Jeremy Stretch
215c31e7a0 Fixes #21: Added screenshots to README 2016-06-27 11:43:33 -04:00
Jeremy Stretch
5935a8843e Improved maintenance mode message 2016-06-27 11:22:36 -04:00
25 changed files with 214 additions and 55 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
*.pyc
configuration.py
.idea
*.sh
/*.sh
fabfile.py

8
.travis.yml Normal file
View File

@@ -0,0 +1,8 @@
language: python
python:
- "2.7"
install:
- pip install -r requirements.txt
- pip install pep8
script:
- ./scripts/cibuild.sh

View File

@@ -48,3 +48,9 @@ Even if it's not quite right for NetBox, we may be able to point you to a tool b
* A use case for the feature; who would use it and what value it would add to NetBox
* A rough description of any changes necessary to the database schema (if applicable)
* Any third-party libraries or other resources which would be involved
# Submitting Pull Requests
When submitting a pull request, please be sure to work off of branch `develop`, rather than branch `master`.
In NetBox, the `develop` branch is used for ongoing development, while `master` is used for tagging new
stable releases.

View File

@@ -1,7 +1,26 @@
# NetBox
NetBox is an IP address management (IPAM) and data center infrastructure management (DCIM) tool. Initially conceived by the network engineering team at [DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically to address the needs of network and infrastructure engineers.
NetBox runs as a web application atop the [Django](https://www.djangoproject.com/) Python framework with a [PostgreSQL](http://www.postgresql.org/) database. For a complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/digitalocean/netbox).
Questions? Comments? Please join us on IRC in **#netbox** on **irc.freenode.net**!
### Build Status
| | python 2.7 |
|-------------|------------|
| **master** | [![Build Status](https://travis-ci.org/digitalocean/netbox.svg?branch=master)](https://travis-ci.org/digitalocean/netbox) |
| **develop** | [![Build Status](https://travis-ci.org/digitalocean/netbox.svg?branch=develop)](https://travis-ci.org/digitalocean/netbox) |
## Screenshots
![Screenshot of main page](docs/screenshot1.png "Main page")
![Screenshot of rack elevation](docs/screenshot2.png "Rack elevation")
![Screenshot of prefix hierarchy](docs/screenshot3.png "Prefix hierarchy")
# Installation
Please see docs/getting-started.md for instructions on installing NetBox.

View File

@@ -206,20 +206,26 @@ Now if we navigate to the name or IP of the server (as defined in `ALLOWED_HOSTS
If the test service does not run, or you cannot reach the NetBox home page, something has gone wrong. Do not proceed with the rest of this guide until the installation has been corrected.
# nginx and gunicorn
# Web Server and gunicorn
## Installation
We'll set up a simple HTTP front end using [nginx](https://www.nginx.com/resources/wiki/) and [gunicorn](http://gunicorn.org/) for the purposes of this guide. (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll also use [supervisord](http://supervisord.org/) for service persistence.
We'll set up a simple HTTP front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we provide example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll also use [supervisord](http://supervisord.org/) for service persistence.
```
# apt-get install nginx gunicorn supervisor
# apt-get install gunicorn supervisor
```
## nginx Configuration
The following will serve as a minimal nginx configuration. Be sure to modify your server name and installation path appropriately.
```
# apt-get install nginx
```
Once nginx is installed, proceed with the following configuration:
```
server {
listen 80;
@@ -256,13 +262,48 @@ Restart the nginx service to use the new configuration.
# service nginx restart
* Restarting nginx nginx
```
## Apache Configuration
If you're feeling adventurous, or you already have Apache installed and can't run a dual-stack on your server, the following configuration should work for Apache:
```
<VirtualHost *:80>
ProxyPreserveHost On
ServerName netbox.example.com
Alias /static/ /opt/netbox/netbox/static
<Directory /opt/netbox/netbox/static>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
# Uncomment the line below if running Apache 2.4
#Require all granted
</Directory>
<Location /static>
ProxyPass !
</Location>
ProxyPass / http://127.0.0.1:8001
ProxyPassReverse / http://127.0.0.1:8001
</VirtualHost>
```
Save the contents of the above example in `/etc/apache2/sites-available/netbox.conf` and reload Apache:
```
# a2ensite netbox; service apache2 restart
```
## gunicorn Configuration
Save the following configuration file in the root netbox installation path (in this example, `/opt/netbox/`.) as `gunicorn_config.py`. Be sure to update the `pythonpath` variable if needed.
Save the following configuration file in the root netbox installation path (in this example, `/opt/netbox/`.) as `gunicorn_config.py`. Be sure to verify the location of the gunicorn executable (e.g. `which gunicorn`) and to update the `pythonpath` variable if needed.
```
command = '/usr/local/bin/gunicorn'
command = '/usr/bin/gunicorn'
pythonpath = '/opt/netbox/netbox'
bind = '127.0.0.1:8001'
workers = 3

View File

@@ -32,11 +32,13 @@ Additionally, you might define an aggregate for each large swath of public IPv4
Any prefixes you create in NetBox (discussed below) will be automatically organized under their respective aggregates. Any space within an aggregate which is not covered by an existing prefix will be annotated as available for allocation.
Aggregates cannot overlap with one another; they can only exist in parallel. For instance, you cannot define both 10.0.0.0/8 and 10.16.0.0/16 as aggregates, because they overlap. 10.16.0.0/16 in this example would be created as a prefix.
### RIRs
Regional Internet Registries (RIRs) are responsible for the allocation of global address space. The five RIRs are ARIN, RIPE, APNIC, LACNIC, and AFRINIC. However, some address space has been set aside for private or internal use only, such as defined in RFCs 1918 and 6598. NetBox considers these RFCs as a sort of RIR as well; that is, an authority which "owns" certain address space.
Each aggregate must be assigned to one RIR. NetBox by default will be populated with the RIRs listed above, however you are free to remove these and/or create your own if you choose.
Each aggregate must be assigned to one RIR. You are free to define whichever RIRs you choose (or create your own).
---

BIN
docs/screenshot1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
docs/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
docs/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -47,6 +47,12 @@ class RackFilter(django_filters.FilterSet):
queryset=RackGroup.objects.all(),
label='Group (ID)',
)
group = django_filters.ModelMultipleChoiceFilter(
name='group',
queryset=RackGroup.objects.all(),
to_field_name='slug',
label='Group',
)
class Meta:
model = Rack

View File

@@ -138,24 +138,23 @@ class RackForm(forms.ModelForm, BootstrapMixin):
class RackFromCSVForm(forms.ModelForm):
site = forms.ModelChoiceField(queryset=Site.objects.all(), to_field_name='name',
error_messages={'invalid_choice': 'Site not found.'})
group = forms.ModelChoiceField(queryset=RackGroup.objects.all(), required=False, to_field_name='name',
error_messages={'invalid_choice': 'Group not found.'})
group_name = forms.CharField(required=False)
class Meta:
model = Rack
fields = ['site', 'group', 'name', 'facility_id', 'u_height']
fields = ['site', 'group_name', 'name', 'facility_id', 'u_height']
def clean(self):
site = self.cleaned_data.get('site')
group = self.cleaned_data.get('group')
group = self.cleaned_data.get('group_name')
# Validate device type
# Validate rack group
if site and group:
try:
self.instance.group = RackGroup.objects.get(site=site, name=group)
except RackGroup.DoesNotExist:
self.add_error('group', "Invalid rack group ({})".format(group))
self.add_error('group_name', "Invalid rack group ({})".format(group))
class RackImportForm(BulkImportForm, BootstrapMixin):
@@ -425,7 +424,7 @@ class DeviceFromCSVForm(forms.ModelForm):
'invalid_choice': 'Invalid site name.',
})
rack_name = forms.CharField()
face = forms.ChoiceField(choices=[('front', 'Front'), ('rear', 'Rear')])
face = forms.ChoiceField(choices=[('Front', 'Front'), ('Rear', 'Rear')])
class Meta:
model = Device
@@ -1037,20 +1036,29 @@ class InterfaceConnectionImportForm(BulkImportForm, BootstrapMixin):
return
connection_list = []
occupied_interfaces = []
for i, record in enumerate(records, start=1):
form = self.fields['csv'].csv_form(data=record)
if form.is_valid():
interface_a = Interface.objects.get(device=form.cleaned_data['device_a'],
name=form.cleaned_data['interface_a'])
if interface_a in occupied_interfaces:
raise forms.ValidationError("{} {} found in multiple connections"
.format(interface_a.device.name, interface_a.name))
interface_b = Interface.objects.get(device=form.cleaned_data['device_b'],
name=form.cleaned_data['interface_b'])
if interface_b in occupied_interfaces:
raise forms.ValidationError("{} {} found in multiple connections"
.format(interface_b.device.name, interface_b.name))
connection = InterfaceConnection(interface_a=interface_a, interface_b=interface_b)
if form.cleaned_data['status'] == 'planned':
connection.connection_status = CONNECTION_STATUS_PLANNED
else:
connection.connection_status = CONNECTION_STATUS_CONNECTED
connection_list.append(connection)
occupied_interfaces.append(interface_a)
occupied_interfaces.append(interface_b)
else:
for field, errors in form.errors.items():
for e in errors:

View File

@@ -183,6 +183,17 @@ class Rack(CreatedUpdatedModel):
def get_absolute_url(self):
return reverse('dcim:rack', args=[self.pk])
def clean(self):
# Validate that Rack is tall enough to house the installed Devices
if self.pk:
top_device = Device.objects.filter(rack=self).exclude(position__isnull=True).order_by('-position').first()
if top_device:
min_height = top_device.position + top_device.device_type.u_height - 1
if self.u_height < min_height:
raise ValidationError("Rack must be at least {}U tall with currently installed devices."
.format(min_height))
def to_csv(self):
return ','.join([
self.site.name,

View File

@@ -47,7 +47,7 @@ class SiteTest(APITestCase):
graph_fields = [
'name',
'embed_url',
'link',
'embed_link',
]
def test_get_list(self, endpoint='/api/dcim/sites/'):

View File

@@ -64,7 +64,7 @@ class RackTestCase(TestCase):
rack=rack1,
position=10,
face=RACK_FACE_REAR,
)
)
device1.save()
# Validate rack height

View File

@@ -353,7 +353,7 @@ class ComponentTemplateCreateView(View):
if not form.errors:
self.model.objects.bulk_create(component_templates)
messages.success(request, "Added {} compontent(s) to {}".format(len(component_templates), devicetype))
messages.success(request, "Added {} component(s) to {}".format(len(component_templates), devicetype))
if '_addanother' in request.POST:
return redirect(request.path)
else:

View File

@@ -121,6 +121,12 @@ class Aggregate(CreatedUpdatedModel):
raise ValidationError("{} is already covered by an existing aggregate ({})"
.format(self.prefix, covering_aggregates[0]))
# Ensure that the aggregate being added does not cover an existing aggregate
covered_aggregates = Aggregate.objects.filter(prefix__net_contained=str(self.prefix))
if covered_aggregates:
raise ValidationError("{} is overlaps with an existing aggregate ({})"
.format(self.prefix, covered_aggregates[0]))
def save(self, *args, **kwargs):
if self.prefix:
# Infer address family from IPNetwork object

View File

@@ -395,16 +395,24 @@ def ipaddress(request, pk):
ipaddress = get_object_or_404(IPAddress.objects.select_related('interface__device'), pk=pk)
# Parent prefixes table
parent_prefixes = Prefix.objects.filter(vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip))
related_ips = IPAddress.objects.select_related('interface__device').exclude(pk=ipaddress.pk)\
.filter(vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address))
parent_prefixes_table = tables.PrefixBriefTable(parent_prefixes)
# Duplicate IPs table
duplicate_ips = IPAddress.objects.filter(vrf=ipaddress.vrf, address=str(ipaddress.address))\
.exclude(pk=ipaddress.pk).select_related('interface__device', 'nat_inside')
duplicate_ips_table = tables.IPAddressBriefTable(duplicate_ips)
# Related IP table
related_ips = IPAddress.objects.select_related('interface__device').exclude(address=str(ipaddress.address))\
.filter(vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address))
related_ips_table = tables.IPAddressBriefTable(related_ips)
RequestConfig(request, paginate={'klass': EnhancedPaginator}).configure(related_ips_table)
return render(request, 'ipam/ipaddress.html', {
'ipaddress': ipaddress,
'parent_prefixes': parent_prefixes,
'parent_prefixes_table': parent_prefixes_table,
'duplicate_ips_table': duplicate_ips_table,
'related_ips_table': related_ips_table,
})

View File

@@ -9,7 +9,8 @@ body {
padding-top: 70px;
}
.container {
width: 1340px;
width: auto;
max-width: 1340px;
}
.wrapper {
min-height: 100%;

View File

@@ -7,9 +7,9 @@ $(document).ready(function() {
// Slugify
function slugify(s, num_chars) {
s = s.replace(/[^-\.\+\w\s]/g, ''); // Remove unneeded chars
s = s.replace(/[^\-\.\w\s]/g, ''); // Remove unneeded chars
s = s.replace(/^\s+|\s+$/g, ''); // Trim leading/trailing spaces
s = s.replace(/[-\s]+/g, '-'); // Convert spaces to hyphens
s = s.replace(/[\-\.\s]+/g, '-'); // Convert spaces and decimals to hyphens
s = s.toLowerCase(); // Convert to lowercase
return s.substring(0, num_chars); // Trim to first num_chars chars
}

View File

@@ -219,7 +219,7 @@
{% if settings.MAINTENANCE_MODE %}
<div class="alert alert-warning text-center" role="alert">
<h4><i class="fa fa-exclamation-triangle"></i> Maintenance Mode</h4>
<p>The application is currently in maintenance mode.</p>
<p>NetBox is currently in maintenance mode. Functionality may be limited.</p>
</div>
{% endif %}
{% for message in messages %}
@@ -246,7 +246,7 @@
<p class="text-muted">
<i class="fa fa-fw fa-book text-primary"></i> <a href="{% url 'docs_root' %}">Docs</a> &middot;
<i class="fa fa-fw fa-cloud text-primary"></i> <a href="/api/docs/">API</a> &middot;
<i class="fa fa-fw fa-code text-primary"></i><a href="https://github.com/digitalocean/netbox">Code</a>
<i class="fa fa-fw fa-code text-primary"></i> <a href="https://github.com/digitalocean/netbox">Code</a>
</p>
</div>
</div>

View File

@@ -8,6 +8,14 @@
<h1>Interface Connections Import</h1>
<div class="row">
<div class="col-md-6">
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<form action="." method="post" class="form">
{% csrf_token %}
{% render_form form %}

View File

@@ -119,31 +119,14 @@
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Parent Prefixes</strong>
</div>
{% if parent_prefixes %}
<table class="table table-hover panel-body">
{% for p in parent_prefixes %}
<tr>
<td>
<a href="{% url 'ipam:prefix' pk=p.pk %}">{{ p }}</a>
</td>
<td>
{% if p.site %}
<a href="{% url 'dcim:site' slug=p.site.slug %}">{{ p.site }}</a>
{% endif %}
</td>
<td>{{ p.status }}</td>
<td>{{ p.role }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="panel-body text-muted">None</div>
{% endif %}
</div>
{% with heading='Parent Prefixes' %}
{% render_table parent_prefixes_table 'panel_table.html' %}
{% endwith %}
{% if duplicate_ips_table.rows %}
{% with heading='Duplicate IP Addresses' panel_class='danger' %}
{% render_table duplicate_ips_table 'panel_table.html' %}
{% endwith %}
{% endif %}
{% with heading='Related IP Addresses' %}
{% render_table related_ips_table 'panel_table.html' %}
{% endwith %}

View File

@@ -19,11 +19,11 @@ def expand_pattern(string):
lead, pattern, remnant = re.split(EXPANSION_PATTERN, string, maxsplit=1)
x, y = pattern.split('-')
for i in range(int(x), int(y) + 1):
if remnant:
if re.search(EXPANSION_PATTERN, remnant):
for string in expand_pattern(remnant):
yield "{}{}{}".format(lead, i, string)
else:
yield "{}{}".format(lead, i)
yield "{}{}{}".format(lead, i, remnant)
#

View File

@@ -120,7 +120,7 @@ class ObjectEditView(View):
'obj': obj,
'obj_type': self.model._meta.verbose_name,
'form': form,
'cancel_url': reverse(self.cancel_url) if self.cancel_url else obj.get_absolute_url(),
'cancel_url': obj.get_absolute_url() if obj else reverse(self.cancel_url),
})
def post(self, request, *args, **kwargs):
@@ -157,7 +157,7 @@ class ObjectEditView(View):
'obj': obj,
'obj_type': self.model._meta.verbose_name,
'form': form,
'cancel_url': reverse(self.cancel_url) if self.cancel_url else obj.get_absolute_url(),
'cancel_url': obj.get_absolute_url() if obj else reverse(self.cancel_url),
})

52
scripts/cibuild.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Exit code starts at 0 but is modified if any checks fail
EXIT=0
# Output a line prefixed with a timestamp
info()
{
echo "$(date +'%F %T') |"
}
# Track number of seconds required to run script
START=$(date +%s)
echo "$(info) starting build checks."
# Syntax check all python source files
SYNTAX=$(find . -name "*.py" -type f -exec python -m py_compile {} \; 2>&1)
if [[ ! -z $SYNTAX ]]; then
echo -e "$SYNTAX"
echo -e "\n$(info) detected one or more syntax errors, failing build."
EXIT=1
fi
# Check all python source files for PEP 8 compliance, but explicitly
# ignore:
# - E501: line greater than 80 characters in length
pep8 --ignore=E501 netbox/
RC=$?
if [[ $RC != 0 ]]; then
echo -e "\n$(info) one or more PEP 8 errors detected, failing build."
EXIT=$RC
fi
# Prepare configuration file for use in CI
CONFIG="netbox/netbox/configuration.py"
cp netbox/netbox/configuration.example.py $CONFIG
sed -i -e "s/ALLOWED_HOSTS = \[\]/ALLOWED_HOSTS = \['*'\]/g" $CONFIG
sed -i -e "s/SECRET_KEY = ''/SECRET_KEY = 'netboxci'/g" $CONFIG
# Run NetBox tests
./netbox/manage.py test netbox/
RC=$?
if [[ $RC != 0 ]]; then
echo -e "\n$(info) one or more tests failed, failing build."
EXIT=$RC
fi
# Show build duration
END=$(date +%s)
echo "$(info) exiting with code $EXIT after $(($END - $START)) seconds."
exit $EXIT