[libvirt] [libvirt-virshcmdref 01/10] clarify the format of XML file used in command attach-device and detach-device

--- source/attach-device.xml | 4 +++- source/detach-device.xml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/source/attach-device.xml b/source/attach-device.xml index 18e3262..aab8313 100644 --- a/source/attach-device.xml +++ b/source/attach-device.xml @@ -27,7 +27,9 @@ <value type="string" requirement="required">file</value> <description> <text> - the XML file describing the device to be attached + the XML file describing the device to be attached, with + a root element of something that belongs inside <devices> + of domain xml </text> <text> "--file" itself is optional diff --git a/source/detach-device.xml b/source/detach-device.xml index 85ab17b..6262d49 100644 --- a/source/detach-device.xml +++ b/source/detach-device.xml @@ -27,7 +27,8 @@ <value type="string" requirement="required">file</value> <description> <text> - the XML file describing the device to be detached + the XML file describing the device to be detached, with + a root element of something that belongs inside <devices> </text> <text> "--file" itself is optional -- 1.7.3.1

--- source/attach-device.xml | 9 ++++++ source/detach-device.xml | 9 ++++++ source/update-device.xml | 66 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/source/attach-device.xml b/source/attach-device.xml index aab8313..a44be0d 100644 --- a/source/attach-device.xml +++ b/source/attach-device.xml @@ -153,5 +153,14 @@ virsh # <bold>attach-device</bold> <value>example-domain</value> <value>/tmp/new Detach a device from an XML file </description> </item> + <item> + <link type="internal" href="update-device" /> + <name> + update-device + </name> + <description> + Update a device from an XML file + </description> + </item> </reference> </command> diff --git a/source/detach-device.xml b/source/detach-device.xml index 6262d49..405ce01 100644 --- a/source/detach-device.xml +++ b/source/detach-device.xml @@ -136,6 +136,15 @@ virsh # <bold>detach-device</bold> <value>example-domain</value> <value>/tmp/nic Attach a device from an XML file </description> </item> + <item> + <link type="internal" href="update-device" /> + <name> + update-device + </name> + <description> + Update a device from an XML file + </description> + </item> </reference> </command> diff --git a/source/update-device.xml b/source/update-device.xml index 6354b5a..8d9ab8c 100644 --- a/source/update-device.xml +++ b/source/update-device.xml @@ -9,7 +9,50 @@ </text> </description> - <options /> + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--file</keyword> + <value type="string" requirement="required">file</value> + <description> + <text> + the XML file describing the device to be updated, with + a root element of something that belongs inside <devices> + of domain xml + </text> + <text> + "--file" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="required">--persistent</keyword> + <description> + <text> + with this option, the XML file of domain will be updated + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="required">--force</keyword> + <description> + <text> + force device update + </text> + </description> + </parameter> + </options> <availability from="0.8.0" /> @@ -19,6 +62,25 @@ <examples type="fullcontext" /> - <reference type="seealso" /> + <reference type="seealso"> + <item> + <link type="internal" href="attach-device" /> + <name> + attach-device + </name> + <description> + attach a device from an XML file + </description> + </item> + <item> + <link type="internal" href="detach-device" /> + <name> + detach-device + </name> + <description> + Detach a device from an XML file + </description> + </item> + </reference> </command> -- 1.7.3.1

--- source/attach-interface.xml | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/source/attach-interface.xml b/source/attach-interface.xml index cc5d9ad..b728e71 100644 --- a/source/attach-interface.xml +++ b/source/attach-interface.xml @@ -87,7 +87,11 @@ <value type="string" requirement="required">script</value> <description> <text> - script used to bridge network interface + script used to bridge network interface, for qemu domains the + script parameter is only acceptable when the interface type + is "ethernet" (a "generic ethernet" interface), for Xen domains + the script parameter is only acceptable when the interface type + is "bridge", in all other cases it is currently ignored. </text> <text> "--script" itself is optional -- 1.7.3.1

--- source/dump.xml | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 108 insertions(+), 5 deletions(-) diff --git a/source/dump.xml b/source/dump.xml index 3879bbe..5c09aa1 100644 --- a/source/dump.xml +++ b/source/dump.xml @@ -5,20 +5,123 @@ <description> <text> - Core dump a guest domain + dump the core of a domain to a file for analysis </text> </description> - <options /> + <options> + <parameter requirement="optional"> + <keyword requirement="optional">--live</keyword> + <description> + <text> + perform a live core dump if supported + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--crach</keyword> + <description> + <text> + crash the domain after core dump + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--bypass-cache</keyword> + <description> + <text> + avoid file system cache when dumping + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--file</keyword> + <value type="string" requirement="required">file</value> + <description> + <text> + file in which the core of the domain is saved + </text> + <text> + '--file' itself is optional + </text> + </description> + </parameter> + </options> <availability from="0.1.9" /> <notes /> - <examples type="usage" /> + <examples type="usage"> + <example> + <terminal>virsh # <bold>dump</bold> <value>example-domain</value> <value>/mnt/data/dumps/example-domain.dump</value></terminal> + <text> + Dumps the core of <value>example-domain</value> to file <value>/mnt/data/dumps/example-domain.dump</value>. + </text> + </example> + </examples> - <examples type="fullcontext" /> + <examples type="fullcontext"> + <example> + <text> + In this example we will dump the core of a domain and analysis it + using a tool called crash. + </text> + <text> + For the purpose of showing how to use virsh dump combined with crash, + we will crash the domain by pressing ALT-SysRq-c, the SysRq command + to crash system by a NULL pointer dereference. Steps are: + </text> + <text> + Enable SysRq in example-domain: + </text> + <terminal>example-domain# echo 1 > /proc/sys/kernel/sysrq</terminal> + <text> + Disable kdump in example-domain: + </text> + <terminal>example-domain# /etc/init.d/kdump stop</terminal> + <text> + Send key ALT-SysRq-c: + </text> + <terminal>virsh # <bold>send-key</bold> <value>example-domain</value> KEY_LEFTALT KEY_SYSRQ KEY_C</terminal> + <text> + Now example-domain is crashed, dump the core of it: + </text> + <terminal>virsh # dump example-domain /mnt/data/dumps/example-domain.dump</terminal> + <text> + After dump finished, we can analysis the core file by crash: + </text> + <terminal>crash /patch/to/vmlinux /mnt/data/dumps/example-domain.dump</terminal> + <text> + the vmlinux is the file containing debug information, usually it locates at the root + of kernel source dir if you compile the kernel by yourself, or is shipped with package + kernel-debuginfo. + </text> + </example> + </examples> - <reference type="seealso" /> + <reference type="seealso"> + <item> + <link type="external" href="http://people.redhat.com/anderson/" /> + <name> + crash + </name> + <description> + Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles + </description> + </item> + </reference> </command> -- 1.7.3.1

On 09/14/2011 01:28 AM, Hu Tao wrote:
+<parameter requirement="optional"> +<keyword requirement="optional">--crach</keyword>
s/crach/crash/
+<examples type="fullcontext"> +<example> +<text> + In this example we will dump the core of a domain and analysis it
s/analysis/analyze/
+<terminal>crash /patch/to/vmlinux /mnt/data/dumps/example-domain.dump</terminal> +<text>
Trailing whitespace.
+ the vmlinux is the file containing debug information, usually it locates at the root
s/locates/is located/ ACK to 1-3 as-is, and to 4 with these nits fixed, so I've pushed. I'll see if I can get review time for 5-10 soon, but I've run out of time today. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

