diff --git a/CHANGES b/CHANGES index 2a58295..3c3820e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +unreleased +------------------------ + - Feature: image format type support: qcow2 and raw + v3.3.1 - 19.02.2024 ------------------------ - Feature: error text color config option diff --git a/sh/upgrade_db.sh b/sh/upgrade_db.sh index 1d08ae8..9fbbae3 100755 --- a/sh/upgrade_db.sh +++ b/sh/upgrade_db.sh @@ -6,7 +6,7 @@ if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then fi DB_PATH="$1" -DB_ACTUAL_VERSION=20 +DB_ACTUAL_VERSION=21 DB_CURRENT_VERSION=$(sqlite3 "$DB_PATH" -line 'PRAGMA user_version;' | sed 's/.*[[:space:]]=[[:space:]]//') USER=$(whoami) RC=0 @@ -376,6 +376,14 @@ while [ "$DB_CURRENT_VERSION" != "$DB_ACTUAL_VERSION" ]; do ) || RC=1 ;; + ( 20 ) + ( + sqlite3 "$DB_PATH" -line 'ALTER TABLE drives ADD format TEXT NOT NULL DEFAULT "qcow2";' && + sqlite3 "$DB_PATH" -line 'PRAGMA user_version=21' + ) || RC=1 + ;; + + ( * ) echo "Unsupported database user_version" >&2 exit 1 diff --git a/src/nm_add_drive.c b/src/nm_add_drive.c index a0f5fd1..a33e480 100644 --- a/src/nm_add_drive.c +++ b/src/nm_add_drive.c @@ -11,19 +11,24 @@ #include static const char NM_LC_DRIVE_FORM_MSG[] = "Drive interface"; +static const char NM_LC_DRIVE_FORM_FORMAT[] = "Disk image format"; static const char NM_LC_DRIVE_FORM_DIS[] = "Discard mode"; static const char NM_LC_DRIVE_FORM_SZ_START[] = "Size [1-"; static const char NM_LC_DRIVE_FORM_SZ_END[] = "]Gb"; static void nm_add_drive_init_windows(nm_form_t *form); static size_t nm_add_drive_labels_setup(void); -static void nm_add_drive_to_db(const nm_str_t *name, const nm_str_t *size, - const nm_str_t *type, const nm_vect_t *drives, - const nm_str_t *discard); +static void nm_add_drive_to_db(const nm_str_t *name, + const nm_str_t *size, + const nm_str_t *type, + const nm_vect_t *drives, + const nm_str_t *discard, + const nm_str_t *format); enum { NM_LBL_DRVSIZE = 0, NM_FLD_DRVSIZE, NM_LBL_DRVTYPE, NM_FLD_DRVTYPE, + NM_LBL_FORMAT, NM_FLD_FORMAT, NM_LBL_DISCARD, NM_FLD_DISCARD, NM_FLD_COUNT }; @@ -57,13 +62,14 @@ void nm_add_drive(const nm_str_t *name) nm_form_t *form = NULL; nm_str_t drv_size = NM_INIT_STR; nm_str_t drv_type = NM_INIT_STR; + nm_str_t format = NM_INIT_STR; nm_str_t discard = NM_INIT_STR; nm_vmctl_data_t vm = NM_VMCTL_INIT_DATA; nm_vect_t err = NM_INIT_VECT; size_t msg_len; nm_vmctl_get_data(name, &vm); - if ((vm.drives.n_memb / 4) == NM_DRIVE_LIMIT) { + if ((vm.drives.n_memb / NM_DRV_IDX_COUNT) == NM_DRIVE_LIMIT) { nm_str_t warn_msg = NM_INIT_STR; nm_str_format(&warn_msg, _("%zu %s"), NM_DRIVE_LIMIT, NM_NSG_DRV_LIM); @@ -94,6 +100,10 @@ void nm_add_drive(const nm_str_t *name) fields[n] = nm_field_enum_new( n / 2, form_data, nm_form_drive_drv, false, false); break; + case NM_FLD_FORMAT: + fields[n] = nm_field_enum_new( + n / 2, form_data, nm_form_drive_fmt, false, false); + break; case NM_FLD_DISCARD: fields[n] = nm_field_enum_new( n / 2, form_data, nm_form_yes_no, false, false); @@ -107,6 +117,7 @@ void nm_add_drive(const nm_str_t *name) nm_add_drive_labels_setup(); set_field_buffer(fields[NM_FLD_DRVTYPE], 0, NM_DEFAULT_DRVINT); + set_field_buffer(fields[NM_FLD_FORMAT], 0, NM_DEFAULT_DRVFMT); set_field_buffer(fields[NM_FLD_DISCARD], 0, nm_form_yes_no[1]); nm_fields_unset_status(fields); @@ -119,6 +130,7 @@ void nm_add_drive(const nm_str_t *name) nm_get_field_buf(fields[NM_FLD_DRVSIZE], &drv_size); nm_get_field_buf(fields[NM_FLD_DRVTYPE], &drv_type); + nm_get_field_buf(fields[NM_FLD_FORMAT], &format); nm_get_field_buf(fields[NM_FLD_DISCARD], &discard); if (!drv_size.len) { @@ -131,10 +143,11 @@ void nm_add_drive(const nm_str_t *name) goto out; } - if (nm_add_drive_to_fs(name, &drv_size, &vm.drives) != NM_OK) { + if (nm_add_drive_to_fs(name, &drv_size, &vm.drives, &format) != NM_OK) { nm_bug(_("%s: cannot create image file"), __func__); } - nm_add_drive_to_db(name, &drv_size, &drv_type, &vm.drives, &discard); + nm_add_drive_to_db(name, &drv_size, &drv_type, + &vm.drives, &discard, &format); out: NM_FORM_EXIT(); @@ -163,6 +176,9 @@ static size_t nm_add_drive_labels_setup(void) case NM_LBL_DRVTYPE: nm_str_format(&buf, "%s", _(NM_LC_DRIVE_FORM_MSG)); break; + case NM_LBL_FORMAT: + nm_str_format(&buf, "%s", _(NM_LC_DRIVE_FORM_FORMAT)); + break; case NM_LBL_DISCARD: nm_str_format(&buf, "%s", _(NM_LC_DRIVE_FORM_DIS)); break; @@ -294,7 +310,7 @@ void nm_del_drive(const nm_str_t *name) } int nm_add_drive_to_fs(const nm_str_t *name, const nm_str_t *size, - const nm_vect_t *drives) + const nm_vect_t *drives, const nm_str_t *format) { nm_str_t buf = NM_INIT_STR; nm_vect_t argv = NM_INIT_VECT; @@ -304,7 +320,7 @@ int nm_add_drive_to_fs(const nm_str_t *name, const nm_str_t *size, nm_vect_insert_cstr(&argv, "create"); nm_vect_insert_cstr(&argv, "-f"); - nm_vect_insert_cstr(&argv, "qcow2"); + nm_vect_insert_cstr(&argv, format->data); /* * @TODO Fix conversion from size_t to char @@ -313,7 +329,7 @@ int nm_add_drive_to_fs(const nm_str_t *name, const nm_str_t *size, size_t drive_count = 0; if (drives != NULL) { - drive_count = drives->n_memb / 4; + drive_count = drives->n_memb / NM_DRV_IDX_COUNT; } char drv_ch = 'a' + drive_count; @@ -339,19 +355,20 @@ int nm_add_drive_to_fs(const nm_str_t *name, const nm_str_t *size, static void nm_add_drive_to_db(const nm_str_t *name, const nm_str_t *size, const nm_str_t *type, const nm_vect_t *drives, - const nm_str_t *discard) + const nm_str_t *discard, const nm_str_t *format) { /* * @TODO Fix conversion from size_t to char * (might be a problem if there is too many drives) */ - size_t drive_count = drives->n_memb / 4; + size_t drive_count = drives->n_memb / NM_DRV_IDX_COUNT; char drv_ch = 'a' + drive_count; nm_str_t query = NM_INIT_STR; nm_str_format(&query, NM_SQL_DRIVES_INSERT_ADD, name->data, name->data, drv_ch, type->data, size->data, - (nm_str_cmp_st(discard, "yes") == NM_OK) ? NM_ENABLE : NM_DISABLE); + (nm_str_cmp_st(discard, "yes") == NM_OK) ? NM_ENABLE : NM_DISABLE, + format->data); nm_db_edit(query.data); nm_str_free(&query); diff --git a/src/nm_add_drive.h b/src/nm_add_drive.h index ee9028d..a3bf77d 100644 --- a/src/nm_add_drive.h +++ b/src/nm_add_drive.h @@ -8,7 +8,7 @@ void nm_add_drive(const nm_str_t *name); void nm_del_drive(const nm_str_t *name); int nm_add_drive_to_fs(const nm_str_t *name, const nm_str_t *size, - const nm_vect_t *drives); + const nm_vect_t *drives, const nm_str_t *format); static const size_t NM_DRIVE_LIMIT = 30; diff --git a/src/nm_add_vm.c b/src/nm_add_vm.c index b3884ee..61442ae 100644 --- a/src/nm_add_vm.c +++ b/src/nm_add_vm.c @@ -13,19 +13,20 @@ #include #include -static const char NM_LC_VM_FORM_NAME[] = "Name"; -static const char NM_LC_VM_FORM_ARCH[] = "Architecture"; -static const char NM_LC_VM_FORM_CPU[] = "CPU count"; -static const char NM_LC_VM_FORM_MEM_BEGIN[] = "Memory [4-"; -static const char NM_LC_VM_FORM_MEM_END[] = "]Mb"; -static const char NM_LC_VM_FORM_DRV_BEGIN[] = "Disk [1-"; -static const char NM_LC_VM_FORM_DRV_END[] = "]Gb"; -static const char NM_LC_VM_FORM_DRV_IF[] = "Disk interface"; -static const char NM_LC_VM_FORM_DRV_DIS[] = "Discard mode"; -static const char NM_LC_VM_FORM_IMP_PATH[] = "Path to disk image"; -static const char NM_LC_VM_FORM_INS_PATH[] = "Path to ISO/IMG"; -static const char NM_LC_VM_FORM_NET_IFS[] = "Network interfaces"; -static const char NM_LC_VM_FORM_NET_DRV[] = "Net driver"; +static const char NM_LC_VM_FORM_NAME[] = "Name"; +static const char NM_LC_VM_FORM_ARCH[] = "Architecture"; +static const char NM_LC_VM_FORM_CPU[] = "CPU count"; +static const char NM_LC_VM_FORM_MEM_BEGIN[] = "Memory [4-"; +static const char NM_LC_VM_FORM_MEM_END[] = "]Mb"; +static const char NM_LC_VM_FORM_DRV_BEGIN[] = "Disk [1-"; +static const char NM_LC_VM_FORM_DRV_END[] = "]Gb"; +static const char NM_LC_VM_FORM_DRV_IF[] = "Disk interface"; +static const char NM_LC_VM_FORM_DRV_FORMAT[] = "Disk image format"; +static const char NM_LC_VM_FORM_DRV_DIS[] = "Discard mode"; +static const char NM_LC_VM_FORM_IMP_PATH[] = "Path to disk image"; +static const char NM_LC_VM_FORM_INS_PATH[] = "Path to ISO/IMG"; +static const char NM_LC_VM_FORM_NET_IFS[] = "Network interfaces"; +static const char NM_LC_VM_FORM_NET_DRV[] = "Net driver"; static void nm_add_vm_init_windows(nm_form_t *form); static void nm_add_vm_fields_setup(void); @@ -41,6 +42,7 @@ enum { NM_LBL_RAMTOT, NM_FLD_RAMTOT, NM_LBL_DISKSZ, NM_FLD_DISKSZ, NM_LBL_DISKIN, NM_FLD_DISKIN, + NM_LBL_DISKFMT, NM_FLD_DISKFMT, NM_LBL_DISCARD, NM_FLD_DISCARD, NM_LBL_SOURCE, NM_FLD_SOURCE, NM_LBL_IFSCNT, NM_FLD_IFSCNT, @@ -139,6 +141,10 @@ static void nm_add_vm_main(void) fields[n] = nm_field_enum_new( n / 2, form_data, nm_form_drive_drv, false, false); break; + case NM_FLD_DISKFMT: + fields[n] = nm_field_enum_new( + n / 2, form_data, nm_form_drive_fmt, false, false); + break; case NM_FLD_DISCARD: fields[n] = nm_field_enum_new( n / 2, form_data, nm_form_yes_no, false, false); @@ -215,6 +221,7 @@ static void nm_add_vm_fields_setup(void) *nm_cfg_get()->qemu_targets.data); set_field_buffer(fields[NM_FLD_CPUNUM], 0, "1"); set_field_buffer(fields[NM_FLD_DISKIN], 0, NM_DEFAULT_DRVINT); + set_field_buffer(fields[NM_FLD_DISKFMT], 0, NM_DEFAULT_DRVFMT); set_field_buffer(fields[NM_FLD_DISCARD], 0, nm_form_yes_no[1]); set_field_buffer(fields[NM_FLD_IFSCNT], 0, "1"); if (import) { @@ -256,6 +263,9 @@ static size_t nm_add_vm_labels_setup(void) case NM_LBL_DISKIN: nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_DRV_IF)); break; + case NM_LBL_DISKFMT: + nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_DRV_FORMAT)); + break; case NM_LBL_DISCARD: nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_DRV_DIS)); break; @@ -305,6 +315,7 @@ static int nm_add_vm_get_data(nm_vm_t *vm) if (!import) { nm_get_field_buf(fields[NM_FLD_DISKSZ], &vm->drive.size); } + nm_get_field_buf(fields[NM_FLD_DISKFMT], &vm->drive.format); nm_get_field_buf(fields[NM_FLD_DISCARD], &discard); nm_get_field_buf(fields[NM_FLD_DISKIN], &vm->drive.driver); nm_get_field_buf(fields[NM_FLD_IFSCNT], &ifs_buf); @@ -317,6 +328,7 @@ static int nm_add_vm_get_data(nm_vm_t *vm) if (!import) { nm_form_check_data(_("Disk"), vm->drive.size, err); } + nm_form_check_data(_("Disk image format"), vm->drive.format, err); nm_form_check_data(_("Disk interface"), vm->drive.driver, err); nm_form_check_data(_("Discard mode"), discard, err); nm_form_check_data(_("Path to ISO/IMG"), vm->srcp, err); @@ -402,7 +414,8 @@ void nm_add_vm_to_db(nm_vm_t *vm, uint64_t mac, vm->name.data, vm->name.data, vm->drive.driver.data, vm->drive.size.data, NM_ENABLE, /* boot flag */ - vm->drive.discard ? NM_ENABLE : NM_DISABLE + vm->drive.discard ? NM_ENABLE : NM_DISABLE, + vm->drive.format.data ); nm_db_edit(query.data); } else { /* imported from OVF */ @@ -412,7 +425,8 @@ void nm_add_vm_to_db(nm_vm_t *vm, uint64_t mac, nm_drive_file(drives->data[n])->data, NM_DEFAULT_DRVINT, nm_drive_size(drives->data[n])->data, n == 0 ? NM_ENABLE : NM_DISABLE, /* boot flag */ - vm->drive.discard ? NM_ENABLE : NM_DISABLE + vm->drive.discard ? NM_ENABLE : NM_DISABLE, + vm->drive.format.data ); nm_db_edit(query.data); } @@ -454,10 +468,38 @@ void nm_add_vm_to_db(nm_vm_t *vm, uint64_t mac, nm_str_free(&query); } +static void nm_convert_drives(const nm_str_t *vm_dir, const nm_str_t *src_img, + const nm_str_t *dst_img, const nm_str_t *format) +{ + nm_str_t buf = NM_INIT_STR; + nm_vect_t argv = NM_INIT_VECT; + + nm_str_format(&buf, "%s/qemu-img", nm_cfg_get()->qemu_bin_path.data); + nm_vect_insert(&argv, buf.data, buf.len + 1, NULL); + + nm_vect_insert_cstr(&argv, "convert"); + nm_vect_insert_cstr(&argv, "-O"); + nm_vect_insert_cstr(&argv, format->data); + nm_vect_insert_cstr(&argv, src_img->data); + nm_vect_insert_cstr(&argv, dst_img->data); + + nm_cmd_str(&buf, &argv); + nm_debug("add_vm: exec: %s\n", buf.data); + + nm_vect_end_zero(&argv); + if (nm_spawn_process(&argv, NULL) != NM_OK) { + rmdir(vm_dir->data); + nm_bug(_("%s: cannot conver image file"), __func__); + } + + nm_vect_free(&argv, NULL); + nm_str_free(&buf); +} + static void nm_add_vm_to_fs(nm_vm_t *vm) { nm_str_t vm_dir = NM_INIT_STR; - nm_str_t buf = NM_INIT_STR; + nm_str_t dst_img = NM_INIT_STR; nm_str_format(&vm_dir, "%s/%s", nm_cfg_get()->vm_dir.data, vm->name.data); @@ -467,16 +509,17 @@ static void nm_add_vm_to_fs(nm_vm_t *vm) } if (!import) { - if (nm_add_drive_to_fs(&vm->name, &vm->drive.size, NULL) != NM_OK) { + if (nm_add_drive_to_fs(&vm->name, &vm->drive.size, + NULL, &vm->drive.format) != NM_OK) { nm_bug(_("%s: cannot create image file"), __func__); } } else { - nm_str_format(&buf, "%s/%s_a.img", vm_dir.data, vm->name.data); - nm_copy_file(&vm->srcp, &buf); + nm_str_format(&dst_img, "%s/%s_a.img", vm_dir.data, vm->name.data); + nm_convert_drives(&vm_dir, &vm->srcp, &dst_img, &vm->drive.format); } nm_str_free(&vm_dir); - nm_str_free(&buf); + nm_str_free(&dst_img); } /* vim:set ts=4 sw=4: */ diff --git a/src/nm_clone_vm.c b/src/nm_clone_vm.c index 8c7d76e..737f89d 100644 --- a/src/nm_clone_vm.c +++ b/src/nm_clone_vm.c @@ -226,7 +226,8 @@ static void nm_clone_vm_to_db(const nm_str_t *src, const nm_str_t *dst, nm_vect_str(&vm->drives, NM_SQL_DRV_TYPE + idx_shift)->data, nm_vect_str(&vm->drives, NM_SQL_DRV_SIZE + idx_shift)->data, nm_vect_str(&vm->drives, NM_SQL_DRV_BOOT + idx_shift)->data, - nm_vect_str(&vm->drives, NM_SQL_DRV_DISC + idx_shift)->data); + nm_vect_str(&vm->drives, NM_SQL_DRV_DISC + idx_shift)->data, + nm_vect_str(&vm->drives, NM_SQL_DRV_FMT + idx_shift)->data); nm_db_edit(query.data); drv_ch++; diff --git a/src/nm_core.h b/src/nm_core.h index 5dd42ae..d58ed07 100644 --- a/src/nm_core.h +++ b/src/nm_core.h @@ -62,6 +62,7 @@ static const char NM_DISABLE[] = "0"; static const char NM_DEFAULT_NETDRV[] = "virtio-net-pci"; static const char NM_DEFAULT_DRVINT[] = "virtio"; +static const char NM_DEFAULT_DRVFMT[] = "qcow2"; static const char NM_DEFAULT_USBVER[] = "XHCI"; static const char NM_VM_PID_FILE[] = "qemu.pid"; static const char NM_VM_QMP_FILE[] = "qmp.sock"; diff --git a/src/nm_database.h b/src/nm_database.h index 592450c..530ad4c 100644 --- a/src/nm_database.h +++ b/src/nm_database.h @@ -6,7 +6,7 @@ #include -#define NM_DB_VERSION "20" +#define NM_DB_VERSION "21" /* PRAGMAS */ static const char NM_SQL_PRAGMA_FOREIGN_ON[] = @@ -42,6 +42,7 @@ static const char NM_SQL_DRIVES_CREATE[] = "CREATE TABLE drives(drive_name TEXT NOT NULL, drive_drv TEXT NOT NULL, " "capacity INTEGER NOT NULL, boot INTEGER NOT NULL, " "discard INTEGER NOT NULL, vm_id INTEGER NOT NULL, " + "format TEXT NOT NULL, " "FOREIGN KEY(vm_id) REFERENCES vms(id) ON DELETE CASCADE)"; static const char NM_SQL_SNAPS_CREATE[] = @@ -301,35 +302,35 @@ static const char NM_SQL_IFACES_UPDATE_RESET[] = /* DRIVES */ static const char NM_SQL_DRIVES_INSERT_CLONED[] = "INSERT INTO drives(vm_id, drive_name, drive_drv, " - "capacity, boot, discard) " + "capacity, boot, discard, format) " "VALUES((SELECT id FROM vms WHERE name='%s'), " - "'%s_%c.img', '%s', '%s', %s, %s)"; + "'%s_%c.img', '%s', '%s', %s, %s, '%s')"; static const char NM_SQL_DRIVES_INSERT_NEW[] = "INSERT INTO drives(vm_id, drive_name, drive_drv, " - "capacity, boot, discard) " + "capacity, boot, discard, format) " "VALUES((SELECT id FROM vms WHERE name='%s'), " - "'%s_a.img', '%s', %s, %s, %s)"; + "'%s_a.img', '%s', %s, %s, %s, '%s')"; static const char NM_SQL_DRIVES_INSERT_ADD[] = "INSERT INTO drives(vm_id, drive_name, drive_drv, " - "capacity, boot, discard) " + "capacity, boot, discard, format) " "VALUES((SELECT id FROM vms WHERE name='%s'), " - "'%s_%c.img', '%s', %s, 0, %s)"; + "'%s_%c.img', '%s', %s, 0, %s, '%s')"; static const char NM_SQL_DRIVES_INSERT_IMPORTED[] = "INSERT INTO drives(vm_id, drive_name, drive_drv, " - "capacity, boot, discard) " + "capacity, boot, discard, format) " "VALUES((SELECT id FROM vms WHERE name='%s'), " - "'%s', '%s', '%s', '%s', '%s')"; + "'%s', '%s', '%s', '%s', '%s', '%s')"; static const char NM_SQL_DRIVES_SELECT[] = - "SELECT drive_name, drive_drv, capacity, boot, discard " + "SELECT drive_name, drive_drv, capacity, boot, discard, format " "FROM drives WHERE vm_id=(SELECT id FROM vms WHERE name='%s') " "ORDER BY drive_name ASC"; static const char NM_SQL_DRIVES_SELECT_BY_ID[] = - "SELECT drive_name, drive_drv, capacity, boot, discard " + "SELECT drive_name, drive_drv, capacity, boot, discard, format " "FROM drives WHERE vm_id=%s ORDER BY drive_name ASC"; static const char NM_SQL_DRIVES_SELECT_CAP[] = @@ -344,6 +345,11 @@ static const char NM_SQL_DRIVES_SELECT_NAME[] = "SELECT drive_name FROM drives WHERE " "vm_id=(SELECT id FROM vms WHERE name='%s')"; +static const char NM_SQL_DRIVES_CHECK_SNAP[] = + "SELECT drive_name FROM drives " + "WHERE vm_id=(SELECT id FROM vms WHERE name='%s') AND boot=1 " + "AND format='qcow2'"; + static const char NM_SQL_DRIVES_UPDATE_DISCARD[] = "UPDATE drives SET discard=%s " "WHERE vm_id=(SELECT id FROM vms WHERE name='%s')"; @@ -512,6 +518,7 @@ enum select_drive_idx { NM_SQL_DRV_SIZE, NM_SQL_DRV_BOOT, NM_SQL_DRV_DISC, + NM_SQL_DRV_FMT, NM_DRV_IDX_COUNT }; diff --git a/src/nm_form.c b/src/nm_form.c index 62c4e35..ef91bdc 100644 --- a/src/nm_form.c +++ b/src/nm_form.c @@ -41,6 +41,12 @@ const char *nm_form_drive_drv[] = { NULL }; +const char *nm_form_drive_fmt[] = { + "qcow2", + "raw", + NULL +}; + const char *nm_form_macvtap[] = { "no", "macvtap:bridge", diff --git a/src/nm_form.h b/src/nm_form.h index 90bc391..b2689ac 100644 --- a/src/nm_form.h +++ b/src/nm_form.h @@ -77,10 +77,12 @@ typedef struct { typedef struct { nm_str_t driver; nm_str_t size; + nm_str_t format; uint32_t discard:1; } nm_vm_drive_t; -#define NM_INIT_VM_DRIVE (nm_vm_drive_t) { NM_INIT_STR, NM_INIT_STR, 0 } +#define NM_INIT_VM_DRIVE (nm_vm_drive_t) { NM_INIT_STR, NM_INIT_STR, \ + NM_INIT_STR, 0 } typedef struct { int field_hpad; @@ -226,6 +228,7 @@ void *nm_file_progress(void *data); extern const char *nm_form_yes_no[]; extern const char *nm_form_net_drv[]; extern const char *nm_form_drive_drv[]; +extern const char *nm_form_drive_fmt[]; extern const char *nm_form_macvtap[]; extern const char *nm_form_usbtype[]; extern const char *nm_form_svg_layer[]; diff --git a/src/nm_main_loop.c b/src/nm_main_loop.c index f886d74..56d31a8 100644 --- a/src/nm_main_loop.c +++ b/src/nm_main_loop.c @@ -342,6 +342,10 @@ void nm_start_main_loop(void) nm_warn(_(NM_MSG_NO_DAEMON)); break; } + if (nm_vm_snapshot_check_format(name) == false) { + nm_warn(_(NM_MSG_BAD_FMT)); + break; + } if (!vm_status) { nm_warn(NM_MSG_MUST_RUN); break; diff --git a/src/nm_ovf_import.c b/src/nm_ovf_import.c index bad203f..175869b 100644 --- a/src/nm_ovf_import.c +++ b/src/nm_ovf_import.c @@ -37,6 +37,7 @@ static const char NM_LC_OVF_FORM_PATH[] = "Path to OVA"; static const char NM_LC_OVF_FORM_ARCH[] = "Architecture"; static const char NM_LC_OVF_FORM_NAME[] = "Name (optional)"; static const char NM_LC_OVF_FORM_VER[] = "OVF version"; +static const char NM_LC_OVF_FORM_DRV_FORMAT[] = "Disk image format"; static const char NM_XML_OVF_NS[] = "ovf"; static const char NM_XML_RASD_NS[] = "rasd"; static const char NM_XML_SASD_NS[] = "sasd"; @@ -128,7 +129,7 @@ static void nm_ovf_get_text(nm_str_t *res, nm_xml_xpath_ctx_pt ctx, const char *xpath, const char *param); static inline void nm_drive_free(nm_drive_t *d); static void nm_ovf_convert_drives(const nm_vect_t *drives, const nm_str_t *name, - const char *templ_path); + const char *templ_path, const nm_str_t *format); static void nm_ovf_to_db(nm_vm_t *vm, const nm_vect_t *drives); static int nm_ova_get_data(nm_vm_t *vm, int *version); @@ -137,6 +138,7 @@ enum { NM_OVA_LBL_ARCH, NM_OVA_FLD_ARCH, NM_OVA_LBL_NAME, NM_OVA_FLD_NAME, NM_OVA_LBL_VER, NM_OVA_FLD_VER, + NM_OVA_LBL_FMT, NM_OVA_FLD_FMT, NM_FLD_COUNT }; @@ -237,6 +239,10 @@ void nm_ovf_import(void) fields[n] = nm_field_enum_new( n / 2, form_data, nm_ovf_version, false, false); break; + case NM_OVA_FLD_FMT: + fields[n] = nm_field_enum_new( + n / 2, form_data, nm_form_drive_fmt, false, false); + break; default: fields[n] = nm_field_label_new(n / 2, form_data); break; @@ -309,7 +315,7 @@ void nm_ovf_import(void) goto out; } - nm_ovf_convert_drives(&drives, &vm.name, templ_path.data); + nm_ovf_convert_drives(&drives, &vm.name, templ_path.data, &vm.drive.format); nm_ovf_to_db(&vm, &drives); out: @@ -343,6 +349,7 @@ static void nm_ovf_fields_setup(void) set_field_buffer(fields[NM_OVA_FLD_ARCH], 0, *nm_cfg_get()->qemu_targets.data); set_field_buffer(fields[NM_OVA_FLD_VER], 0, *nm_ovf_version); + set_field_buffer(fields[NM_OVA_FLD_FMT], 0, NM_DEFAULT_DRVFMT); field_opts_off(fields[NM_OVA_FLD_SRC], O_STATIC); field_opts_off(fields[NM_OVA_FLD_NAME], O_STATIC); } @@ -367,6 +374,9 @@ static size_t nm_ovf_labels_setup(void) case NM_OVA_LBL_VER: nm_str_format(&buf, "%s", _(NM_LC_OVF_FORM_VER)); break; + case NM_OVA_LBL_FMT: + nm_str_format(&buf, "%s", _(NM_LC_OVF_FORM_DRV_FORMAT)); + break; default: continue; } @@ -770,7 +780,7 @@ static inline void nm_drive_free(nm_drive_t *d) } static void nm_ovf_convert_drives(const nm_vect_t *drives, const nm_str_t *name, - const char *templ_path) + const char *templ_path, const nm_str_t *format) { nm_str_t vm_dir = NM_INIT_STR; nm_str_t buf = NM_INIT_STR; @@ -789,7 +799,7 @@ static void nm_ovf_convert_drives(const nm_vect_t *drives, const nm_str_t *name, nm_vect_insert_cstr(&argv, "convert"); nm_vect_insert_cstr(&argv, "-O"); - nm_vect_insert_cstr(&argv, "qcow2"); + nm_vect_insert_cstr(&argv, format->data); nm_str_format(&buf, "%s/%s", templ_path, (nm_drive_file(drives->data[n]))->data); @@ -839,6 +849,7 @@ static int nm_ova_get_data(nm_vm_t *vm, int *version) nm_get_field_buf(fields[NM_OVA_FLD_SRC], &vm->srcp); nm_get_field_buf(fields[NM_OVA_FLD_ARCH], &vm->arch); nm_get_field_buf(fields[NM_OVA_FLD_VER], &ver); + nm_get_field_buf(fields[NM_OVA_FLD_FMT], &vm->drive.format); if (field_status(fields[NM_OVA_FLD_NAME])) { nm_get_field_buf(fields[NM_OVA_FLD_NAME], &vm->name); nm_form_check_data(_(NM_LC_OVF_FORM_NAME), vm->name, err); @@ -846,6 +857,7 @@ static int nm_ova_get_data(nm_vm_t *vm, int *version) nm_form_check_data(_(NM_LC_OVF_FORM_PATH), vm->srcp, err); nm_form_check_data(_(NM_LC_OVF_FORM_ARCH), vm->arch, err); + nm_form_check_data(_(NM_LC_OVF_FORM_DRV_FORMAT), vm->drive.format, err); nm_form_check_data(_(NM_LC_OVF_FORM_VER), ver, err); if (nm_str_cmp_st(&ver, nm_ovf_version[0]) == NM_OK) { diff --git a/src/nm_vm_control.c b/src/nm_vm_control.c index 852d7cc..393a69f 100644 --- a/src/nm_vm_control.c +++ b/src/nm_vm_control.c @@ -1121,12 +1121,13 @@ nm_str_t nm_vmctl_info(const nm_str_t *name) memset(&img_info, 0, sizeof(img_info)); stat(drive_path.data, &img_info); - nm_str_append_format(&info, "disk%zu%-7s%s [%.2gGb/%sGb real/virt, %s] " - "%s\n", n, ":", + nm_str_append_format(&info, "disk%zu%-7s%s [%.2gGb/%sGb real/virt, %s, " + "%s] %s\n", n, ":", nm_vect_str_ctx(&vm.drives, NM_SQL_DRV_NAME + idx_shift), (double) img_info.st_size / 1073741824, nm_vect_str_ctx(&vm.drives, NM_SQL_DRV_SIZE + idx_shift), nm_vect_str_ctx(&vm.drives, NM_SQL_DRV_TYPE + idx_shift), + nm_vect_str_ctx(&vm.drives, NM_SQL_DRV_FMT + idx_shift), boot ? "*" : ""); nm_str_free(&drive_path); diff --git a/src/nm_vm_snapshot.c b/src/nm_vm_snapshot.c index a67a36e..bf7fe69 100644 --- a/src/nm_vm_snapshot.c +++ b/src/nm_vm_snapshot.c @@ -296,6 +296,25 @@ void nm_vm_snapshot_delete(const nm_str_t *name, int vm_status) nm_str_free(&buf); } +bool +nm_vm_snapshot_check_format(const nm_str_t *vm_name) { + nm_str_t query = NM_INIT_STR; + nm_vect_t res = NM_INIT_VECT; + bool ok = true; + + nm_str_format(&query, NM_SQL_DRIVES_CHECK_SNAP, vm_name->data); + nm_db_select(query.data, &res); + + if (res.n_memb == 0) { + ok = false; + } + + nm_vect_free(&res, nm_str_vect_free_cb); + nm_str_free(&query); + + return ok; +} + void nm_vm_cli_snapshot_save(const nm_str_t *vm_name, const nm_str_t *snap_name) { @@ -308,6 +327,12 @@ nm_vm_cli_snapshot_save(const nm_str_t *vm_name, const nm_str_t *snap_name) return; } + if (nm_vm_snapshot_check_format(vm_name) == false) { + fprintf(stderr, + _("Snapshots are only available for the qcow2 format\n")); + goto out; + } + int vm_status = nm_qmp_test_socket(vm_name); if (vm_status != NM_OK) { diff --git a/src/nm_vm_snapshot.h b/src/nm_vm_snapshot.h index a5d7b06..ea33f61 100644 --- a/src/nm_vm_snapshot.h +++ b/src/nm_vm_snapshot.h @@ -14,6 +14,7 @@ void nm_vm_cli_snapshot_load(const nm_str_t *vm_name, const nm_str_t *snap_name); void nm_vm_cli_snapshot_del(const nm_str_t *vm_name, const nm_str_t *snap_name); +bool nm_vm_snapshot_check_format(const nm_str_t *vm_name); #endif /* NM_VM_SNAPSHOT_H_ */ /* vim:set ts=4 sw=4: */ diff --git a/src/nm_window.c b/src/nm_window.c index 4fde874..29f2914 100644 --- a/src/nm_window.c +++ b/src/nm_window.c @@ -527,12 +527,13 @@ nm_print_vm_info(const nm_str_t *name, const nm_vmctl_data_t *vm, int status) stat(drive_path.data, &img_info); nm_str_format(&buf, - "disk%zu%-7s%s [%.2gGb/%sGb real/virt, %s discard=%s] %s", + "disk%zu%-7s%s [%.2gGb/%sGb real/virt, %s, %s, discard=%s] %s", n, ":", nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_NAME + idx_shift), (double) img_info.st_size / 1073741824, nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_SIZE + idx_shift), nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_TYPE + idx_shift), + nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_FMT + idx_shift), (nm_str_cmp_st(nm_vect_str(&vm_->drives, NM_SQL_DRV_DISC + idx_shift), NM_ENABLE) == NM_OK) ? "on" : "off", diff --git a/src/nm_window.h b/src/nm_window.h index 39e82da..668c37a 100644 --- a/src/nm_window.h +++ b/src/nm_window.h @@ -85,6 +85,7 @@ extern nm_filter_t nm_filter; #define NM_MSG_OVA_HEADER "Import OVA image" #define NM_MSG_MUST_STOP "VM must be stopped" NM_MSG_ANY_KEY #define NM_MSG_MUST_RUN "VM must be running" NM_MSG_ANY_KEY +#define NM_MSG_BAD_FMT "Only qcow2 format is supported" NM_MSG_ANY_KEY #define NM_MSG_DELETE "Confirm deletion? (y/n)" #define NM_MSG_INSTALL "Install VM" #define NM_MSG_IMPORT "Import drive image" diff --git a/test/vm_test.py b/test/vm_test.py index c6cfeae..15bc29d 100644 --- a/test/vm_test.py +++ b/test/vm_test.py @@ -11,7 +11,7 @@ def test_01_install(self): tmux = Tmux() tmux.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux.send(*key) self.assertTrue(0 == rc) @@ -36,7 +36,7 @@ def test_02_edit_base_settings(self): tmux_init = Tmux() tmux_init.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux_init.send(*key) self.assertTrue(0 == rc) @@ -72,7 +72,7 @@ def test_03_clone(self): tmux_init = Tmux() tmux_init.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux_init.send(*key) self.assertTrue(0 == rc) @@ -104,7 +104,7 @@ def test_04_edit_viewer_settings(self): tmux_init = Tmux() tmux_init.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux_init.send(*key) self.assertTrue(0 == rc) @@ -140,7 +140,7 @@ def test_05_share_9pfs(self): tmux = Tmux() tmux.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux.send(*key) self.assertTrue(0 == rc) @@ -175,14 +175,14 @@ def test_06_additional_drive(self): tmux = Tmux() tmux.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux.send(*key) self.assertTrue(0 == rc) tmux_add = Tmux() tmux_add.setup(nemu.test_dir) - keys_add = [["a"], ["1"], ["Down"], ["Left"], ["Down"], + keys_add = [["a"], ["1"], ["Down"], ["Left"], ["Down", 2], ["Right"], ["Enter"], ["q"]] for key in keys_add: rc = tmux_add.send(*key) @@ -231,7 +231,7 @@ def test_07_boot_settings(self): tmux = Tmux() tmux.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux.send(*key) self.assertTrue(0 == rc) @@ -269,7 +269,7 @@ def test_08_network(self): tmux = Tmux() tmux.setup(nemu.test_dir) keys = [["I"], ["testvm"], ["Down", 3], ["256"], ["Down"], - ["10"], ["Down", 3], ["/tmp/null.iso"], ["Enter"], ["q"]] + ["10"], ["Down", 4], ["/tmp/null.iso"], ["Enter"], ["q"]] for key in keys: rc = tmux.send(*key) self.assertTrue(0 == rc)