Skip to content

Commit

Permalink
Feature/upstream v2.3.0 (#5)
Browse files Browse the repository at this point in the history
* Closes netbox-community#1553: Introduced support for bulk object creation via the API

* Removed prefix `parent` filter (see netbox-community#1684)

* Restored search method on prefix filter

* Extended prefix 'available-ips' endpoint to accept multiple objects (related to netbox-community#1553)

* Standardize on JSON data format for all POST/PUT test client requests

* netbox-community#1694: Initial work on "next available" prefix provisioning

* Closes netbox-community#1706: Added deprecation warning for Python 2

* Merge branch '150-interface-vlans' into develop-2.3

* Fixed up validation of Interface VLAN assignments

* Fixes netbox-community#1645: Simplified interface serialzier for IP addresses and optimized API view queryset

* Cleaned up bulk IP provisioning a bit

* Add Tenancy to Rack Reservations; Fixes netbox-community#1592 (netbox-community#1672)

* fixed prefix header to represent new serial "vlan_vid"

* shows option in creation now

* fixed visibility on rack page

* cleanup

* Added view to Tenant page

* Moved migration for update from netbox-community#1666 and fixed tenant enumeration in FilterForm

* Fixed conflict #1

* Fixed filters from merge and made migration merge

* added tenant to api

* Fixed migrations problem

* Added Tenant to bulkedit option

* Resolved migration collision from netbox-community#1672

* A bit of cosmetic cleanup from netbox-community#1672

* Added nested representations of user and tenant to the rack reservation serializer

* Fixed version number

* Initial work on virtual chassis support

* Moved VC master designation to membership model

* Added initial UI views for virtual chassis assignment

* Fixes netbox-community#1727: Added missing import for M2M_FIELD_TYPES

* Added a form to edit virtual chassis

* Added views for editing/deleting VCMemberships

* Display member interfaces when viewing VC master device

* Added virtual_chassis_id API filter for interfaces

* Obsoleted ComponentEditView and ComponentDeleteView

* Added virtual chassis tests

* Allow designating primary IPs assigned to a device's peer VC members

* Return all VC member interfaces when filtering for the master device; remove virtual_chassis_id filter

* Ignore VC member interfaces where mgmt_only=True

* PEP8 fixes

* Closes netbox-community#1744: Allow associating a platform with a specific manufacturer

* Closes netbox-community#1283: Added a time zone field to the site model

* Fixes netbox-community#1136: Enforce model validation during bulk update

* Closes netbox-community#1321: Added created and last_updated fields for relevant models to their API serializers

* Cleaned up component tables and checkbox toggling

* More table cleanup

* Converted remaining legacy views to class-based views for the DCIM app

* Added bulk creation API tests

* Fix bulk creation of VCMemberships via API

* Fix bulk creation of Secrets via API

* Closes netbox-community#1781: Enable bulk renaming of device components

* Implemented a view for adding individual devices to an existing virtual chassis

* Completed virtual chassis API serializers

* Reflect virtual chassis membership in display_name

* Extend IP address device filter to match virtual chassis members

* Added VirtualChassis to the docs

* Allow interface mode to be null (for routed interfaces)

* Highlight renamed components and allow for multiple previews

* Renamed device status constants for clarity

* Closes netbox-community#1758: Added 'status' field to Site model

* Closes netbox-community#1821: Added 'description' field to Site model

* Removed support for NAPALM 1.x

* Fixes netbox-community#1847: Fix RecursionError when VC master device is unnamed

* Fixes netbox-community#1848: Allow null value for interface encapsulation mode

* netbox-community#1843: Allow assignment of VC member interfaces to VC master LAG

* Fixes netbox-community#1838: Fix KeyError when attempting to create a VirtualChassis with no devicesselected

* Collapsed VCMembership into the Device model (WIP)

* Added virtual chassis member add view

* Added virtual chassis member remove view

* Cleaned up API for virtual chassis

* Added virtual chassis tests

* Additional validation cleanup

* Allow assignment of services to IPs on any VC member

* Closes netbox-community#1864: Added a 'status' field to the circuit model

* Fixes netbox-community#1867: Allow filtering on device status with multiple values

* Upgraded jquery to v3.3.1

* Fix for bulk interface edit form 802.1Q settings (netbox-community#1882)

* fixes netbox-community#1881 - bulk interface 802.1Q settings form

* fix PEP8 newline

* PEP8 fixup

* Fixes netbox-community#1884: Provide additional context to identify devices when creating/editing avirtual chassis

* VirtualChassis form validation cleanup

* Fixed typo in template

* Exclude devices already assigned to a VC from the list of potential VC members

* Finished VirtualChassis list view

* Post-release version bump

* Initial model for Packages model

* Add package_list

* Add migrations

* Adding package add form

* Import CSV Packages feature

* Adding filters

* Working bulkedit functionality for packages

* Require a package for a customer circuit

* Add edit and delete views for packages

* Add API support

* Expose package detail via the API endpoint

* Formatting correction

* Corrected order of arguments on DeviceVCMembershipForm

* Closes netbox-community#1899: Prefer binary package of psycopg2

* Updated requirements list

* Fixes netbox-community#1907: Allow removing an IP as the primary for a device when editing the IP directly

* Release v2.3.0

* NETOPS-704 Rename Tenant Group to Service Providers and Tenants to Customers

* NETOPS-704 Some small detail names fixed

* Fixes
  • Loading branch information
funzoneq authored Mar 8, 2018
1 parent 2b5c6dc commit 0adb3d7
Show file tree
Hide file tree
Showing 92 changed files with 4,654 additions and 1,146 deletions.
8 changes: 8 additions & 0 deletions docs/data-model/dcim.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,11 @@ Console ports connect only to console server ports, and power ports connect only
Each interface is a assigned a form factor denoting its physical properties. Two special form factors exist: the "virtual" form factor can be used to designate logical interfaces (such as SVIs), and the "LAG" form factor can be used to desinate link aggregation groups to which physical interfaces can be assigned. Each interface can also be designated as management-only (for out-of-band management) and assigned a short description.

Device bays represent the ability of a device to house child devices. For example, you might install four blade servers into a 2U chassis. The chassis would appear in the rack elevation as a 2U device with four device bays. Each server within it would be defined as a 0U device installed in one of the device bays. Child devices do not appear on rack elevations, but they are included in the "Non-Racked Devices" list within the rack view.

---

# Virtual Chassis

A virtual chassis represents a set of devices which share a single control plane: for example, a stack of switches which are managed as a single device. Each device in the virtual chassis is assigned a position and (optionally) a priority. Exactly one device is designated the virtual chassis master: This device will typically be assigned a name, secrets, services, and other attributes related to its management.

It's important to recognize the distinction between a virtual chassis and a chassis-based device. For instance, a virtual chassis is not used to model a chassis switch with removable line cards such as the Juniper EX9208, as its line cards are _not_ physically separate devices capable of operating independently.
4 changes: 1 addition & 3 deletions docs/installation/netbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,7 @@ Checking connectivity... done.
!!! warning
Ensure that the media directory (`/opt/netbox/netbox/media/` in this example) and all its subdirectories are writable by the user account as which NetBox runs. If the NetBox process does not have permission to write to this directory, attempts to upload files (e.g. image attachments) will fail. (The appropriate user account will vary by platform.)

```
# chown -R netbox:netbox /opt/netbox/netbox/media/
```
`# chown -R netbox:netbox /opt/netbox/netbox/media/`

## Install Python Packages

Expand Down
16 changes: 9 additions & 7 deletions netbox/circuits/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from rest_framework import serializers

from circuits.constants import CIRCUIT_STATUS_CHOICES
from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
from extras.api.customfields import CustomFieldModelSerializer
from tenancy.api.serializers import NestedTenantSerializer, NestedPackageSerializer
from utilities.api import ValidatedModelSerializer
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer


#
Expand All @@ -19,7 +20,7 @@ class Meta:
model = Provider
fields = [
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
'custom_fields',
'custom_fields', 'created', 'last_updated',
]


Expand All @@ -37,7 +38,7 @@ class Meta:
model = Provider
fields = [
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
'custom_fields',
'custom_fields', 'created', 'last_updated',
]


Expand Down Expand Up @@ -66,15 +67,16 @@ class Meta:

class CircuitSerializer(CustomFieldModelSerializer):
provider = NestedProviderSerializer()
status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES)
type = NestedCircuitTypeSerializer()
tenant = NestedTenantSerializer()
package = NestedPackageSerializer()

class Meta:
model = Circuit
fields = [
'id', 'cid', 'provider', 'type', 'tenant', 'package', 'install_date', 'commit_rate', 'description', 'comments',
'custom_fields',
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'package', 'install_date', 'commit_rate', 'description',
'comments', 'custom_fields', 'created', 'last_updated',
]


Expand All @@ -91,8 +93,8 @@ class WritableCircuitSerializer(CustomFieldModelSerializer):
class Meta:
model = Circuit
fields = [
'id', 'cid', 'provider', 'type', 'tenant', 'package', 'install_date', 'commit_rate', 'description', 'comments',
'custom_fields',
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'package', 'install_date', 'commit_rate', 'description',
'comments', 'custom_fields', 'created', 'last_updated',
]


Expand Down
9 changes: 4 additions & 5 deletions netbox/circuits/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
from django.shortcuts import get_object_or_404
from rest_framework.decorators import detail_route
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from circuits import filters
from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
from extras.api.serializers import RenderedGraphSerializer
from extras.api.views import CustomFieldModelViewSet
from extras.models import Graph, GRAPH_TYPE_PROVIDER
from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
from utilities.api import FieldChoicesViewSet, ModelViewSet
from . import serializers


Expand All @@ -28,7 +27,7 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
# Providers
#

class ProviderViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
class ProviderViewSet(CustomFieldModelViewSet):
queryset = Provider.objects.all()
serializer_class = serializers.ProviderSerializer
write_serializer_class = serializers.WritableProviderSerializer
Expand Down Expand Up @@ -59,7 +58,7 @@ class CircuitTypeViewSet(ModelViewSet):
# Circuits
#

class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
class CircuitViewSet(CustomFieldModelViewSet):
queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
serializer_class = serializers.CircuitSerializer
write_serializer_class = serializers.WritableCircuitSerializer
Expand All @@ -70,7 +69,7 @@ class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
# Circuit Terminations
#

class CircuitTerminationViewSet(WritableSerializerMixin, ModelViewSet):
class CircuitTerminationViewSet(ModelViewSet):
queryset = CircuitTermination.objects.select_related('circuit', 'site', 'interface__device')
serializer_class = serializers.CircuitTerminationSerializer
write_serializer_class = serializers.WritableCircuitTerminationSerializer
Expand Down
16 changes: 16 additions & 0 deletions netbox/circuits/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
from __future__ import unicode_literals


# Circuit statuses
CIRCUIT_STATUS_DEPROVISIONING = 0
CIRCUIT_STATUS_ACTIVE = 1
CIRCUIT_STATUS_PLANNED = 2
CIRCUIT_STATUS_PROVISIONING = 3
CIRCUIT_STATUS_OFFLINE = 4
CIRCUIT_STATUS_DECOMMISSIONED = 5
CIRCUIT_STATUS_CHOICES = [
[CIRCUIT_STATUS_PLANNED, 'Planned'],
[CIRCUIT_STATUS_PROVISIONING, 'Provisioning'],
[CIRCUIT_STATUS_ACTIVE, 'Active'],
[CIRCUIT_STATUS_OFFLINE, 'Offline'],
[CIRCUIT_STATUS_DEPROVISIONING, 'Deprovisioning'],
[CIRCUIT_STATUS_DECOMMISSIONED, 'Decommissioned'],
]

# CircuitTermination sides
TERM_SIDE_A = 'A'
TERM_SIDE_Y = 'Y'
Expand Down
5 changes: 5 additions & 0 deletions netbox/circuits/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NumericInFilter
from .constants import CIRCUIT_STATUS_CHOICES
from .models import Provider, Circuit, CircuitTermination, CircuitType


Expand Down Expand Up @@ -77,6 +78,10 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Circuit type (slug)',
)
status = django_filters.MultipleChoiceFilter(
choices=CIRCUIT_STATUS_CHOICES,
null_value=None
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
Expand Down
25 changes: 21 additions & 4 deletions netbox/circuits/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from tenancy.forms import TenancyForm
from tenancy.models import Tenant
from utilities.forms import (
APISelect, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, FilterChoiceField,
SmallTextarea, SlugField,
APISelect, add_blank_choice, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField,
CSVChoiceField, FilterChoiceField, SmallTextarea, SlugField,
)
from .constants import CIRCUIT_STATUS_CHOICES
from .models import Circuit, CircuitTermination, CircuitType, Provider

#
Expand Down Expand Up @@ -104,7 +105,7 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
class Meta:
model = Circuit
fields = [
'cid', 'type', 'provider', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant', 'package',
'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant', 'package',
'comments',
]
help_texts = {
Expand Down Expand Up @@ -142,6 +143,11 @@ class CircuitCSVForm(forms.ModelForm):
'invalid_choice': 'Invalid circuit type.'
}
)
status = CSVChoiceField(
choices=CIRCUIT_STATUS_CHOICES,
required=False,
help_text='Operational status'
)
tenant = forms.ModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
Expand All @@ -154,13 +160,16 @@ class CircuitCSVForm(forms.ModelForm):

class Meta:
model = Circuit
fields = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments']
fields = [
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
]


class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput)
type = forms.ModelChoiceField(queryset=CircuitType.objects.all(), required=False)
provider = forms.ModelChoiceField(queryset=Provider.objects.all(), required=False)
status = forms.ChoiceField(choices=add_blank_choice(CIRCUIT_STATUS_CHOICES), required=False, initial='')
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
commit_rate = forms.IntegerField(required=False, label='Commit rate (Kbps)')
description = forms.CharField(max_length=100, required=False)
Expand All @@ -170,6 +179,13 @@ class Meta:
nullable_fields = ['tenant', 'commit_rate', 'description', 'comments']


def circuit_status_choices():
status_counts = {}
for status in Circuit.objects.values('status').annotate(count=Count('status')).order_by('status'):
status_counts[status['status']] = status['count']
return [(s[0], '{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in CIRCUIT_STATUS_CHOICES]


class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Circuit
q = forms.CharField(required=False, label='Search')
Expand All @@ -181,6 +197,7 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
queryset=Provider.objects.annotate(filter_count=Count('circuits')),
to_field_name='slug'
)
status = forms.MultipleChoiceField(choices=circuit_status_choices, required=False)
tenant = FilterChoiceField(
queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
to_field_name='slug',
Expand Down
20 changes: 20 additions & 0 deletions netbox/circuits/migrations/0010_circuit_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.9 on 2018-02-06 18:48
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('circuits', '0009_unicode_literals'),
]

operations = [
migrations.AddField(
model_name='circuit',
name='status',
field=models.PositiveSmallIntegerField(choices=[[2, 'Planned'], [3, 'Provisioning'], [1, 'Active'], [4, 'Offline'], [0, 'Deprovisioning'], [5, 'Decommissioned']], default=1),
),
]
16 changes: 16 additions & 0 deletions netbox/circuits/migrations/0011_merge_20180301_1214.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.10 on 2018-03-01 12:14
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('circuits', '0010_circuit_package'),
('circuits', '0010_circuit_status'),
]

operations = [
]
10 changes: 8 additions & 2 deletions netbox/circuits/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible

from dcim.constants import STATUS_CLASSES
from dcim.fields import ASNField
from extras.models import CustomFieldModel, CustomFieldValue
from tenancy.models import Tenant, Package
from utilities.models import CreatedUpdatedModel
from .constants import *
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES


@python_2_unicode_compatible
Expand Down Expand Up @@ -89,6 +90,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
cid = models.CharField(max_length=50, verbose_name='Circuit ID')
provider = models.ForeignKey('Provider', related_name='circuits', on_delete=models.PROTECT)
type = models.ForeignKey('CircuitType', related_name='circuits', on_delete=models.PROTECT)
status = models.PositiveSmallIntegerField(choices=CIRCUIT_STATUS_CHOICES, default=CIRCUIT_STATUS_ACTIVE)
tenant = models.ForeignKey(Tenant, related_name='circuits', blank=True, null=True, on_delete=models.PROTECT)
package = models.ForeignKey(Package, related_name='package', blank=True, null=True, on_delete=models.SET_NULL)
install_date = models.DateField(blank=True, null=True, verbose_name='Date installed')
Expand All @@ -97,7 +99,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')

csv_headers = ['cid', 'provider', 'type', 'tenant', 'package', 'install_date', 'commit_rate', 'description', 'comments']
csv_headers = ['cid', 'provider', 'type', 'status', 'tenant', 'package', 'install_date', 'commit_rate', 'description', 'comments']

class Meta:
ordering = ['provider', 'cid']
Expand All @@ -114,13 +116,17 @@ def to_csv(self):
self.cid,
self.provider.name,
self.type.name,
self.get_status_display(),
self.tenant.name if self.tenant else None,
self.install_date,
self.commit_rate,
self.description,
self.comments,
)

def get_status_class(self):
return STATUS_CLASSES[self.status]

def _get_termination(self, side):
for ct in self.terminations.all():
if ct.term_side == side:
Expand Down
7 changes: 6 additions & 1 deletion netbox/circuits/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
{% endif %}
"""

STATUS_LABEL = """
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
"""


class CircuitTerminationColumn(tables.Column):

Expand Down Expand Up @@ -76,10 +80,11 @@ class CircuitTable(BaseTable):
pk = ToggleColumn()
cid = tables.LinkColumn(verbose_name='ID')
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
tenant = tables.TemplateColumn(template_code=COL_TENANT)
termination_a = CircuitTerminationColumn(orderable=False, verbose_name='A Side')
termination_z = CircuitTerminationColumn(orderable=False, verbose_name='Z Side')

class Meta(BaseTable.Meta):
model = Circuit
fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'termination_a', 'termination_z', 'description')
fields = ('pk', 'cid', 'status', 'type', 'provider', 'tenant', 'termination_a', 'termination_z', 'description')
Loading

0 comments on commit 0adb3d7

Please sign in to comment.