--- source/migrate.xml | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 166 insertions(+), 2 deletions(-) diff --git a/source/migrate.xml b/source/migrate.xml index a85851b..4c88e74 100644 --- a/source/migrate.xml +++ b/source/migrate.xml @@ -9,13 +9,177 @@ </text> </description> - <options /> + <options> + <parameter requirement="optional"> + <keyword requirement="optional">--live</keyword> + <description> + <text> + live migration + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--p2p</keyword> + <description> + <text> + peer-2-peer migration + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--direct</keyword> + <description> + <text> + direct migration + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--tunnelled</keyword> + <description> + <text> + tunnelled migration + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--persistent</keyword> + <description> + <text> + persist VM on destination + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--undefinesource</keyword> + <description> + <text> + undefine VM on source + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--suspend</keyword> + <description> + <text> + do not restart the domain on the destination host + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--copy-storage-all</keyword> + <description> + <text> + migration with non-shared storage with full disk copy + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--copy-storage-inc</keyword> + <description> + <text> + migration with non-shared storage with incremental copy + (same base image shared between source and destination) + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--change-protection</keyword> + <description> + <text> + prevent any configuration changes to domain until migration ends + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--verbose</keyword> + <description> + <text> + display the progress of migration + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--desturi</keyword> + <value type="string" requirement="required">desturi</value> + <description> + <text> + connection URI of the destination host as seen from the + client(normal migration) or source(p2p migration) + </text> + <text> + "--desturi" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--migrateuri</keyword> + <value type="string" requirement="required">migrateuri</value> + <description> + <text> + migration URI, usually can be omitted + </text> + <text> + "--migrateuri" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--dname</keyword> + <value type="string" requirement="required">dname</value> + <description> + <text> + rename to new name during migration(if supported) + </text> + <text> + "--dname" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--timeout</keyword> + <value type="number" requirement="required">timeout</value> + <description> + <text> + force guest to suspend if live migration exceeds timeout(in seconds) + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--xml</keyword> + <value type="string" requirement="required">xmlfile</value> + <description> + <text> + filename containing updated XML for the target + </text> + </description> + </parameter> +</options> <availability from="0.3.2" /> <notes /> - <examples type="usage" /> + <examples type="usage"> + <example> + <terminal>virsh # <bold>migrate</bold> <value>example-domain</value> <value>qemu+ssh://root@192.168.0.10/system</value></terminal> + <text> + migrates domain <value>example-domain</value> to host <value>192.168.0.10</value>. + </text> + </example> + </examples> <examples type="fullcontext" /> -- 1.7.3.1

