[Libguestfs] [PATCH 3/3] p2v: Generate the code and docs for parsing the kernel command line.

Richard W.M. Jones rjones at redhat.com
Fri Jun 29 12:16:21 UTC 2018


As a side effect, a lot more fields are now settable on the
kernel command line.

Existing kernel command lines & corresponding documentation should
still remain backwards compatible.
---
 .gitignore               |   2 +
 generator/main.ml        |   4 +
 generator/p2v_config.ml  | 458 ++++++++++++++++++++++++++++++++++++++-
 generator/p2v_config.mli |   2 +
 p2v/Makefile.am          |   8 +-
 p2v/kernel.c             | 154 -------------
 p2v/p2v.h                |   4 +-
 p2v/virt-p2v.pod         | 131 +----------
 po-docs/language.mk      |   8 +
 po-docs/podfiles         |   1 +
 10 files changed, 475 insertions(+), 297 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4c0ff4e1e..d855459f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -435,6 +435,7 @@ Makefile.in
 /p2v/dependencies.debian
 /p2v/dependencies.redhat
 /p2v/dependencies.suse
+/p2v/kernel-config.c
 /p2v/p2v-config.h
 /p2v/stamp-test-virt-p2v-pxe-data-files
 /p2v/stamp-test-virt-p2v-pxe-hostkey
@@ -457,6 +458,7 @@ Makefile.in
 /p2v/virt-p2v.1
 /p2v/virt-p2v.i686
 /p2v/virt-p2v.img
+/p2v/virt-p2v-kernel-config.pod
 /p2v/virt-p2v-make-disk
 /p2v/virt-p2v-make-disk.1
 /p2v/virt-p2v-make-kickstart
diff --git a/generator/main.ml b/generator/main.ml
index ecc551f72..72f31ecab 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -367,6 +367,10 @@ Run it from the top source directory using the command
             P2v_config.generate_p2v_config_h;
   output_to "p2v/config.c"
             P2v_config.generate_p2v_config_c;
+  output_to "p2v/kernel-config.c"
+            P2v_config.generate_p2v_kernel_config_c;
+  output_to "p2v/virt-p2v-kernel-config.pod"
+            P2v_config.generate_p2v_kernel_config_pod;
 
   (* Generate the list of files generated -- last. *)
   printf "generated %d lines of code\n" (get_lines_generated ());
diff --git a/generator/p2v_config.ml b/generator/p2v_config.ml
index e0347d6c2..11aec5ee3 100644
--- a/generator/p2v_config.ml
+++ b/generator/p2v_config.ml
@@ -26,7 +26,7 @@ open Pr
 
 let generate_header = generate_header ~inputs:["generator/p2v_config.ml"]
 
-type enum_choice = string * string (* name, comment *)
+type enum_choice = string * string * string (* name, cmdline, comment *)
 type enum = string * enum_choice list
 
 type config_entry =
@@ -39,17 +39,22 @@ type config_entry =
   | ConfigStringList of string
   | ConfigSection of string * config_entry list
 
+type manual_entry = {
+  shortopt : string;
+  description : string;
+}
+
 (* 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";
+    "BASIS_UNKNOWN",   "unknown",   "RTC could not be read";
+    "BASIS_UTC",       "utc",       "RTC is either UTC or an offset from UTC";
+    "BASIS_LOCALTIME", "localtime", "RTC is localtime";
   ];
   "output_allocation", [
-    "OUTPUT_ALLOCATION_NONE",   "output allocation not set";
-    "OUTPUT_ALLOCATION_SPARSE", "sparse";
-    "OUTPUT_ALLOCATION_PREALLOCATED", "preallocated";
+    "OUTPUT_ALLOCATION_NONE",         "none", "output allocation not set";
+    "OUTPUT_ALLOCATION_SPARSE",       "sparse",       "sparse";
+    "OUTPUT_ALLOCATION_PREALLOCATED", "preallocated", "preallocated";
   ];
 ]
 
@@ -99,6 +104,245 @@ let fields = [
                 ]);
 ]
 
