mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-25 04:50:00 -06:00
* Enable E501 rule * Configure ruff formatter * Reformat migration files to fix line length violations * Fix various E501 errors * Move table template code to template_code.py & ignore E501 errors * Reformat raw SQL
This commit is contained in:
@@ -75,9 +75,9 @@ class ClusterSerializer(NetBoxModelSerializer):
|
||||
class Meta:
|
||||
model = Cluster
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'scope_id', 'scope',
|
||||
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
|
||||
'virtualmachine_count', 'allocated_vcpus', 'allocated_memory', 'allocated_disk'
|
||||
'id', 'url', 'display_url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'scope_type',
|
||||
'scope_id', 'scope', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'device_count', 'virtualmachine_count', 'allocated_vcpus', 'allocated_memory', 'allocated_disk'
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'virtualmachine_count')
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
|
||||
class Meta:
|
||||
model = VirtualMachine
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant',
|
||||
'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
|
||||
'id', 'url', 'display_url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role',
|
||||
'tenant', 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
|
||||
'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'interface_count', 'virtual_disk_count',
|
||||
]
|
||||
@@ -62,8 +62,8 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
|
||||
|
||||
class Meta(VirtualMachineSerializer.Meta):
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant',
|
||||
'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
|
||||
'id', 'url', 'display_url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role',
|
||||
'tenant', 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description',
|
||||
'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
|
||||
'last_updated', 'interface_count', 'virtual_disk_count',
|
||||
]
|
||||
|
||||
@@ -73,7 +73,9 @@ class ClusterImportForm(ScopedImportForm, NetBoxModelImportForm):
|
||||
|
||||
class Meta:
|
||||
model = Cluster
|
||||
fields = ('name', 'type', 'group', 'status', 'scope_type', 'scope_id', 'tenant', 'description', 'comments', 'tags')
|
||||
fields = (
|
||||
'name', 'type', 'group', 'status', 'scope_type', 'scope_id', 'tenant', 'description', 'comments', 'tags',
|
||||
)
|
||||
labels = {
|
||||
'scope_id': _('Scope ID'),
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import utilities.query_functions
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
@@ -100,17 +99,79 @@ class Migration(migrations.Migration):
|
||||
('local_context_data', models.JSONField(blank=True, null=True)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('status', models.CharField(default='active', max_length=50)),
|
||||
('vcpus', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0.01)])),
|
||||
(
|
||||
'vcpus',
|
||||
models.DecimalField(
|
||||
blank=True,
|
||||
decimal_places=2,
|
||||
max_digits=6,
|
||||
null=True,
|
||||
validators=[django.core.validators.MinValueValidator(0.01)],
|
||||
),
|
||||
),
|
||||
('memory', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('disk', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('comments', models.TextField(blank=True)),
|
||||
('cluster', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='virtualization.cluster')),
|
||||
('platform', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='virtual_machines', to='dcim.platform')),
|
||||
('primary_ip4', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')),
|
||||
('primary_ip6', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')),
|
||||
('role', models.ForeignKey(blank=True, limit_choices_to={'vm_role': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.devicerole')),
|
||||
(
|
||||
'cluster',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='virtualization.cluster',
|
||||
),
|
||||
),
|
||||
(
|
||||
'platform',
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='virtual_machines',
|
||||
to='dcim.platform',
|
||||
),
|
||||
),
|
||||
(
|
||||
'primary_ip4',
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='+',
|
||||
to='ipam.ipaddress',
|
||||
),
|
||||
),
|
||||
(
|
||||
'primary_ip6',
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='+',
|
||||
to='ipam.ipaddress',
|
||||
),
|
||||
),
|
||||
(
|
||||
'role',
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
limit_choices_to={'vm_role': True},
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='dcim.devicerole',
|
||||
),
|
||||
),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='tenancy.tenant')),
|
||||
(
|
||||
'tenant',
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='tenancy.tenant',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('name', 'pk'),
|
||||
@@ -120,12 +181,24 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
name='group',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='virtualization.clustergroup'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='clusters',
|
||||
to='virtualization.clustergroup',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
name='site',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='dcim.site'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='clusters',
|
||||
to='dcim.site',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
@@ -135,12 +208,20 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='tenancy.tenant'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='clusters',
|
||||
to='tenancy.tenant',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
name='type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='virtualization.clustertype'),
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='virtualization.clustertype'
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VMInterface',
|
||||
@@ -151,16 +232,59 @@ class Migration(migrations.Migration):
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('enabled', models.BooleanField(default=True)),
|
||||
('mac_address', dcim.fields.MACAddressField(blank=True, null=True)),
|
||||
('mtu', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65536)])),
|
||||
(
|
||||
'mtu',
|
||||
models.PositiveIntegerField(
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(65536),
|
||||
],
|
||||
),
|
||||
),
|
||||
('mode', models.CharField(blank=True, max_length=50)),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
|
||||
(
|
||||
'_name',
|
||||
utilities.fields.NaturalOrderingField(
|
||||
'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface
|
||||
),
|
||||
),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_interfaces', to='virtualization.vminterface')),
|
||||
('tagged_vlans', models.ManyToManyField(blank=True, related_name='vminterfaces_as_tagged', to='ipam.VLAN')),
|
||||
(
|
||||
'parent',
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='child_interfaces',
|
||||
to='virtualization.vminterface',
|
||||
),
|
||||
),
|
||||
(
|
||||
'tagged_vlans',
|
||||
models.ManyToManyField(blank=True, related_name='vminterfaces_as_tagged', to='ipam.VLAN'),
|
||||
),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
('untagged_vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vminterfaces_as_untagged', to='ipam.vlan')),
|
||||
('virtual_machine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='virtualization.virtualmachine')),
|
||||
(
|
||||
'untagged_vlan',
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='vminterfaces_as_untagged',
|
||||
to='ipam.vlan',
|
||||
),
|
||||
),
|
||||
(
|
||||
'virtual_machine',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='interfaces',
|
||||
to='virtualization.virtualmachine',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'interface',
|
||||
|
||||
@@ -7,7 +7,6 @@ import utilities.ordering
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [
|
||||
('virtualization', '0023_virtualmachine_natural_ordering'),
|
||||
('virtualization', '0024_cluster_relax_uniqueness'),
|
||||
@@ -22,7 +21,7 @@ class Migration(migrations.Migration):
|
||||
('virtualization', '0033_unique_constraints'),
|
||||
('virtualization', '0034_standardize_description_comments'),
|
||||
('virtualization', '0035_virtualmachine_interface_count'),
|
||||
('virtualization', '0036_virtualmachine_config_template')
|
||||
('virtualization', '0036_virtualmachine_config_template'),
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
@@ -40,7 +39,9 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='virtualmachine',
|
||||
name='_name',
|
||||
field=utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||
field=utilities.fields.NaturalOrderingField(
|
||||
'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
@@ -64,7 +65,13 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='bridge',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='virtualization.vminterface'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='bridge_interfaces',
|
||||
to='virtualization.vminterface',
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
@@ -94,7 +101,13 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='vrf',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vminterfaces', to='ipam.vrf'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='vminterfaces',
|
||||
to='ipam.vrf',
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
@@ -129,17 +142,35 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='virtualmachine',
|
||||
name='site',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.site'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='dcim.site',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='virtualmachine',
|
||||
name='device',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.device'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='dcim.device',
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='virtualmachine',
|
||||
name='cluster',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='virtualization.cluster'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='virtual_machines',
|
||||
to='virtualization.cluster',
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cluster',
|
||||
@@ -155,7 +186,9 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='cluster',
|
||||
constraint=models.UniqueConstraint(fields=('group', 'name'), name='virtualization_cluster_unique_group_name'),
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=('group', 'name'), name='virtualization_cluster_unique_group_name'
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='cluster',
|
||||
@@ -163,15 +196,28 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='virtualmachine',
|
||||
constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('cluster'), models.F('tenant'), name='virtualization_virtualmachine_unique_name_cluster_tenant'),
|
||||
constraint=models.UniqueConstraint(
|
||||
django.db.models.functions.text.Lower('name'),
|
||||
models.F('cluster'),
|
||||
models.F('tenant'),
|
||||
name='virtualization_virtualmachine_unique_name_cluster_tenant',
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='virtualmachine',
|
||||
constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('cluster'), condition=models.Q(('tenant__isnull', True)), name='virtualization_virtualmachine_unique_name_cluster', violation_error_message='Virtual machine name must be unique per cluster.'),
|
||||
constraint=models.UniqueConstraint(
|
||||
django.db.models.functions.text.Lower('name'),
|
||||
models.F('cluster'),
|
||||
condition=models.Q(('tenant__isnull', True)),
|
||||
name='virtualization_virtualmachine_unique_name_cluster',
|
||||
violation_error_message='Virtual machine name must be unique per cluster.',
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='vminterface',
|
||||
constraint=models.UniqueConstraint(fields=('virtual_machine', 'name'), name='virtualization_vminterface_unique_virtual_machine_name'),
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=('virtual_machine', 'name'), name='virtualization_vminterface_unique_virtual_machine_name'
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cluster',
|
||||
@@ -186,11 +232,19 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='virtualmachine',
|
||||
name='interface_count',
|
||||
field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='virtual_machine', to_model='virtualization.VMInterface'),
|
||||
field=utilities.fields.CounterCacheField(
|
||||
default=0, editable=False, to_field='virtual_machine', to_model='virtualization.VMInterface'
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='virtualmachine',
|
||||
name='config_template',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(class)ss', to='extras.configtemplate'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='%(class)ss',
|
||||
to='extras.configtemplate',
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -5,7 +5,6 @@ import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0036_virtualmachine_config_template'),
|
||||
]
|
||||
@@ -14,6 +13,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='vminterface',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='child_interfaces', to='virtualization.vminterface'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.RESTRICT,
|
||||
related_name='child_interfaces',
|
||||
to='virtualization.vminterface',
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,7 +9,6 @@ import utilities.tracking
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0099_cachedvalue_ordering'),
|
||||
('virtualization', '0037_protect_child_interfaces'),
|
||||
@@ -19,7 +18,9 @@ class Migration(migrations.Migration):
|
||||
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'),
|
||||
field=utilities.fields.CounterCacheField(
|
||||
default=0, editable=False, to_field='virtual_machine', to_model='virtualization.VirtualDisk'
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VirtualDisk',
|
||||
@@ -27,13 +28,28 @@ class Migration(migrations.Migration):
|
||||
('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)),
|
||||
(
|
||||
'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)),
|
||||
(
|
||||
'_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')),
|
||||
(
|
||||
'virtual_machine',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='%(class)ss',
|
||||
to='virtualization.virtualmachine',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'virtual disk',
|
||||
@@ -45,6 +61,8 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='virtualdisk',
|
||||
constraint=models.UniqueConstraint(fields=('virtual_machine', 'name'), name='virtualization_virtualdisk_unique_virtual_machine_name'),
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=('virtual_machine', 'name'), name='virtualization_virtualdisk_unique_virtual_machine_name'
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -2,7 +2,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0038_virtualdisk'),
|
||||
]
|
||||
|
||||
@@ -18,14 +18,10 @@ def convert_disk_size(apps, schema_editor):
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0039_virtualmachine_serial_number'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=convert_disk_size,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
migrations.RunPython(code=convert_disk_size, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
|
||||
@@ -11,7 +11,6 @@ def set_null_values(apps, schema_editor):
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0040_convert_disk_size'),
|
||||
]
|
||||
@@ -22,8 +21,5 @@ class Migration(migrations.Migration):
|
||||
name='mode',
|
||||
field=models.CharField(blank=True, max_length=50, null=True),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=set_null_values,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
migrations.RunPython(code=set_null_values, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0074_vlantranslationpolicy_vlantranslationrule'),
|
||||
('virtualization', '0041_charfield_null_choices'),
|
||||
@@ -13,6 +12,8 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='vlan_translation_policy',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlantranslationpolicy'),
|
||||
field=models.ForeignKey(
|
||||
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlantranslationpolicy'
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0075_vlan_qinq'),
|
||||
('virtualization', '0042_vminterface_vlan_translation_policy'),
|
||||
@@ -13,7 +12,13 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='qinq_svlan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)ss_svlan', to='ipam.vlan'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='%(class)ss_svlan',
|
||||
to='ipam.vlan',
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vminterface',
|
||||
@@ -23,6 +28,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='vminterface',
|
||||
name='untagged_vlan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)ss_as_untagged', to='ipam.vlan'),
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='%(class)ss_as_untagged',
|
||||
to='ipam.vlan',
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -11,13 +11,11 @@ def copy_site_assignments(apps, schema_editor):
|
||||
Site = apps.get_model('dcim', 'Site')
|
||||
|
||||
Cluster.objects.filter(site__isnull=False).update(
|
||||
scope_type=ContentType.objects.get_for_model(Site),
|
||||
scope_id=models.F('site_id')
|
||||
scope_type=ContentType.objects.get_for_model(Site), scope_id=models.F('site_id')
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('virtualization', '0043_qinq_svlan'),
|
||||
@@ -41,11 +39,6 @@ class Migration(migrations.Migration):
|
||||
to='contenttypes.contenttype',
|
||||
),
|
||||
),
|
||||
|
||||
# Copy over existing site assignments
|
||||
migrations.RunPython(
|
||||
code=copy_site_assignments,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
|
||||
migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
|
||||
@@ -19,7 +19,6 @@ def populate_denormalized_fields(apps, schema_editor):
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0044_cluster_scope'),
|
||||
]
|
||||
@@ -69,13 +68,8 @@ class Migration(migrations.Migration):
|
||||
to='dcim.sitegroup',
|
||||
),
|
||||
),
|
||||
|
||||
# Populate denormalized FK values
|
||||
migrations.RunPython(
|
||||
code=populate_denormalized_fields,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
|
||||
migrations.RunPython(code=populate_denormalized_fields, reverse_code=migrations.RunPython.noop),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='cluster',
|
||||
name='virtualization_cluster_unique_site_name',
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0196_qinq_svlan'),
|
||||
('virtualization', '0045_clusters_cached_relations'),
|
||||
|
||||
@@ -2,7 +2,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('virtualization', '0046_alter_cluster__location_alter_cluster__region_and_more'),
|
||||
('dcim', '0197_natural_sort_collation'),
|
||||
|
||||
@@ -10,9 +10,7 @@ def populate_mac_addresses(apps, schema_editor):
|
||||
|
||||
mac_addresses = [
|
||||
MACAddress(
|
||||
mac_address=vminterface.mac_address,
|
||||
assigned_object_type=vminterface_ct,
|
||||
assigned_object_id=vminterface.pk
|
||||
mac_address=vminterface.mac_address, assigned_object_type=vminterface_ct, assigned_object_id=vminterface.pk
|
||||
)
|
||||
for vminterface in VMInterface.objects.filter(mac_address__isnull=False)
|
||||
]
|
||||
@@ -24,7 +22,6 @@ def populate_mac_addresses(apps, schema_editor):
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0199_macaddress'),
|
||||
('virtualization', '0047_natural_ordering'),
|
||||
@@ -39,13 +36,10 @@ class Migration(migrations.Migration):
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='+',
|
||||
to='dcim.macaddress'
|
||||
to='dcim.macaddress',
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
code=populate_mac_addresses,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
migrations.RunPython(code=populate_mac_addresses, reverse_code=migrations.RunPython.noop),
|
||||
migrations.RemoveField(
|
||||
model_name='vminterface',
|
||||
name='mac_address',
|
||||
|
||||
@@ -100,7 +100,7 @@ class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = Cluster
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'scope', 'scope_type', 'description',
|
||||
'comments', 'device_count', 'vm_count', 'contacts', 'tags', 'created', 'last_updated',
|
||||
'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'scope', 'scope_type',
|
||||
'description', 'comments', 'device_count', 'vm_count', 'contacts', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'type', 'group', 'status', 'tenant', 'site', 'device_count', 'vm_count')
|
||||
|
||||
32
netbox/virtualization/tables/template_code.py
Normal file
32
netbox/virtualization/tables/template_code.py
Normal file
@@ -0,0 +1,32 @@
|
||||
VMINTERFACE_BUTTONS = """
|
||||
{% if perms.virtualization.change_vminterface %}
|
||||
<span class="dropdown">
|
||||
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Add">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
{% if perms.ipam.add_ipaddress %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">IP Address</a></li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_macaddress %}
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:macaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">MAC Address</a></li>
|
||||
{% endif %}
|
||||
{% if perms.vpn.add_l2vpntermination %}
|
||||
<li><a class="dropdown-item" href="{% url 'vpn:l2vpntermination_add' %}?virtual_machine={{ object.pk }}&vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">L2VPN Termination</a></li>
|
||||
{% endif %}
|
||||
{% if perms.ipam.add_fhrpgroupassignment %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:fhrpgroupassignment_add' %}?interface_type={{ record|content_type_id }}&interface_id={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">Assign FHRP Group</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if perms.vpn.add_tunnel and not record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunnel_add' %}?termination1_type=virtualization.virtualmachine&termination1_parent={{ record.virtual_machine.pk }}&termination1_termination={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Create a tunnel" class="btn btn-success btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% elif perms.vpn.delete_tunneltermination and record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunneltermination_delete' pk=record.tunnel_termination.pk %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Remove tunnel" class="btn btn-danger btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
"""
|
||||
@@ -6,6 +6,7 @@ from netbox.tables import NetBoxTable, columns
|
||||
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
|
||||
from utilities.templatetags.helpers import humanize_megabytes
|
||||
from virtualization.models import VirtualDisk, VirtualMachine, VMInterface
|
||||
from .template_code import *
|
||||
|
||||
__all__ = (
|
||||
'VirtualDiskTable',
|
||||
@@ -15,39 +16,6 @@ __all__ = (
|
||||
'VMInterfaceTable',
|
||||
)
|
||||
|
||||
VMINTERFACE_BUTTONS = """
|
||||
{% if perms.virtualization.change_vminterface %}
|
||||
<span class="dropdown">
|
||||
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Add">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
{% if perms.ipam.add_ipaddress %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">IP Address</a></li>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_macaddress %}
|
||||
<li><a class="dropdown-item" href="{% url 'dcim:macaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">MAC Address</a></li>
|
||||
{% endif %}
|
||||
{% if perms.vpn.add_l2vpntermination %}
|
||||
<li><a class="dropdown-item" href="{% url 'vpn:l2vpntermination_add' %}?virtual_machine={{ object.pk }}&vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">L2VPN Termination</a></li>
|
||||
{% endif %}
|
||||
{% if perms.ipam.add_fhrpgroupassignment %}
|
||||
<li><a class="dropdown-item" href="{% url 'ipam:fhrpgroupassignment_add' %}?interface_type={{ record|content_type_id }}&interface_id={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">Assign FHRP Group</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if perms.vpn.add_tunnel and not record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunnel_add' %}?termination1_type=virtualization.virtualmachine&termination1_parent={{ record.virtual_machine.pk }}&termination1_termination={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Create a tunnel" class="btn btn-success btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% elif perms.vpn.delete_tunneltermination and record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunneltermination_delete' pk=record.tunnel_termination.pk %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Remove tunnel" class="btn btn-danger btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# Virtual machines
|
||||
|
||||
@@ -109,9 +109,24 @@ class ClusterTest(APIViewTestCases.APIViewTestCase):
|
||||
ClusterGroup.objects.bulk_create(cluster_groups)
|
||||
|
||||
clusters = (
|
||||
Cluster(name='Cluster 1', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
|
||||
Cluster(name='Cluster 2', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
|
||||
Cluster(name='Cluster 3', type=cluster_types[0], group=cluster_groups[0], status=ClusterStatusChoices.STATUS_PLANNED),
|
||||
Cluster(
|
||||
name='Cluster 1',
|
||||
type=cluster_types[0],
|
||||
group=cluster_groups[0],
|
||||
status=ClusterStatusChoices.STATUS_PLANNED,
|
||||
),
|
||||
Cluster(
|
||||
name='Cluster 2',
|
||||
type=cluster_types[0],
|
||||
group=cluster_groups[0],
|
||||
status=ClusterStatusChoices.STATUS_PLANNED,
|
||||
),
|
||||
Cluster(
|
||||
name='Cluster 3',
|
||||
type=cluster_types[0],
|
||||
group=cluster_groups[0],
|
||||
status=ClusterStatusChoices.STATUS_PLANNED,
|
||||
),
|
||||
)
|
||||
for cluster in clusters:
|
||||
cluster.save()
|
||||
@@ -169,9 +184,25 @@ class VirtualMachineTest(APIViewTestCases.APIViewTestCase):
|
||||
device2 = create_test_device('device2', site=sites[1], cluster=clusters[1])
|
||||
|
||||
virtual_machines = (
|
||||
VirtualMachine(name='Virtual Machine 1', site=sites[0], cluster=clusters[0], device=device1, local_context_data={'A': 1}),
|
||||
VirtualMachine(name='Virtual Machine 2', site=sites[0], cluster=clusters[0], local_context_data={'B': 2}),
|
||||
VirtualMachine(name='Virtual Machine 3', site=sites[0], cluster=clusters[0], local_context_data={'C': 3}),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 1',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
device=device1,
|
||||
local_context_data={'A': 1},
|
||||
),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 2',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
local_context_data={'B': 2
|
||||
}),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 3',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
local_context_data={'C': 3}
|
||||
),
|
||||
)
|
||||
VirtualMachine.objects.bulk_create(virtual_machines)
|
||||
|
||||
|
||||
@@ -117,9 +117,27 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
ClusterType.objects.bulk_create(clustertypes)
|
||||
|
||||
clusters = (
|
||||
Cluster(name='Cluster 1', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, scope=sites[0]),
|
||||
Cluster(name='Cluster 2', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, scope=sites[0]),
|
||||
Cluster(name='Cluster 3', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, scope=sites[0]),
|
||||
Cluster(
|
||||
name='Cluster 1',
|
||||
group=clustergroups[0],
|
||||
type=clustertypes[0],
|
||||
status=ClusterStatusChoices.STATUS_ACTIVE,
|
||||
scope=sites[0],
|
||||
),
|
||||
Cluster(
|
||||
name='Cluster 2',
|
||||
group=clustergroups[0],
|
||||
type=clustertypes[0],
|
||||
status=ClusterStatusChoices.STATUS_ACTIVE,
|
||||
scope=sites[0],
|
||||
),
|
||||
Cluster(
|
||||
name='Cluster 3',
|
||||
group=clustergroups[0],
|
||||
type=clustertypes[0],
|
||||
status=ClusterStatusChoices.STATUS_ACTIVE,
|
||||
scope=sites[0],
|
||||
),
|
||||
)
|
||||
for cluster in clusters:
|
||||
cluster.save()
|
||||
@@ -214,9 +232,30 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
|
||||
virtual_machines = (
|
||||
VirtualMachine(name='Virtual Machine 1', site=sites[0], cluster=clusters[0], device=devices[0], role=roles[0], platform=platforms[0]),
|
||||
VirtualMachine(name='Virtual Machine 2', site=sites[0], cluster=clusters[0], device=devices[0], role=roles[0], platform=platforms[0]),
|
||||
VirtualMachine(name='Virtual Machine 3', site=sites[0], cluster=clusters[0], device=devices[0], role=roles[0], platform=platforms[0]),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 1',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
device=devices[0],
|
||||
role=roles[0],
|
||||
platform=platforms[0],
|
||||
),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 2',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
device=devices[0],
|
||||
role=roles[0],
|
||||
platform=platforms[0],
|
||||
),
|
||||
VirtualMachine(
|
||||
name='Virtual Machine 3',
|
||||
site=sites[0],
|
||||
cluster=clusters[0],
|
||||
device=devices[0],
|
||||
role=roles[0],
|
||||
platform=platforms[0],
|
||||
),
|
||||
)
|
||||
VirtualMachine.objects.bulk_create(virtual_machines)
|
||||
|
||||
|
||||
@@ -177,7 +177,11 @@ class ClusterView(generic.ObjectView):
|
||||
queryset = Cluster.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return instance.virtual_machines.aggregate(vcpus_sum=Sum('vcpus'), memory_sum=Sum('memory'), disk_sum=Sum('disk'))
|
||||
return instance.virtual_machines.aggregate(
|
||||
vcpus_sum=Sum('vcpus'),
|
||||
memory_sum=Sum('memory'),
|
||||
disk_sum=Sum('disk')
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
|
||||
|
||||
Reference in New Issue
Block a user