mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 20:26:48 +00:00
fix(instances): a snapshot needs free space in the VM's storage pools
A disk-only snapshot was taken without any space check, though its overlays grow as the guest writes and a later delete merges them down; a full pool pauses the guest and can leave the snapshot undeletable. Only a memory snapshot was checked, for the memory alone. Before a snapshot, each storage pool with a disk of the VM must have free a share of those disks' virtual size (10%, at least 1 GiB), and the pool of the memory file the VM's memory on top. Both values are on the Settings page (Snapshot Free Percent, Snapshot Free Min GiB; 0 turns a rule off, an invalid value keeps the default). The pools are refreshed first, and a failed refresh or an unreadable volume refuses the snapshot instead of skipping the check; a disk in no pool is not checked. Nothing is reserved: later writes can still fill a pool.
This commit is contained in:
parent
f766890825
commit
5f8f7a8643
8 changed files with 227 additions and 30 deletions
35
appsettings/migrations/0017_snapshot_free_space_settings.py
Normal file
35
appsettings/migrations/0017_snapshot_free_space_settings.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from django.core.management.color import no_style
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
SETTINGS = [
|
||||
("Snapshot Free Percent", "SNAPSHOT_FREE_SPACE_PERCENT", "10",
|
||||
"Free space a storage pool keeps for a snapshot, in percent of the VM's disks in it (0: none)"),
|
||||
("Snapshot Free Min GiB", "SNAPSHOT_FREE_SPACE_MIN_GIB", "1",
|
||||
"Free space a storage pool keeps for a snapshot at least, in GiB (0: none)"),
|
||||
]
|
||||
|
||||
|
||||
def add_settings(apps, schema_editor):
|
||||
setting = apps.get_model("appsettings", "AppSettings")
|
||||
db_alias = schema_editor.connection.alias
|
||||
# see 0014: move a PostgreSQL id sequence past the fixed ids first
|
||||
with schema_editor.connection.cursor() as cursor:
|
||||
for sql in schema_editor.connection.ops.sequence_reset_sql(no_style(), [setting]):
|
||||
cursor.execute(sql)
|
||||
for name, key, value, description in SETTINGS:
|
||||
setting.objects.using(db_alias).get_or_create(
|
||||
key=key, defaults={"name": name, "value": value, "choices": "", "description": description}
|
||||
)
|
||||
|
||||
|
||||
def del_settings(apps, schema_editor):
|
||||
setting = apps.get_model("appsettings", "AppSettings")
|
||||
db_alias = schema_editor.connection.alias
|
||||
setting.objects.using(db_alias).filter(key__in=[key for _, key, _, _ in SETTINGS]).delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("appsettings", "0016_nic_type_rtl8139")]
|
||||
|
||||
operations = [migrations.RunPython(add_settings, del_settings)]
|
||||
|
|
@ -40,3 +40,14 @@ class NicTypeTestCase(TestCase):
|
|||
nic_type.fix_rtl8139(apps, SimpleNamespace(connection=connection))
|
||||
setting.refresh_from_db()
|
||||
self.assertEqual((setting.value, setting.choices), ("rtl8139", "default,e1000,e1000e,rtl8139,virtio"))
|
||||
|
||||
|
||||
class SnapshotFreeSpaceSettingsTestCase(TestCase):
|
||||
def test_the_rows_fit_their_columns(self):
|
||||
# PostgreSQL refuses a value longer than the column, sqlite does not
|
||||
for key in ("SNAPSHOT_FREE_SPACE_PERCENT", "SNAPSHOT_FREE_SPACE_MIN_GIB"):
|
||||
setting = AppSettings.objects.get(key=key)
|
||||
for field in ("name", "key", "value", "description"):
|
||||
limit = AppSettings._meta.get_field(field).max_length
|
||||
if limit:
|
||||
self.assertLessEqual(len(getattr(setting, field)), limit, f"{key} {field}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue