Skip to content

Commit

Permalink
Merge pull request #3980 from netbox-community/develop
Browse files Browse the repository at this point in the history
Release v2.7.2
  • Loading branch information
jeremystretch committed Jan 21, 2020
2 parents 295d4f0 + 2581a55 commit e13d4ff
Show file tree
Hide file tree
Showing 24 changed files with 849 additions and 124 deletions.
58 changes: 58 additions & 0 deletions docs/core-functionality/power.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Power Panel

A power panel represents the distribution board where power circuits – and their circuit breakers – terminate on. If you have multiple power panels in your data center, you should model them as such in NetBox to assist you in determining the redundancy of your power allocation.

# Power Feed

A power feed identifies the power outlet/drop that goes to a rack and is terminated to a power panel. Power feeds have a supply type (AC/DC), voltage, amperage, and phase type (single/three).

Power feeds are optionally assigned to a rack. In addition, a power port – and only one – can connect to a power feed; in the context of a PDU, the power feed is analogous to the power outlet that a PDU's power port/inlet connects to.

!!! info
The power usage of a rack is calculated when a power feed (or multiple) is assigned to that rack and connected to a power port.

# Power Outlet

Power outlets represent the ports on a PDU that supply power to other devices. Power outlets are downstream-facing towards power ports. A power outlet can be associated with a power port on the same device and a feed leg (i.e. in a case of a three-phase supply). This indicates which power port supplies power to a power outlet.

# Power Port

A power port is the inlet of a device where it draws its power. Power ports are upstream-facing towards power outlets. Alternatively, a power port can connect to a power feed – as mentioned in the power feed section – to indicate the power source of a PDU's inlet.

!!! info
If the draw of a power port is left empty, it will be dynamically calculated based on the power outlets associated with that power port. This is usually the case on the power ports of devices that supply power, like a PDU.


# Example

Below is a simple diagram demonstrating how power is modelled in NetBox.

!!! note
The power feeds are connected to the same power panel for illustrative purposes; usually, you would have such feeds diversely connected to panels to avoid the single point of failure.

```
+---------------+
| Power panel 1 |
+---------------+
| |
| |
+--------------+ +--------------+
| Power feed 1 | | Power feed 2 |
+--------------+ +--------------+
| |
| |
| | <-- Power ports
+---------+ +---------+
| PDU 1 | | PDU 2 |
+---------+ +---------+
| \ / | <-- Power outlets
| \ / |
| \ / |
| X |
| / \ |
| / \ |
| / \ | <-- Power ports
+--------+ +--------+
| Server | | Router |
+--------+ +--------+
```
14 changes: 14 additions & 0 deletions docs/core-functionality/secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ Each user within NetBox can associate his or her account with an RSA public key.

User keys may be created by users individually, however they are of no use until they have been activated by a user who already possesses an active user key.

## Supported Key Format

Public key formats supported

- PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY)
- X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
- **OpenSSH line format is not supported.**

Private key formats supported (unencrypted)

- PKCS#1 RSAPrivateKey** (PEM header: BEGIN RSA PRIVATE KEY)
- PKCS#8 PrivateKeyInfo* (PEM header: BEGIN PRIVATE KEY)


## Creating the First User Key

When NetBox is first installed, it contains no encryption keys. Before it can store secrets, a user (typically the superuser) must create a user key. This can be done by navigating to Profile > User Key.
Expand Down
24 changes: 24 additions & 0 deletions docs/release-notes/version-2.7.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# v2.7.2 (2020-01-21)

## Enhancements

