Introduce abstract ComponentModel for VM components

This commit is contained in:
Jeremy Stretch 2023-11-17 13:42:42 -05:00
parent 7eb6b2fc47
commit 9e0d6bf067
11 changed files with 155 additions and 175 deletions

View File

@ -15,9 +15,7 @@
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">
{% trans "Virtual Disk" %}
</h5>
<h5 class="card-header">{% trans "Virtual Disk" %}</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
@ -29,7 +27,7 @@
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row"><i class="mdi mdi-harddisk"></i> {% trans "Disk Space" %}</th>
<th scope="row"><i class="mdi mdi-harddisk"></i> {% trans "Size" %}</th>
<td>
{% if object.size %}
{{ object.size }} {% trans "GB" context "Abbreviation for gigabyte" %}
@ -38,6 +36,10 @@
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
</div>

View File

@ -173,5 +173,6 @@ class VirtualDiskSerializer(NetBoxModelSerializer):
class Meta:
model = VirtualDisk
fields = [
'id', 'url', 'virtual_machine', 'name', 'size', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields', 'created',
'last_updated',
]

View File

@ -321,7 +321,7 @@ class VirtualDiskFilterSet(NetBoxModelFilterSet):
class Meta:
model = VirtualDisk
fields = ['id', 'name', 'size']
fields = ['id', 'name', 'size', 'description']
def search(self, queryset, name, value):
if not value.strip():

View File

@ -34,7 +34,7 @@ class VMInterfaceBulkCreateForm(
class VirtualDiskBulkCreateForm(
form_from_model(VirtualDisk, ['size', 'tags']),
form_from_model(VirtualDisk, ['size', 'description', 'tags']),
VirtualMachineBulkAddComponentForm
):
replication_fields = ('name',)

View File

@ -331,12 +331,17 @@ class VirtualDiskBulkEditForm(NetBoxModelBulkEditForm):
required=False,
label=_('Size (GB)')
)
description = forms.CharField(
label=_('Description'),
max_length=100,
required=False
)
model = VirtualDisk
fieldsets = (
(None, ('size',)),
(None, ('size', 'description')),
)
nullable_fields = ()
nullable_fields = ('description',)
class VirtualDiskBulkRenameForm(BulkRenameForm):

View File

@ -212,5 +212,5 @@ class VirtualDiskImportForm(NetBoxModelImportForm):
class Meta:
model = VirtualDisk
fields = (
'virtual_machine', 'name', 'size', 'tags'
'virtual_machine', 'name', 'size', 'description', 'tags'
)

View File

@ -369,13 +369,13 @@ class VirtualDiskForm(NetBoxModelForm):
)
fieldsets = (
(None, ('virtual_machine', 'name', 'size', 'tags')),
(None, ('virtual_machine', 'name', 'size', 'description', 'tags')),
)
class Meta:
model = VirtualDisk
fields = [
'virtual_machine', 'name', 'size', 'tags',
'virtual_machine', 'name', 'size', 'description', 'tags',
]
def __init__(self, *args, **kwargs):

View File

@ -0,0 +1,50 @@
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
import utilities.fields
import utilities.json
import utilities.ordering
import utilities.query_functions
import utilities.tracking
class Migration(migrations.Migration):
dependencies = [
('extras', '0099_cachedvalue_ordering'),
('virtualization', '0037_protect_child_interfaces'),
]
operations = [
migrations.AddField(
model_name='virtualmachine',
name='virtual_disk_count',
field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='virtual_machine', to_model='virtualization.VirtualDisk'),
),
migrations.CreateModel(
name='VirtualDisk',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
('name', models.CharField(max_length=64)),
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
('description', models.CharField(blank=True, max_length=200)),
('size', models.PositiveIntegerField()),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
('virtual_machine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='virtualization.virtualmachine')),
],
options={
'verbose_name': 'virtual disk',
'verbose_name_plural': 'virtual disks',
'ordering': ('virtual_machine', utilities.query_functions.CollateAsChar('_name')),
'abstract': False,
},
bases=(models.Model, utilities.tracking.TrackingModelMixin),
),
migrations.AddConstraint(
model_name='virtualdisk',
constraint=models.UniqueConstraint(fields=('virtual_machine', 'name'), name='virtualization_virtualdisk_unique_virtual_machine_name'),
),
]

View File

@ -1,70 +0,0 @@
# Generated by Django 4.2.5 on 2023-10-23 15:13
from django.db import migrations, models
import django.db.models.deletion
import django.db.models.functions.text
import taggit.managers
import utilities.fields
import utilities.json
import utilities.ordering
import utilities.tracking
class Migration(migrations.Migration):
dependencies = [
('extras', '0098_webhook_custom_field_data_webhook_tags'),
('virtualization', '0037_protect_child_interfaces'),
]
operations = [
migrations.AddField(
model_name='virtualmachine',
name='virtual_disk_count',
field=utilities.fields.CounterCacheField(
default=0, editable=False, to_field='virtual_machine', to_model='virtualization.VirtualDisk'
),
),
migrations.CreateModel(
name='VirtualDisk',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
(
'custom_field_data',
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
('name', models.CharField(max_length=64)),
(
'_name',
utilities.fields.NaturalOrderingField(
'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize
),
),
('size', models.PositiveIntegerField()),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
(
'virtual_machine',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='%(class)ss',
to='virtualization.virtualmachine',
),
),
],
options={
'verbose_name': 'virtual disk',
'verbose_name_plural': 'virtual disks',
'ordering': ('virtual_machine', '_name'),
},
bases=(models.Model, utilities.tracking.TrackingModelMixin),
),
migrations.AddConstraint(
model_name='virtualdisk',
constraint=models.UniqueConstraint(
django.db.models.functions.text.Lower('name'),
models.F('virtual_machine'),
name='virtualization_virtualdisk_unique_name_virtual_machine',
),
),
]

