p2v: Generate config struct and p2v/config.c.

Mostly refactoring to make it easier to add fields to this struct in
future.

I'd like to call the header p2v/config.h but that's not possible since
it conflicts with the autoconf-generated file.
This commit is contained in:
Richard W.M. Jones
2018-06-22 13:53:17 +01:00
parent ec2228ea05
commit 818fd1e197
9 changed files with 431 additions and 271 deletions

2
.gitignore vendored
View File

@@ -431,10 +431,12 @@ Makefile.in
/ocaml/t/*.bc
/ocaml/t/*.opt
/p2v/about-authors.c
/p2v/config.c
/p2v/dependencies.archlinux
/p2v/dependencies.debian
/p2v/dependencies.redhat
/p2v/dependencies.suse
/p2v/p2v-config.h
/p2v/stamp-test-virt-p2v-pxe-data-files
/p2v/stamp-test-virt-p2v-pxe-hostkey
/p2v/stamp-test-virt-p2v-pxe-kernel

View File

@@ -101,6 +101,8 @@ sources = \
proc_nr.mli \
python.ml \
python.mli \
p2v_config.ml \
p2v_config.mli \
ruby.ml \
ruby.mli \
structs.ml \
@@ -165,6 +167,7 @@ objects = \
errnostring.cmo \
customize.cmo \
UEFI.cmo \
p2v_config.cmo \
main.cmo
EXTRA_DIST = $(sources) files-generated.txt

View File

@@ -363,6 +363,11 @@ Run it from the top source directory using the command
output_to "customize/customize-options.pod"
Customize.generate_customize_options_pod;
output_to "p2v/p2v-config.h"
P2v_config.generate_p2v_config_h;
output_to "p2v/config.c"
P2v_config.generate_p2v_config_c;
(* Generate the list of files generated -- last. *)
printf "generated %d lines of code\n" (get_lines_generated ());
let files = List.sort compare (get_files_generated ()) in

389
generator/p2v_config.ml Normal file
View File

@@ -0,0 +1,389 @@
(* libguestfs
* Copyright (C) 2018 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
(* Please read generator/README first. *)
open Printf
open Std_utils
open Docstrings
open Pr
let generate_header = generate_header ~inputs:["generator/p2v_config.ml"]
type enum_choice = string * string (* name, comment *)
type enum = string * enum_choice list
type config_entry =
| ConfigString of string
| ConfigInt of string * int (* field name, initial value *)
| ConfigUnsigned of string
| ConfigUInt64 of string
| ConfigEnum of string
| ConfigBool of string
| ConfigStringList of string
| ConfigSection of string * config_entry list
(* Enums. *)
let enums = [
"basis", [
"BASIS_UNKNOWN", "RTC could not be read";
"BASIS_UTC", "RTC is either UTC or an offset from UTC";
"BASIS_LOCALTIME", "RTC is localtime";
];
"output_allocation", [
"OUTPUT_ALLOCATION_NONE", "output allocation not set";
"OUTPUT_ALLOCATION_SPARSE", "sparse";
"OUTPUT_ALLOCATION_PREALLOCATED", "preallocated";
];
]
(* Configuration fields. *)
let fields = [
ConfigString "server";
ConfigInt ("port", 22);
ConfigString "username";
ConfigString "password";
ConfigString "identity_url";
ConfigString "identity_file";
ConfigBool "identity_file_needs_update";
ConfigBool "sudo";
ConfigString "guestname";
ConfigInt ("vcpus", 0);
ConfigUInt64 "memory";
ConfigSection ("cpu", [
ConfigString "vendor";
ConfigString "model";
ConfigUnsigned "sockets";
ConfigUnsigned "cores";
ConfigUnsigned "threads";
ConfigBool "acpi";
ConfigBool "apic";
ConfigBool "pae";
]);
ConfigSection ("rtc", [
ConfigEnum "basis";
ConfigInt ("offset", 0);
]);
ConfigStringList "disks";
ConfigStringList "removable";
ConfigStringList "interfaces";
ConfigStringList "network_map";
ConfigString "output";
ConfigEnum "output_allocation";
ConfigString "output_connection";
ConfigString "output_format";
ConfigString "output_storage";
]
let name_of_config_entry = function
| ConfigString n
| ConfigInt (n, _)
| ConfigUnsigned n
| ConfigUInt64 n
| ConfigEnum n
| ConfigBool n
| ConfigStringList n
| ConfigSection (n, _) -> n
let rec generate_p2v_config_h () =
generate_header CStyle GPLv2plus;
pr "\
#ifndef GUESTFS_P2V_CONFIG_H
#define GUESTFS_P2V_CONFIG_H
#include <stdbool.h>
#include <stdint.h>
";
(* Generate enums. *)
List.iter (
fun (name, fields) ->
pr "enum %s {\n" name;
List.iter (
fun (n, comment) ->
pr " %-25s /* %s */\n" (n ^ ",") comment
) fields;
pr "};\n";
pr "\n"
) enums;
(* Generate struct config. *)
generate_config_struct "config" fields;
pr "\
extern struct config *new_config (void);
extern struct config *copy_config (struct config *);
extern void free_config (struct config *);
extern void print_config (struct config *, FILE *);
#endif /* GUESTFS_P2V_CONFIG_H */
"
and generate_config_struct name fields =
(* If there are any ConfigSection (sub-structs) in any of the
* fields then output those first.
*)
List.iter (
function
| ConfigSection (name, fields) ->
generate_config_struct (name ^ "_config") fields
| _ -> ()
) fields;
(* Now generate this struct. *)
pr "struct %s {\n" name;
List.iter (
function
| ConfigString n -> pr " char *%s;\n" n
| ConfigInt (n, _) -> pr " int %s;\n" n
| ConfigUnsigned n -> pr " unsigned %s;\n" n
| ConfigUInt64 n -> pr " uint64_t %s;\n" n
| ConfigEnum n -> pr " enum %s %s;\n" n n
| ConfigBool n -> pr " bool %s;\n" n
| ConfigStringList n -> pr " char **%s;\n" n
| ConfigSection (n, _) -> pr " struct %s_config %s;\n" n n
) fields;
pr "};\n";
pr "\n"
let rec generate_p2v_config_c () =
generate_header CStyle GPLv2plus;
pr "\
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <errno.h>
#include <error.h>
#include \"p2v.h\"
#include \"p2v-config.h\"
/**
* Allocate a new config struct.
*/
struct config *
new_config (void)
{
struct config *c;
c = calloc (1, sizeof *c);
if (c == NULL)
error (EXIT_FAILURE, errno, \"calloc\");
";
generate_field_initialization "c->" fields;
pr "\
return c;
}
/**
* Copy a config struct.
*/
struct config *
copy_config (struct config *old)
{
struct config *c = new_config ();
memcpy (c, old, sizeof *c);
/* Need to deep copy strings and string lists. */
";
generate_field_copy "c->" fields;
pr "\
return c;
}
/**
* Free a config struct.
*/
void
free_config (struct config *c)
{
if (c == NULL)
return;
";
generate_field_free "c->" fields;
pr "\
}
";
List.iter (
fun (name, fields) ->
pr "static void\n";
pr "print_%s (enum %s v, FILE *fp)\n" name name;
pr "{\n";
pr " switch (v) {\n";
List.iter (
fun (n, comment) ->
pr " case %s:\n" n;
pr " fprintf (fp, \"%s\");\n" comment;
pr " break;\n";
) fields;
pr " }\n";
pr "}\n";
pr "\n";
) enums;
pr "\
/**
* Print the conversion parameters and other important information.
*/
void
print_config (struct config *c, FILE *fp)
{
size_t i;
fprintf (fp, \"%%-20s %%s\\n\", \"local version\", PACKAGE_VERSION_FULL);
fprintf (fp, \"%%-20s %%s\\n\", \"remote version\",
v2v_version ? v2v_version : \"unknown\");
";
generate_field_print None "c->" fields;
pr "\
}
"
and generate_field_initialization v fields =
List.iter (
function
| ConfigInt (_, 0) -> ()
| ConfigInt (n, i) ->
pr " %s%s = %d;\n" v n i
| ConfigString _
| ConfigUnsigned _
| ConfigUInt64 _
| ConfigEnum _
| ConfigBool _
| ConfigStringList _ -> ()
| ConfigSection (n, fields) ->
let v = sprintf "%s%s." v n in
generate_field_initialization v fields
) fields
and generate_field_copy v fields =
List.iter (
function
| ConfigString n ->
pr " if (%s%s) {\n" v n;
pr " %s%s = strdup (%s%s);\n" v n v n;
pr " if (%s%s == NULL)\n" v n;
pr " error (EXIT_FAILURE, errno, \"strdup: %%s\", \"%s\");\n" n;
pr " }\n";
| ConfigStringList n ->
pr " if (%s%s) {\n" v n;
pr " %s%s = guestfs_int_copy_string_list (%s%s);\n" v n v n;
pr " if (%s%s == NULL)\n" v n;
pr " error (EXIT_FAILURE, errno, \"copy string list: %%s\", \"%s\");\n" n;
pr " }\n";
| ConfigInt _
| ConfigUnsigned _
| ConfigUInt64 _
| ConfigEnum _
| ConfigBool _ -> ()
| ConfigSection (n, fields) ->
let v = sprintf "%s%s." v n in
generate_field_copy v fields
) fields
and generate_field_free v fields =
List.iter (
function
| ConfigString n ->
pr " free (%s%s);\n" v n
| ConfigStringList n ->
pr " guestfs_int_free_string_list (%s%s);\n" v n
| ConfigInt _
| ConfigUnsigned _
| ConfigUInt64 _
| ConfigEnum _
| ConfigBool _ -> ()
| ConfigSection (n, fields) ->
let v = sprintf "%s%s." v n in
generate_field_free v fields
) fields
and generate_field_print prefix v fields =
List.iter (
fun field ->
let printable_name =
match prefix with
| None -> name_of_config_entry field
| Some prefix -> prefix ^ "." ^ name_of_config_entry field in
match field with
| ConfigString n ->
pr " fprintf (fp, \"%%-20s %%s\\n\",\n";
pr " \"%s\", %s%s ? %s%s : \"(none)\");\n"
printable_name v n v n
| ConfigInt (n, _) ->
pr " fprintf (fp, \"%%-20s %%d\\n\",\n";
pr " \"%s\", %s%s);\n" printable_name v n
| ConfigUnsigned n ->
pr " fprintf (fp, \"%%-20s %%u\\n\",\n";
pr " \"%s\", %s%s);\n" printable_name v n
| ConfigUInt64 n ->
pr " fprintf (fp, \"%%-20s %%\" PRIu64 \"\\n\",\n";
pr " \"%s\", %s%s);\n" printable_name v n
| ConfigEnum n ->
pr " fprintf (fp, \"%%-20s \", \"%s\");\n" printable_name;
pr " print_%s (%s%s, fp);\n" n v n;
pr " fprintf (fp, \"\\n\");\n"
| ConfigBool n ->
pr " fprintf (fp, \"%%-20s %%s\\n\",\n";
pr " \"%s\", %s%s ? \"true\" : \"false\");\n"
printable_name v n
| ConfigStringList n ->
pr " fprintf (fp, \"%%-20s\", \"%s\");\n" printable_name;
pr " if (%s%s) {\n" v n;
pr " for (i = 0; %s%s[i] != NULL; ++i)\n" v n;
pr " fprintf (fp, \" %%s\", %s%s[i]);\n" v n;
pr " }\n";
pr " else\n";
pr " fprintf (fp, \" (none)\\n\");\n";
pr " fprintf (fp, \"\\n\");\n"
| ConfigSection (n, fields) ->
let v = sprintf "%s%s." v n in
generate_field_print (Some printable_name) v fields
) fields

20
generator/p2v_config.mli Normal file
View File

@@ -0,0 +1,20 @@
(* libguestfs
* Copyright (C) 2009-2018 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
val generate_p2v_config_h : unit -> unit
val generate_p2v_config_c : unit -> unit

View File

@@ -18,7 +18,9 @@
include $(top_srcdir)/subdir-rules.mk
generator_built = \
about-authors.c
about-authors.c \
config.c \
p2v-config.h
BUILT_SOURCES = \
$(generator_built)
@@ -90,6 +92,7 @@ virt_p2v_SOURCES = \
main.c \
nbd.c \
p2v.h \
p2v-config.h \
physical-xml.c \
rtc.c \
ssh.c \

View File

@@ -1,208 +0,0 @@
/* virt-p2v
* Copyright (C) 2009-2018 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
#include <locale.h>
#include <libintl.h>
#include "p2v.h"
struct config *
new_config (void)
{
struct config *c;
c = calloc (1, sizeof *c);
if (c == NULL)
error (EXIT_FAILURE, errno, "calloc");
c->port = 22;
c->output_allocation = OUTPUT_ALLOCATION_NONE;
return c;
}
struct config *
copy_config (struct config *old)
{
struct config *c = new_config ();
memcpy (c, old, sizeof *c);
/* Need to deep copy strings and string lists. */
if (c->server)
c->server = strdup (c->server);
if (c->username)
c->username = strdup (c->username);
if (c->password)
c->password = strdup (c->password);
if (c->identity_url)
c->identity_url = strdup (c->identity_url);
if (c->identity_file)
c->identity_file = strdup (c->identity_file);
if (c->guestname)
c->guestname = strdup (c->guestname);
if (c->cpu.vendor)
c->cpu.vendor = strdup (c->cpu.vendor);
if (c->cpu.model)
c->cpu.model = strdup (c->cpu.model);
if (c->disks)
c->disks = guestfs_int_copy_string_list (c->disks);
if (c->removable)
c->removable = guestfs_int_copy_string_list (c->removable);
if (c->interfaces)
c->interfaces = guestfs_int_copy_string_list (c->interfaces);
if (c->network_map)
c->network_map = guestfs_int_copy_string_list (c->network_map);
if (c->output)
c->output = strdup (c->output);
if (c->output_connection)
c->output_connection = strdup (c->output_connection);
if (c->output_format)
c->output_format = strdup (c->output_format);
if (c->output_storage)
c->output_storage = strdup (c->output_storage);
return c;
}
void
free_config (struct config *c)
{
free (c->server);
free (c->username);
free (c->password);
free (c->identity_url);
free (c->identity_file);
free (c->guestname);
free (c->cpu.vendor);
free (c->cpu.model);
guestfs_int_free_string_list (c->disks);
guestfs_int_free_string_list (c->removable);
guestfs_int_free_string_list (c->interfaces);
guestfs_int_free_string_list (c->network_map);
free (c->output);
free (c->output_connection);
free (c->output_format);
free (c->output_storage);
free (c);
}
/**
* Print the conversion parameters and other important information.
*/
void
print_config (struct config *config, FILE *fp)
{
size_t i;
fprintf (fp, "local version . %s\n", PACKAGE_VERSION_FULL);
fprintf (fp, "remote version . %s\n",
v2v_version ? v2v_version : "unknown");
fprintf (fp, "conversion server %s\n",
config->server ? config->server : "none");
fprintf (fp, "port . . . . . . %d\n", config->port);
fprintf (fp, "username . . . . %s\n",
config->username ? config->username : "none");
fprintf (fp, "password . . . . %s\n",
config->password && strlen (config->password) > 0 ? "***" : "none");
fprintf (fp, "identity URL . . %s\n",
config->identity_url ? config->identity_url : "none");
fprintf (fp, "sudo . . . . . . %s\n",
config->sudo ? "true" : "false");
fprintf (fp, "guest name . . . %s\n",
config->guestname ? config->guestname : "none");
fprintf (fp, "vcpus . . . . . %d\n", config->vcpus);
fprintf (fp, "memory . . . . . %" PRIu64 "\n", config->memory);
if (config->cpu.vendor)
fprintf (fp, "cpu vendor . . . %s\n", config->cpu.vendor);
if (config->cpu.model)
fprintf (fp, "cpu model . . . %s\n", config->cpu.model);
if (config->cpu.sockets > 0)
fprintf (fp, "cpu sockets . . %u\n", config->cpu.sockets);
if (config->cpu.cores > 0)
fprintf (fp, "cpu cores . . . %u\n", config->cpu.cores);
if (config->cpu.threads > 0)
fprintf (fp, "cpu threads . . %u\n", config->cpu.threads);
fprintf (fp, "flags . . . . . %s%s%s\n",
config->cpu.acpi ? " acpi" : "",
config->cpu.apic ? " apic" : "",
config->cpu.pae ? " pae" : "");
fprintf (fp, "rtc offset . . . ");
switch (config->rtc.basis) {
case BASIS_UNKNOWN:
fprintf (fp, "unknown");
break;
case BASIS_UTC:
fprintf (fp, "%d seconds from UTC", config->rtc.offset);
break;
case BASIS_LOCALTIME:
fprintf (fp, "%d seconds from localtime", config->rtc.offset);
break;
}
fprintf (fp, "\n");
fprintf (fp, "disks . . . . . ");
if (config->disks != NULL) {
for (i = 0; config->disks[i] != NULL; ++i)
fprintf (fp, " %s", config->disks[i]);
}
fprintf (fp, "\n");
fprintf (fp, "removable . . . ");
if (config->removable != NULL) {
for (i = 0; config->removable[i] != NULL; ++i)
fprintf (fp, " %s", config->removable[i]);
}
fprintf (fp, "\n");
fprintf (fp, "interfaces . . . ");
if (config->interfaces != NULL) {
for (i = 0; config->interfaces[i] != NULL; ++i)
fprintf (fp, " %s", config->interfaces[i]);
}
fprintf (fp, "\n");
fprintf (fp, "network map . . ");
if (config->network_map != NULL) {
for (i = 0; config->network_map[i] != NULL; ++i)
fprintf (fp, " %s", config->network_map[i]);
}
fprintf (fp, "\n");
fprintf (fp, "output . . . . . %s\n",
config->output ? config->output : "none");
fprintf (fp, "output alloc . . ");
switch (config->output_allocation) {
case OUTPUT_ALLOCATION_NONE: fprintf (fp, "none"); break;
case OUTPUT_ALLOCATION_SPARSE: fprintf (fp, "sparse"); break;
case OUTPUT_ALLOCATION_PREALLOCATED: fprintf (fp, "preallocated"); break;
default: fprintf (fp, "unknown? (%d)", config->output_allocation);
}
fprintf (fp, "\n");
fprintf (fp, "output conn . . %s\n",
config->output_connection ? config->output_connection : "none");
fprintf (fp, "output format . %s\n",
config->output_format ? config->output_format : "none");
fprintf (fp, "output storage . %s\n",
config->output_storage ? config->output_storage : "none");
}

View File

@@ -28,6 +28,7 @@
#define DEBUG_STDERR 1
#include "miniexpect.h"
#include "p2v-config.h"
/* We don't use libguestfs directly here, and we don't link to it
* either (in fact, we don't want libguestfs on the ISO). However
@@ -59,61 +60,6 @@ extern int feature_colours_option;
/* virt-p2v --colours option (used by ansi_* macros). */
extern int force_colour;
/* config.c */
struct cpu_config {
char *vendor; /* eg. "Intel" */
char *model; /* eg. "Broadwell" */
unsigned sockets; /* number of sockets */
unsigned cores; /* number of cores per socket */
unsigned threads; /* number of hyperthreads per core */
bool acpi;
bool apic;
bool pae;
};
struct rtc_config {
enum {
BASIS_UNKNOWN, /* RTC could not be read. */
BASIS_UTC, /* RTC is either UTC or an offset from UTC. */
BASIS_LOCALTIME, /* RTC is localtime. */
} basis;
int offset; /* RTC seconds offset from basis. */
};
struct config {
char *server;
int port;
char *username;
char *password;
char *identity_url;
char *identity_file; /* Used to cache the downloaded identity_url. */
int identity_file_needs_update;
int sudo;
char *guestname;
int vcpus;
uint64_t memory;
struct cpu_config cpu;
struct rtc_config rtc;
char **disks;
char **removable;
char **interfaces;
char **network_map;
char *output;
int output_allocation;
char *output_connection;
char *output_format;
char *output_storage;
};
#define OUTPUT_ALLOCATION_NONE 0
#define OUTPUT_ALLOCATION_SPARSE 1
#define OUTPUT_ALLOCATION_PREALLOCATED 2
extern struct config *new_config (void);
extern struct config *copy_config (struct config *);
extern void free_config (struct config *);
extern void print_config (struct config *, FILE *);
/* cpuid.c */
extern void get_cpu_config (struct cpu_config *);

View File

@@ -33,21 +33,21 @@ $VG virt-p2v --cmdline='p2v.server=localhost p2v.port=123 p2v.username=user p2v.
cat $out
# Check the output contains what we expect.
grep "^conversion server.*localhost" $out
grep "^server.*localhost" $out
grep "^port.*123" $out
grep "^username.*user" $out
grep "^sudo.*false" $out
grep "^guest name.*test" $out
grep "^guestname.*test" $out
grep "^vcpus.*4" $out
grep "^memory.*"$((1024*1024*1024)) $out
grep "^disks.*sda sdb sdc" $out
grep "^removable.*sdd" $out
grep "^interfaces.*eth0 eth1" $out
grep "^network map.*em1:wired other" $out
grep "^network_map.*em1:wired other" $out
grep "^output.*local" $out
grep "^output alloc.*sparse" $out
grep "^output conn.*qemu:///session" $out
grep "^output format.*raw" $out
grep "^output storage.*/var/tmp" $out
grep "^output_allocation.*sparse" $out
grep "^output_connection.*qemu:///session" $out
grep "^output_format.*raw" $out
grep "^output_storage.*/var/tmp" $out
rm $out