diff --git a/docs/api/examples.md b/docs/api/examples.md index 5082534bc..4ec2f0f33 100644 --- a/docs/api/examples.md +++ b/docs/api/examples.md @@ -4,7 +4,8 @@ Supported HTTP methods: * `GET`: Retrieve an object or list of objects * `POST`: Create a new object -* `PUT`: Update an existing object +* `PUT`: Update an existing object, all mandatory fields must be specified +* `PATCH`: Updates an existing object, only specifiying the field to be changed * `DELETE`: Delete an existing object To authenticate a request, attach your token in an `Authorization` header: @@ -104,12 +105,19 @@ $ curl -X POST -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0 ### Modify an existing site -Make an authenticated `PUT` request to the site detail endpoint. As with a create (POST) request, all mandatory fields must be included. +Make an authenticated `PUT` request to the site detail endpoint. As with a create (`POST`) request, all mandatory fields must be included. ``` $ curl -X PUT -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/sites/16/ --data '{"name": "Renamed Site", "slug": "renamed-site"}' ``` +### Modify an object by changing a field + +Make an authenticated `PATCH` request to the device endpoint. With `PATCH`, unlike `POST` and `PUT`, we only specify the field that is being changed. In this example, we add a serial number to a device. +``` +$ curl -X PATCH -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/devices/2549/ --data '{"serial": "FTX1123A090"}' +``` + ### Delete an existing site Send an authenticated `DELETE` request to the site detail endpoint. diff --git a/docs/api/overview.md b/docs/api/overview.md index bdf0a2f4c..39a4109f9 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -6,7 +6,7 @@ REST stands for [representational state transfer](https://en.wikipedia.org/wiki/ * `GET`: Retrieve an object or list of objects * `POST`: Create an object -* `PUT` / `PATCH`: Modify an existing object +* `PUT` / `PATCH`: Modify an existing object. `PUT` requires all mandatory fields to be specified, while `PATCH` only expects the field that is being modified to be specified. * `DELETE`: Delete an existing object The NetBox API represents all objects in [JavaScript Object Notation (JSON)](http://www.json.org/). This makes it very easy to interact with NetBox data on the command line with common tools. For example, we can request an IP address from NetBox and output the JSON using `curl` and `jq`. (Piping the output through `jq` isn't strictly required but makes it much easier to read.) diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index 22916d54c..9cc4e75fd 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -17,7 +17,7 @@ ADMINS = [ ## BANNER_BOTTOM -Setting these variables will display content in a banner at the top and/or bottom of the page, respectively. To replicate the content of the top banner in the bottom banner, set: +Setting these variables will display content in a banner at the top and/or bottom of the page, respectively. HTML is allowed. To replicate the content of the top banner in the bottom banner, set: ``` BANNER_TOP = 'Your banner text' @@ -26,6 +26,12 @@ BANNER_BOTTOM = BANNER_TOP --- +## BANNER_LOGIN + +The value of this variable will be displayed on the login page above the login form. HTML is allowed. + +--- + ## BASE_PATH Default: None diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md index b0928c1b5..d1aa5d001 100644 --- a/docs/installation/netbox.md +++ b/docs/installation/netbox.md @@ -20,7 +20,7 @@ Python 3: ```no-highlight # yum install -y epel-release -# yum install -y gcc python34 python34-devel python34-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel +# yum install -y gcc python34 python34-devel python34-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config # easy_install-3.4 pip # ln -s -f python3.4 /usr/bin/python ``` @@ -29,7 +29,7 @@ Python 2: ```no-highlight # yum install -y epel-release -# yum install -y gcc python2 python-devel python-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel +# yum install -y gcc python2 python-devel python-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config ``` You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub. diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index a0fa14764..3df486a1b 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -12,7 +12,7 @@ from ipam.models import IPAddress from tenancy.forms import TenancyForm from tenancy.models import Tenant from utilities.forms import ( - APISelect, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, + APISelect, ArrayFieldSelectMultiple, add_blank_choice, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, FlexibleModelChoiceField, Livesearch, SelectWithDisabled, SmallTextarea, SlugField, FilterTreeNodeMultipleChoiceField, @@ -28,12 +28,6 @@ from .models import ( ) -FORM_STATUS_CHOICES = [ - ['', '---------'], -] - -FORM_STATUS_CHOICES += STATUS_CHOICES - DEVICE_BY_PK_RE = '{\d+\}' @@ -863,7 +857,7 @@ class DeviceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): device_role = forms.ModelChoiceField(queryset=DeviceRole.objects.all(), required=False, label='Role') tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False) - status = forms.ChoiceField(choices=FORM_STATUS_CHOICES, required=False, initial='', label='Status') + status = forms.ChoiceField(choices=add_blank_choice(STATUS_CHOICES), required=False, initial='') serial = forms.CharField(max_length=50, required=False, label='Serial Number') class Meta: diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 9cf93cb4b..abff09c15 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -98,7 +98,7 @@ class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet): # Create the new IP address data = request.data.copy() data['address'] = '{}/{}'.format(ipaddress, prefix.prefix.prefixlen) - data['vrf'] = prefix.vrf + data['vrf'] = prefix.vrf.pk if prefix.vrf else None serializer = serializers.WritableIPAddressSerializer(data=data) if serializer.is_valid(): serializer.save() diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index e19376e8e..4152b44c1 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -25,11 +25,8 @@ IP_FAMILY_CHOICES = [ (6, 'IPv6'), ] -PREFIX_MASK_LENGTH_CHOICES = [ - ('', '---------'), -] + [(i, i) for i in range(1, 128)] - -IPADDRESS_MASK_LENGTH_CHOICES = PREFIX_MASK_LENGTH_CHOICES + [(128, 128)] +PREFIX_MASK_LENGTH_CHOICES = add_blank_choice([(i, i) for i in range(1, 128)]) +IPADDRESS_MASK_LENGTH_CHOICES = add_blank_choice([(i, i) for i in range(1, 129)]) # diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index bbd5e1827..214293b7d 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -512,6 +512,16 @@ class VLANGroup(models.Model): def get_absolute_url(self): return "{}?group_id={}".format(reverse('ipam:vlan_list'), self.pk) + def get_next_available_vid(self): + """ + Return the first available VLAN ID (1-4094) in the group. + """ + vids = [vlan['vid'] for vlan in self.vlans.order_by('vid').values('vid')] + for i in range(1, 4095): + if i not in vids: + return i + return None + @python_2_unicode_compatible class VLAN(CreatedUpdatedModel, CustomFieldModel): diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index 96127aec5..512b75649 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -120,6 +120,13 @@ VLAN_ROLE_LINK = """ """ VLANGROUP_ACTIONS = """ +{% with next_vid=record.get_next_available_vid %} + {% if next_vid and perms.ipam.add_vlan %} + + + + {% endif %} +{% endwith %} {% if perms.ipam.change_vlangroup %} {% endif %} diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index 192653dc4..ce7a62464 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -38,11 +38,14 @@ ADMINS = [ # ['John Doe', 'jdoe@example.com'], ] -# 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. +# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same +# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. BANNER_TOP = '' BANNER_BOTTOM = '' +# Text to include on the login page above the login form. HTML is allowed. +BANNER_LOGIN = '' + # Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: # BASE_PATH = 'netbox/' BASE_PATH = '' diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 02cb5953d..e9d8c04f5 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -29,8 +29,9 @@ for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: # Import optional configuration parameters ADMINS = getattr(configuration, 'ADMINS', []) -BANNER_BOTTOM = getattr(configuration, 'BANNER_BOTTOM', False) -BANNER_TOP = getattr(configuration, 'BANNER_TOP', False) +BANNER_BOTTOM = getattr(configuration, 'BANNER_BOTTOM', '') +BANNER_LOGIN = getattr(configuration, 'BANNER_LOGIN', '') +BANNER_TOP = getattr(configuration, 'BANNER_TOP', '') BASE_PATH = getattr(configuration, 'BASE_PATH', '') if BASE_PATH: BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only diff --git a/netbox/project-static/js/secrets.js b/netbox/project-static/js/secrets.js index 638638623..d1ec3d883 100644 --- a/netbox/project-static/js/secrets.js +++ b/netbox/project-static/js/secrets.js @@ -43,7 +43,7 @@ $(document).ready(function() { success: function (response, status) { if (response.plaintext) { console.log("Secret retrieved successfully"); - $('#secret_' + secret_id).html(response.plaintext); + $('#secret_' + secret_id).text(response.plaintext); $('button.unlock-secret[secret-id=' + secret_id + ']').hide(); $('button.lock-secret[secret-id=' + secret_id + ']').show(); } else { diff --git a/netbox/templates/login.html b/netbox/templates/login.html index 07b10bfcc..a34b934cc 100644 --- a/netbox/templates/login.html +++ b/netbox/templates/login.html @@ -2,8 +2,13 @@ {% load form_helpers %} {% block content %} -
+
+ {% if settings.BANNER_LOGIN %} +
+ {{ settings.BANNER_LOGIN|safe }} +
+ {% endif %} {% if form.non_field_errors %}
Errors