diff --git a/README.md b/README.md
index cc563c284db..00343a85574 100644
--- a/README.md
+++ b/README.md
@@ -15,62 +15,14 @@ Questions? Comments? Please join us on IRC in **#netbox** on **irc.freenode.net*
## Screenshots
-![Screenshot of main page](docs/screenshot1.png "Main page")
+![Screenshot of main page](docs/media/screenshot1.png "Main page")
-![Screenshot of rack elevation](docs/screenshot2.png "Rack elevation")
+![Screenshot of rack elevation](docs/media/screenshot2.png "Rack elevation")
-![Screenshot of prefix hierarchy](docs/screenshot3.png "Prefix hierarchy")
+![Screenshot of prefix hierarchy](docs/media/screenshot3.png "Prefix hierarchy")
# Installation
Please see docs/getting-started.md for instructions on installing NetBox.
To upgrade NetBox, please download the [latest release](https://github.com/digitalocean/netbox/releases) and run `upgrade.sh`.
-
-# Components
-
-NetBox understands all of the physical and logical building blocks that comprise network infrastructure, and the manners in which they are all related.
-
-## DCIM
-
-DCIM comprises all the physical installations and connections which comprise a network. NetBox tracks where devices are installed, as well as their individual power, console, and network connections.
-
-**Site:** A physical location (typically a building) where network devices are installed. Devices in different sites cannot be directly connected to one another.
-
-**Rack:** An equipment rack into which devices are installed. Each rack belongs to a site.
-
-**Device:** Any type of rack-mounted device. For example, routers, switches, servers, console servers, PDUs, etc. 0U (non-rack-mounted) devices are supported.
-
-## IPAM
-
-IPAM deals with the IP addressing and VLANs in use on a network. NetBox makes a distinction between IP prefixes (networks) and individual IP addresses.
-
-Because NetBox is a combined DCIM/IPAM system, IP addresses can be assigned to device interfaces in the application just as they are in the real world.
-
-**Aggregate:** A top-level aggregate of IP address space; for example, 10.0.0.0/8 or 2001:db8::/32. Each aggregate belongs to a regional Internet registry (RIR) like ARIN or RIPE, or to an authoritative standard such as RFC 1918.
-
-**VRF:** A virtual routing table. VRF support is currently still under development.
-
-**Prefix:** An IPv4 or IPv6 network. A prefix can be assigned to a VRF; if not, it is considered to belong to the global table. Prefixes are grouped by aggregates automatically and can optionally be assigned to sites.
-
-**IP Address:** An individual IPv4 or IPv6 address (with CIDR mask). IP address can be assigned to device interfaces.
-
-**VLAN:** VLANs are assigned to sites, and can optionally have one or more IP prefixes assigned to them. VLAN IDs are unique only within the scope of a site.
-
-## Circuits
-
-Long-distance data connections are typically referred to as _circuits_. NetBox provides a method for managing circuits and their providers. Individual circuits can be terminated to device interfaces.
-
-**Provider:** An entity to which a network connects to. This can be a transit provider, peer, or some other organization.
-
-**Circuit:** A data circuit which connects to a provider. The local end of a circuit can be assigned to a device interface.
-
-## Secrets
-
-NetBox provides encrypted storage of sensitive data it calls _secrets_. Each user may be issued an encryption key with which stored secrets can be retrieved.
-
-Note that NetBox does not merely hash secrets, a function which is only useful for validation. It employs fully reversible AES-256 encryption so that secret data can be retrieved and consumed by other services.
-
-**Secrets** Any piece of confidential data which must be retrievable. For example: passwords, SNMP communities, RADIUS shared secrets, etc.
-
-**User Key:** An individual user's encrypted copy of the master key, which can be used to retrieve secret data.
diff --git a/docker-compose.yml b/docker-compose.yml
index 860022707cd..d435066d6c9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,6 +9,7 @@ services:
POSTGRES_PASSWORD: J5brHrAXFLQSif0K
POSTGRES_DB: netbox
netbox:
+ build: .
image: digitalocean/netbox
links:
- postgres
diff --git a/docs/api-integration.md b/docs/api-integration.md
new file mode 100644
index 00000000000..99185adf6f4
--- /dev/null
+++ b/docs/api-integration.md
@@ -0,0 +1,19 @@
+# API Integration
+
+NetBox features a read-only REST API which can be used to integrate it with
+other applications.
+
+In the future, both read and write actions will be available via the API.
+
+## Clients
+
+The easiest way to start integrating your applications with NetBox is to make
+use of an API client. If you build or discover an API client that is not part
+of this list, please send a pull request!
+
+- **Go**: [github.com/digitalocean/go-netbox](https://github.com/digitalocean/go-netbox)
+
+## Documentation
+
+If you wish to build a new API client or simply explore the NetBox API,
+Swagger documentation can be found at the URL `/api/docs/` on a NetBox server.
diff --git a/docs/configuration/mandatory-settings.md b/docs/configuration/mandatory-settings.md
new file mode 100644
index 00000000000..07a6d8ede48
--- /dev/null
+++ b/docs/configuration/mandatory-settings.md
@@ -0,0 +1,45 @@
+NetBox's local configuration is held in `netbox/netbox/configuration.py`. An example configuration is provided at `netbox/netbox/configuration.example.py`. You may copy or rename the example configuration and make changes as appropriate. NetBox will not run without a configuration file.
+
+## ALLOWED_HOSTS
+
+This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name.
+
+Example:
+
+```
+ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
+```
+
+---
+
+## DATABASE
+
+NetBox requires access to a PostgreSQL database service to store data. This service can run locally or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
+
+* NAME - Database name
+* USER - PostgreSQL username
+* PASSWORD - PostgreSQL password
+* HOST - Name or IP address of the database server (use `localhost` if running locally)
+* PORT - TCP port of the PostgreSQL service; leave blank for default port (5432)
+
+Example:
+
+```
+DATABASE = {
+ 'NAME': 'netbox', # Database name
+ 'USER': 'netbox', # PostgreSQL username
+ 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
+ 'HOST': 'localhost', # Database server
+ 'PORT': '', # Database port (leave blank for default)
+}
+```
+
+---
+
+## SECRET_KEY
+
+This is a secret cryptographic key is used to improve the security of cookies and password resets. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions.
+
+Please note that this key is **not** used for hashing user passwords or for the encrypted storage of secret data in NetBox.
+
+`SECRET_KEY` should be at least 50 characters in length and contain a random mix of letters, digits, and symbols. The script located at `netbox/generate_secret_key.py` may be used to generate a suitable key.
diff --git a/docs/configuration.md b/docs/configuration/optional-settings.md
similarity index 50%
rename from docs/configuration.md
rename to docs/configuration/optional-settings.md
index bd4706e6ff7..0aa59a1967d 100644
--- a/docs/configuration.md
+++ b/docs/configuration/optional-settings.md
@@ -1,62 +1,6 @@
-
Configuration
+The following are optional settings which may be declared in `netbox/netbox/configuration.py`.
-NetBox's local configuration is held in `netbox/netbox/configuration.py`. An example configuration is provided at `netbox/netbox/configuration.example.py`. You may copy or rename the example configuration and make changes as appropriate. NetBox will not run without a configuration file.
-
-[TOC]
-
-# Mandatory Settings
-
----
-
-#### ALLOWED_HOSTS
-
-This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name.
-
-Example:
-
-```
-ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
-```
-
----
-
-#### DATABASE
-
-NetBox requires access to a PostgreSQL database service to store data. This service can run locally or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
-
-* NAME - Database name
-* USER - PostgreSQL username
-* PASSWORD - PostgreSQL password
-* HOST - Name or IP address of the database server (use `localhost` if running locally)
-* PORT - TCP port of the PostgreSQL service; leave blank for default port (5432)
-
-Example:
-
-```
-DATABASE = {
- 'NAME': 'netbox', # Database name
- 'USER': 'netbox', # PostgreSQL username
- 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
- 'HOST': 'localhost', # Database server
- 'PORT': '', # Database port (leave blank for default)
-}
-```
-
----
-
-#### SECRET_KEY
-
-This is a secret cryptographic key is used to improve the security of cookies and password resets. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions.
-
-Please note that this key is **not** used for hashing user passwords or for the encrypted storage of secret data in NetBox.
-
-`SECRET_KEY` should be at least 50 characters in length and contain a random mix of letters, digits, and symbols. The script located at `netbox/generate_secret_key.py` may be used to generate a suitable key.
-
-# Optional Settings
-
----
-
-#### ADMINS
+## ADMINS
NetBox will email details about critical errors to the administrators listed here. This should be a list of (name, email) tuples. For example:
@@ -69,7 +13,7 @@ ADMINS = [
---
-#### DEBUG
+## DEBUG
Default: False
@@ -77,7 +21,7 @@ This setting enables debugging. This should be done only during development or t
---
-#### EMAIL
+## EMAIL
In order to send email, NetBox needs an email server configured. The following items can be defined within the `EMAIL` setting:
@@ -90,7 +34,7 @@ In order to send email, NetBox needs an email server configured. The following i
---
-#### LOGIN_REQUIRED
+## LOGIN_REQUIRED
Default: False,
@@ -98,7 +42,7 @@ Setting this to True will permit only authenticated users to access any part of
---
-#### MAINTENANCE_MODE
+## MAINTENANCE_MODE
Default: False
@@ -106,15 +50,15 @@ Setting this to True will display a "maintenance mode" banner at the top of ever
---
-#### NETBOX_USERNAME
+## NETBOX_USERNAME
-#### NETBOX_PASSWORD
+## NETBOX_PASSWORD
If provided, NetBox will use these credentials to authenticate against devices when collecting data.
---
-#### PAGINATE_COUNT
+## PAGINATE_COUNT
Default: 50
@@ -122,7 +66,7 @@ Determine how many objects to display per page within each list of objects.
---
-#### TIME_ZONE
+## TIME_ZONE
Default: UTC
@@ -130,7 +74,7 @@ The time zone NetBox will use when dealing with dates and times. It is recommend
---
-#### Date and Time Formatting
+## Date and Time Formatting
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date).
diff --git a/docs/circuits.md b/docs/data-model/circuits.md
similarity index 98%
rename from docs/circuits.md
rename to docs/data-model/circuits.md
index 357e5eae68c..563e5df7c6d 100644
--- a/docs/circuits.md
+++ b/docs/data-model/circuits.md
@@ -1,9 +1,5 @@
-Circuits
-
The circuits component of NetBox deals with the management of long-haul Internet and private transit links and providers.
-[TOC]
-
# Providers
A provider is any entity which provides some form of connectivity. This obviously includes carriers which offer Internet and private transit service. However, it might also include Internet exchange (IX) points and even organizations with whom you peer directly.
diff --git a/docs/dcim.md b/docs/data-model/dcim.md
similarity index 99%
rename from docs/dcim.md
rename to docs/data-model/dcim.md
index cc2cf5073be..6512995eab4 100644
--- a/docs/dcim.md
+++ b/docs/data-model/dcim.md
@@ -1,9 +1,5 @@
-DCIM
-
Data center infrastructure management (DCIM) entails all physical assets: sites, racks, devices, cabling, etc.
-[TOC]
-
# Sites
How you define sites will depend on the nature of your organization, but typically a site will equate a building or campus. For example, a chain of banks might create a site to represent each of its branches, a site for its corporate headquarters, and two additional sites for its presence in two colocation facilities.
diff --git a/docs/extras.md b/docs/data-model/extras.md
similarity index 99%
rename from docs/extras.md
rename to docs/data-model/extras.md
index bd47d35d957..f9fc82e18f1 100644
--- a/docs/extras.md
+++ b/docs/data-model/extras.md
@@ -1,9 +1,5 @@
-Extras
-
This section entails features of NetBox which are not crucial to its primary functions, but that provide additional value.
-[TOC]
-
# Export Templates
NetBox allows users to define custom templates that can be used when exporting objects. To create an export template, navigate to Extras > Export Templates under the admin interface.
diff --git a/docs/ipam.md b/docs/data-model/ipam.md
similarity index 99%
rename from docs/ipam.md
rename to docs/data-model/ipam.md
index ff381009795..c6da1d657f9 100644
--- a/docs/ipam.md
+++ b/docs/data-model/ipam.md
@@ -1,9 +1,5 @@
-IPAM
-
IP address management (IPAM) entails the allocation of IP networks, addresses, and related numeric resources.
-[TOC]
-
# VRFs
A VRF object in NetBox represents a virtual routing and forwarding (VRF) domain within a network. Each VRF is essentially a separate routing table: the same IP prefix or address can exist in multiple VRFs. VRFs are commonly used to isolate customers or organizations from one another within a network.
diff --git a/docs/secrets.md b/docs/data-model/secrets.md
similarity index 99%
rename from docs/secrets.md
rename to docs/data-model/secrets.md
index 9b7519fba42..ef82c196baa 100644
--- a/docs/secrets.md
+++ b/docs/data-model/secrets.md
@@ -1,9 +1,5 @@
-Secrets
-
"Secrets" are small amounts of data that must be kept confidential; for example, passwords and SNMP community strings. NetBox provides encrypted storage of secret data.
-[TOC]
-
# Secrets
A secret represents a single credential or other string which must be stored securely. Each secret is assigned to a device within NetBox. The plaintext value of a secret is encrypted to a ciphertext immediately prior to storage within the database using a 256-bit AES master key. A SHA256 hash of the plaintext is also stored along with each ciphertext to validate the decrypted plaintext.
diff --git a/docs/getting-started.md b/docs/getting-started.md
deleted file mode 100644
index d3215ab51e8..00000000000
--- a/docs/getting-started.md
+++ /dev/null
@@ -1,502 +0,0 @@
-Getting Started
-
-This guide documents the process of installing NetBox on an Ubuntu 14.04 server with [nginx](https://www.nginx.com/) and [gunicorn](http://gunicorn.org/).
-
-[TOC]
-
-# PostgreSQL
-
-## Installation
-
-The following packages are needed to install PostgreSQL:
-
-* postgresql
-* libpq-dev
-* python-psycopg2
-
-```
-# sudo apt-get install -y postgresql libpq-dev python-psycopg2
-```
-
-## Configuration
-
-At a minimum, we need to create a database for NetBox and assign it a username and password for authentication. This is done with the following commands.
-
-DO NOT USE THE PASSWORD FROM THE EXAMPLE.
-
-```
-# sudo -u postgres psql
-psql (9.3.13)
-Type "help" for help.
-
-postgres=# CREATE DATABASE netbox;
-CREATE DATABASE
-postgres=# CREATE USER netbox WITH PASSWORD 'J5brHrAXFLQSif0K';
-CREATE ROLE
-postgres=# GRANT ALL PRIVILEGES ON DATABASE netbox TO netbox;
-GRANT
-postgres=# \q
-```
-
-You can verify that authentication works using the following command:
-
-```
-# psql -U netbox -h localhost -W
-```
-
----
-
-# NetBox
-
-## Installation
-
-NetBox requires following dependencies:
-
-* python2.7
-* python-dev
-* python-pip
-* libxml2-dev
-* libxslt1-dev
-* libffi-dev
-* graphviz
-
-```
-# sudo apt-get install -y python2.7 python-dev git python-pip libxml2-dev libxslt1-dev libffi-dev graphviz
-```
-
-You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.
-
-### Option A: Download a Release
-
-Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
-
-```
-# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
-# tar -xzf vX.Y.Z.tar.gz -C /opt
-# cd /opt/
-# ln -s netbox-1.0.4/ netbox
-# cd /opt/netbox/
-```
-
-### Option B: Clone the Git Repository
-
-Create the base directory for the NetBox installation. For this guide, we'll use `/opt/netbox`.
-
-```
-# mkdir -p /opt/netbox/
-# cd /opt/netbox/
-```
-
-If `git` is not already installed, install it:
-
-```
-# sudo apt-get install -y git
-```
-
-Next, clone the **master** branch of the NetBox GitHub repository into the current directory:
-
-```
-# git clone -b master https://github.com/digitalocean/netbox.git .
-Cloning into '.'...
-remote: Counting objects: 1994, done.
-remote: Compressing objects: 100% (150/150), done.
-remote: Total 1994 (delta 80), reused 0 (delta 0), pack-reused 1842
-Receiving objects: 100% (1994/1994), 472.36 KiB | 0 bytes/s, done.
-Resolving deltas: 100% (1495/1495), done.
-Checking connectivity... done.
-```
-
-### Install Python Packages
-
-Install the necessary Python packages using pip. (If you encounter any compilation errors during this step, ensure that you've installed all of the required dependencies.)
-
-```
-# sudo pip install -r requirements.txt
-```
-
-## Configuration
-
-Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`.
-
-```
-# cd netbox/netbox/
-# cp configuration.example.py configuration.py
-```
-
-Open `configuration.py` with your preferred editor and set the following variables:
-
-* ALLOWED_HOSTS
-* DATABASE
-* SECRET_KEY
-
-### ALLOWED_HOSTS
-
-This is a list of the valid hostnames by which this server can be reached. You must specify at least one name or IP address.
-
-Example:
-
-```
-ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
-```
-
-### DATABASE
-
-This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, replace `localhost` with its address.
-
-Example:
-
-```
-DATABASE = {
- 'NAME': 'netbox', # Database name
- 'USER': 'netbox', # PostgreSQL username
- 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
- 'HOST': 'localhost', # Database server
- 'PORT': '', # Database port (leave blank for default)
-}
-```
-
-### SECRET_KEY
-
-Generate a random secret key of at least 50 alphanumeric characters. This key must be unique to this installation and must not be shared outside the local system.
-
-You may use the script located at `netbox/generate_secret_key.py` to generate a suitable key.
-
-## Run Migrations
-
-Before NetBox can run, we need to install the database schema. This is done by running `./manage.py migrate` from the `netbox` directory (`/opt/netbox/netbox/` in our example):
-
-```
-# cd /opt/netbox/netbox/
-# ./manage.py migrate
-Operations to perform:
- Apply all migrations: dcim, sessions, admin, ipam, utilities, auth, circuits, contenttypes, extras, secrets, users
-Running migrations:
- Rendering model states... DONE
- Applying contenttypes.0001_initial... OK
- Applying auth.0001_initial... OK
- Applying admin.0001_initial... OK
- ...
-```
-
-If this step results in a PostgreSQL authentication error, ensure that the username and password created in the database match what has been specified in `configuration.py`
-
-## Create a Super User
-
-NetBox does not come with any predefined user accounts. You'll need to create a super user to be able to log into NetBox:
-
-```
-# ./manage.py createsuperuser
-Username: admin
-Email address: admin@example.com
-Password:
-Password (again):
-Superuser created successfully.
-```
-
-## Collect Static Files
-
-```
-# ./manage.py collectstatic
-
-You have requested to collect static files at the destination
-location as specified in your settings:
-
- /opt/netbox/netbox/static
-
-This will overwrite existing files!
-Are you sure you want to do this?
-
-Type 'yes' to continue, or 'no' to cancel: yes
-```
-
-## Test the Application
-
-At this point, NetBox should be able to run. We can verify this by starting a development instance:
-
-```
-# ./manage.py runserver 0.0.0.0:8000 --insecure
-Performing system checks...
-
-System check identified no issues (0 silenced).
-June 17, 2016 - 16:17:36
-Django version 1.9.7, using settings 'netbox.settings'
-Starting development server at http://0.0.0.0:8000/
-Quit the server with CONTROL-C.
-```
-
-Now if we navigate to the name or IP of the server (as defined in `ALLOWED_HOSTS`) we should be greeted with the NetBox home page. Note that this built-in web service is for development and testing purposes only. It is not suited for production use.
-
-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.
-
-# Web Server and gunicorn
-
-## Installation
-
-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.
-
-```
-# sudo apt-get install -y gunicorn supervisor
-```
-
-## nginx Configuration
-
-The following will serve as a minimal nginx configuration. Be sure to modify your server name and installation path appropriately.
-
-```
-# sudo apt-get install -y nginx
-```
-
-Once nginx is installed, proceed with the following configuration:
-
-```
-server {
- listen 80;
-
- server_name netbox.example.com;
-
- access_log off;
-
- location /static/ {
- alias /opt/netbox/netbox/static/;
- }
-
- location / {
- proxy_pass http://127.0.0.1:8001;
- proxy_set_header X-Forwarded-Host $server_name;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-Proto $scheme;
- add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
- }
-}
-```
-
-Save this configuration to `/etc/nginx/sites-available/netbox`. Then, delete `/etc/nginx/sites-enabled/default` and create a symlink in the `sites-enabled` directory to the configuration file you just created.
-
-```
-# cd /etc/nginx/sites-enabled/
-# rm default
-# ln -s /etc/nginx/sites-available/netbox
-```
-
-Restart the nginx service to use the new configuration.
-
-```
-# service nginx restart
- * Restarting nginx nginx
-```
-## Apache Configuration
-
-```
-# sudo apt-get install -y apache2
-```
-
-Once Apache is installed, proceed with the following configuration (Be sure to modify the `ServerName` appropriately):
-
-```
-
- ProxyPreserveHost On
-
- ServerName netbox.example.com
-
- Alias /static /opt/netbox/netbox/static
-
-
- Options Indexes FollowSymLinks MultiViews
- AllowOverride None
- Require all granted
-
-
-
- ProxyPass !
-
-
- ProxyPass / http://127.0.0.1:8001/
- ProxyPassReverse / http://127.0.0.1:8001/
-
-```
-
-Save the contents of the above example in `/etc/apache2/sites-available/netbox.conf`, enable the `proxy` and `proxy_http` modules, and reload Apache:
-
-```
-# a2enmod proxy
-# a2enmod proxy_http
-# 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 verify the location of the gunicorn executable (e.g. `which gunicorn`) and to update the `pythonpath` variable if needed.
-
-```
-command = '/usr/bin/gunicorn'
-pythonpath = '/opt/netbox/netbox'
-bind = '127.0.0.1:8001'
-workers = 3
-user = 'www-data'
-```
-
-## supervisord Configuration
-
-Save the following as `/etc/supervisor/conf.d/netbox.conf`. Update the `command` and `directory` paths as needed.
-
-```
-[program:netbox]
-command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
-directory = /opt/netbox/netbox/
-user = www-data
-```
-
-Finally, restart the supervisor service to detect and run the gunicorn service:
-
-```
-# service supervisor restart
-```
-
-At this point, you should be able to connect to the nginx HTTP service at the server name or IP address you provided. If you are unable to connect, check that the nginx service is running and properly configured. If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.
-
-Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You will almost certainly want to make some changes to better suit your production environment.
-
-## Let's Encrypt SSL + nginx
-
-To add SSL support to the installation we'll start by installing the arbitrary precision calculator language.
-
-```
-# sudo apt-get install -y bc
-```
-
-Next we'll clone Let's Encrypt into /opt/:
-
-```
-# sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
-```
-
-To ensure Let's Encrypt can publicly access the directory it needs for certificate validation you'll need to edit `/etc/nginx/sites-available/netbox` and add:
-
-```
- location /.well-known/ {
- alias /opt/netbox/netbox/.well-known/;
- allow all;
- }
-```
-
-Then restart nginix:
-
-```
-# sudo services nginx restart
-```
-
-To create the certificate use the following commands ensuring to change `netbox.example.com` to the domain name of the server:
-
-```
-# cd /opt/letsencrypt
-# ./letsencrypt-auto certonly -a webroot --webroot-path=/opt/netbox/netbox/ -d netbox.example.com
-```
-
-If you wish to add support for the `www` prefix you'd use:
-
-```
-# cd /opt/letsencrypt
-# ./letsencrypt-auto certonly -a webroot --webroot-path=/opt/netbox/netbox/ -d netbox.example.com -d www.netbox.example.com
-```
-
-Make sure you have DNS records setup for the hostnames you use and that they resolve back the netbox server.
-
-You will be prompted for your email address to receive notifications about your SSL and then asked to accept the subscriber agreement.
-
-If successful you'll now have four files in `/etc/letsencrypt/live/netbox.example.com` (remember, your hostname is different)
-
-```
-cert.pem
-chain.pem
-fullchain.pem
-privkey.pem
-```
-
-Now edit your nginx configuration `/etc/nginx/sites-available/netbox` and at the top edit to the following:
-
-```
- #listen 80;
- #listen [::]80;
- listen 443;
- listen [::]443;
-
- ssl on;
- ssl_certificate /etc/letsencrypt/live/netbox.example.com/cert.pem;
- ssl_certificate_key /etc/letsencrypt/live/netbox.example.com/privkey.pem;
-```
-
-If you are not using IPv6 then you do not need `listen [::]443;` The two commented lines are for non-SSL for both IPv4 and IPv6.
-
-Lastly, restart nginx:
-
-```
-# sudo services nginx restart
-```
-
-You should now have netbox running on a SSL protected connection.
-
-# Upgrading
-
-## Installation of Upgrade
-
-As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository.
-
-### Option A: Download a Release
-
-Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`. For this guide we are using 1.0.4 as the old version and 1.0.7 as the new version.
-
-Download & extract latest version:
-```
-# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
-# tar -xzf vX.Y.Z.tar.gz -C /opt
-# cd /opt/
-# ln -sf netbox-1.0.7/ netbox
-```
-
-Copy the 'configuration.py' you created when first installing to the new version:
-```
-# cp /opt/netbox-1.0.4/configuration.py /opt/netbox/configuration.py
-```
-
-### Option B: Clone the Git Repository (latest master release)
-
-For this guide, we'll use `/opt/netbox`.
-
-Check that your git branch is up to date & is set to master:
-```
-# cd /opt/netbox
-# git status
-```
-
-If not on branch master, set it and verify status:
-```
-# git checkout master
-# git status
-```
-
-Pull down the set branch from git status above:
-```
-# git pull
-```
-
-
-## Upgrade Script & Netbox Restart
-
-Once the new code is in place, run the upgrade script (which may need to be run as root depending on how your environment is configured).
-
-```
-# ./upgrade.sh
-```
-
-This script:
-
-* Installs or upgrades any new required Python packages
-* Applies any database migrations that were included in the release
-* Collects all static files to be served by the HTTP service
-
-Finally, restart the WSGI service to run the new code. If you followed this guide for the initial installation, this is done using `supervisorctl`:
-
-```
-# sudo supervisorctl restart netbox
-```
diff --git a/docs/index.md b/docs/index.md
index 86ece6841b0..e9f57253df3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,3 +1,53 @@
-# NetBox Documentation
+# What is NetBox?
-NetBox is an IP address management (IPAM) and data center infrastructure management (DCIM) application.
+NetBox is an open source web application designed to help manage and document computer networks. 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. It encompasses the following aspects of network management:
+
+* **IP address management (IPAM)** - IP networks and addresses, VRFs, and VLANs
+* **Equipment racks** - Organized by group and site
+* **Devices** - Types of devices and where they are installed
+* **Connections** - Network, console, and power connections among devices
+* **Data circuits** - Long-haul communications circuits and providers
+* **Secrets** - Encrypted storage of sensitive credentials
+
+# What NetBox Isn't
+
+While NetBox strives to cover many areas of network management, the scope of its feature set is necessarily limited. This ensures that development focuses on core functionality and that scope creep is reasonably contained. To that end, it might help to provide some examples of functionality that NetBox **does not** provide:
+
+* Network monitoring
+* DNS server
+* RADIUS server
+* Configuration management
+* Facilities management
+
+That said, NetBox _can_ be used to great effect in populating external tools with the data they need to perform these functions.
+
+# Design Philosophy
+
+NetBox was designed with the following tenets foremost in mind.
+
+## Replicate the Real World
+
+Careful consideration has been given to the data model to ensure that it can accurately reflect a real-world network. For instance, IP addresses are assigned not to devices, but to specific interfaces attached to a device, and an interface may have multiple IP addresses assigned to it.
+
+## Serve as a "Source of Truth"
+
+NetBox intends to represent the _desired_ state of a network versus its _operational_ state. As such, automated import of live network state is strongly discouraged. All data created in NetBox should first be vetted by a human to ensure its integrity. NetBox can then be used to populate monitoring and provisioning systems with a high degree of confidence.
+
+## Keep it Simple
+
+When given a choice between a relatively simple [80% solution](https://en.wikipedia.org/wiki/Pareto_principle) and a much more complex complete solution, the former will typically be favored. This ensures a lean codebase with a low learning curve.
+
+# Application Stack
+
+NetBox is built on the [Django](https://djangoproject.com/) Python framework and utilizes a [PostgreSQL](https://www.postgresql.org/) database. It runs as a WSGI service behind your choice of HTTP server.
+
+| Function | Component |
+|--------------|-------------------|
+| HTTP Service | nginx or Apache |
+| WSGI Service | gunicorn or uWSGI |
+| Application | Django/Python |
+| Database | PostgreSQL |
+
+# Getting Started
+
+See the [getting started](getting-started.md) guide for help with getting NetBox up and running quickly.
diff --git a/docs/getting-started-docker.md b/docs/installation/docker.md
similarity index 58%
rename from docs/getting-started-docker.md
rename to docs/installation/docker.md
index c5de5bbf1eb..efc9685a91d 100644
--- a/docs/getting-started-docker.md
+++ b/docs/installation/docker.md
@@ -1,13 +1,11 @@
-Getting Started with NetBox and Docker
-
-This guide assumes that the latest versions of [Docker](https://www.docker.com/) and [docker-compose](https://docs.docker.com/compose/) are already installed in your host.
+This guide demonstrates how to build and run NetBox as a Docker container. It assumes that the latest versions of [Docker](https://www.docker.com/) and [docker-compose](https://docs.docker.com/compose/) are already installed in your host.
# Quickstart
To get NetBox up and running:
```
-git clone https://github.com/digitalocean/netbox.git
+git clone -b master https://github.com/digitalocean/netbox.git
cd netbox
docker-compose up -d
```
@@ -15,13 +13,13 @@ docker-compose up -d
The application will be available on http://localhost/ after a few minutes.
Default credentials:
-* user: admin
-* password: admin
+
+* Username: **admin**
+* Password: **admin**
# Configuration
-You can configure the app at runtime using variables (see docker-compose.yml).
-Possible environment variables:
+You can configure the app at runtime using variables (see `docker-compose.yml`). Possible environment variables include:
* SUPERUSER_NAME
* SUPERUSER_EMAIL
@@ -51,4 +49,3 @@ Possible environment variables:
* SHORT_TIME_FORMAT
* DATETIME_FORMAT
* SHORT_DATETIME_FORMAT
-
diff --git a/docs/installation/ldap.md b/docs/installation/ldap.md
new file mode 100644
index 00000000000..5a90ec5e315
--- /dev/null
+++ b/docs/installation/ldap.md
@@ -0,0 +1,101 @@
+This guide explains how to implement LDAP authentication using an external server. User authentication will fall back to
+built-in Django users in the event of a failure.
+
+# Requirements
+
+## Install openldap-devel
+
+On Ubuntu:
+
+```
+sudo apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev
+```
+
+On CentOS:
+
+```
+sudo yum install -y python-devel openldap-devel
+```
+
+## Install django-auth-ldap
+
+```
+sudo pip install django-auth-ldap
+```
+
+# Configuration
+
+Create a file in the same directory as `configuration.py` (typically `netbox/netbox/`) named `ldap_config.py`. Define all of the parameters required below in `ldap_config.py`.
+
+## General Server Configuration
+
+```python
+import ldap
+
+# Server URI
+AUTH_LDAP_SERVER_URI = "ldaps://ad.example.com"
+
+# The following may be needed if you are binding to Active Directory.
+AUTH_LDAP_CONNECTION_OPTIONS = {
+ ldap.OPT_REFERRALS: 0
+}
+
+# Set the DN and password for the NetBox service account.
+AUTH_LDAP_BIND_DN = "CN=NETBOXSA, OU=Service Accounts,DC=example,DC=com"
+AUTH_LDAP_BIND_PASSWORD = "demo"
+
+# Include this setting if you want to ignore certificate errors. This might be needed to accept a self-signed cert.
+# Note that this is a NetBox-specific setting which sets:
+# ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
+LDAP_IGNORE_CERT_ERRORS = True
+```
+
+## User Authentication
+
+```python
+from django_auth_ldap.config import LDAPSearch
+
+# This search matches users with the sAMAccountName equal to the provided username. This is required if the user's
+# username is not in their DN (Active Directory).
+AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=Users,dc=example,dc=com",
+ ldap.SCOPE_SUBTREE,
+ "(sAMAccountName=%(user)s)")
+
+# If a user's DN is producible from their username, we don't need to search.
+AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com"
+
+# You can map user attributes to Django attributes as so.
+AUTH_LDAP_USER_ATTR_MAP = {
+ "first_name": "givenName",
+ "last_name": "sn"
+}
+```
+
+# User Groups for Permissions
+
+```python
+from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
+
+# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group
+# heirarchy.
+AUTH_LDAP_GROUP_SEARCH = LDAPSearch("dc=example,dc=com", ldap.SCOPE_SUBTREE,
+ "(objectClass=group)")
+AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
+
+# Define a group required to login.
+AUTH_LDAP_REQUIRE_GROUP = "CN=NETBOX_USERS,DC=example,DC=com"
+
+# Define special user types using groups. Exercise great caution when assigning superuser status.
+AUTH_LDAP_USER_FLAGS_BY_GROUP = {
+ "is_active": "cn=active,ou=groups,dc=example,dc=com",
+ "is_staff": "cn=staff,ou=groups,dc=example,dc=com",
+ "is_superuser": "cn=superuser,ou=groups,dc=example,dc=com"
+}
+
+# For more granular permissions, we can map LDAP groups to Django groups.
+AUTH_LDAP_FIND_GROUP_PERMS = True
+
+# Cache groups for one hour to reduce LDAP traffic
+AUTH_LDAP_CACHE_GROUPS = True
+AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
+```
diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md
new file mode 100644
index 00000000000..fde5ab019b8
--- /dev/null
+++ b/docs/installation/netbox.md
@@ -0,0 +1,181 @@
+# Installation
+
+NetBox requires following system dependencies:
+
+* python2.7
+* python-dev
+* python-pip
+* libxml2-dev
+* libxslt1-dev
+* libffi-dev
+* graphviz
+* libpq-dev
+
+```
+# sudo apt-get install -y python2.7 python-dev git python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev
+```
+
+You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub.
+
+## Option A: Download a Release
+
+Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive and extract it to your desired path. In this example, we'll use `/opt/netbox`.
+
+```
+# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
+# tar -xzf vX.Y.Z.tar.gz -C /opt
+# cd /opt/
+# ln -s netbox-X.Y.Z/ netbox
+# cd /opt/netbox/
+```
+
+## Option B: Clone the Git Repository
+
+Create the base directory for the NetBox installation. For this guide, we'll use `/opt/netbox`.
+
+```
+# mkdir -p /opt/netbox/
+# cd /opt/netbox/
+```
+
+If `git` is not already installed, install it:
+
+```
+# sudo apt-get install -y git
+```
+
+Next, clone the **master** branch of the NetBox GitHub repository into the current directory:
+
+```
+# git clone -b master https://github.com/digitalocean/netbox.git .
+Cloning into '.'...
+remote: Counting objects: 1994, done.
+remote: Compressing objects: 100% (150/150), done.
+remote: Total 1994 (delta 80), reused 0 (delta 0), pack-reused 1842
+Receiving objects: 100% (1994/1994), 472.36 KiB | 0 bytes/s, done.
+Resolving deltas: 100% (1495/1495), done.
+Checking connectivity... done.
+```
+
+## Install Python Packages
+
+Install the required Python packages using pip. (If you encounter any compilation errors during this step, ensure that you've installed all of the system dependencies listed above.)
+
+```
+# sudo pip install -r requirements.txt
+```
+
+# Configuration
+
+Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`.
+
+```
+# cd netbox/netbox/
+# cp configuration.example.py configuration.py
+```
+
+Open `configuration.py` with your preferred editor and set the following variables:
+
+* ALLOWED_HOSTS
+* DATABASE
+* SECRET_KEY
+
+## ALLOWED_HOSTS
+
+This is a list of the valid hostnames by which this server can be reached. You must specify at least one name or IP address.
+
+Example:
+
+```
+ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
+```
+
+## DATABASE
+
+This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, replace `localhost` with its address.
+
+Example:
+
+```
+DATABASE = {
+ 'NAME': 'netbox', # Database name
+ 'USER': 'netbox', # PostgreSQL username
+ 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
+ 'HOST': 'localhost', # Database server
+ 'PORT': '', # Database port (leave blank for default)
+}
+```
+
+## SECRET_KEY
+
+Generate a random secret key of at least 50 alphanumeric characters. This key must be unique to this installation and must not be shared outside the local system.
+
+You may use the script located at `netbox/generate_secret_key.py` to generate a suitable key.
+
+# Run Database Migrations
+
+Before NetBox can run, we need to install the database schema. This is done by running `./manage.py migrate` from the `netbox` directory (`/opt/netbox/netbox/` in our example):
+
+```
+# cd /opt/netbox/netbox/
+# ./manage.py migrate
+Operations to perform:
+ Apply all migrations: dcim, sessions, admin, ipam, utilities, auth, circuits, contenttypes, extras, secrets, users
+Running migrations:
+ Rendering model states... DONE
+ Applying contenttypes.0001_initial... OK
+ Applying auth.0001_initial... OK
+ Applying admin.0001_initial... OK
+ ...
+```
+
+If this step results in a PostgreSQL authentication error, ensure that the username and password created in the database match what has been specified in `configuration.py`
+
+# Create a Super User
+
+NetBox does not come with any predefined user accounts. You'll need to create a super user to be able to log into NetBox:
+
+```
+# ./manage.py createsuperuser
+Username: admin
+Email address: admin@example.com
+Password:
+Password (again):
+Superuser created successfully.
+```
+
+# Collect Static Files
+
+```
+# ./manage.py collectstatic
+
+You have requested to collect static files at the destination
+location as specified in your settings:
+
+ /opt/netbox/netbox/static
+
+This will overwrite existing files!
+Are you sure you want to do this?
+
+Type 'yes' to continue, or 'no' to cancel: yes
+```
+
+# Test the Application
+
+At this point, NetBox should be able to run. We can verify this by starting a development instance:
+
+```
+# ./manage.py runserver 0.0.0.0:8000 --insecure
+Performing system checks...
+
+System check identified no issues (0 silenced).
+June 17, 2016 - 16:17:36
+Django version 1.9.7, using settings 'netbox.settings'
+Starting development server at http://0.0.0.0:8000/
+Quit the server with CONTROL-C.
+```
+
+Now if we navigate to the name or IP of the server (as defined in `ALLOWED_HOSTS`) we should be greeted with the NetBox home page. Note that this built-in web service is for development and testing purposes only. It is not suited for production use.
+
+!!! warning
+ 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.
diff --git a/docs/installation/postgresql.md b/docs/installation/postgresql.md
new file mode 100644
index 00000000000..c4ad03b6a18
--- /dev/null
+++ b/docs/installation/postgresql.md
@@ -0,0 +1,42 @@
+NetBox requires a PostgreSQL database to store data. MySQL is not supported, as NetBox leverage's PostgreSQL's built-in [network address types](https://www.postgresql.org/docs/9.1/static/datatype-net-types.html).
+
+# Installation
+
+The following packages are needed to install PostgreSQL with Python support:
+
+* postgresql
+* libpq-dev
+* python-psycopg2
+
+```
+# sudo apt-get install -y postgresql libpq-dev python-psycopg2
+```
+
+# Configuration
+
+At a minimum, we need to create a database for NetBox and assign it a username and password for authentication. This is done with the following commands.
+
+!!! danger
+ DO NOT USE THE PASSWORD FROM THE EXAMPLE.
+
+```
+# sudo -u postgres psql
+psql (9.3.13)
+Type "help" for help.
+
+postgres=# CREATE DATABASE netbox;
+CREATE DATABASE
+postgres=# CREATE USER netbox WITH PASSWORD 'J5brHrAXFLQSif0K';
+CREATE ROLE
+postgres=# GRANT ALL PRIVILEGES ON DATABASE netbox TO netbox;
+GRANT
+postgres=# \q
+```
+
+You can verify that authentication works issuing the following command and providing the configured password:
+
+```
+# psql -U netbox -h localhost -W
+```
+
+If successful, you will enter a `postgres` prompt. Type `\q` to exit.
diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md
new file mode 100644
index 00000000000..55353b77a0f
--- /dev/null
+++ b/docs/installation/upgrading.md
@@ -0,0 +1,63 @@
+# Install the Latest Code
+
+As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository.
+
+## Option A: Download a Release
+
+Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`. For this guide we are using 1.0.4 as the old version and 1.0.7 as the new version.
+
+Download & extract latest version:
+```
+# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
+# tar -xzf vX.Y.Z.tar.gz -C /opt
+# cd /opt/
+# ln -sf netbox-1.0.7/ netbox
+```
+
+Copy the 'configuration.py' you created when first installing to the new version:
+```
+# cp /opt/netbox-1.0.4/configuration.py /opt/netbox/configuration.py
+```
+
+## Option B: Clone the Git Repository (latest master release)
+
+For this guide, we'll use `/opt/netbox`.
+
+Check that your git branch is up to date & is set to master:
+```
+# cd /opt/netbox
+# git status
+```
+
+If not on branch master, set it and verify status:
+```
+# git checkout master
+# git status
+```
+
+Pull down the set branch from git status above:
+```
+# git pull
+```
+
+# Run the Upgrade Script
+
+Once the new code is in place, run the upgrade script (which may need to be run as root depending on how your environment is configured).
+
+```
+# ./upgrade.sh
+```
+
+This script:
+
+* Installs or upgrades any new required Python packages
+* Applies any database migrations that were included in the release
+* Collects all static files to be served by the HTTP service
+
+# Restart the WSGI Service
+
+Finally, restart the WSGI service to run the new code. If you followed this guide for the initial installation, this is done using `supervisorctl`:
+
+```
+# sudo supervisorctl restart netbox
+```
diff --git a/docs/installation/web-server.md b/docs/installation/web-server.md
new file mode 100644
index 00000000000..b30e2659c0a
--- /dev/null
+++ b/docs/installation/web-server.md
@@ -0,0 +1,132 @@
+# Web Server Installation
+
+We'll set up a simple WSGI 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/) to enable service persistence.
+
+```
+# sudo apt-get install -y gunicorn supervisor
+```
+
+## Option A: nginx
+
+The following will serve as a minimal nginx configuration. Be sure to modify your server name and installation path appropriately.
+
+```
+# sudo apt-get install -y nginx
+```
+
+Once nginx is installed, proceed with the following configuration:
+
+```
+server {
+ listen 80;
+
+ server_name netbox.example.com;
+
+ access_log off;
+
+ location /static/ {
+ alias /opt/netbox/netbox/static/;
+ }
+
+ location / {
+ proxy_pass http://127.0.0.1:8001;
+ proxy_set_header X-Forwarded-Host $server_name;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
+ }
+}
+```
+
+Save this configuration to `/etc/nginx/sites-available/netbox`. Then, delete `/etc/nginx/sites-enabled/default` and create a symlink in the `sites-enabled` directory to the configuration file you just created.
+
+```
+# cd /etc/nginx/sites-enabled/
+# rm default
+# ln -s /etc/nginx/sites-available/netbox
+```
+
+Restart the nginx service to use the new configuration.
+
+```
+# service nginx restart
+ * Restarting nginx nginx
+```
+
+To enable SSL, consider this guide on [securing nginx with Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04).
+
+## Option B: Apache
+
+```
+# sudo apt-get install -y apache2
+```
+
+Once Apache is installed, proceed with the following configuration (Be sure to modify the `ServerName` appropriately):
+
+```
+
+ ProxyPreserveHost On
+
+ ServerName netbox.example.com
+
+ Alias /static /opt/netbox/netbox/static
+
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Require all granted
+
+
+
+ ProxyPass !
+
+
+ ProxyPass / http://127.0.0.1:8001/
+ ProxyPassReverse / http://127.0.0.1:8001/
+
+```
+
+Save the contents of the above example in `/etc/apache2/sites-available/netbox.conf`, enable the `proxy` and `proxy_http` modules, and reload Apache:
+
+```
+# a2enmod proxy
+# a2enmod proxy_http
+# a2ensite netbox
+# service apache2 restart
+```
+
+To enable SSL, consider this guide on [securing Apache with Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-14-04).
+
+# gunicorn Installation
+
+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/bin/gunicorn'
+pythonpath = '/opt/netbox/netbox'
+bind = '127.0.0.1:8001'
+workers = 3
+user = 'www-data'
+```
+
+# supervisord Installation
+
+Save the following as `/etc/supervisor/conf.d/netbox.conf`. Update the `command` and `directory` paths as needed.
+
+```
+[program:netbox]
+command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
+directory = /opt/netbox/netbox/
+user = www-data
+```
+
+Finally, restart the supervisor service to detect and run the gunicorn service:
+
+```
+# service supervisor restart
+```
+
+At this point, you should be able to connect to the nginx HTTP service at the server name or IP address you provided. If you are unable to connect, check that the nginx service is running and properly configured. If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.
+
+!!! info
+ Please keep in mind that the configurations provided here are bare minimums required to get NetBox up and running. You will almost certainly want to make some changes to better suit your production environment.
diff --git a/docs/screenshot1.png b/docs/media/screenshot1.png
similarity index 100%
rename from docs/screenshot1.png
rename to docs/media/screenshot1.png
diff --git a/docs/screenshot2.png b/docs/media/screenshot2.png
similarity index 100%
rename from docs/screenshot2.png
rename to docs/media/screenshot2.png
diff --git a/docs/screenshot3.png b/docs/media/screenshot3.png
similarity index 100%
rename from docs/screenshot3.png
rename to docs/media/screenshot3.png
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000000..d0bcb1ba382
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,24 @@
+site_name: NetBox
+
+pages:
+ - 'Introduction': 'index.md'
+ - 'Installation':
+ - 'PostgreSQL': 'installation/postgresql.md'
+ - 'NetBox': 'installation/netbox.md'
+ - 'Web Server': 'installation/web-server.md'
+ - 'LDAP (Optional)': 'installation/ldap.md'
+ - 'Upgrading': 'installation/upgrading.md'
+ - 'Alternate Install: Docker': 'installation/docker.md'
+ - 'Configuration':
+ - 'Mandatory Settings': 'configuration/mandatory-settings.md'
+ - 'Optional Settings': 'configuration/optional-settings.md'
+ - 'Data Model':
+ - 'Circuits': 'data-model/circuits.md'
+ - 'DCIM': 'data-model/dcim.md'
+ - 'IPAM': 'data-model/ipam.md'
+ - 'Secrets': 'data-model/secrets.md'
+ - 'Extras': 'data-model/extras.md'
+ - 'API Integration': 'api-integration.md'
+
+markdown_extensions:
+ - admonition:
diff --git a/netbox/dcim/admin.py b/netbox/dcim/admin.py
index 4dae1d99800..775f046cd21 100644
--- a/netbox/dcim/admin.py
+++ b/netbox/dcim/admin.py
@@ -89,7 +89,7 @@ def get_queryset(self, request):
power_port_count=Count('power_port_templates', distinct=True),
power_outlet_count=Count('power_outlet_templates', distinct=True),
interface_count=Count('interface_templates', distinct=True),
- devicebay_count=Count('devicebay_templates', distinct=True),
+ devicebay_count=Count('device_bay_templates', distinct=True),
)
def console_ports(self, instance):
diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py
index dd3de9bafbd..a1730182324 100644
--- a/netbox/dcim/api/serializers.py
+++ b/netbox/dcim/api/serializers.py
@@ -221,12 +221,14 @@ class DeviceSerializer(serializers.ModelSerializer):
platform = PlatformNestedSerializer()
rack = RackNestedSerializer()
primary_ip = DeviceIPAddressNestedSerializer()
+ primary_ip4 = DeviceIPAddressNestedSerializer()
+ primary_ip6 = DeviceIPAddressNestedSerializer()
parent_device = serializers.SerializerMethodField()
class Meta:
model = Device
fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'platform', 'serial', 'rack', 'position',
- 'face', 'parent_device', 'status', 'primary_ip', 'comments']
+ 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6', 'comments']
def get_parent_device(self, obj):
try:
diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py
index 243d92e3601..d573cdddee6 100644
--- a/netbox/dcim/api/views.py
+++ b/netbox/dcim/api/views.py
@@ -194,7 +194,7 @@ class DeviceListView(generics.ListAPIView):
List devices (filterable)
"""
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'platform', 'rack__site')\
- .prefetch_related('primary_ip__nat_outside')
+ .prefetch_related('primary_ip4__nat_outside', 'primary_ip6__nat_outside')
serializer_class = serializers.DeviceSerializer
filter_class = filters.DeviceFilter
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
diff --git a/netbox/dcim/fixtures/dcim.json b/netbox/dcim/fixtures/dcim.json
index 89c889b65f1..7c011eb891d 100644
--- a/netbox/dcim/fixtures/dcim.json
+++ b/netbox/dcim/fixtures/dcim.json
@@ -1919,7 +1919,8 @@
"position": 1,
"face": 0,
"status": true,
- "primary_ip": 1,
+ "primary_ip4": 1,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -1938,7 +1939,8 @@
"position": 17,
"face": 0,
"status": true,
- "primary_ip": 5,
+ "primary_ip4": 5,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -1957,7 +1959,8 @@
"position": 33,
"face": 0,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -1976,7 +1979,8 @@
"position": 34,
"face": 0,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -1995,7 +1999,8 @@
"position": 34,
"face": 0,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2014,7 +2019,8 @@
"position": 33,
"face": 0,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2033,7 +2039,8 @@
"position": 1,
"face": 0,
"status": true,
- "primary_ip": 3,
+ "primary_ip4": 3,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2052,7 +2059,8 @@
"position": 17,
"face": 0,
"status": true,
- "primary_ip": 19,
+ "primary_ip4": 19,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2071,7 +2079,8 @@
"position": 42,
"face": 0,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2090,7 +2099,8 @@
"position": null,
"face": null,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
@@ -2109,7 +2119,8 @@
"position": null,
"face": null,
"status": true,
- "primary_ip": null,
+ "primary_ip4": null,
+ "primary_ip6": null,
"comments": ""
}
},
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
index 8a00086966b..89292717b4e 100644
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -340,7 +340,7 @@ class DeviceForm(forms.ModelForm, BootstrapMixin):
disabled_indicator='device'))
manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(),
widget=forms.Select(attrs={'filter-for': 'device_type'}))
- device_type = forms.ModelChoiceField(queryset=DeviceType.objects.all(), label='Model', widget=APISelect(
+ device_type = forms.ModelChoiceField(queryset=DeviceType.objects.all(), label='Device type', widget=APISelect(
api_url='/api/dcim/device-types/?manufacturer_id={{manufacturer}}',
display_field='model'
))
@@ -349,7 +349,7 @@ class DeviceForm(forms.ModelForm, BootstrapMixin):
class Meta:
model = Device
fields = ['name', 'device_role', 'device_type', 'serial', 'site', 'rack', 'position', 'face', 'status',
- 'platform', 'primary_ip', 'comments']
+ 'platform', 'primary_ip4', 'primary_ip6', 'comments']
help_texts = {
'device_role': "The function this device serves",
'serial': "Chassis serial number",
@@ -369,20 +369,23 @@ def __init__(self, *args, **kwargs):
self.initial['site'] = self.instance.rack.site
self.initial['manufacturer'] = self.instance.device_type.manufacturer
- # Compile list of IPs assigned to this device
- primary_ip_choices = []
- interface_ips = IPAddress.objects.filter(interface__device=self.instance)
- primary_ip_choices += [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips]
- nat_ips = IPAddress.objects.filter(nat_inside__interface__device=self.instance)\
- .select_related('nat_inside__interface')
- primary_ip_choices += [(ip.id, '{} ({} NAT)'.format(ip.address, ip.nat_inside.interface)) for ip in nat_ips]
- self.fields['primary_ip'].choices = [(None, '---------')] + primary_ip_choices
+ # Compile list of choices for primary IPv4 and IPv6 addresses
+ for family in [4, 6]:
+ ip_choices = []
+ interface_ips = IPAddress.objects.filter(family=family, interface__device=self.instance)
+ ip_choices += [(ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips]
+ nat_ips = IPAddress.objects.filter(family=family, nat_inside__interface__device=self.instance)\
+ .select_related('nat_inside__interface')
+ ip_choices += [(ip.id, '{} ({} NAT)'.format(ip.address, ip.nat_inside.interface)) for ip in nat_ips]
+ self.fields['primary_ip{}'.format(family)].choices = [(None, '---------')] + ip_choices
else:
# An object that doesn't exist yet can't have any IPs assigned to it
- self.fields['primary_ip'].choices = []
- self.fields['primary_ip'].widget.attrs['readonly'] = True
+ self.fields['primary_ip4'].choices = []
+ self.fields['primary_ip4'].widget.attrs['readonly'] = True
+ self.fields['primary_ip6'].choices = []
+ self.fields['primary_ip6'].widget.attrs['readonly'] = True
# Limit rack choices
if self.is_bound:
diff --git a/netbox/dcim/migrations/0006_add_device_primary_ip4_ip6.py b/netbox/dcim/migrations/0006_add_device_primary_ip4_ip6.py
new file mode 100644
index 00000000000..670a174f97d
--- /dev/null
+++ b/netbox/dcim/migrations/0006_add_device_primary_ip4_ip6.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.7 on 2016-07-11 18:40
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ipam', '0001_initial'),
+ ('dcim', '0005_auto_20160706_1722'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='device',
+ name='primary_ip4',
+ field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.IPAddress', verbose_name=b'Primary IPv4'),
+ ),
+ migrations.AddField(
+ model_name='device',
+ name='primary_ip6',
+ field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.IPAddress', verbose_name=b'Primary IPv6'),
+ ),
+ ]
diff --git a/netbox/dcim/migrations/0007_device_copy_primary_ip.py b/netbox/dcim/migrations/0007_device_copy_primary_ip.py
new file mode 100644
index 00000000000..055eac7d07f
--- /dev/null
+++ b/netbox/dcim/migrations/0007_device_copy_primary_ip.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.7 on 2016-07-11 18:40
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+def copy_primary_ip(apps, schema_editor):
+ Device = apps.get_model('dcim', 'Device')
+ for d in Device.objects.select_related('primary_ip'):
+ if not d.primary_ip:
+ continue
+ if d.primary_ip.family == 4:
+ d.primary_ip4 = d.primary_ip
+ elif d.primary_ip.family == 6:
+ d.primary_ip6 = d.primary_ip
+ d.save()
+
+
+def restore_primary_ip(apps, schema_editor):
+ Device = apps.get_model('dcim', 'Device')
+ for d in Device.objects.select_related('primary_ip4', 'primary_ip6'):
+ if d.primary_ip:
+ continue
+ # Prefer IPv6 over IPv4
+ if d.primary_ip6:
+ d.primary_ip = d.primary_ip6
+ elif d.primary_ip4:
+ d.primary_ip = d.primary_ip4
+ d.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dcim', '0006_add_device_primary_ip4_ip6'),
+ ]
+
+ operations = [
+ migrations.RunPython(copy_primary_ip, restore_primary_ip),
+ ]
diff --git a/netbox/dcim/migrations/0008_device_remove_primary_ip.py b/netbox/dcim/migrations/0008_device_remove_primary_ip.py
new file mode 100644
index 00000000000..91465e878ec
--- /dev/null
+++ b/netbox/dcim/migrations/0008_device_remove_primary_ip.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.7 on 2016-07-11 19:01
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dcim', '0007_device_copy_primary_ip'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='device',
+ name='primary_ip',
+ ),
+ ]
diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py
index 2f1a62d3623..df07fa748e2 100644
--- a/netbox/dcim/models.py
+++ b/netbox/dcim/models.py
@@ -263,7 +263,7 @@ def units(self):
@property
def display_name(self):
if self.facility_id:
- return "{} ({})".format(self.name, self.facility_id)
+ return u"{} ({})".format(self.name, self.facility_id)
return self.name
def get_rack_units(self, face=RACK_FACE_FRONT, exclude=None, remove_redundant=False):
@@ -605,8 +605,10 @@ class Device(CreatedUpdatedModel):
help_text='Number of the lowest U position occupied by the device')
face = models.PositiveSmallIntegerField(blank=True, null=True, choices=RACK_FACE_CHOICES, verbose_name='Rack face')
status = models.BooleanField(choices=STATUS_CHOICES, default=STATUS_ACTIVE, verbose_name='Status')
- primary_ip = models.OneToOneField('ipam.IPAddress', related_name='primary_for', on_delete=models.SET_NULL,
- blank=True, null=True, verbose_name='Primary IP')
+ primary_ip4 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip4_for', on_delete=models.SET_NULL,
+ blank=True, null=True, verbose_name='Primary IPv4')
+ primary_ip6 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip6_for', on_delete=models.SET_NULL,
+ blank=True, null=True, verbose_name='Primary IPv6')
comments = models.TextField(blank=True)
class Meta:
@@ -696,9 +698,9 @@ def display_name(self):
if self.name:
return self.name
elif self.position:
- return "{} ({} U{})".format(self.device_type, self.rack.name, self.position)
+ return u"{} ({} U{})".format(self.device_type, self.rack.name, self.position)
else:
- return "{} ({})".format(self.device_type, self.rack.name)
+ return u"{} ({})".format(self.device_type, self.rack.name)
@property
def identifier(self):
@@ -709,6 +711,15 @@ def identifier(self):
return self.name
return '{{{}}}'.format(self.pk)
+ @property
+ def primary_ip(self):
+ if self.primary_ip6:
+ return self.primary_ip6
+ elif self.primary_ip4:
+ return self.primary_ip4
+ else:
+ return None
+
def get_children(self):
"""
Return the set of child Devices installed in DeviceBays within this Device.
diff --git a/netbox/dcim/tests/test_apis.py b/netbox/dcim/tests/test_apis.py
index 1f31fac14a7..b687644820f 100644
--- a/netbox/dcim/tests/test_apis.py
+++ b/netbox/dcim/tests/test_apis.py
@@ -318,6 +318,8 @@ class DeviceTest(APITestCase):
'parent_device',
'status',
'primary_ip',
+ 'primary_ip4',
+ 'primary_ip6',
'comments',
]
@@ -375,6 +377,10 @@ def test_get_list_flat(self, endpoint='/api/dcim/devices/?format=json_flat'):
'primary_ip_address',
'primary_ip_family',
'primary_ip_id',
+ 'primary_ip4_address',
+ 'primary_ip4_family',
+ 'primary_ip4_id',
+ 'primary_ip6',
'rack_display_name',
'rack_facility_id',
'rack_id',
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index 4f0d7ec29f5..e2b1d5ac914 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -501,7 +501,8 @@ class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
#
class DeviceListView(ObjectListView):
- queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'rack__site', 'primary_ip')
+ queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'rack__site', 'primary_ip4',
+ 'primary_ip6')
filter = filters.DeviceFilter
filter_form = forms.DeviceFilterForm
table = tables.DeviceTable
@@ -1634,7 +1635,10 @@ def ipaddress_assign(request, pk):
ipaddress.interface))
if form.cleaned_data['set_as_primary']:
- device.primary_ip = ipaddress
+ if ipaddress.family == 4:
+ device.primary_ip4 = ipaddress
+ elif ipaddress.family == 6:
+ device.primary_ip6 = ipaddress
device.save()
if '_addanother' in request.POST:
diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py
index 0c7a411cd73..eb7d39e1d25 100644
--- a/netbox/ipam/forms.py
+++ b/netbox/ipam/forms.py
@@ -329,7 +329,7 @@ def __init__(self, *args, **kwargs):
class IPAddressFromCSVForm(forms.ModelForm):
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd',
- error_messages={'invalid_choice': 'Site not found.'})
+ error_messages={'invalid_choice': 'VRF not found.'})
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
error_messages={'invalid_choice': 'Device not found.'})
interface_name = forms.CharField(required=False)
@@ -368,7 +368,10 @@ def save(self, commit=True):
name=self.cleaned_data['interface_name'])
# Set as primary for device
if self.cleaned_data['is_primary']:
- self.instance.primary_for = self.cleaned_data['device']
+ if self.instance.family == 4:
+ self.instance.primary_ip4_for = self.cleaned_data['device']
+ elif self.instance.family == 6:
+ self.instance.primary_ip6_for = self.cleaned_data['device']
return super(IPAddressFromCSVForm, self).save(commit=commit)
diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py
index 4dbfa801adb..402e023300f 100644
--- a/netbox/ipam/models.py
+++ b/netbox/ipam/models.py
@@ -314,12 +314,20 @@ def save(self, *args, **kwargs):
super(IPAddress, self).save(*args, **kwargs)
def to_csv(self):
+
+ # Determine if this IP is primary for a Device
+ is_primary = False
+ if self.family == 4 and getattr(self, 'primary_ip4_for', False):
+ is_primary = True
+ elif self.family == 6 and getattr(self, 'primary_ip6_for', False):
+ is_primary = True
+
return ','.join([
str(self.address),
self.vrf.rd if self.vrf else '',
self.device.identifier if self.device else '',
self.interface.name if self.interface else '',
- 'True' if getattr(self, 'primary_for', False) else '',
+ 'True' if is_primary else '',
self.description,
])
@@ -367,7 +375,7 @@ def to_csv(self):
@property
def display_name(self):
- return "{} ({})".format(self.vid, self.name)
+ return u"{} ({})".format(self.vid, self.name)
def get_status_class(self):
return STATUS_CHOICE_CLASSES[self.status]
diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py
index b230783fc98..1db9a62556a 100644
--- a/netbox/ipam/views.py
+++ b/netbox/ipam/views.py
@@ -364,7 +364,7 @@ def prefix_ipaddresses(request, pk):
# Find all IPAddresses belonging to this Prefix
ipaddresses = IPAddress.objects.filter(address__net_contained_or_equal=str(prefix.prefix))\
- .select_related('vrf', 'interface__device', 'primary_for')
+ .select_related('vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for')
ip_table = tables.IPAddressTable(ipaddresses)
ip_table.model = IPAddress
@@ -383,7 +383,7 @@ def prefix_ipaddresses(request, pk):
#
class IPAddressListView(ObjectListView):
- queryset = IPAddress.objects.select_related('vrf', 'interface__device', 'primary_for')
+ queryset = IPAddress.objects.select_related('vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for')
filter = filters.IPAddressFilter
filter_form = forms.IPAddressFilterForm
table = tables.IPAddressTable
@@ -443,9 +443,14 @@ def save_obj(self, obj):
obj.save()
# Update primary IP for device if needed
try:
- device = obj.primary_for
- device.primary_ip = obj
- device.save()
+ if obj.family == 4 and obj.primary_ip4_for:
+ device = obj.primary_ip4_for
+ device.primary_ip4 = obj
+ device.save()
+ elif obj.family == 6 and obj.primary_ip6_for:
+ device = obj.primary_ip6_for
+ device.primary_ip6 = obj
+ device.save()
except Device.DoesNotExist:
pass
diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py
index 96e60585985..aba0eb3f59a 100644
--- a/netbox/netbox/configuration.example.py
+++ b/netbox/netbox/configuration.example.py
@@ -73,3 +73,8 @@
SHORT_TIME_FORMAT = 'H:i:s'
DATETIME_FORMAT = 'N j, Y g:i a'
SHORT_DATETIME_FORMAT = 'Y-m-d H:i'
+
+# Optionally display a persistent banner at the top and/or bottom of every page. To display the same content in both
+# banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP.
+BANNER_TOP = ''
+BANNER_BOTTOM = ''
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
index b4e0c24bda3..a5322d9112e 100644
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -1,3 +1,4 @@
+import logging
import os
import socket
@@ -11,7 +12,7 @@
"the documentation.")
-VERSION = '1.1.0'
+VERSION = '1.2.0'
# Import local configuration
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
@@ -37,8 +38,39 @@
SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s')
DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a')
SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i')
+BANNER_TOP = getattr(configuration, 'BANNER_TOP', False)
+BANNER_BOTTOM = getattr(configuration, 'BANNER_BOTTOM', False)
CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
+# Attempt to import LDAP configuration if it has been defined
+LDAP_IGNORE_CERT_ERRORS = False
+try:
+ from ldap_config import *
+ LDAP_CONFIGURED = True
+except ImportError:
+ LDAP_CONFIGURED = False
+
+# LDAP configuration (optional)
+if LDAP_CONFIGURED:
+ try:
+ import ldap
+ import django_auth_ldap
+ # Prepend LDAPBackend to the default ModelBackend
+ AUTHENTICATION_BACKENDS = [
+ 'django_auth_ldap.backend.LDAPBackend',
+ 'django.contrib.auth.backends.ModelBackend',
+ ]
+ # Optionally disable strict certificate checking
+ if LDAP_IGNORE_CERT_ERRORS:
+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
+ # Enable logging for django_auth_ldap
+ logger = logging.getLogger('django_auth_ldap')
+ logger.addHandler(logging.StreamHandler())
+ logger.setLevel(logging.DEBUG)
+ except ImportError:
+ raise ImproperlyConfigured("LDAP authentication has been configured, but django-auth-ldap is not installed. "
+ "You can remove netbox/ldap.py to disable LDAP.")
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Database
diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py
index 39502e4d0b4..a7f90854400 100644
--- a/netbox/netbox/urls.py
+++ b/netbox/netbox/urls.py
@@ -2,7 +2,7 @@
from django.contrib import admin
from django.views.defaults import page_not_found
-from views import home, docs, trigger_500
+from views import home, trigger_500
from users.views import login, logout
@@ -30,10 +30,6 @@
url(r'^api/docs/', include('rest_framework_swagger.urls')),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
- # Dcoumentation
- url(r'^docs/$', docs, kwargs={'path': 'index'}, name='docs_root'),
- url(r'^docs/(?P[\w-]+)/$', docs, name='docs'),
-
# Error testing
url(r'^404/$', page_not_found),
url(r'^500/$', trigger_500),
diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py
index fa7e213123c..38988f6c7b1 100644
--- a/netbox/netbox/views.py
+++ b/netbox/netbox/views.py
@@ -45,25 +45,6 @@ def home(request):
})
-def docs(request, path):
- """
- Display a page of Markdown-formatted documentation.
- """
- filename = '{}/docs/{}.md'.format(settings.BASE_DIR.rsplit('/', 1)[0], path)
- try:
- with open(filename, 'r') as docfile:
- markup = docfile.read()
- except:
- raise Http404
-
- content = mark_safe(markdown(markup, extensions=['mdx_gfm', 'toc']))
-
- return render(request, 'docs.html', {
- 'content': content,
- 'path': path,
- })
-
-
def trigger_500(request):
"""Hot-wired method of triggering a server error to test reporting."""
diff --git a/netbox/project-static/css/base.css b/netbox/project-static/css/base.css
index 04bab2c63cb..b1a9e88c319 100644
--- a/netbox/project-static/css/base.css
+++ b/netbox/project-static/css/base.css
@@ -28,6 +28,42 @@ body {
footer p {
margin: 20px 0;
}
+@media (max-width: 1120px) {
+ .navbar-header {
+ float: none;
+ }
+ .navbar-left,.navbar-right {
+ float: none !important;
+ }
+ .navbar-toggle {
+ display: block;
+ }
+ .navbar-collapse {
+ border-top: 1px solid transparent;
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
+ }
+ .navbar-fixed-top {
+ top: 0;
+ border-width: 0 0 1px;
+ }
+ .navbar-collapse.collapse {
+ display: none!important;
+ }
+ .navbar-nav {
+ float: none!important;
+ margin-top: 7.5px;
+ }
+ .navbar-nav>li {
+ float: none;
+ }
+ .navbar-nav>li>a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
+ .collapse.in {
+ display:block !important;
+ }
+}
/* Forms */
label {
@@ -259,6 +295,9 @@ ul.rack_near_face li.empty:hover a {
.dark_gray:hover { background-color: #2c3e50; }
/* Misc */
+.banner-bottom {
+ margin-bottom: 50px;
+}
.panel table {
margin-bottom: 0;
}
diff --git a/netbox/secrets/api/views.py b/netbox/secrets/api/views.py
index 869739e3205..21a71e22fc7 100644
--- a/netbox/secrets/api/views.py
+++ b/netbox/secrets/api/views.py
@@ -52,7 +52,7 @@ def get(self, request, private_key=None):
queryset = self.filter_queryset(self.get_queryset())
# Attempt to decrypt each Secret if a private key was provided.
- if private_key is not None:
+ if private_key:
try:
uk = UserKey.objects.get(user=request.user)
except UserKey.DoesNotExist:
@@ -96,7 +96,7 @@ def get(self, request, pk, private_key=None):
secret = get_object_or_404(Secret, pk=pk)
# Attempt to decrypt the Secret if a private key was provided.
- if private_key is not None:
+ if private_key:
try:
uk = UserKey.objects.get(user=request.user)
except UserKey.DoesNotExist:
diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html
index bb6d8bd8ff6..a6ab34c26a9 100644
--- a/netbox/templates/_base.html
+++ b/netbox/templates/_base.html
@@ -224,6 +224,11 @@
+ {% if settings.BANNER_TOP %}
+
+ {{ settings.BANNER_TOP|safe }}
+
+ {% endif %}
{% if settings.MAINTENANCE_MODE %}
Maintenance Mode
@@ -239,20 +244,25 @@ Maintenance Mode
{% endfor %}
{% block content %}{% endblock %}
-
+
+ {% if settings.BANNER_BOTTOM %}
+
+ {{ settings.BANNER_BOTTOM|safe }}
+
+ {% endif %}