1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Implemented tags for all primary models

This commit is contained in:
Jeremy Stretch
2018-05-10 12:53:11 -04:00
parent b0dafcf50f
commit 9b3869790d
43 changed files with 262 additions and 34 deletions

View File

@ -1,13 +1,14 @@
from __future__ import unicode_literals
from rest_framework import serializers
from taggit.models import Tag
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
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer, WritableNestedSerializer
from utilities.api import ChoiceFieldSerializer, TagField, ValidatedModelSerializer, WritableNestedSerializer
#
@ -15,11 +16,12 @@ from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer, Writa
#
class ProviderSerializer(CustomFieldModelSerializer):
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Provider
fields = [
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
@ -60,12 +62,13 @@ class CircuitSerializer(CustomFieldModelSerializer):
status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES, required=False)
type = NestedCircuitTypeSerializer()
tenant = NestedTenantSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Circuit
fields = [
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
'comments', 'custom_fields', 'created', 'last_updated',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
from django import forms
from django.db.models import Count
from taggit.forms import TagField
from dcim.models import Site, Device, Interface, Rack
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
@ -22,10 +23,11 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
class ProviderForm(BootstrapMixin, CustomFieldForm):
slug = SlugField()
comments = CommentField()
tags = TagField(required=False)
class Meta:
model = Provider
fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags']
widgets = {
'noc_contact': SmallTextarea(attrs={'rows': 5}),
'admin_contact': SmallTextarea(attrs={'rows': 5}),
@ -102,12 +104,13 @@ class CircuitTypeCSVForm(forms.ModelForm):
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
comments = CommentField()
tags = TagField(required=False)
class Meta:
model = Circuit
fields = [
'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
'comments',
'comments', 'tags',
]
help_texts = {
'cid': "Unique circuit ID",

View File

@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from taggit.managers import TaggableManager
from dcim.constants import STATUS_CLASSES
from dcim.fields import ASNField
@ -56,6 +57,8 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
class Meta:
@ -166,6 +169,8 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = [
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
]

View File

@ -784,8 +784,8 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm):
class Meta:
model = Device
fields = [
'name', 'device_role', 'tags', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', 'face',
'status', 'platform', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments',
'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', 'face',
'status', 'platform', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments', 'tags',
]
help_texts = {
'device_role': "The function this device serves",

View File

@ -5,6 +5,7 @@ from collections import OrderedDict
from rest_framework import serializers
from rest_framework.reverse import reverse
from rest_framework.validators import UniqueTogetherValidator
from taggit.models import Tag
from dcim.api.serializers import NestedDeviceSerializer, InterfaceSerializer, NestedSiteSerializer
from dcim.models import Interface
@ -14,7 +15,9 @@ from ipam.constants import (
)
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ChoiceFieldSerializer, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer
from utilities.api import (
ChoiceFieldSerializer, SerializedPKRelatedField, TagField, ValidatedModelSerializer, WritableNestedSerializer,
)
from virtualization.api.serializers import NestedVirtualMachineSerializer
@ -24,12 +27,13 @@ from virtualization.api.serializers import NestedVirtualMachineSerializer
class VRFSerializer(CustomFieldModelSerializer):
tenant = NestedTenantSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = VRF
fields = [
'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'display_name', 'custom_fields', 'created',
'last_updated',
'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags', 'display_name', 'custom_fields',
'created', 'last_updated',
]
@ -85,11 +89,13 @@ class NestedRIRSerializer(WritableNestedSerializer):
class AggregateSerializer(CustomFieldModelSerializer):
rir = NestedRIRSerializer()
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Aggregate
fields = [
'id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields', 'created', 'last_updated',
'id', 'family', 'prefix', 'rir', 'date_added', 'description', 'tags', 'custom_fields', 'created',
'last_updated',
]
read_only_fields = ['family']
@ -147,11 +153,12 @@ class VLANSerializer(CustomFieldModelSerializer):
tenant = NestedTenantSerializer(required=False, allow_null=True)
status = ChoiceFieldSerializer(choices=VLAN_STATUS_CHOICES, required=False)
role = NestedRoleSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = VLAN
fields = [
'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'tags', 'display_name',
'custom_fields', 'created', 'last_updated',
]
validators = []
@ -190,12 +197,13 @@ class PrefixSerializer(CustomFieldModelSerializer):
vlan = NestedVLANSerializer(required=False, allow_null=True)
status = ChoiceFieldSerializer(choices=PREFIX_STATUS_CHOICES, required=False)
role = NestedRoleSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Prefix
fields = [
'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
'custom_fields', 'created', 'last_updated',
'tags', 'custom_fields', 'created', 'last_updated',
]
read_only_fields = ['family']
@ -252,12 +260,13 @@ class IPAddressSerializer(CustomFieldModelSerializer):
status = ChoiceFieldSerializer(choices=IPADDRESS_STATUS_CHOICES, required=False)
role = ChoiceFieldSerializer(choices=IPADDRESS_ROLE_CHOICES, required=False)
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = IPAddress
fields = [
'id', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'description', 'nat_inside',
'nat_outside', 'custom_fields', 'created', 'last_updated',
'nat_outside', 'tags', 'custom_fields', 'created', 'last_updated',
]
read_only_fields = ['family']

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
from django import forms
from django.core.exceptions import MultipleObjectsReturned
from django.db.models import Count
from taggit.forms import TagField
from dcim.models import Site, Rack, Device, Interface
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
@ -32,10 +33,11 @@ IPADDRESS_MASK_LENGTH_CHOICES = add_blank_choice([(i, i) for i in range(1, 129)]
#
class VRFForm(BootstrapMixin, TenancyForm, CustomFieldForm):
tags = TagField(required=False)
class Meta:
model = VRF
fields = ['name', 'rd', 'enforce_unique', 'description', 'tenant_group', 'tenant']
fields = ['name', 'rd', 'enforce_unique', 'description', 'tenant_group', 'tenant', 'tags']
labels = {
'rd': "RD",
}
@ -121,10 +123,11 @@ class RIRFilterForm(BootstrapMixin, forms.Form):
#
class AggregateForm(BootstrapMixin, CustomFieldForm):
tags = TagField(required=False)
class Meta:
model = Aggregate
fields = ['prefix', 'rir', 'date_added', 'description']
fields = ['prefix', 'rir', 'date_added', 'description', 'tags']
help_texts = {
'prefix': "IPv4 or IPv6 network",
'rir': "Regional Internet Registry responsible for this prefix",
@ -228,10 +231,14 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
api_url='/api/ipam/vlans/?site_id={{site}}&group_id={{vlan_group}}', display_field='display_name'
)
)
tags = TagField(required=False)
class Meta:
model = Prefix
fields = ['prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant']
fields = [
'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant',
'tags',
]
def __init__(self, *args, **kwargs):
@ -455,12 +462,13 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
)
)
primary_for_parent = forms.BooleanField(required=False, label='Make this the primary IP for the device/VM')
tags = TagField(required=False)
class Meta:
model = IPAddress
fields = [
'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_parent', 'nat_site',
'nat_rack', 'nat_inside', 'tenant_group', 'tenant',
'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags',
]
def __init__(self, *args, **kwargs):
@ -780,10 +788,11 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm):
api_url='/api/ipam/vlan-groups/?site_id={{site}}',
)
)
tags = TagField(required=False)
class Meta:
model = VLAN
fields = ['site', 'group', 'vid', 'name', 'status', 'role', 'description', 'tenant_group', 'tenant']
fields = ['site', 'group', 'vid', 'name', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags']
help_texts = {
'site': "Leave blank if this VLAN spans multiple sites",
'group': "VLAN group (optional)",

View File

@ -10,6 +10,7 @@ from django.db.models import Q
from django.db.models.expressions import RawSQL
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from taggit.managers import TaggableManager
from dcim.models import Interface
from extras.models import CustomFieldModel
@ -56,6 +57,8 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
class Meta:
@ -155,6 +158,8 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['prefix', 'rir', 'date_added', 'description']
class Meta:
@ -325,6 +330,7 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
)
objects = PrefixQuerySet.as_manager()
tags = TaggableManager()
csv_headers = [
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description',
@ -564,6 +570,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
)
objects = IPAddressManager()
tags = TaggableManager()
csv_headers = [
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary',
@ -759,6 +766,8 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
class Meta:

View File

@ -2,10 +2,11 @@ from __future__ import unicode_literals
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from taggit.models import Tag
from dcim.api.serializers import NestedDeviceSerializer
from secrets.models import Secret, SecretRole
from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
from utilities.api import TagField, ValidatedModelSerializer, WritableNestedSerializer
#
@ -35,10 +36,11 @@ class SecretSerializer(ValidatedModelSerializer):
device = NestedDeviceSerializer()
role = NestedSecretRoleSerializer()
plaintext = serializers.CharField()
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Secret
fields = ['id', 'device', 'role', 'name', 'plaintext', 'hash', 'created', 'last_updated']
fields = ['id', 'device', 'role', 'name', 'plaintext', 'hash', 'tags', 'created', 'last_updated']
validators = []
def validate(self, data):

View File

@ -4,6 +4,7 @@ from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from django import forms
from django.db.models import Count
from taggit.forms import TagField
from dcim.models import Device
from utilities.forms import BootstrapMixin, BulkEditForm, FilterChoiceField, FlexibleModelChoiceField, SlugField
@ -70,10 +71,11 @@ class SecretForm(BootstrapMixin, forms.ModelForm):
label='Plaintext (verify)',
widget=forms.PasswordInput()
)
tags = TagField(required=False)
class Meta:
model = Secret
fields = ['role', 'name', 'plaintext', 'plaintext2']
fields = ['role', 'name', 'plaintext', 'plaintext2', 'tags']
def __init__(self, *args, **kwargs):

View File

@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.encoding import force_bytes, python_2_unicode_compatible
from taggit.managers import TaggableManager
from utilities.models import CreatedUpdatedModel
from .exceptions import InvalidKey
@ -336,6 +337,8 @@ class Secret(CreatedUpdatedModel):
editable=False
)
tags = TaggableManager()
plaintext = None
csv_headers = ['device', 'role', 'name', 'plaintext']