* [#3135](https://github.com/netbox-community/netbox/issues/3135) - Documented power modelling
* [#3842](https://github.com/netbox-community/netbox/issues/3842) - Add 802.11ax interface type
* [#3954](https://github.com/netbox-community/netbox/issues/3954) - Add `device_bays` filter for devices and device types

## Bug Fixes

* [#3721](https://github.com/netbox-community/netbox/issues/3721) - Allow Unicode characters in tag slugs
* [#3923](https://github.com/netbox-community/netbox/issues/3923) - Indicate validation failure when using SSH-style RSA keys
* [#3951](https://github.com/netbox-community/netbox/issues/3951) - Fix exception in webhook worker due to missing constant
* [#3953](https://github.com/netbox-community/netbox/issues/3953) - Fix validation error when creating child devices
* [#3960](https://github.com/netbox-community/netbox/issues/3960) - Fix legacy device status choice
* [#3962](https://github.com/netbox-community/netbox/issues/3962) - Fix display of unnamed devices in rack elevations
* [#3963](https://github.com/netbox-community/netbox/issues/3963) - Restore tooltip for devices in rack elevations
* [#3964](https://github.com/netbox-community/netbox/issues/3964) - Show borders around devices in rack elevations
* [#3965](https://github.com/netbox-community/netbox/issues/3965) - Indicate the presence of "background" devices in rack elevations
* [#3966](https://github.com/netbox-community/netbox/issues/3966) - Fix filtering of device components by region/site
* [#3967](https://github.com/netbox-community/netbox/issues/3967) - Resolve migration of "other" interface type

---

# v2.7.1 (2020-01-16)

## Bug Fixes
Expand Down
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pages:
- 4. LDAP (Optional): 'installation/4-ldap.md'
- Upgrading NetBox: 'installation/upgrading.md'
- Migrating to Python3: 'installation/migrating-to-python3.md'
- Migrating to systemd: 'installation/migrating-to-systemd.md'
- Configuration:
- Configuring NetBox: 'configuration/index.md'
- Required Settings: 'configuration/required-settings.md'
Expand All @@ -24,6 +25,7 @@ pages:
- Virtual Machines: 'core-functionality/virtual-machines.md'
- Services: 'core-functionality/services.md'
- Circuits: 'core-functionality/circuits.md'
- Power: 'core-functionality/power.md'
- Secrets: 'core-functionality/secrets.md'
- Tenancy: 'core-functionality/tenancy.md'
- Additional Features:
Expand Down
3 changes: 3 additions & 0 deletions netbox/dcim/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ class InterfaceTypeChoices(ChoiceSet):
TYPE_80211N = 'ieee802.11n'
TYPE_80211AC = 'ieee802.11ac'
TYPE_80211AD = 'ieee802.11ad'
TYPE_80211AX = 'ieee802.11ax'

# Cellular
TYPE_GSM = 'gsm'
Expand Down Expand Up @@ -650,6 +651,7 @@ class InterfaceTypeChoices(ChoiceSet):
(TYPE_80211N, 'IEEE 802.11n'),
(TYPE_80211AC, 'IEEE 802.11ac'),
(TYPE_80211AD, 'IEEE 802.11ad'),
(TYPE_80211AX, 'IEEE 802.11ax'),
)
),
(
Expand Down Expand Up @@ -800,6 +802,7 @@ class InterfaceTypeChoices(ChoiceSet):
TYPE_SUMMITSTACK128: 5310,
TYPE_SUMMITSTACK256: 5320,
TYPE_SUMMITSTACK512: 5330,
TYPE_OTHER: 32767,
}


Expand Down
63 changes: 24 additions & 39 deletions netbox/dcim/filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import django_filters
from django.contrib.auth.models import User
from django.db.models import Q

from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
Expand Down Expand Up @@ -356,6 +355,10 @@ class DeviceTypeFilterSet(CustomFieldFilterSet, CreatedUpdatedFilterSet):
method='_pass_through_ports',
label='Has pass-through ports',
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
label='Has device bays',
)
tag = TagFilter()

class Meta:
Expand Down Expand Up @@ -395,6 +398,9 @@ def _pass_through_ports(self, queryset, name, value):
rearport_templates__isnull=value
)

def _device_bays(self, queryset, name, value):
return queryset.exclude(device_bay_templates__isnull=value)


class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
devicetype_id = django_filters.ModelMultipleChoiceFilter(
Expand Down Expand Up @@ -623,6 +629,10 @@ class DeviceFilterSet(LocalConfigContextFilterSet, TenancyFilterSet, CustomField
method='_pass_through_ports',
label='Has pass-through ports',
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
label='Has device bays',
)
tag = TagFilter()

class Meta:
Expand Down Expand Up @@ -676,21 +686,25 @@ def _pass_through_ports(self, queryset, name, value):
rearports__isnull=value
)

def _device_bays(self, queryset, name, value):
return queryset.exclude(device_bays__isnull=value)


class DeviceComponentFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__region',
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region__in',
label='Region (ID)',
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__region__in',
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
label='Region name (slug)',
field_name='device__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
Expand All @@ -700,6 +714,7 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
)
device_id = django_filters.ModelMultipleChoiceFilter(
Expand Down Expand Up @@ -787,35 +802,13 @@ class Meta:
fields = ['id', 'name', 'feed_leg', 'description', 'connection_status']


class InterfaceFilterSet(django_filters.FilterSet):
"""
Not using DeviceComponentFilterSet for Interfaces because we need to check for VirtualChassis membership.
"""
class InterfaceFilterSet(DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__region',
queryset=Region.objects.all(),
label='Region (ID)',
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__region__in',
queryset=Region.objects.all(),
label='Region name (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
to_field_name='slug',
queryset=Site.objects.all(),
label='Site name (slug)',
)
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
# members
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
Expand Down Expand Up @@ -859,14 +852,6 @@ class Meta:
model = Interface
fields = ['id', 'name', 'connection_status', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'description']

def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value)
).distinct()

def filter_device(self, queryset, name, value):
try:
devices = Device.objects.filter(**{'{}__in'.format(name): value})
Expand Down
22 changes: 13 additions & 9 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,25 @@ class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
required=False,
label='Search'
)
region = TreeNodeChoiceField(
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelect(
api_url="/api/dcim/regions/"
widget=APISelectMultiple(
api_url='/api/dcim/regions/',
value_field='slug',
filter_for={
'site': 'region'
}
)
)
site = forms.ModelChoiceField(
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
required=False,
help_text='Name of parent site',
error_messages={
'invalid_choice': 'Site not found.',
}
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug"
)
)


Expand Down
20 changes: 20 additions & 0 deletions netbox/dcim/migrations/0091_interface_type_other.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import migrations


def interface_type_to_slug(apps, schema_editor):
Interface = apps.get_model('dcim', 'Interface')
Interface.objects.filter(type=32767).update(type='other')


class Migration(migrations.Migration):

dependencies = [
('dcim', '0090_cable_termination_models'),
]

operations = [
# Missed type "other" in the initial migration (see #3967)
migrations.RunPython(
code=interface_type_to_slug
),
]
Loading

0 comments on commit e13d4ff

Please sign in to comment.