View File

@ -252,11 +252,19 @@ class VirtualMachine(ContactsMixin, RenderConfigMixin, ConfigContextModel, Prima
return None
class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
#
# VM components
#
class ComponentModel(NetBoxModel):
"""
An abstract model inherited by any model which has a parent VirtualMachine.
"""
virtual_machine = models.ForeignKey(
to='virtualization.VirtualMachine',
on_delete=models.CASCADE,
related_name='interfaces'
related_name='%(class)ss'
)
name = models.CharField(
verbose_name=_('name'),
@ -273,6 +281,42 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
max_length=200,
blank=True
)
class Meta:
abstract = True
ordering = ('virtual_machine', CollateAsChar('_name'))
constraints = (
models.UniqueConstraint(
fields=('virtual_machine', 'name'),
name='%(app_label)s_%(class)s_unique_virtual_machine_name'
),
)
def __str__(self):
return self.name
def to_objectchange(self, action):
objectchange = super().to_objectchange(action)
objectchange.related_object = self.virtual_machine
return objectchange
@property
def parent_object(self):
return self.virtual_machine
class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
virtual_machine = models.ForeignKey(
to='virtualization.VirtualMachine',
on_delete=models.CASCADE,
related_name='interfaces' # Override ComponentModel
)
_name = NaturalOrderingField(
target_field='name',
naturalize_function=naturalize_interface,
max_length=100,
blank=True
)
untagged_vlan = models.ForeignKey(
to='ipam.VLAN',
on_delete=models.SET_NULL,
@ -314,20 +358,10 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
related_query_name='vminterface',
)
class Meta:
ordering = ('virtual_machine', CollateAsChar('_name'))
constraints = (
models.UniqueConstraint(
fields=('virtual_machine', 'name'),
name='%(app_label)s_%(class)s_unique_virtual_machine_name'
),
)
class Meta(ComponentModel.Meta):
verbose_name = _('interface')
verbose_name_plural = _('interfaces')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('virtualization:vminterface', kwargs={'pk': self.pk})
@ -375,61 +409,19 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
).format(untagged_vlan=self.untagged_vlan)
})
def to_objectchange(self, action):
objectchange = super().to_objectchange(action)
objectchange.related_object = self.virtual_machine
return objectchange
@property
def parent_object(self):
return self.virtual_machine
@property
def l2vpn_termination(self):
return self.l2vpn_terminations.first()
class VirtualDisk(NetBoxModel, TrackingModelMixin):
virtual_machine = models.ForeignKey(
to=VirtualMachine,
on_delete=models.CASCADE,
related_name='%(class)ss'
)
name = models.CharField(
verbose_name=_('name'),
max_length=64
)
_name = NaturalOrderingField(
target_field='name',
max_length=100,
blank=True
)
class VirtualDisk(ComponentModel, TrackingModelMixin):
size = models.PositiveIntegerField(
verbose_name=_('size (GB)'),
)
class Meta:
ordering = ('virtual_machine', '_name')
constraints = (
models.UniqueConstraint(
Lower('name'), 'virtual_machine',
name='%(app_label)s_%(class)s_unique_name_virtual_machine'
),
)
class Meta(ComponentModel.Meta):
verbose_name = _('virtual disk')
verbose_name_plural = _('virtual disks')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('virtualization:virtualdisk', args=[self.pk])
def to_objectchange(self, action):
objectchange = super().to_objectchange(action)
objectchange.related_object = self.virtual_machine
return objectchange
@property
def parent_object(self):
return self.virtual_machine

View File

@ -178,9 +178,9 @@ class VirtualDiskTable(NetBoxTable):
class Meta(NetBoxTable.Meta):
model = VirtualDisk
fields = (
'pk', 'id', 'virtual_machine', 'name', 'size', 'tags',
'pk', 'id', 'virtual_machine', 'name', 'size', 'description', 'tags',
)
default_columns = ('pk', 'name', 'virtual_machine', 'size')
default_columns = ('pk', 'name', 'virtual_machine', 'size', 'description')
row_attrs = {
'data-name': lambda record: record.name,
}
@ -193,6 +193,6 @@ class VirtualMachineVirtualDiskTable(VirtualDiskTable):
class Meta(VirtualDiskTable.Meta):
fields = (
'pk', 'id', 'name', 'size', 'tags', 'actions',
'pk', 'id', 'name', 'size', 'description', 'tags', 'actions',
)
default_columns = ('pk', 'name', 'size')
default_columns = ('pk', 'name', 'size', 'description')