View File

@ -110,6 +110,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ circuit.tags.all|join:" " }}</td>
</tr>
</table>
</div>
{% with circuit.get_custom_fields as custom_fields %}

View File

@ -44,6 +44,12 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}
{% block javascript %}

View File

@ -102,6 +102,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ provider.tags.all|join:" " }}</td>
</tr>
<tr>
<td>Circuits</td>
<td>

View File

@ -33,4 +33,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -84,4 +84,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -12,7 +12,6 @@
{% render_field form.u_height %}
{% render_field form.is_full_depth %}
{% render_field form.interface_ordering %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
@ -38,4 +37,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -11,7 +11,6 @@
{% render_field form.group %}
{% render_field form.role %}
{% render_field form.serial %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
@ -44,4 +43,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -13,7 +13,6 @@
{% render_field form.asn %}
{% render_field form.time_zone %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
@ -47,4 +46,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -81,6 +81,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ aggregate.tags.all|join:" " }}</td>
</tr>
</table>
</div>
</div>

View File

@ -19,4 +19,10 @@
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -133,6 +133,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ ipaddress.tags.all|join:" " }}</td>
</tr>
</table>
</div>
{% with ipaddress.get_custom_fields as custom_fields %}

View File

@ -66,6 +66,12 @@
{% render_field form.nat_inside %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>

View File

@ -121,6 +121,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ prefix.tags.all|join:" " }}</td>
</tr>
<tr>
<td>Utilization</td>
<td>{% utilization_graph prefix.get_utilization %}</td>

View File

@ -28,6 +28,12 @@
{% render_field form.tenant %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>

View File

@ -80,6 +80,10 @@
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ vlan.tags.all|join:" " }}</td>
</tr>
</table>
</div>

View File

@ -21,6 +21,12 @@
{% render_field form.tenant %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>

View File

@ -77,6 +77,10 @@
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ vrf.tags.all|join:" " }}</td>
</tr>
</table>
</div>

View File

@ -18,6 +18,12 @@
{% render_field form.tenant %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>

View File

@ -55,6 +55,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ secret.tags.all|join:" " }}</td>
</tr>
</table>
</div>
</div>

View File

@ -54,6 +54,12 @@
{% render_field form.plaintext2 %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
</div>
</div>
<div class="row">

View File

@ -68,6 +68,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ tenant.tags.all|join:" " }}</td>
</tr>
</table>
</div>
{% with tenant.get_custom_fields as custom_fields %}

View File

@ -26,4 +26,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -76,6 +76,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ cluster.tags.all|join:" " }}</td>
</tr>
<tr>
<td>Virtual Machines</td>
<td><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ cluster.pk }}">{{ cluster.virtual_machines.count }}</a></td>

View File

@ -0,0 +1,34 @@
{% extends 'utilities/obj_edit.html' %}
{% load form_helpers %}
{% block form %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Cluster</strong></div>
<div class="panel-body">
{% render_field form.name %}
{% render_field form.type %}
{% render_field form.group %}
{% render_field form.site %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Comments</strong></div>
<div class="panel-body">
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -121,6 +121,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ vm.tags.all|join:" " }}</td>
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with custom_fields=vm.get_custom_fields %}

View File