+(* Some /proc/cmdline p2v.* options were renamed when we introduced
+ * the generator.  This map creates backwards compatibility mappings
+ * for these.
+ *)
+let cmdline_aliases = [
+  "p2v.remote.server",     ["p2v.server"];
+  "p2v.remote.port",       ["p2v.port"];
+  "p2v.auth.username",     ["p2v.username"];
+  "p2v.auth.password",     ["p2v.password"];
+  "p2v.auth.identity.url", ["p2v.identity"];
+  "p2v.auth.sudo",         ["p2v.sudo"];
+  "p2v.guestname",         ["p2v.name"];
+  "p2v.network_map",       ["p2v.network"];
+  "p2v.output.type",       ["p2v.o"];
+  "p2v.output.allocation", ["p2v.oa"];
+  "p2v.output.connection", ["p2v.oc"];
+  "p2v.output.format",     ["p2v.of"];
+  "p2v.output.storage",    ["p2v.os"];
+]
+
+(* Some config entries are not exposed on the kernel command line. *)
+let cmdline_ignore = [
+  "p2v.auth.identity.file";
+  "p2v.auth.identity.file_needs_update";
+]
+
+(* Man page snippets for each kernel command line setting. *)
+let cmdline_manual = [
+  "p2v.remote.server", {
+    shortopt = "SERVER";
+    description = "
+The name or IP address of the conversion server.
+
+This is always required if you are using the kernel configuration
+method.  If virt-p2v does not find this on the kernel command line
+then it switches to the GUI (interactive) configuration method.";
+  };
+  "p2v.remote.port", {
+    shortopt = "PORT";
+    description = "
+The SSH port number on the conversion server (default: C<22>).";
+  };
+  "p2v.auth.username", {
+    shortopt = "USERNAME";
+    description = "
+The SSH username that we log in as on the conversion server
+(default: C<root>).";
+  };
+  "p2v.auth.password", {
+    shortopt = "PASSWORD";
+    description = "
+The SSH password that we use to log in to the conversion server.
+
+The default is to try with no password.  If this fails then virt-p2v
+will ask the user to type the password (probably several times during
+conversion).
+
+This setting is ignored if C<p2v.auth.identity.url> is present.";
+  };
+  "p2v.auth.identity.url", {
+    shortopt = "URL";
+    description = "
+Provide a URL pointing to an SSH identity (private key) file.  The URL
+is interpreted by L<curl(1)> so any URL that curl supports can be used
+here, including C<https://> and C<file://>.  For more information on
+using SSH identities, see L</SSH IDENTITIES> below.
+
+If C<p2v.auth.identity.url> is present, it overrides C<p2v.auth.password>.
+There is no fallback.";
+  };
+  "p2v.auth.sudo", {
+    shortopt = ""; (* ignored for booleans *)
+    description = "
+Use C<p2v.sudo> to tell virt-p2v to use L<sudo(8)> to gain root
+privileges on the conversion server after logging in as a non-root
+user (default: do not use sudo).";
+  };
+  "p2v.guestname", {
+    shortopt = "GUESTNAME";
+    description = "
+The name of the guest that is created.  The default is to try to
+derive a name from the physical machine’s hostname (if possible) else
+use a randomly generated name.";
+  };
+  "p2v.vcpus", {
+    shortopt = "N";
+    description = "
+The number of virtual CPUs to give to the guest.  The default is to
+use the same as the number of physical CPUs.";
+  };
+  "p2v.memory", {
+    shortopt = "n(M|G)";
+    description = "
+The size of the guest memory.  You must specify the unit such as
+megabytes or gigabytes by using for example C<p2v.memory=1024M> or
+C<p2v.memory=1G>.
+
+The default is to use the same amount of RAM as on the physical
+machine.";
+  };
+  "p2v.cpu.vendor", {
+    shortopt = "VENDOR";
+    description = "
+The vCPU vendor, eg. \"Intel\" or \"AMD\".  The default is to use
+the same CPU vendor as the physical machine.";
+  };
+  "p2v.cpu.model", {
+    shortopt = "MODEL";
+    description = "
+The vCPU model, eg. \"IvyBridge\".  The default is to use the same
+CPU model as the physical machine.";
+  };
+  "p2v.cpu.sockets", {
+    shortopt = "N";
+    description = "
+Number of vCPU sockets to use.  The default is to use the same as the
+physical machine.";
+  };
+  "p2v.cpu.cores", {
+    shortopt = "N";
+    description = "
+Number of vCPU cores to use.  The default is to use the same as the
+physical machine.";
+  };
+  "p2v.cpu.threads", {
+    shortopt = "N";
+    description = "
+Number of vCPU hyperthreads to use.  The default is to use the same
+as the physical machine.";
+  };
+  "p2v.cpu.acpi", {
+    shortopt = ""; (* ignored for booleans *)
+    description = "
+Whether to enable ACPI in the remote virtual machine.  The default is
+to use the same as the physical machine.";
+  };
+  "p2v.cpu.apic", {
+    shortopt = ""; (* ignored for booleans *)
+    description = "
+Whether to enable APIC in the remote virtual machine.  The default is
+to use the same as the physical machine.";
+  };
+  "p2v.cpu.pae", {
+    shortopt = ""; (* ignored for booleans *)
+    description = "
+Whether to enable PAE in the remote virtual machine.  The default is
+to use the same as the physical machine.";
+  };
+  "p2v.rtc.basis", {
+    shortopt = ""; (* ignored for enums. *)
+    description = "
+Set the basis of the Real Time Clock in the virtual machine.  The
+default is to try to detect this setting from the physical machine.";
+  };
+  "p2v.rtc.offset", {
+    shortopt = "[+|-]HOURS";
+    description = "
+The offset of the Real Time Clock from UTC.  The default is to try
+to detect this setting from the physical machine.";
+  };
+  "p2v.disks", {
+    shortopt = "sda,sdb,...";
+    description = "
+A list of physical hard disks to convert, for example:
+
+ p2v.disks=sda,sdc
+
+The default is to convert all local hard disks that are found.";
+  };
+  "p2v.removable", {
+    shortopt = "sra,srb,...";
+    description = "
+A list of removable media to convert.  The default is to create
+virtual removable devices for every physical removable device found.
+Note that the content of removable media is never copied over.";
+  };
+  "p2v.interfaces", {
+    shortopt = "em1,...";
+    description = "
+A list of network interfaces to convert.  The default is to create
+virtual network interfaces for every physical network interface found.";
+  };
+  "p2v.network_map", {
+    shortopt = "interface:target,...";
+    description = "
+Controls how network interfaces are connected to virtual networks on
+the target hypervisor.  The default is to connect all network
+interfaces to the target C<default> network.
+
+You give a comma-separated list of C<interface:target> pairs, plus
+optionally a default target.  For example:
+
+ p2v.network=em1:ovirtmgmt
+
+maps interface C<em1> to target network C<ovirtmgmt>.
+
+ p2v.network=em1:ovirtmgmt,em2:management,other
+
+maps interface C<em1> to C<ovirtmgmt>, and C<em2> to C<management>,
+and any other interface that is found to C<other>.";
+  };
+  "p2v.output.type", {
+    shortopt = "(libvirt|local|...)";
+    description = "
+Set the output mode.  This is the same as the virt-v2v I<-o> option.
+See L<virt-v2v(1)/OPTIONS>.
+
+If not specified, the default is C<local>, and the converted guest is
+written to F</var/tmp>.";
+  };
+  "p2v.output.allocation", {
+    shortopt = ""; (* ignored for enums *)
+    description = "
+Set the output allocation mode.  This is the same as the virt-v2v
+I<-oa> option.  See L<virt-v2v(1)/OPTIONS>.";
+  };
+  "p2v.output.connection", {
+    shortopt = "URI";
+    description = "
+Set the output connection libvirt URI.  This is the same as the
+virt-v2v I<-oc> option.  See L<virt-v2v(1)/OPTIONS> and
+L<http://libvirt.org/uri.html>";
+  };
+  "p2v.output.format", {
+    shortopt = "(raw|qcow2|...)";
+    description = "
+Set the output format.  This is the same as the virt-v2v I<-of>
+option.  See L<virt-v2v(1)/OPTIONS>.";
+  };
+  "p2v.output.storage", {
+    shortopt = "STORAGE";
+    description = "
+Set the output storage.  This is the same as the virt-v2v I<-os>
+option.  See L<virt-v2v(1)/OPTIONS>.
+
+If not specified, the default is F</var/tmp> (on the conversion server).";
+  };
+]
+
 let name_of_config_entry = function
   | ConfigString n
   | ConfigInt (n, _)
