mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-20 10:16:42 -06:00
commit
242cb7c7cb
@ -7,10 +7,18 @@ NetBox uses [PostgreSQL](https://www.postgresql.org/) for its database, so gener
|
||||
|
||||
## Export the Database
|
||||
|
||||
Use the `pg_dump` utility to export the entire database to a file:
|
||||
|
||||
```no-highlight
|
||||
pg_dump netbox > netbox.sql
|
||||
```
|
||||
|
||||
When replicating a production database for development purposes, you may find it convenient to exclude changelog data, which can easily account for the bulk of a database's size. To do this, exclude the `extras_objectchange` table data from the export. The table will still be included in the output file, but will not be populated with any data.
|
||||
|
||||
```no-highlight
|
||||
pg_dump --exclude-table-data=extras_objectchange netbox > netbox.sql
|
||||
```
|
||||
|
||||
## Load an Exported Database
|
||||
|
||||
!!! warning
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit b3a449437792668041d5cfb9cd6d025e1a5b3470
|
@ -123,7 +123,7 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
group = NestedRackGroupSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False)
|
||||
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
|
||||
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
@ -223,7 +223,7 @@ class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
manufacturer = NestedManufacturerSerializer()
|
||||
interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
|
||||
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False)
|
||||
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
|
||||
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
@ -396,7 +396,7 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||
site = NestedSiteSerializer()
|
||||
rack = NestedRackSerializer(required=False, allow_null=True)
|
||||
face = ChoiceField(choices=RACK_FACE_CHOICES, required=False)
|
||||
face = ChoiceField(choices=RACK_FACE_CHOICES, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=DEVICE_STATUS_CHOICES, required=False)
|
||||
primary_ip = DeviceIPAddressSerializer(read_only=True)
|
||||
primary_ip4 = DeviceIPAddressSerializer(required=False, allow_null=True)
|
||||
@ -576,7 +576,7 @@ class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||
interface_connection = serializers.SerializerMethodField(read_only=True)
|
||||
circuit_termination = InterfaceCircuitTerminationSerializer(read_only=True)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
||||
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
||||
tagged_vlans = SerializedPKRelatedField(
|
||||
queryset=VLAN.objects.all(),
|
||||
|
@ -7,7 +7,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.contrib.postgres.fields import ArrayField, JSONField
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Count, Q, ObjectDoesNotExist
|
||||
@ -1933,11 +1933,20 @@ class Interface(ComponentModel):
|
||||
"""
|
||||
Include the connected Interface (if any).
|
||||
"""
|
||||
|
||||
# It's possible that an Interface can be deleted _after_ its parent Device/VM, in which case trying to resolve
|
||||
# the component parent will raise DoesNotExist. For more discussion, see
|
||||
# https://github.com/digitalocean/netbox/issues/2323
|
||||
try:
|
||||
parent_obj = self.get_component_parent()
|
||||
except ObjectDoesNotExist:
|
||||
parent_obj = None
|
||||
|
||||
ObjectChange(
|
||||
user=user,
|
||||
request_id=request_id,
|
||||
changed_object=self,
|
||||
related_object=self.get_component_parent(),
|
||||
related_object=parent_obj,
|
||||
action=action,
|
||||
object_data=serialize_object(self, extra={
|
||||
'connected_interface': self.connected_interface.pk if self.connection else None,
|
||||
|
@ -1,9 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import OrderedDict
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
from collections import OrderedDict
|
||||
import sys
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
@ -23,10 +24,29 @@ def get_report(module_name, report_name):
|
||||
"""
|
||||
Return a specific report from within a module.
|
||||
"""
|
||||
module = importlib.import_module(module_name)
|
||||
file_path = '{}/{}.py'.format(settings.REPORTS_ROOT, module_name)
|
||||
|
||||
# Python 3.5+
|
||||
if sys.version_info >= (3, 5):
|
||||
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
try:
|
||||
spec.loader.exec_module(module)
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
# Python 2.7
|
||||
else:
|
||||
import imp
|
||||
try:
|
||||
module = imp.load_source(module_name, file_path)
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
report = getattr(module, report_name, None)
|
||||
if report is None:
|
||||
return None
|
||||
|
||||
return report()
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@ import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
|
||||
from extras.models import Webhook
|
||||
from extras.constants import OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE
|
||||
@ -18,23 +17,16 @@ def enqueue_webhooks(instance, action):
|
||||
if not settings.WEBHOOKS_ENABLED or instance._meta.model_name not in WEBHOOK_MODELS:
|
||||
return
|
||||
|
||||
type_create = action == OBJECTCHANGE_ACTION_CREATE
|
||||
type_update = action == OBJECTCHANGE_ACTION_UPDATE
|
||||
type_delete = action == OBJECTCHANGE_ACTION_DELETE
|
||||
|
||||
# Find assigned webhooks
|
||||
# Retrieve any applicable Webhooks
|
||||
action_flag = {
|
||||
OBJECTCHANGE_ACTION_CREATE: 'type_create',
|
||||
OBJECTCHANGE_ACTION_UPDATE: 'type_update',
|
||||
OBJECTCHANGE_ACTION_DELETE: 'type_delete',
|
||||
}[action]
|
||||
obj_type = ContentType.objects.get_for_model(instance.__class__)
|
||||
webhooks = Webhook.objects.filter(
|
||||
Q(enabled=True) &
|
||||
(
|
||||
Q(type_create=type_create) |
|
||||
Q(type_update=type_update) |
|
||||
Q(type_delete=type_delete)
|
||||
) &
|
||||
Q(obj_type=obj_type)
|
||||
)
|
||||
webhooks = Webhook.objects.filter(obj_type=obj_type, enabled=True, **{action_flag: True})
|
||||
|
||||
if webhooks:
|
||||
if webhooks.exists():
|
||||
# Get the Model's API serializer class and serialize the object
|
||||
serializer_class = get_serializer_for_model(instance.__class__)
|
||||
serializer_context = {
|
||||
|
@ -37,8 +37,12 @@ def process_webhook(webhook, data, model_class, event, timestamp):
|
||||
prepared_request = requests.Request(**params).prepare()
|
||||
|
||||
if webhook.secret != '':
|
||||
# sign the request with the secret
|
||||
hmac_prep = hmac.new(bytearray(webhook.secret, 'utf8'), prepared_request.body, digestmod=hashlib.sha512)
|
||||
# Sign the request with a hash of the secret key and its content.
|
||||
hmac_prep = hmac.new(
|
||||
key=webhook.secret.encode('utf8'),
|
||||
msg=prepared_request.body.encode('utf8'),
|
||||
digestmod=hashlib.sha512
|
||||
)
|
||||
prepared_request.headers['X-Hook-Signature'] = hmac_prep.hexdigest()
|
||||
|
||||
with requests.Session() as session:
|
||||
|
@ -258,7 +258,7 @@ class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
status = ChoiceField(choices=IPADDRESS_STATUS_CHOICES, required=False)
|
||||
role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False)
|
||||
role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False, allow_null=True)
|
||||
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
|
@ -22,7 +22,7 @@ if sys.version_info[0] < 3:
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
VERSION = '2.4.1'
|
||||
VERSION = '2.4.2'
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
0
netbox/reports/__init__.py
Normal file
0
netbox/reports/__init__.py
Normal file
@ -194,10 +194,13 @@
|
||||
</small>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% if forloop.last %}
|
||||
<div class="list-group-item text-right">
|
||||
<a href="{% url 'extras:objectchange_list' %}">View All Changes</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<div class="list-group-item">
|
||||
Welcome to NetBox! {% if perms.add_site %} <a href="{% url 'dcim:site_add' %}">Add a site</a> to get started.{% endif %}
|
||||
</div>
|
||||
<div class="list-group-item text-muted">No change history found</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,7 +40,7 @@
|
||||
{% include 'inc/created_updated.html' with obj=vrf %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
|
||||
<a href="{{ aggregate.get_absolute_url }}">VRF</a>
|
||||
<a href="{{ vrf.get_absolute_url }}">VRF</a>
|
||||
</li>
|
||||
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
|
||||
<a href="{% url 'ipam:vrf_changelog' pk=vrf.pk %}">Changelog</a>
|
||||
|
@ -74,6 +74,12 @@ class ChoiceField(Field):
|
||||
return {'value': obj, 'label': self._choices[obj]}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
# Hotwiring boolean values
|
||||
if hasattr(data, 'lower'):
|
||||
if data.lower() == 'true':
|
||||
return True
|
||||
if data.lower() == 'false':
|
||||
return False
|
||||
return data
|
||||
|
||||
|
||||
|
@ -146,7 +146,7 @@ class InterfaceVLANSerializer(WritableNestedSerializer):
|
||||
class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
virtual_machine = NestedVirtualMachineSerializer()
|
||||
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, default=IFACE_FF_VIRTUAL, required=False)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False)
|
||||
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
|
||||
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
||||
tagged_vlans = SerializedPKRelatedField(
|
||||
queryset=VLAN.objects.all(),
|
||||
|
Loading…
Reference in New Issue
Block a user