X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=9e17d6e4de9856c0984dfb13f0f4caaab4830d7f;hp=8c75b933efef4fd07ed48db2c12ae2721f45238d;hb=c41fe04a652437c920acb0e820762c53bf44a139;hpb=08c333a73893a0fc413e7619c8e64bebaebc741a diff --git a/src/generator.ml b/src/generator.ml index 8c75b93..9e17d6e 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -33,6 +33,7 @@ *) #load "unix.cma";; +#load "str.cma";; open Printf @@ -115,6 +116,7 @@ type flags = | FishAlias of string (* provide an alias for this cmd in guestfish *) | FishAction of string (* call this function in guestfish *) | NotInFish (* do not export via guestfish *) + | NotInDocs (* do not add this function to documentation *) let protocol_limit_warning = "Because of the message protocol, there is a transfer limit @@ -135,12 +137,30 @@ can easily destroy all your data>." * the virtual machine and block devices are reused between tests. * So don't try testing kill_subprocess :-x * - * Between each test we umount-all and lvm-remove-all (except InitNone). + * Between each test we blockdev-setrw, umount-all, lvm-remove-all. + * + * If the appliance is running an older Linux kernel (eg. RHEL 5) then + * devices are named /dev/hda etc. To cope with this, the test suite + * adds some hairly logic to detect this case, and then automagically + * replaces all strings which match "/dev/sd.*" with "/dev/hd.*". + * When writing test cases you shouldn't have to worry about this + * difference. * * Don't assume anything about the previous contents of the block * devices. Use 'Init*' to create some initial scenarios. + * + * You can add a prerequisite clause to any individual test. This + * is a run-time check, which, if it fails, causes the test to be + * skipped. Useful if testing a command which might not work on + * all variations of libguestfs builds. A test that has prerequisite + * of 'Always' is run unconditionally. + * + * In addition, packagers can skip individual tests by setting the + * environment variables: eg: + * SKIP_TEST__=1 SKIP_TEST_COMMAND_3=1 (skips test #3 of command) + * SKIP_TEST_=1 SKIP_TEST_ZEROFREE=1 (skips all zerofree tests) *) -type tests = (test_init * test) list +type tests = (test_init * test_prereq * test) list and test = (* Run the command sequence and just expect nothing to fail. *) | TestRun of seq @@ -184,6 +204,21 @@ and test_field_compare = | CompareFieldsIntEq of string * string | CompareFieldsStrEq of string * string +(* Test prerequisites. *) +and test_prereq = + (* Test always runs. *) + | Always + (* Test is currently disabled - eg. it fails, or it tests some + * unimplemented feature. + *) + | Disabled + (* 'string' is some C code (a function body) that should return + * true or false. The test will run if the code returns true. + *) + | If of string + (* As for 'If' but the test runs _unless_ the code returns true. *) + | Unless of string + (* Some initial scenarios for testing. *) and test_init = (* Do nothing, block devices could contain random stuff including @@ -219,7 +254,81 @@ and cmd = string list * Apart from that, long descriptions are just perldoc paragraphs. *) -let non_daemon_functions = [ +(* These test functions are used in the language binding tests. *) + +let test_all_args = [ + String "str"; + OptString "optstr"; + StringList "strlist"; + Bool "b"; + Int "integer"; + FileIn "filein"; + FileOut "fileout"; +] + +let test_all_rets = [ + (* except for RErr, which is tested thoroughly elsewhere *) + "test0rint", RInt "valout"; + "test0rint64", RInt64 "valout"; + "test0rbool", RBool "valout"; + "test0rconststring", RConstString "valout"; + "test0rstring", RString "valout"; + "test0rstringlist", RStringList "valout"; + "test0rintbool", RIntBool ("valout", "valout"); + "test0rpvlist", RPVList "valout"; + "test0rvglist", RVGList "valout"; + "test0rlvlist", RLVList "valout"; + "test0rstat", RStat "valout"; + "test0rstatvfs", RStatVFS "valout"; + "test0rhashtable", RHashtable "valout"; +] + +let test_functions = [ + ("test0", (RErr, test_all_args), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +parameter type correctly. + +It echos the contents of each parameter to stdout. + +You probably don't want to call this function."); +] @ List.flatten ( + List.map ( + fun (name, ret) -> + [(name, (ret, [String "val"]), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +return type correctly. + +It converts string C to the return type. + +You probably don't want to call this function."); + (name ^ "err", (ret, []), -1, [NotInFish; NotInDocs], + [], + "internal test function - do not use", + "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +return type correctly. + +This function always returns an error. + +You probably don't want to call this function.")] + ) test_all_rets +) + +(* non_daemon_functions are any functions which don't get processed + * in the daemon, eg. functions for setting and getting local + * configuration values. + *) + +let non_daemon_functions = test_functions @ [ ("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"], [], "launch the qemu subprocess", @@ -284,6 +393,29 @@ The first character of C string must be a C<-> (dash). C can be NULL."); + ("set_qemu", (RErr, [String "qemu"]), -1, [FishAlias "qemu"], + [], + "set the qemu binary", + "\ +Set the qemu binary that we will use. + +The default is chosen when the library was compiled by the +configure script. + +You can also override this by setting the C +environment variable. + +Setting C to C restores the default qemu binary."); + + ("get_qemu", (RConstString "qemu", []), -1, [], + [], + "get the qemu binary", + "\ +Return the current qemu binary. + +This is always non-NULL. If it wasn't set already, then this will +return the default qemu binary name."); + ("set_path", (RErr, [String "path"]), -1, [FishAlias "path"], [], "set the search path", @@ -293,9 +425,6 @@ Set the path that libguestfs searches for kernel and initrd.img. The default is C<$libdir/guestfs> unless overridden by setting C environment variable. -The string C is stashed in the libguestfs handle, so the caller -must make sure it remains valid for the lifetime of the handle. - Setting C to C restores the default path."); ("get_path", (RConstString "path", []), -1, [], @@ -307,13 +436,39 @@ Return the current search path. This is always non-NULL. If it wasn't set already, then this will return the default path."); + ("set_append", (RErr, [String "append"]), -1, [FishAlias "append"], + [], + "add options to kernel command line", + "\ +This function is used to add additional options to the +guest kernel command line. + +The default is C unless overridden by setting +C environment variable. + +Setting C to C means I additional options +are passed (libguestfs always adds a few of its own)."); + + ("get_append", (RConstString "append", []), -1, [], + [], + "get the additional kernel options", + "\ +Return the additional kernel options which are added to the +guest kernel command line. + +If C then no options are added."); + ("set_autosync", (RErr, [Bool "autosync"]), -1, [FishAlias "autosync"], [], "set autosync mode", "\ If C is true, this enables autosync. Libguestfs will make a -best effort attempt to run C when the handle is closed -(also if the program exits without closing handles)."); +best effort attempt to run C followed by +C when the handle is closed +(also if the program exits without closing handles). + +This is disabled by default (except in guestfish where it is +enabled by default)."); ("get_autosync", (RBool "autosync", []), -1, [], [], @@ -380,11 +535,44 @@ This returns the current state as an opaque integer. This is only useful for printing debug and internal error messages. For more information on states, see L."); + + ("set_busy", (RErr, []), -1, [NotInFish], + [], + "set state to busy", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + + ("set_ready", (RErr, []), -1, [NotInFish], + [], + "set state to ready", + "\ +This sets the state to C. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + + ("end_busy", (RErr, []), -1, [NotInFish], + [], + "leave the busy state", + "\ +This sets the state to C, or if in C then it leaves the +state as is. This is only used when implementing +actions using the low-level API. + +For more information on states, see L."); + ] +(* daemon_functions are any functions which cause some action + * to take place in the daemon. + *) + let daemon_functions = [ ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [], - [InitEmpty, TestOutput ( + [InitEmpty, Always, TestOutput ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -410,7 +598,7 @@ The filesystem options C and C are set with this call, in order to improve reliability."); ("sync", (RErr, []), 2, [], - [ InitEmpty, TestRun [["sync"]]], + [ InitEmpty, Always, TestRun [["sync"]]], "sync disks, writes are flushed through to the disk image", "\ This syncs the disk, so that any writes are flushed through to the @@ -420,7 +608,7 @@ You should always call this if you have modified a disk image, before closing the handle."); ("touch", (RErr, [String "path"]), 3, [], - [InitBasicFS, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["exists"; "/new"]])], "update file timestamps or create a new file", @@ -430,7 +618,7 @@ update the timestamps on a file, or, if the file does not exist, to create a new zero-length file."); ("cat", (RString "content", [String "path"]), 4, [ProtocolLimitWarning], - [InitBasicFS, TestOutput ( + [InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "new file contents"; "0"]; ["cat"; "/new"]], "new file contents")], "list the contents of a file", @@ -455,7 +643,7 @@ This command is mostly useful for interactive sessions. It is I intended that you try to parse the output string."); ("ls", (RStringList "listing", [String "directory"]), 6, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["touch"; "/new"]; ["touch"; "/newer"]; ["touch"; "/newest"]; @@ -470,7 +658,7 @@ This command is mostly useful for interactive sessions. Programs should probably use C instead."); ("list_devices", (RStringList "devices", []), 7, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])], "list the block devices", "\ @@ -479,9 +667,9 @@ List all the block devices. The full block device names are returned, eg. C"); ("list_partitions", (RStringList "partitions", []), 8, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["list_partitions"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])], "list the partitions", @@ -494,9 +682,9 @@ This does not return logical volumes. For that you will need to call C."); ("pvs", (RStringList "physvols", []), 9, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["pvs"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -513,9 +701,9 @@ PVs (eg. C). See also C."); ("vgs", (RStringList "volgroups", []), 10, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["vgs"]], ["VG"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -534,9 +722,9 @@ detected (eg. C). See also C."); ("lvs", (RStringList "logvols", []), 11, [], - [InitBasicFSonLVM, TestOutputList ( + [InitBasicFSonLVM, Always, TestOutputList ( [["lvs"]], ["/dev/VG/LV"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -579,10 +767,10 @@ List all the logical volumes detected. This is the equivalent of the L command. The \"full\" version includes all fields."); ("read_lines", (RStringList "lines", [String "path"]), 15, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"]; ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]); - InitBasicFS, TestOutputList ( + InitBasicFS, Always, TestOutputList ( [["write_file"; "/new"; ""; "0"]; ["read_lines"; "/new"]], [])], "read file as lines", @@ -757,12 +945,12 @@ This is just a shortcut for listing C C and sorting the resulting nodes into alphabetical order."); ("rm", (RErr, [String "path"]), 29, [], - [InitBasicFS, TestRun + [InitBasicFS, Always, TestRun [["touch"; "/new"]; ["rm"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["rm"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["mkdir"; "/new"]; ["rm"; "/new"]]], "remove a file", @@ -770,12 +958,12 @@ C and sorting the resulting nodes into alphabetical order."); Remove the single file C."); ("rmdir", (RErr, [String "path"]), 30, [], - [InitBasicFS, TestRun + [InitBasicFS, Always, TestRun [["mkdir"; "/new"]; ["rmdir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["rmdir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["touch"; "/new"]; ["rmdir"; "/new"]]], "remove a directory", @@ -783,7 +971,7 @@ Remove the single file C."); Remove the single directory C."); ("rm_rf", (RErr, [String "path"]), 31, [], - [InitBasicFS, TestOutputFalse + [InitBasicFS, Always, TestOutputFalse [["mkdir"; "/new"]; ["mkdir"; "/new/foo"]; ["touch"; "/new/foo/bar"]; @@ -796,23 +984,23 @@ contents if its a directory. This is like the C shell command."); ("mkdir", (RErr, [String "path"]), 32, [], - [InitBasicFS, TestOutputTrue + [InitBasicFS, Always, TestOutputTrue [["mkdir"; "/new"]; ["is_dir"; "/new"]]; - InitBasicFS, TestLastFail + InitBasicFS, Always, TestLastFail [["mkdir"; "/new/foo/bar"]]], "create a directory", "\ Create a directory named C."); ("mkdir_p", (RErr, [String "path"]), 33, [], - [InitBasicFS, TestOutputTrue + [InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; ["is_dir"; "/new/foo/bar"]]; - InitBasicFS, TestOutputTrue + InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; ["is_dir"; "/new/foo"]]; - InitBasicFS, TestOutputTrue + InitBasicFS, Always, TestOutputTrue [["mkdir_p"; "/new/foo/bar"]; ["is_dir"; "/new"]]], "create a directory and parents", @@ -838,10 +1026,10 @@ names, you will need to locate and parse the password file yourself (Augeas support makes this relatively easy)."); ("exists", (RBool "existsflag", [String "path"]), 36, [], - [InitBasicFS, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["exists"; "/new"]]); - InitBasicFS, TestOutputTrue ( + InitBasicFS, Always, TestOutputTrue ( [["mkdir"; "/new"]; ["exists"; "/new"]])], "test if file or directory exists", @@ -852,10 +1040,10 @@ This returns C if and only if there is a file, directory See also C, C, C."); ("is_file", (RBool "fileflag", [String "path"]), 37, [], - [InitBasicFS, TestOutputTrue ( + [InitBasicFS, Always, TestOutputTrue ( [["touch"; "/new"]; ["is_file"; "/new"]]); - InitBasicFS, TestOutputFalse ( + InitBasicFS, Always, TestOutputFalse ( [["mkdir"; "/new"]; ["is_file"; "/new"]])], "test if file exists", @@ -867,10 +1055,10 @@ other objects like directories. See also C."); ("is_dir", (RBool "dirflag", [String "path"]), 38, [], - [InitBasicFS, TestOutputFalse ( + [InitBasicFS, Always, TestOutputFalse ( [["touch"; "/new"]; ["is_dir"; "/new"]]); - InitBasicFS, TestOutputTrue ( + InitBasicFS, Always, TestOutputTrue ( [["mkdir"; "/new"]; ["is_dir"; "/new"]])], "test if file exists", @@ -882,7 +1070,7 @@ other objects like files. See also C."); ("pvcreate", (RErr, [String "device"]), 39, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -895,7 +1083,7 @@ where C should usually be a partition name such as C."); ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -909,7 +1097,7 @@ This creates an LVM volume group called C from the non-empty list of physical volumes C."); ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; ["pvcreate"; "/dev/sda1"]; ["pvcreate"; "/dev/sda2"]; @@ -930,7 +1118,7 @@ This creates an LVM volume group called C on the volume group C, with C megabytes."); ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [], - [InitEmpty, TestOutput ( + [InitEmpty, Always, TestOutput ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -939,7 +1127,7 @@ on the volume group C, with C megabytes."); "make a filesystem", "\ This creates a filesystem on C (usually a partition -of LVM logical volume). The filesystem type is C, for +or LVM logical volume). The filesystem type is C, for example C."); ("sfdisk", (RErr, [String "device"; @@ -966,25 +1154,27 @@ information refer to the L manpage. To create a single partition occupying the whole disk, you would pass C as a single element list, when the single element being -the string C<,> (comma)."); +the string C<,> (comma). + +See also: C, C"); ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning], - [InitBasicFS, TestOutput ( + [InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "new file contents"; "0"]; ["cat"; "/new"]], "new file contents"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "\nnew file contents\n"; "0"]; ["cat"; "/new"]], "\nnew file contents\n"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "\n\n"; "0"]; ["cat"; "/new"]], "\n\n"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; ""; "0"]; ["cat"; "/new"]], ""); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "\n\n\n"; "0"]; ["cat"; "/new"]], "\n\n\n"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "\n"; "0"]; ["cat"; "/new"]], "\n")], "create a file", @@ -995,15 +1185,20 @@ with length C. As a special case, if C is C<0> then the length is calculated using C (so in this case -the content cannot contain embedded ASCII NULs)."); +the content cannot contain embedded ASCII NULs). + +I Owing to a bug, writing content containing ASCII NUL +characters does I work, even if the length is specified. +We hope to resolve this bug in a future version. In the meantime +use C."); ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"], - [InitEmpty, TestOutputList ( + [InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["mounts"]], ["/dev/sda1"]); - InitEmpty, TestOutputList ( + InitEmpty, Always, TestOutputList ( [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; @@ -1016,7 +1211,7 @@ specified either by its mountpoint (path) or the device which contains the filesystem."); ("mounts", (RStringList "devices", []), 46, [], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["mounts"]], ["/dev/sda1"])], "show mounted filesystems", "\ @@ -1026,8 +1221,22 @@ the list of devices (eg. C, C). Some internal mounts are not shown."); ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"], - [InitBasicFS, TestOutputList ( + [InitBasicFS, Always, TestOutputList ( [["umount_all"]; + ["mounts"]], []); + (* check that umount_all can unmount nested mounts correctly: *) + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mkfs"; "ext2"; "/dev/sda2"]; + ["mkfs"; "ext2"; "/dev/sda3"]; + ["mount"; "/dev/sda1"; "/"]; + ["mkdir"; "/mp1"]; + ["mount"; "/dev/sda2"; "/mp1"]; + ["mkdir"; "/mp1/mp2"]; + ["mount"; "/dev/sda3"; "/mp1/mp2"]; + ["mkdir"; "/mp1/mp2/mp3"]; + ["umount_all"]; ["mounts"]], [])], "unmount all filesystems", "\ @@ -1043,13 +1252,13 @@ This command removes all LVM logical volumes, volume groups and physical volumes."); ("file", (RString "description", [String "path"]), 49, [], - [InitBasicFS, TestOutput ( + [InitBasicFS, Always, TestOutput ( [["touch"; "/new"]; ["file"; "/new"]], "empty"); - InitBasicFS, TestOutput ( + InitBasicFS, Always, TestOutput ( [["write_file"; "/new"; "some content\n"; "0"]; ["file"; "/new"]], "ASCII text"); - InitBasicFS, TestLastFail ( + InitBasicFS, Always, TestLastFail ( [["file"; "/nofile"]])], "determine file type", "\ @@ -1061,8 +1270,55 @@ The exact command which runs is C. Note in particular that the filename is not prepended to the output (the C<-b> option)."); - ("command", (RString "output", [StringList "arguments"]), 50, [], - [], (* XXX how to test? *) + ("command", (RString "output", [StringList "arguments"]), 50, [ProtocolLimitWarning], + [InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 1"]], "Result1"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 2"]], "Result2\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 3"]], "\nResult3"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 4"]], "\nResult4\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 5"]], "\nResult5\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 6"]], "\n\nResult6\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 7"]], ""); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 8"]], "\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 9"]], "\n\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 10"]], "Result10-1\nResult10-2\n"); + InitBasicFS, Always, TestOutput ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command 11"]], "Result11-1\nResult11-2"); + InitBasicFS, Always, TestLastFail ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command"; "/test-command"]])], "run a command from the guest filesystem", "\ This call runs a command from the guest filesystem. The @@ -1075,6 +1331,13 @@ The first element is the name of the program to run. Subsequent elements are parameters. The list must be non-empty (ie. must contain a program name). +The return value is anything printed to I by +the command. + +If the command returns a non-zero exit status, then +this function returns an error message. The error message +string is the content of I from the command. + The C<$PATH> environment variable will contain at least C and C. If you require a program from another location, you should provide the full path in the @@ -1086,15 +1349,58 @@ correct places. It is the caller's responsibility to ensure all filesystems that are needed are mounted at the right locations."); - ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [], - [], (* XXX how to test? *) + ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [ProtocolLimitWarning], + [InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 1"]], ["Result1"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 2"]], ["Result2"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 3"]], ["";"Result3"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 4"]], ["";"Result4"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 5"]], ["";"Result5";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 6"]], ["";"";"Result6";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 7"]], []); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 8"]], [""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 9"]], ["";""]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 10"]], ["Result10-1";"Result10-2"]); + InitBasicFS, Always, TestOutputList ( + [["upload"; "test-command"; "/test-command"]; + ["chmod"; "493"; "/test-command"]; + ["command_lines"; "/test-command 11"]], ["Result11-1";"Result11-2"])], "run a command, returning lines", "\ This is the same as C, but splits the result into a list of lines."); ("stat", (RStat "statbuf", [String "path"]), 52, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["touch"; "/new"]; ["stat"; "/new"]], [CompareWithInt ("size", 0)])], "get file information", @@ -1104,7 +1410,7 @@ Returns file information for the given C. This is the same as the C system call."); ("lstat", (RStat "statbuf", [String "path"]), 53, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["touch"; "/new"]; ["lstat"; "/new"]], [CompareWithInt ("size", 0)])], "get file information for a symbolic link", @@ -1118,7 +1424,7 @@ refers to. This is the same as the C system call."); ("statvfs", (RStatVFS "statbuf", [String "path"]), 54, [], - [InitBasicFS, TestOutputStruct ( + [InitBasicFS, Always, TestOutputStruct ( [["statvfs"; "/"]], [CompareWithInt ("bfree", 487702); CompareWithInt ("blocks", 490020); CompareWithInt ("bsize", 1024)])], @@ -1132,10 +1438,10 @@ This is the same as the C system call."); ("tune2fs_l", (RHashtable "superblock", [String "device"]), 55, [], [], (* XXX test *) - "get ext2/ext3 superblock details", + "get ext2/ext3/ext4 superblock details", "\ -This returns the contents of the ext2 or ext3 filesystem superblock -on C. +This returns the contents of the ext2, ext3 or ext4 filesystem +superblock on C. It is the same as running C. See L manpage for more details. The list of fields returned isn't @@ -1143,7 +1449,7 @@ clearly defined, and depends on both the version of C that libguestfs was built against, and the filesystem itself."); ("blockdev_setro", (RErr, [String "device"]), 56, [], - [InitEmpty, TestOutputTrue ( + [InitEmpty, Always, TestOutputTrue ( [["blockdev_setro"; "/dev/sda"]; ["blockdev_getro"; "/dev/sda"]])], "set block device to read-only", @@ -1153,7 +1459,7 @@ Sets the block device named C to read-only. This uses the L command."); ("blockdev_setrw", (RErr, [String "device"]), 57, [], - [InitEmpty, TestOutputFalse ( + [InitEmpty, Always, TestOutputFalse ( [["blockdev_setrw"; "/dev/sda"]; ["blockdev_getro"; "/dev/sda"]])], "set block device to read-write", @@ -1163,7 +1469,7 @@ Sets the block device named C to read-write. This uses the L command."); ("blockdev_getro", (RBool "ro", [String "device"]), 58, [], - [InitEmpty, TestOutputTrue ( + [InitEmpty, Always, TestOutputTrue ( [["blockdev_setro"; "/dev/sda"]; ["blockdev_getro"; "/dev/sda"]])], "is block device set to read-only", @@ -1174,7 +1480,7 @@ Returns a boolean indicating if the block device is read-only This uses the L command."); ("blockdev_getss", (RInt "sectorsize", [String "device"]), 59, [], - [InitEmpty, TestOutputInt ( + [InitEmpty, Always, TestOutputInt ( [["blockdev_getss"; "/dev/sda"]], 512)], "get sectorsize of block device", "\ @@ -1187,7 +1493,7 @@ for that). This uses the L command."); ("blockdev_getbsz", (RInt "blocksize", [String "device"]), 60, [], - [InitEmpty, TestOutputInt ( + [InitEmpty, Always, TestOutputInt ( [["blockdev_getbsz"; "/dev/sda"]], 4096)], "get blocksize of block device", "\ @@ -1210,7 +1516,7 @@ I). This uses the L command."); ("blockdev_getsz", (RInt64 "sizeinsectors", [String "device"]), 62, [], - [InitEmpty, TestOutputInt ( + [InitEmpty, Always, TestOutputInt ( [["blockdev_getsz"; "/dev/sda"]], 1024000)], "get total size of device in 512-byte sectors", "\ @@ -1224,7 +1530,7 @@ useful I. This uses the L command."); ("blockdev_getsize64", (RInt64 "sizeinbytes", [String "device"]), 63, [], - [InitEmpty, TestOutputInt ( + [InitEmpty, Always, TestOutputInt ( [["blockdev_getsize64"; "/dev/sda"]], 524288000)], "get total size of device in bytes", "\ @@ -1235,7 +1541,7 @@ See also C. This uses the L command."); ("blockdev_flushbufs", (RErr, [String "device"]), 64, [], - [InitEmpty, TestRun + [InitEmpty, Always, TestRun [["blockdev_flushbufs"; "/dev/sda"]]], "flush device buffers", "\ @@ -1245,7 +1551,7 @@ with C. This uses the L command."); ("blockdev_rereadpt", (RErr, [String "device"]), 65, [], - [InitEmpty, TestRun + [InitEmpty, Always, TestRun [["blockdev_rereadpt"; "/dev/sda"]]], "reread partition table", "\ @@ -1254,7 +1560,10 @@ Reread the partition table on C. This uses the L command."); ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [], - [], + [InitBasicFS, Always, TestOutput ( + (* Pick a file from cwd which isn't likely to change. *) + [["upload"; "COPYING.LIB"; "/COPYING.LIB"]; + ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")], "upload a file from the local machine", "\ Upload local file C to C on the @@ -1265,7 +1574,12 @@ C can also be a named pipe. See also C."); ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [], - [], + [InitBasicFS, Always, TestOutput ( + (* Pick a file from cwd which isn't likely to change. *) + [["upload"; "COPYING.LIB"; "/COPYING.LIB"]; + ["download"; "/COPYING.LIB"; "testdownload.tmp"]; + ["upload"; "testdownload.tmp"; "/upload"]; + ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")], "download a file to the local machine", "\ Download file C and save it as C @@ -1275,6 +1589,683 @@ C can also be a named pipe. See also C, C."); + ("checksum", (RString "checksum", [String "csumtype"; String "path"]), 68, [], + [InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "crc"; "/new"]], "935282863"); + InitBasicFS, Always, TestLastFail ( + [["checksum"; "crc"; "/new"]]); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "md5"; "/new"]], "d8e8fca2dc0f896fd7cb4cb0031ba249"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "sha1"; "/new"]], "4e1243bd22c66e76c2ba9eddc1f91394e57f9f83"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "sha224"; "/new"]], "52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "sha256"; "/new"]], "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "sha384"; "/new"]], "109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d"); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "test\n"; "0"]; + ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123")], + "compute MD5, SHAx or CRC checksum of file", + "\ +This call computes the MD5, SHAx or CRC checksum of the +file named C. + +The type of checksum to compute is given by the C +parameter which must have one of the following values: + +=over 4 + +=item C + +Compute the cyclic redundancy check (CRC) specified by POSIX +for the C command. + +=item C + +Compute the MD5 hash (using the C program). + +=item C + +Compute the SHA1 hash (using the C program). + +=item C + +Compute the SHA224 hash (using the C program). + +=item C + +Compute the SHA256 hash (using the C program). + +=item C + +Compute the SHA384 hash (using the C program). + +=item C + +Compute the SHA512 hash (using the C program). + +=back + +The checksum is returned as a printable string."); + + ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [], + [InitBasicFS, Always, TestOutput ( + [["tar_in"; "../images/helloworld.tar"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack tarfile to directory", + "\ +This command uploads and unpacks local file C (an +I tar file) into C. + +To upload a compressed tarball, use C."); + + ("tar_out", (RErr, [String "directory"; FileOut "tarfile"]), 70, [], + [], + "pack directory into tarfile", + "\ +This command packs the contents of C and downloads +it to local file C. + +To download a compressed tarball, use C."); + + ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [], + [InitBasicFS, Always, TestOutput ( + [["tgz_in"; "../images/helloworld.tar.gz"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack compressed tarball to directory", + "\ +This command uploads and unpacks local file C (a +I tar file) into C. + +To upload an uncompressed tarball, use C."); + + ("tgz_out", (RErr, [String "directory"; FileOut "tarball"]), 72, [], + [], + "pack directory into compressed tarball", + "\ +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C."); + + ("mount_ro", (RErr, [String "device"; String "mountpoint"]), 73, [], + [InitBasicFS, Always, TestLastFail ( + [["umount"; "/"]; + ["mount_ro"; "/dev/sda1"; "/"]; + ["touch"; "/new"]]); + InitBasicFS, Always, TestOutput ( + [["write_file"; "/new"; "data"; "0"]; + ["umount"; "/"]; + ["mount_ro"; "/dev/sda1"; "/"]; + ["cat"; "/new"]], "data")], + "mount a guest disk, read-only", + "\ +This is the same as the C command, but it +mounts the filesystem with the read-only (I<-o ro>) flag."); + + ("mount_options", (RErr, [String "options"; String "device"; String "mountpoint"]), 74, [], + [], + "mount a guest disk with mount options", + "\ +This is the same as the C command, but it +allows you to set the mount options as for the +L I<-o> flag."); + + ("mount_vfs", (RErr, [String "options"; String "vfstype"; String "device"; String "mountpoint"]), 75, [], + [], + "mount a guest disk with mount options and vfstype", + "\ +This is the same as the C command, but it +allows you to set both the mount options and the vfstype +as for the L I<-o> and I<-t> flags."); + + ("debug", (RString "result", [String "subcmd"; StringList "extraargs"]), 76, [], + [], + "debugging and internals", + "\ +The C command exposes some internals of +C (the guestfs daemon) that runs inside the +qemu subprocess. + +There is no comprehensive help for this command. You have +to look at the file C in the libguestfs source +to find out what you can do."); + + ("lvremove", (RErr, [String "device"]), 77, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG/LV1"]; + ["lvs"]], ["/dev/VG/LV2"]); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["lvremove"; "/dev/VG"]; + ["vgs"]], ["VG"])], + "remove an LVM logical volume", + "\ +Remove an LVM logical volume C, where C is +the path to the LV, such as C. + +You can also remove all LVs in a volume group by specifying +the VG name, C."); + + ("vgremove", (RErr, [String "vgname"]), 78, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["vgs"]], [])], + "remove an LVM volume group", + "\ +Remove an LVM volume group C, (for example C). + +This also forcibly removes all logical volumes in the volume +group (if any)."); + + ("pvremove", (RErr, [String "device"]), 79, [], + [InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["lvs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["vgs"]], []); + InitEmpty, Always, TestOutputList ( + [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV1"; "VG"; "50"]; + ["lvcreate"; "LV2"; "VG"; "50"]; + ["vgremove"; "VG"]; + ["pvremove"; "/dev/sda1"]; + ["pvs"]], [])], + "remove an LVM physical volume", + "\ +This wipes a physical volume C so that LVM will no longer +recognise it. + +The implementation uses the C command which refuses to +wipe physical volumes that contain any volume groups, so you have +to remove those first."); + + ("set_e2label", (RErr, [String "device"; String "label"]), 80, [], + [InitBasicFS, Always, TestOutput ( + [["set_e2label"; "/dev/sda1"; "testlabel"]; + ["get_e2label"; "/dev/sda1"]], "testlabel")], + "set the ext2/3/4 filesystem label", + "\ +This sets the ext2/3/4 filesystem label of the filesystem on +C to C