@@ -126,7 +370,7 @@ let rec generate_p2v_config_h () =
     fun (name, fields) ->
       pr "enum %s {\n" name;
       List.iter (
-        fun (n, comment) ->
+        fun (n, _, comment) ->
           pr "  %-25s /* %s */\n" (n ^ ",") comment
       ) fields;
       pr "};\n";
@@ -256,9 +500,9 @@ pr "\
       pr "{\n";
       pr "  switch (v) {\n";
       List.iter (
-        fun (n, comment) ->
+        fun (n, cmdline, _) ->
           pr "  case %s:\n" n;
-          pr "    fprintf (fp, \"%s\");\n" comment;
+          pr "    fprintf (fp, \"%s\");\n" cmdline;
           pr "    break;\n";
       ) fields;
       pr "  }\n";
@@ -395,3 +639,197 @@ and generate_field_print prefix v fields =
          let v = sprintf "%s%s." v n in
          generate_field_print (Some printable_name) v fields
   ) fields
+
+let rec generate_p2v_kernel_config_c () =
+  generate_header CStyle GPLv2plus;
+
+  pr "\
+#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 \"xstrtol.h\"
+
+#include \"p2v.h\"
+#include \"p2v-config.h\"
+
+/**
+ * Read the kernel command line and parse out any C<p2v.*> fields that
+ * we understand into the config struct.
+ */
+void
+update_config_from_kernel_cmdline (struct config *c, char **cmdline)
+{
+  const char *p;
+  strtol_error xerr;
+  unsigned long long ull;
+
+";
+
+  generate_field_config "p2v" "c->" fields;
+
+  pr "  if (c->auth.identity.url != NULL)
+    c->auth.identity.file_needs_update = 1;
+
+  /* Undocumented command line parameter used for testing command line
+   * parsing.
+   */
+  p = get_cmdline_key (cmdline, \"p2v.dump_config_and_exit\");
+  if (p) {
+    print_config (c, stdout);
+    exit (EXIT_SUCCESS);
+  }
+}
+"
+
+and generate_field_config prefix v fields =
+  List.iter (
+    function
+    | ConfigSection (n, fields) ->
+       let prefix = sprintf "%s.%s" prefix n in
+       let v = sprintf "%s%s." v n in
+       generate_field_config prefix v fields
+
+    | field ->
+      let n = name_of_config_entry field in
+      let key = sprintf "%s.%s" prefix n in
+
+      if not (List.mem key cmdline_ignore) then (
+        (* Is there an alias for this field? *)
+        let aliases =
+          try List.assoc key cmdline_aliases
+          with Not_found -> [] in
+
+        pr "  if ((p = get_cmdline_key (cmdline, \"%s\")) != NULL" key;
+        List.iter (
+          fun alias ->
+            pr " ||\n";
+            pr "      (p = get_cmdline_key (cmdline, \"%s\")) != NULL" alias;
+        ) aliases;
+        pr ") {\n";
+
+        (* Parse the field. *)
+        (match field with
+         | ConfigString n ->
+            pr "    free (%s%s);\n" v n;
+            pr "    %s%s = strdup (p);\n" v n;
+            pr "    if (%s%s == NULL)\n" v n;
+            pr "      error (EXIT_FAILURE, errno, \"strdup\");\n"
+         | ConfigStringList n ->
+            pr "    guestfs_int_free_string_list (%s%s);\n" v n;
+            pr "    %s%s = guestfs_int_split_string (',', p);\n" v n;
+            pr "    if (%s%s == NULL)\n" v n;
+            pr "      error (EXIT_FAILURE, errno, \"strdup\");\n"
+         | ConfigInt (n, _) ->
+            pr "    if (sscanf (p, \"%%d\", &%s%s) != 1)\n" v n;
+            pr "      error (EXIT_FAILURE, errno,\n";
+            pr "             \"cannot parse %%s=%%s from the kernel command line\",\n";
+            pr "             %S, p);\n" key
+         | ConfigUnsigned n ->
+            pr "    if (sscanf (p, \"%%u\", &%s%s) != 1)\n" v n;
+            pr "      error (EXIT_FAILURE, errno,\n";
+            pr "             \"cannot parse %%s=%%s from the kernel command line\",\n";
+            pr "             %S, p);\n" key
+         | ConfigUInt64 n ->
+            pr "    xerr = xstrtoull (p, NULL, 0, &ull, \"0kKMGTPEZY\");\n";
+            pr "    if (xerr != LONGINT_OK)\n";
+            pr "      error (EXIT_FAILURE, 0,\n";
+            pr "             \"cannot parse %%s=%%s from the kernel command line\",\n";
+            pr "             %S, p);\n" key;
+            pr "    %s%s = ull;\n" v n
+         | ConfigEnum (n, enum) ->
+            let enum_choices =
+              try List.assoc enum enums
+              with Not_found -> failwithf "cannot find ConfigEnum %s" enum in
+            pr "    ";
+            List.iter (
+              fun (name, cmdline, _) ->
+                pr "if (STREQ (p, \"%s\"))\n" cmdline;
+                pr "      %s%s = %s;\n" v n name;
+                pr "    else ";
+            ) enum_choices;
+            pr "{\n";
+            pr "      error (EXIT_FAILURE, 0,\n";
+            pr "             \"invalid value %%s=%%s from the kernel command line\",\n";
+            pr "             %S, p);\n" key;
+            pr "    }\n"
+         | ConfigBool n ->
+            pr "    %s%s = guestfs_int_is_true (p) || STREQ (p, \"\");\n" v n
+
+         | ConfigSection _ -> assert false (* see above *)
+        );
+
+        pr "  }\n";
+        pr "\n";
+      )
+  ) fields
+
+let rec generate_p2v_kernel_config_pod () =
+  generate_field_config_pod "p2v" fields
+
+and generate_field_config_pod prefix fields =
+  List.iter (
+    function
+    | ConfigSection (n, fields) ->
+       let prefix = sprintf "%s.%s" prefix n in
+       generate_field_config_pod prefix fields
+
+    | field ->
+      let n = name_of_config_entry field in
+      let key = sprintf "%s.%s" prefix n in
+
+      if not (List.mem key cmdline_ignore) then (
+        let manual_entry =
+          try List.assoc key cmdline_manual
+          with Not_found ->
+            failwithf "generator/p2v_config.ml: missing manual entry for %s"
+                      key in
+
+        (* For booleans there is no shortopt field.  For enums
+         * we generate it.
+         *)
+        let shortopt =
+          match field with
+          | ConfigBool _ ->
+             assert (manual_entry.shortopt = "");
+             ""
+          | ConfigEnum (_, enum) ->
+             assert (manual_entry.shortopt = "");
+
+             let enum_choices =
+              try List.assoc enum enums
+              with Not_found -> failwithf "cannot find ConfigEnum %s" enum in
+             "=(" ^
+               String.concat "|"
+                             (List.map (fun (_, cmdline, _) -> cmdline)
+                                       enum_choices) ^
+             ")"
+          | ConfigString _
+          | ConfigInt _
+          | ConfigUnsigned _
+          | ConfigUInt64 _
+          | ConfigStringList _
+          | ConfigSection _ -> "=" ^ manual_entry.shortopt in
+
+        (* The description must not end with \n *)
+        if String.is_suffix manual_entry.description "\n" then
+          failwithf "generator/p2v_config.ml: description of %s must not end with \\n"
+                    key;
+
+        (* Is there an alias for this field? *)
+        let aliases =
+          try List.assoc key cmdline_aliases
+          with Not_found -> [] in
+        List.iter (
+          fun k -> pr "=item B<%s%s>\n\n" k shortopt
+        ) (key :: aliases);
+
+        pr "%s\n\n" manual_entry.description
+      )
+  ) fields
diff --git a/generator/p2v_config.mli b/generator/p2v_config.mli
index 55d0363a3..43a02e5f1 100644
--- a/generator/p2v_config.mli
+++ b/generator/p2v_config.mli
@@ -18,3 +18,5 @@
 
 val generate_p2v_config_h : unit -> unit
 val generate_p2v_config_c : unit -> unit
+val generate_p2v_kernel_config_c : unit -> unit
+val generate_p2v_kernel_config_pod : unit -> unit
diff --git a/p2v/Makefile.am b/p2v/Makefile.am
index f66c2a09b..cfdec535d 100644
--- a/p2v/Makefile.am
+++ b/p2v/Makefile.am
@@ -20,7 +20,9 @@ include $(top_srcdir)/subdir-rules.mk
 generator_built = \
 	about-authors.c \
 	config.c \
-	p2v-config.h
+	kernel-config.c \
+	p2v-config.h \
+	virt-p2v-kernel-config.pod
 
 BUILT_SOURCES = \
 	$(generator_built)
@@ -89,6 +91,7 @@ virt_p2v_SOURCES = \
 	inhibit.c \
 	kernel.c \
 	kernel-cmdline.c \
+	kernel-config.c \
 	main.c \
 	nbd.c \
 	p2v.h \
@@ -165,10 +168,11 @@ noinst_DATA = \
 
 virt-p2v.1 $(top_builddir)/website/virt-p2v.1.html: stamp-virt-p2v.pod
 
-stamp-virt-p2v.pod: virt-p2v.pod
+stamp-virt-p2v.pod: virt-p2v.pod virt-p2v-kernel-config.pod
 	$(PODWRAPPER) \
 	  --man virt-p2v.1 \
 	  --html $(top_builddir)/website/virt-p2v.1.html \
+	  --insert $(srcdir)/virt-p2v-kernel-config.pod:__KERNEL_CONFIG__ \
 	  --license GPLv2+ \
 	  --warning safe \
 	  $<
diff --git a/p2v/kernel.c b/p2v/kernel.c
index 4f999755e..33fff0356 100644
--- a/p2v/kernel.c
+++ b/p2v/kernel.c
@@ -42,160 +42,6 @@
 static void notify_ui_callback (int type, const char *data);
 static void run_command (const char *stage, const char *command);
 
-void
-update_config_from_kernel_cmdline (struct config *config, char **cmdline)
-{
-  const char *p;
-
-  p = get_cmdline_key (cmdline, "p2v.server");
-  if (p) {
-    free (config->remote.server);
-    config->remote.server = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.port");
-  if (p) {
-    if (sscanf (p, "%d", &config->remote.port) != 1)
-      error (EXIT_FAILURE, 0,
-             "cannot parse p2v.port from kernel command line");
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.username");
-  if (p) {
-    free (config->auth.username);
-    config->auth.username = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.password");
-  if (p) {
-    free (config->auth.password);
-    config->auth.password = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.identity");
-  if (p) {
-    free (config->auth.identity.url);
-    config->auth.identity.url = strdup (p);
-    config->auth.identity.file_needs_update = 1;
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.sudo");
-  if (p)
-    config->auth.sudo = 1;
-
-  p = get_cmdline_key (cmdline, "p2v.name");
-  if (p) {
-    free (config->guestname);
-    config->guestname = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.vcpus");
-  if (p) {
-    if (sscanf (p, "%d", &config->vcpus) != 1)
-      error (EXIT_FAILURE, 0,
-             "cannot parse p2v.vcpus from kernel command line");
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.memory");
-  if (p) {
-    char mem_code;
-
-    if (sscanf (p, "%" SCNu64 "%c", &config->memory, &mem_code) != 2)
-      error (EXIT_FAILURE, 0,
-             "cannot parse p2v.memory from kernel command line");
-    config->memory *= 1024;
-    if (mem_code == 'M' || mem_code == 'm'
-        || mem_code == 'G' || mem_code == 'g')
-      config->memory *= 1024;
-    if (mem_code == 'G' || mem_code == 'g')
-      config->memory *= 1024;
-    if (mem_code != 'M' && mem_code != 'm'
-        && mem_code != 'G' && mem_code != 'g')
-      error (EXIT_FAILURE, 0,
-             "p2v.memory on kernel command line must be followed by 'G' or 'M'");
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.disks");
-  if (p) {
-    CLEANUP_FREE char *t;
-
-    t = strdup (p);
-    guestfs_int_free_string_list (config->disks);
-    config->disks = guestfs_int_split_string (',', t);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.removable");
-  if (p) {
-    CLEANUP_FREE char *t;
-
-    t = strdup (p);
-    guestfs_int_free_string_list (config->removable);
-    config->removable = guestfs_int_split_string (',', t);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.interfaces");
-  if (p) {
-    CLEANUP_FREE char *t;
-
-    t = strdup (p);
-    guestfs_int_free_string_list (config->interfaces);
-    config->interfaces = guestfs_int_split_string (',', t);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.network");
-  if (p) {
-    CLEANUP_FREE char *t;
-
-    t = strdup (p);
-    guestfs_int_free_string_list (config->network_map);
-    config->network_map = guestfs_int_split_string (',', t);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.o");
-  if (p) {
-    free (config->output.type);
-    config->output.type = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.oa");
-  if (p) {
-    if (STREQ (p, "sparse"))
-      config->output.allocation = OUTPUT_ALLOCATION_SPARSE;
-    else if (STREQ (p, "preallocated"))
-      config->output.allocation = OUTPUT_ALLOCATION_PREALLOCATED;
-    else
-      fprintf (stderr, "%s: warning: don't know what p2v.oa=%s means\n",
-               getprogname (), p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.oc");
-  if (p) {
-    free (config->output.connection);
-    config->output.connection = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.of");
-  if (p) {
-    free (config->output.format);
-    config->output.format = strdup (p);
-  }
-
-  p = get_cmdline_key (cmdline, "p2v.os");
-  if (p) {
-    free (config->output.storage);
-    config->output.storage = strdup (p);
-  }
-
-  /* Undocumented command line parameter used for testing command line
-   * parsing.
-   */
-  p = get_cmdline_key (cmdline, "p2v.dump_config_and_exit");
-  if (p) {
-    print_config (config, stdout);
-    exit (EXIT_SUCCESS);
-  }
-}
-
 /* Perform conversion using the kernel method. */
 void
 kernel_conversion (struct config *config, char **cmdline, int cmdline_source)
diff --git a/p2v/p2v.h b/p2v/p2v.h
index 021ea2946..ee661514c 100644
--- a/p2v/p2v.h
+++ b/p2v/p2v.h
@@ -74,8 +74,10 @@ extern const char *get_cmdline_key (char **cmdline, const char *key);
 #define CMDLINE_SOURCE_COMMAND_LINE 1 /* --cmdline */
 #define CMDLINE_SOURCE_PROC_CMDLINE 2 /* /proc/cmdline */
 
-/* kernel.c */
+/* kernel-config.c */
 extern void update_config_from_kernel_cmdline (struct config *config, char **cmdline);
+
+/* kernel.c */
 extern void kernel_conversion (struct config *, char **cmdline, int cmdline_source);
 
 /* gui.c */
diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod
index fc2e7d310..48548f8c6 100644
--- a/p2v/virt-p2v.pod
+++ b/p2v/virt-p2v.pod
@@ -302,136 +302,7 @@ You have to set some or all of the following command line arguments:
 
 =over 4
 
-=item B<p2v.server=SERVER>
-
-The name or IP address of the conversion server.
-
-This is always required if you are using the kernel configuration
-method.  If virt-p2v does not find this on the kernel command line
-then it switches to the GUI (interactive) configuration method.
-
-=item B<p2v.port=NN>
-
-The SSH port number on the conversion server (default: C<22>).
-
-=item B<p2v.username=USERNAME>
-
-The SSH username that we log in as on the conversion server
-(default: C<root>).
-
-=item B<p2v.password=PASSWORD>
-
-The SSH password that we use to log in to the conversion server.
-
-The default is to try with no password.  If this fails then virt-p2v
-will ask the user to type the password (probably several times during
-conversion).
-
-This setting is ignored if C<p2v.identity> is present.
-
-=item B<p2v.identity=URL>
-
-Provide a URL pointing to an SSH identity (private key) file.  The URL
-is interpreted by L<curl(1)> so any URL that curl supports can be used
-here, including C<https://> and C<file://>.  For more information on
-using SSH identities, see L</SSH IDENTITIES> below.
-
-If C<p2v.identity> is present, it overrides C<p2v.password>.  There is
-no fallback.
-
-=item B<p2v.sudo>
-
-Use C<p2v.sudo> to tell virt-p2v to use L<sudo(8)> to gain root
-privileges on the conversion server after logging in as a non-root
-user (default: do not use sudo).
-
-=item B<p2v.name=GUESTNAME>
-
-The name of the guest that is created.  The default is to try to
-derive a name from the physical machine’s hostname (if possible) else
-use a randomly generated name.
-
-=item B<p2v.vcpus=NN>
-
-The number of virtual CPUs to give to the guest.  The default is to
-use the same as the number of physical CPUs.
-
-=item B<p2v.memory=NN(M|G)>
-
-The size of the guest memory.  You must specify the unit as either
-megabytes or gigabytes by using (eg) C<p2v.memory=1024M> or
-C<p2v.memory=1G>.
-
-The default is to use the same amount of RAM as on the physical
-machine.
-
-=item B<p2v.disks=sdX,sdY,..>
-
-A list of physical hard disks to convert, for example:
-
- p2v.disks=sda,sdc
-
-The default is to convert all local hard disks that are found.
-
-=item B<p2v.removable=srX,srY,..>
-
-A list of removable media to convert.  The default is to create
-virtual removable devices for every physical removable device found.
-Note that the content of removable media is never copied over.
-
-=item B<p2v.interfaces=em1,..>
-
-A list of network interfaces to convert.  The default is to create
-virtual network interfaces for every physical network interface found.
-
-=item B<p2v.network=interface:target,...>
-
-Controls how network interfaces are connected to virtual networks on
-the target hypervisor.  The default is to connect all network
-interfaces to the target C<default> network.
-
-You give a comma-separated list of C<interface:target> pairs, plus
-optionally a default target.  For example:
-
- p2v.network=em1:ovirtmgmt
-
-maps interface C<em1> to target network C<ovirtmgmt>.
-
- p2v.network=em1:ovirtmgmt,em2:management,other
-
-maps interface C<em1> to C<ovirtmgmt>, and C<em2> to C<management>,
-and any other interface that is found to C<other>.
-
-=item B<p2v.o=[libvirt|local|...]>
-
-Set the output mode.  This is the same as the virt-v2v I<-o> option.
-See L<virt-v2v(1)/OPTIONS>.
-
-If not specified, the default is C<local>, and the converted guest is
-written to F</var/tmp>.
-
-=item B<p2v.oa=sparse|preallocated>
-
-Set the output allocation mode.  This is the same as the virt-v2v
-I<-oa> option.  See L<virt-v2v(1)/OPTIONS>.
-
-=item B<p2v.oc=...>
-
-Set the output connection libvirt URI.  This is the same as the
-virt-v2v I<-oc> option.  See L<virt-v2v(1)/OPTIONS> and
-L<http://libvirt.org/uri.html>
-
-=item B<p2v.of=raw|qcow2|...>
-
-Set the output format.  This is the same as the virt-v2v I<-of>
-option.  See L<virt-v2v(1)/OPTIONS>.
-
-=item B<p2v.os=...>
-
-Set the output storage.  This is the same as the virt-v2v I<-os>
-option.  See L<virt-v2v(1)/OPTIONS>.
-
-If not specified, the default is F</var/tmp> (on the conversion server).
+__KERNEL_CONFIG__
 
 =item B<p2v.pre=COMMAND>
 
diff --git a/po-docs/language.mk b/po-docs/language.mk
index 3eeb058e0..0163edab3 100644
--- a/po-docs/language.mk
+++ b/po-docs/language.mk
@@ -148,6 +148,14 @@ virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.po
           --insert $(srcdir)/sysprep-operations.pod:__OPERATIONS__ \
 	  $<
 
+virt-p2v.1: virt-p2v.pod virt-p2v-kernel-config.pod
+	$(PODWRAPPER) \
+	  --no-strict-checks \
+	  --man $@ \
+	  --license GPLv2+ \
+	  --insert $(srcdir)/virt-p2v-kernel-config.pod:__KERNEL_CONFIG__ \
+	  $<
+
 %.1: %.pod
 	$(PODWRAPPER) \
 	  --no-strict-checks \
diff --git a/po-docs/podfiles b/po-docs/podfiles
index 31a3aca3a..b6bd39b68 100644
--- a/po-docs/podfiles
+++ b/po-docs/podfiles
@@ -53,6 +53,7 @@
 ../lua/examples/guestfs-lua.pod
 ../make-fs/virt-make-fs.pod
 ../ocaml/examples/guestfs-ocaml.pod
+../p2v/virt-p2v-kernel-config.pod
 ../p2v/virt-p2v-make-disk.pod
 ../p2v/virt-p2v-make-kickstart.pod
 ../p2v/virt-p2v-make-kiwi.pod
-- 
2.17.1




More information about the Libguestfs mailing list