@ -54,4 +54,10 @@
{% render_field form.comments %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Tags</strong></div>
<div class="panel-body">
{% render_field form.tags %}
</div>
</div>
{% endblock %}

View File

@ -1,10 +1,11 @@
from __future__ import unicode_literals
from rest_framework import serializers
from taggit.models import Tag
from extras.api.customfields import CustomFieldModelSerializer
from tenancy.models import Tenant, TenantGroup
from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
from utilities.api import TagField, ValidatedModelSerializer, WritableNestedSerializer
#
@ -32,10 +33,14 @@ class NestedTenantGroupSerializer(WritableNestedSerializer):
class TenantSerializer(CustomFieldModelSerializer):
group = NestedTenantGroupSerializer(required=False)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Tenant
fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_fields', 'created', 'last_updated']
fields = [
'id', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
class NestedTenantSerializer(WritableNestedSerializer):

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
from django import forms
from django.db.models import Count
from taggit.forms import TagField
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
from utilities.forms import (
@ -40,10 +41,11 @@ class TenantGroupCSVForm(forms.ModelForm):
class TenantForm(BootstrapMixin, CustomFieldForm):
slug = SlugField()
comments = CommentField()
tags = TagField(required=False)
class Meta:
model = Tenant
fields = ['name', 'slug', 'group', 'description', 'comments']
fields = ['name', 'slug', 'group', 'description', 'comments', 'tags']
class TenantCSVForm(forms.ModelForm):

View File

@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from taggit.managers import TaggableManager
from extras.models import CustomFieldModel
from utilities.models import CreatedUpdatedModel
@ -74,6 +75,8 @@ class Tenant(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
class Meta:

View File

@ -1,14 +1,15 @@
from __future__ import unicode_literals
from rest_framework import serializers
from taggit.models import Tag
from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
from dcim.constants import IFACE_FF_VIRTUAL, IFACE_MODE_CHOICES
from dcim.constants import IFACE_MODE_CHOICES
from dcim.models import Interface
from extras.api.customfields import CustomFieldModelSerializer
from ipam.models import IPAddress, VLAN
from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer, WritableNestedSerializer
from utilities.api import ChoiceFieldSerializer, TagField, ValidatedModelSerializer, WritableNestedSerializer
from virtualization.constants import VM_STATUS_CHOICES
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -59,10 +60,13 @@ class ClusterSerializer(CustomFieldModelSerializer):
type = NestedClusterTypeSerializer()
group = NestedClusterGroupSerializer(required=False, allow_null=True)
site = NestedSiteSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = Cluster
fields = ['id', 'name', 'type', 'group', 'site', 'comments', 'custom_fields', 'created', 'last_updated']
fields = [
'id', 'name', 'type', 'group', 'site', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
class NestedClusterSerializer(WritableNestedSerializer):
@ -95,12 +99,13 @@ class VirtualMachineSerializer(CustomFieldModelSerializer):
primary_ip = VirtualMachineIPAddressSerializer(read_only=True)
primary_ip4 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
primary_ip6 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
class Meta:
model = VirtualMachine
fields = [
'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6',
'vcpus', 'memory', 'disk', 'comments', 'custom_fields', 'created', 'last_updated',
'vcpus', 'memory', 'disk', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]

View File

@ -4,6 +4,7 @@ from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Count
from mptt.forms import TreeNodeChoiceField
from taggit.forms import TagField
from dcim.constants import IFACE_FF_VIRTUAL, IFACE_MODE_ACCESS, IFACE_MODE_TAGGED_ALL
from dcim.forms import INTERFACE_MODE_HELP_TEXT
@ -78,10 +79,11 @@ class ClusterGroupCSVForm(forms.ModelForm):
class ClusterForm(BootstrapMixin, CustomFieldForm):
comments = CommentField(widget=SmallTextarea)
tags = TagField(required=False)
class Meta:
model = Cluster
fields = ['name', 'type', 'group', 'site', 'comments']
fields = ['name', 'type', 'group', 'site', 'comments', 'tags']
class ClusterCSVForm(forms.ModelForm):
@ -244,12 +246,13 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
api_url='/api/virtualization/clusters/?group_id={{cluster_group}}'
)
)
tags = TagField(required=False)
class Meta:
model = VirtualMachine
fields = [
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
'vcpus', 'memory', 'disk', 'comments',
'vcpus', 'memory', 'disk', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):

View File

@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from taggit.managers import TaggableManager
from dcim.models import Device
from extras.models import CustomFieldModel
@ -124,6 +125,8 @@ class Cluster(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = ['name', 'type', 'group', 'site', 'comments']
class Meta:
@ -242,6 +245,8 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
csv_headers = [
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
]

View File

@ -126,6 +126,7 @@ class ClusterView(View):
class ClusterCreateView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'virtualization.add_cluster'
template_name = 'virtualization/cluster_edit.html'
model = Cluster
model_form = forms.ClusterForm