On 09/14/2011 01:28 AM, Hu Tao wrote:
--- source/migrate.xml | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 166 insertions(+), 2 deletions(-) +<text> + connection URI of the destination host as seen from the + client(normal migration) or source(p2p migration)
Unlike our source code conventions of no space before (, English text should use the space. ACK with that nit fixed, and pushed. Further improvements would be multiple examples, such as a peer-to-peer migration, and demonstrating how the target URI matters according to whether it is the target from your machine (non-p2p) or from the perspective of the source machine (p2p). But those can come later. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

--- source/migrate-setmaxdowntime.xml | 40 ++++++++++++++++++++++++++++++++++-- 1 files changed, 37 insertions(+), 3 deletions(-) diff --git a/source/migrate-setmaxdowntime.xml b/source/migrate-setmaxdowntime.xml index 366f4bc..ef16f68 100644 --- a/source/migrate-setmaxdowntime.xml +++ b/source/migrate-setmaxdowntime.xml @@ -10,7 +10,32 @@ </text> </description> - <options /> + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--downtime</keyword> + <value type="number" requirement="required">downtime</value> + <description> + <text> + maximum tolerable downtime (in milliseconds) for migration + </text> + <text> + "--downtime" itself is optional + </text> + </description> + </parameter> + </options> <availability from="0.8.0" /> @@ -20,6 +45,15 @@ <examples type="fullcontext" /> - <reference type="seealso" /> - + <reference type="seealso"> + <item> + <link type="internal" href="migrate" /> + <name> + migrate> + </name> + <description> + migrate a domain to another host. + </description> + </item> + </reference> </command> -- 1.7.3.1

--- common.sh | 3 +- source/migrate-setspeed.xml | 67 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletions(-) create mode 100644 source/migrate-setspeed.xml diff --git a/common.sh b/common.sh index 79c8057..a84c832 100755 --- a/common.sh +++ b/common.sh @@ -4,7 +4,8 @@ DOMAIN_COMMANDS="attach-device attach-disk attach-interface autostart detach-device detach-disk detach-interface domid domjobabort domjobinfo domname domuuid domxml-from-native domxml-to-native dump dumpxml echo edit freecell hostname inject-nmi managedsave managedsave-remove - maxvcpus memtune migrate migrate-setmaxdowntime reboot restore resume + maxvcpus memtune migrate migrate-setmaxdowntime migrate-setspeed + reboot restore resume save schedinfo send-key setmaxmem setmem setvcpus shutdown start suspend ttyconsole undefine update-device vcpucount vcpuinfo vcpupin version vncdisplay" diff --git a/source/migrate-setspeed.xml b/source/migrate-setspeed.xml new file mode 100644 index 0000000..5d27e99 --- /dev/null +++ b/source/migrate-setspeed.xml @@ -0,0 +1,67 @@ +<?xml version='1.0' encoding='utf-8' ?> + +<command> + <name>migrate-setspeed</name> + + <description> + <text> + Set the maximum migration bandwidth + </text> + </description> + + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--bandwidth</keyword> + <value type="number" requirement="required">bandwidth</value> + <description> + <text> + migration bandwidth limit in Mbps + </text> + <text> + "--bandwidth" itself is optional + </text> + </description> + </parameter> + </options> + + <availability from="0.9.3" /> + + <notes /> + + <examples type="usage" /> + + <examples type="fullcontext" /> + + <reference type="seealso"> + <item> + <link type="internal" href="migrate" /> + <name> + migrate + </name> + <description> + migrate a domain to another host + </description> + </item> + <item> + <link type="internal" href="migrate-getspeed" /> + <name> + migrate-getspeed + </name> + <description> + get the maximum migration bandwidth + </description> + </item> + </reference> +</command> -- 1.7.3.1

--- common.sh | 2 +- source/migrate-getspeed.xml | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletions(-) create mode 100644 source/migrate-getspeed.xml diff --git a/common.sh b/common.sh index a84c832..6454fe9 100755 --- a/common.sh +++ b/common.sh @@ -5,7 +5,7 @@ DOMAIN_COMMANDS="attach-device attach-disk attach-interface autostart domname domuuid domxml-from-native domxml-to-native dump dumpxml echo edit freecell hostname inject-nmi managedsave managedsave-remove maxvcpus memtune migrate migrate-setmaxdowntime migrate-setspeed - reboot restore resume + migrate-getspeed reboot restore resume save schedinfo send-key setmaxmem setmem setvcpus shutdown start suspend ttyconsole undefine update-device vcpucount vcpuinfo vcpupin version vncdisplay" diff --git a/source/migrate-getspeed.xml b/source/migrate-getspeed.xml new file mode 100644 index 0000000..dd3f17b --- /dev/null +++ b/source/migrate-getspeed.xml @@ -0,0 +1,55 @@ +<?xml version='1.0' encoding='utf-8' ?> + +<command> + <name>migrate-getspeed</name> + + <description> + <text> + Get the maximum migration bandwidth + </text> + </description> + + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + </options> + + <availability from="0.9.5" /> + + <notes /> + + <examples type="usage" /> + + <examples type="fullcontext" /> + + <reference type="seealso"> + <item> + <link type="internal" href="migrate" /> + <name> + migrate + </name> + <description> + migrate a domain to another host + </description> + </item> + <item> + <link type="internal" href="migrate-setspeed" /> + <name> + migrate-setspeed + </name> + <description> + set the maximum migration bandwidth + </description> + </item> + </reference> +</command> -- 1.7.3.1

On 09/14/2011 01:28 AM, Hu Tao wrote:
--- common.sh | 2 +- source/migrate-getspeed.xml | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletions(-) create mode 100644 source/migrate-getspeed.xml
diff --git a/common.sh b/common.sh index a84c832..6454fe9 100755 --- a/common.sh +++ b/common.sh @@ -5,7 +5,7 @@ DOMAIN_COMMANDS="attach-device attach-disk attach-interface autostart domname domuuid domxml-from-native domxml-to-native dump dumpxml echo edit freecell hostname inject-nmi managedsave managedsave-remove maxvcpus memtune migrate migrate-setmaxdowntime migrate-setspeed - reboot restore resume + migrate-getspeed reboot restore resume
Not quite alphabetical, so I fixed that. ACK, and I've pushed 6-8. Just 9 and 10 left to review. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

--- source/setvcpus.xml | 72 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 69 insertions(+), 3 deletions(-) diff --git a/source/setvcpus.xml b/source/setvcpus.xml index 77f3295..4cc65c8 100644 --- a/source/setvcpus.xml +++ b/source/setvcpus.xml @@ -9,7 +9,64 @@ </text> </description> - <options /> + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="required"> + <keyword requirement="optional">--count</keyword> + <value type="number" requirement="required">count</value> + <description> + <text> + number of virtual CPUs + </text> + <text> + "--count" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--maximum</keyword> + <description> + <text> + set maximum limit on next boot + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--config</keyword> + <description> + <text> + affect next boot + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--live</keyword> + <description> + <text> + affect running domain + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--current</keyword> + <description> + <text> + affect curent domain + </text> + </description> + </parameter> + </options> <availability from="0.0.1" /> @@ -19,6 +76,15 @@ <examples type="fullcontext" /> - <reference type="seealso" /> - + <reference type="seealso"> + <item> + <link type="internal" href="vcpucount" /> + <name> + vcpucount + </name> + <description> + show domain vcpu counts + </description> + </item> + </reference> </command> -- 1.7.3.1

--- source/vcpucount.xml | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 65 insertions(+), 2 deletions(-) diff --git a/source/vcpucount.xml b/source/vcpucount.xml index d556a75..2e70c07 100644 --- a/source/vcpucount.xml +++ b/source/vcpucount.xml @@ -9,7 +9,60 @@ </text> </description> - <options /> + <options> + <parameter requirement="required"> + <keyword requirement="optional">--domain</keyword> + <value type="string" requirement="required">domain</value> + <description> + <text> + domain name, id or uuid + </text> + <text> + "--domain" itself is optional + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--maximum</keyword> + <description> + <text> + get maximum cap on vcpus + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--active</keyword> + <description> + <text> + get number of currently active vcpus + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--config</keyword> + <description> + <text> + get value to be used on next boot + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--live</keyword> + <description> + <text> + get value from running domain + </text> + </description> + </parameter> + <parameter requirement="optional"> + <keyword requirement="optional">--current</keyword> + <description> + <text> + get value according to curent domain state + </text> + </description> + </parameter> + </options> <availability from="0.8.5" /> @@ -19,6 +72,16 @@ <examples type="fullcontext" /> - <reference type="seealso" /> + <reference type="seealso"> + <item> + <link type="internal" href="setvcpus" /> + <name> + setvcpus + </name> + <description> + change the number of vurtual CPUs in the guest domain + </description> + </item> + </reference> </command> -- 1.7.3.1

On 09/14/2011 01:29 AM, Hu Tao wrote:
--- source/vcpucount.xml | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 65 insertions(+), 2 deletions(-)
ACK and pushed 9 and 10. Sorry it took me so long to get through this series. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Eric Blake
-
Hu Tao