[libvirt] [PATCH 1/3 V7] add nodeGetCPUmap() for getting available CPU IDs in a cpumap.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Changelog: - fixed typos. - fixed string scan routine. Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt_private.syms |1 + src/nodeinfo.c | 66 ++ src/nodeinfo.h |3 ++ 3 files changed, 70 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a104e70..0f4e64c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -793,6 +793,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; +nodeGetCPUmap; nodeGetCPUStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; diff --git a/src/nodeinfo.c b/src/nodeinfo.c index e0b66f7..2950306 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -31,6 +31,7 @@ #include dirent.h #include sys/utsname.h #include sched.h +#include conf/domain_conf.h #if HAVE_NUMACTL # define NUMA_VERSION1_COMPATIBILITY 1 @@ -569,6 +570,47 @@ int linuxNodeGetMemoryStats(FILE *meminfo, cleanup: return ret; } + +/* + * Linux maintains cpu bit map. For example, if cpuid=5's flag is not set + * and max cpu is 7. The map file shows 0-4,6-7. This function parses + * it and returns cpumap. + */ +static const char * +linuxParseCPUmap(int *max_cpuid, const char *path) +{ +char *map = NULL; +char *str = NULL; +int max_id, i; + +if (virFileReadAll(path, 5 * VIR_DOMAIN_CPUMASK_LEN, str) 0) { +virReportOOMError(); +goto error; +} + +if (VIR_ALLOC_N(map, VIR_DOMAIN_CPUMASK_LEN) 0) { +virReportOOMError(); +goto error; +} +if (virDomainCpuSetParse(str, 0, map, + VIR_DOMAIN_CPUMASK_LEN) 0) { +goto error; +} + +for (i = 0; i VIR_DOMAIN_CPUMASK_LEN; i++) { +if (map[i]) { +max_id = i; +} +} +*max_cpuid = max_id; + +return map; + +error: +VIR_FREE(str); +VIR_FREE(map); +return NULL; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -712,6 +754,30 @@ int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } +const char * +nodeGetCPUmap(virConnectPtr conn ATTRIBUTE_UNUSED, + int *max_id ATTRIBUTE_UNUSED, + const char *mapname ATTRIBUTE_UNUSED) +{ +#ifdef __linux__ +char *path; +const char *cpumap; + +if (virAsprintf(path, CPU_SYS_PATH /%s, mapname) 0) { +virReportOOMError(); +return NULL; +} + +cpumap = linuxParseCPUmap(max_id, path); +VIR_FREE(path); +return cpumap; +#else + nodeReportError(VIR_ERR_NO_SUPPORT, %s, + _(node cpumap not implemented on this platform)); + return NULL; +#endif +} + #if HAVE_NUMACTL # if LIBNUMA_API_VERSION = 1 # define NUMA_MAX_N_CPUS 4096 diff --git a/src/nodeinfo.h b/src/nodeinfo.h index 4766152..852e19d 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -46,4 +46,7 @@ int nodeGetCellsFreeMemory(virConnectPtr conn, int maxCells); unsigned long long nodeGetFreeMemory(virConnectPtr conn); +const char *nodeGetCPUmap(virConnectPtr conn, + int *max_id, + const char *mapname); #endif /* __VIR_NODEINFO_H__*/ -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/3 V7] cpu-stats command shows cpu statistics information of a domain.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Total: cpu_time 14.312 CPU0: cpu_time 3.253 CPU1: cpu_time 1.923 CPU2: cpu_time 7.424 CPU3: cpu_time 1.712 Changed from V5: add --all, --start, --count option Changed from V: rebase Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 154 +++ tools/virsh.pod |8 +++ 2 files changed, 162 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index dc362dc..a431826 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5537,6 +5537,159 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * cpu-stats command + */ +static const vshCmdInfo info_cpu_stats[] = { +{help, N_(show domain cpu statistics)}, +{desc, N_(Display statistics about the domain's CPUs, including per-CPU statistics.)}, +{NULL, NULL}, +}; + +static const vshCmdOptDef opts_cpu_stats[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{all, VSH_OT_BOOL, 0, N_(Show total statistics only)}, +{start, VSH_OT_INT, 0, N_(Show statistics from this CPU)}, +{count, VSH_OT_INT, 0, N_(Number of shown CPUs at most)}, +{NULL, 0, 0, NULL}, +}; + +static bool +cmdCPUStats(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +virTypedParameterPtr params = NULL; +int i, j, pos, max_id, cpu = -1, show_count = -1, nparams; +bool show_all = false, show_per_cpu = false; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +show_all = vshCommandOptBool(cmd, all); +if (vshCommandOptInt(cmd, start, cpu) 0) +show_per_cpu = true; +if (vshCommandOptInt(cmd, count, show_count) 0) +show_per_cpu = true; + +/* default show per_cpu and total */ +if (!show_all !show_per_cpu) { +show_all = true; +show_per_cpu = true; +} + +if (!show_all) +goto do_show_per_cpu; + +/* get supported num of parameter for total statistics */ +if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)) 0) +goto failed_stats; +if (VIR_ALLOC_N(params, nparams)) +goto failed_params; + +/* passing start_cpu == -1 gives us domain's total status */ +if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, 0)) 0) +goto failed_stats; + +vshPrint(ctl, Total:\n); +for (i = 0; i nparams; i++) { +vshPrint(ctl, \t%-10s , params[i].field); +if (STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME)) { +if (params[i].type == VIR_TYPED_PARAM_ULLONG) { +vshPrint(ctl, %12.3lf\n, + params[i].value.ul / 10.0); +} else { +const char *s = vshGetTypedParamValue(ctl, params[i]); +vshPrint(ctl, _(%s(ABI changed? ullong is expected)\n), s); +VIR_FREE(s); +} +} else { +const char *s = vshGetTypedParamValue(ctl, params[i]); +vshPrint(ctl, _(%s\n), s); +VIR_FREE(s); +} +} +virTypedParameterArrayClear(params, nparams); +VIR_FREE(params); + +if (!show_per_cpu) /* show all stats only */ +goto cleanup; + +do_show_per_cpu: +/* check cpu, show_count, and ignore wrong argument */ +if (cpu 0) +cpu = 0; +if (show_count 0) +show_count = INT_MAX; + +/* get max cpu id on the node */ +if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) 0) +goto failed_stats; +/* get percpu information */ +if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)) 0) +goto failed_stats; + +if (VIR_ALLOC_N(params, nparams * 128)) +goto failed_params; + +while (cpu = max_id) { +int ncpus = 128; + +if (cpu + ncpus - 1 max_id) /* id starts from 0. */ +ncpus = max_id + 1 - cpu; + +if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0) 0) +goto failed_stats; + +for (i = 0; i ncpus; i++) { +if (params[i * nparams].type == 0) /* this cpu is not in the map */ +continue; +vshPrint(ctl, CPU%d:\n, cpu + i); + +for (j = 0; j nparams; j++) { +pos = i * nparams + j; +vshPrint(ctl, \t%-10s , params[pos].field); +if (STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME)) { +if (params[j].type == VIR_TYPED_PARAM_ULLONG) { +vshPrint(ctl, %12.3lf\n, + params[pos].value.ul / 10.0); +} else { +const char *s = vshGetTypedParamValue(ctl, params[pos]); +vshPrint(ctl, _(%s(ABI changed? ullong
[libvirt] [PATCH 2/3 V7] qemu driver for virDomainGetCPUstats using cpuacct cgroup.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com * Now, only cpu_time is supported. * cpuacct cgroup is used for providing percpu cputime information. * include/libvirt/libvirt.h.in - defines VIR_DOMAIN_CPU_STATS_CPUTIME * src/qemu/qemu.conf - take care of cpuacct cgroup. * src/qemu/qemu_conf.c - take care of cpuacct cgroup. * src/qemu/qemu_driver.c - added an interface * src/util/cgroup.c/h- added interface for getting percpu cputime Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |3 +- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 146 src/util/cgroup.c |6 ++ src/util/cgroup.h |1 + 5 files changed, 157 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 95428c1..cb87728 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -166,6 +166,7 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - 'cpuacct' - use for CPUs statistics. # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: @@ -177,7 +178,7 @@ # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e95c7a5..a709cbf 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -318,7 +318,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c6bdd29..d6b56ae 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12087,6 +12087,151 @@ cleanup: return ret; } +/* qemuDomainGetCPUStats() with start_cpu == -1 */ +static int +qemuDomainGetTotalcpuStats(virCgroupPtr group, + virTypedParameterPtr params, + int nparams) +{ +unsigned long long cpu_time; +int param_idx = 0; +int ret; + +if (nparams == 0) /* return supported number of params */ +return 1; +/* entry 0 is cputime */ +ret = virCgroupGetCpuacctUsage(group, cpu_time); +if (ret 0) { +virReportSystemError(-ret, %s, _(unable to get cpu account)); +return -1; +} + +virTypedParameterAssign(params[param_idx], VIR_DOMAIN_CPU_STATS_CPUTIME, +VIR_TYPED_PARAM_ULLONG, cpu_time); +return 1; +} + +static int qemuDomainGetPercpuStats(virDomainPtr domain, +virCgroupPtr group, +virTypedParameterPtr params, +unsigned int nparams, +int start_cpu, +unsigned int ncpus) +{ +const char *map = NULL; +int rv = -1; +int i, max_id; +char *pos; +char *buf = NULL; +virTypedParameterPtr ent; +int param_idx; + +/* return the number of supported params ? */ +if (nparams == 0 ncpus != 0) +return 1; /* only cpu_time is supported */ + +/* return percpu cputime in index 0 */ +param_idx = 0; +/* to parse account file, we need present cpu map */ +map = nodeGetCPUmap(domain-conn, max_id, present); +if (!map) +return rv; + +if (ncpus == 0) { /* returns max cpu ID */ +rv = max_id; +goto cleanup; +} +/* we get percpu cputime accounting info. */ +if (virCgroupGetCpuacctPercpuUsage(group, buf)) +goto cleanup; +pos = buf; + +if (max_id start_cpu + ncpus - 1) +max_id = start_cpu + ncpus - 1; + +for (i = 0; i = max_id; i++) { +unsigned long long cpu_time; + +if (!map[i]) +continue; +if (virStrToLong_ull(pos, pos, 10, cpu_time) 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(cpuacct parse error)); +goto cleanup; +} +if (i start_cpu) +continue; +ent = params[ (i - start_cpu) * nparams + param_idx]; +virTypedParameterAssign(ent, VIR_DOMAIN_CPU_STATS_CPUTIME, +VIR_TYPED_PARAM_ULLONG, cpu_time); +} +rv = param_idx + 1; +cleanup
Re: [libvirt] [PATCH 1/3 V6] add nodeGetCPUmap() for getting available CPU IDs in a cpumap.
On 02/23/2012 08:10 AM, Eric Blake wrote: +if (virDomainCpuSetParse(str, 0, map, + VIR_DOMAIN_CPUMASK_LEN) 0) { +goto error; +} + +for (i = 0; i VIR_DOMAIN_CPUMASK_LEN; i++) { +if (map[i]) { +max_id = i; That's off by a factor of 8 - map[i] means that you have visited i*8 bits in cpumask. @map is not bitmask, virDomainCpuSetParse() filled it a char per a cpu. And the return of this function is also cpumap(byte per cpu). I will rework the patches soon after you comments them. Thank you very much. --lai. -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 1/3 V6] add nodeGetCPUmap() for getting available CPU IDs in a cpumap.
Hi, Eric Did you received these new patches? Thanks, Lai On 02/16/2012 03:15 PM, Lai Jiangshan wrote: From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Changelog: - fixed typos. - fixed string scan routine. Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt_private.syms |1 + src/nodeinfo.c | 92 ++ src/nodeinfo.h |3 + 3 files changed, 96 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0c22dec..6e99243 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -792,6 +792,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; +nodeGetCPUmap; nodeGetCPUStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; diff --git a/src/nodeinfo.c b/src/nodeinfo.c index e0b66f7..fc1aaea 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -31,6 +31,7 @@ #include dirent.h #include sys/utsname.h #include sched.h +#include conf/domain_conf.h #if HAVE_NUMACTL # define NUMA_VERSION1_COMPATIBILITY 1 @@ -569,6 +570,73 @@ int linuxNodeGetMemoryStats(FILE *meminfo, cleanup: return ret; } + +/* + * Linux maintains cpu bit map. For example, if cpuid=5's flag is not set + * and max cpu is 7. The map file shows 0-4,6-7. This function parses + * it and returns cpumap. + */ +static const char * +linuxParseCPUmap(int *max_cpuid, const char *path) +{ +FILE *fp; +char *map = NULL; +char *str = NULL; +size_t size = 128, pos = 0; +int max_id, i; + +fp = fopen(path, r); +if (!fp) { +virReportSystemError(errno, _(cannot open %s), path); +goto error; +} + +if (VIR_ALLOC_N(str, size) 0) { +virReportOOMError(); +goto error; +} +for (;;) { +pos += fread(str + pos, 1, size - pos, fp); +if (pos size) +break; + +size = size 1; +if (VIR_REALLOC_N(str, size) 0) { +virReportOOMError(); +goto error; +} +} +if (pos == 0) { +virReportSystemError(errno, _(cannot read from %s), path); +goto error; +} +str[pos - 1] = 0; + +if (VIR_ALLOC_N(map, VIR_DOMAIN_CPUMASK_LEN) 0) { +virReportOOMError(); +goto error; +} +if (virDomainCpuSetParse(str, 0, map, + VIR_DOMAIN_CPUMASK_LEN) 0) { +goto error; +} + +for (i = 0; i VIR_DOMAIN_CPUMASK_LEN; i++) { +if (map[i]) { +max_id = i; +} +} +*max_cpuid = max_id; + +VIR_FORCE_FCLOSE(fp); +return map; + +error: +VIR_FORCE_FCLOSE(fp); +VIR_FREE(str); +VIR_FREE(map); +return NULL; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -712,6 +780,30 @@ int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } +const char * +nodeGetCPUmap(virConnectPtr conn ATTRIBUTE_UNUSED, + int *max_id ATTRIBUTE_UNUSED, + const char *mapname ATTRIBUTE_UNUSED) +{ +#ifdef __linux__ +char *path; +const char *cpumap; + +if (virAsprintf(path, CPU_SYS_PATH /%s, mapname) 0) { +virReportOOMError(); +return NULL; +} + +cpumap = linuxParseCPUmap(max_id, path); +VIR_FREE(path); +return cpumap; +#else + nodeReportError(VIR_ERR_NO_SUPPORT, %s, + _(node cpumap not implemented on this platform)); + return -1; +#endif +} + #if HAVE_NUMACTL # if LIBNUMA_API_VERSION = 1 # define NUMA_MAX_N_CPUS 4096 diff --git a/src/nodeinfo.h b/src/nodeinfo.h index 4766152..852e19d 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -46,4 +46,7 @@ int nodeGetCellsFreeMemory(virConnectPtr conn, int maxCells); unsigned long long nodeGetFreeMemory(virConnectPtr conn); +const char *nodeGetCPUmap(virConnectPtr conn, + int *max_id, + const char *mapname); #endif /* __VIR_NODEINFO_H__*/ -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/3 V6] add nodeGetCPUmap() for getting available CPU IDs in a cpumap.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Changelog: - fixed typos. - fixed string scan routine. Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt_private.syms |1 + src/nodeinfo.c | 92 ++ src/nodeinfo.h |3 + 3 files changed, 96 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0c22dec..6e99243 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -792,6 +792,7 @@ virNodeDeviceObjUnlock; # nodeinfo.h nodeCapsInitNUMA; +nodeGetCPUmap; nodeGetCPUStats; nodeGetCellsFreeMemory; nodeGetFreeMemory; diff --git a/src/nodeinfo.c b/src/nodeinfo.c index e0b66f7..fc1aaea 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -31,6 +31,7 @@ #include dirent.h #include sys/utsname.h #include sched.h +#include conf/domain_conf.h #if HAVE_NUMACTL # define NUMA_VERSION1_COMPATIBILITY 1 @@ -569,6 +570,73 @@ int linuxNodeGetMemoryStats(FILE *meminfo, cleanup: return ret; } + +/* + * Linux maintains cpu bit map. For example, if cpuid=5's flag is not set + * and max cpu is 7. The map file shows 0-4,6-7. This function parses + * it and returns cpumap. + */ +static const char * +linuxParseCPUmap(int *max_cpuid, const char *path) +{ +FILE *fp; +char *map = NULL; +char *str = NULL; +size_t size = 128, pos = 0; +int max_id, i; + +fp = fopen(path, r); +if (!fp) { +virReportSystemError(errno, _(cannot open %s), path); +goto error; +} + +if (VIR_ALLOC_N(str, size) 0) { +virReportOOMError(); +goto error; +} +for (;;) { +pos += fread(str + pos, 1, size - pos, fp); +if (pos size) +break; + +size = size 1; +if (VIR_REALLOC_N(str, size) 0) { +virReportOOMError(); +goto error; +} +} +if (pos == 0) { +virReportSystemError(errno, _(cannot read from %s), path); +goto error; +} +str[pos - 1] = 0; + +if (VIR_ALLOC_N(map, VIR_DOMAIN_CPUMASK_LEN) 0) { +virReportOOMError(); +goto error; +} +if (virDomainCpuSetParse(str, 0, map, + VIR_DOMAIN_CPUMASK_LEN) 0) { +goto error; +} + +for (i = 0; i VIR_DOMAIN_CPUMASK_LEN; i++) { +if (map[i]) { +max_id = i; +} +} +*max_cpuid = max_id; + +VIR_FORCE_FCLOSE(fp); +return map; + +error: +VIR_FORCE_FCLOSE(fp); +VIR_FREE(str); +VIR_FREE(map); +return NULL; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -712,6 +780,30 @@ int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } +const char * +nodeGetCPUmap(virConnectPtr conn ATTRIBUTE_UNUSED, + int *max_id ATTRIBUTE_UNUSED, + const char *mapname ATTRIBUTE_UNUSED) +{ +#ifdef __linux__ +char *path; +const char *cpumap; + +if (virAsprintf(path, CPU_SYS_PATH /%s, mapname) 0) { +virReportOOMError(); +return NULL; +} + +cpumap = linuxParseCPUmap(max_id, path); +VIR_FREE(path); +return cpumap; +#else + nodeReportError(VIR_ERR_NO_SUPPORT, %s, + _(node cpumap not implemented on this platform)); + return -1; +#endif +} + #if HAVE_NUMACTL # if LIBNUMA_API_VERSION = 1 # define NUMA_MAX_N_CPUS 4096 diff --git a/src/nodeinfo.h b/src/nodeinfo.h index 4766152..852e19d 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -46,4 +46,7 @@ int nodeGetCellsFreeMemory(virConnectPtr conn, int maxCells); unsigned long long nodeGetFreeMemory(virConnectPtr conn); +const char *nodeGetCPUmap(virConnectPtr conn, + int *max_id, + const char *mapname); #endif /* __VIR_NODEINFO_H__*/ -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/3 V6] qemu driver for virDomainGetCPUstats using cpuacct cgroup.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com * Now, only cpu_time is supported. * cpuacct cgroup is used for providing percpu cputime information. * include/libvirt/libvirt.h.in - defines VIR_DOMAIN_CPU_STATS_CPUTIME * src/qemu/qemu.conf - take care of cpuacct cgroup. * src/qemu/qemu_conf.c - take care of cpuacct cgroup. * src/qemu/qemu_driver.c - added an interface * src/util/cgroup.c/h- added interface for getting percpu cputime Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |3 +- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 147 src/util/cgroup.c |6 ++ src/util/cgroup.h |1 + 5 files changed, 158 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 95428c1..cb87728 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -166,6 +166,7 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - 'cpuacct' - use for CPUs statistics. # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: @@ -177,7 +178,7 @@ # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e95c7a5..a709cbf 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -318,7 +318,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e2e73b7..e7871ab 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12078,6 +12078,152 @@ cleanup: return ret; } + +/* qemuDomainGetCPUStats() with start_cpu == -1 */ +static int +qemuDomainGetTotalcpuStats(virCgroupPtr group, + virTypedParameterPtr params, + int nparams) +{ +unsigned long long cpu_time; +int param_idx = 0; +int ret; + +if (nparams == 0) /* return supported number of params */ +return 1; +/* entry 0 is cputime */ +ret = virCgroupGetCpuacctUsage(group, cpu_time); +if (ret 0) { +virReportSystemError(-ret, %s, _(unable to get cpu account)); +return -1; +} + +virTypedParameterAssign(params[param_idx], VIR_DOMAIN_CPU_STATS_CPUTIME, +VIR_TYPED_PARAM_ULLONG, cpu_time); +return 1; +} + +static int qemuDomainGetPercpuStats(virDomainPtr domain, +virCgroupPtr group, +virTypedParameterPtr params, +unsigned int nparams, +int start_cpu, +unsigned int ncpus) +{ +const char *map = NULL; +int rv = -1; +int i, max_id; +char *pos; +char *buf = NULL; +virTypedParameterPtr ent; +int param_idx; + +/* return the number of supported params ? */ +if (nparams == 0 ncpus != 0) +return 1; /* only cpu_time is supported */ + +/* return percpu cputime in index 0 */ +param_idx = 0; +/* to parse account file, we need present cpu map */ +map = nodeGetCPUmap(domain-conn, max_id, present); +if (!map) +return rv; + +if (ncpus == 0) { /* returns max cpu ID */ +rv = max_id; +goto cleanup; +} +/* we get percpu cputime accounting info. */ +if (virCgroupGetCpuacctPercpuUsage(group, buf)) +goto cleanup; +pos = buf; + +if (max_id start_cpu + ncpus - 1) +max_id = start_cpu + ncpus - 1; + +for (i = 0; i = max_id; i++) { +unsigned long long cpu_time; + +if (!map[i]) +continue; +if (virStrToLong_ull(pos, pos, 10, cpu_time) 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(cpuacct parse error)); +goto cleanup; +} +if (i start_cpu) +continue; +ent = params[ (i - start_cpu) * nparams + param_idx]; +virTypedParameterAssign(ent, VIR_DOMAIN_CPU_STATS_CPUTIME, +VIR_TYPED_PARAM_ULLONG, cpu_time); +} +rv = param_idx + 1; +cleanup
Re: [libvirt] [PATCH 1/1 V4] virt-top: correct virt-top -1 command via cpuacct cgroup
Hi, Richard virt-top and ocaml site patches can work and are completed weeks ago since public API of libvirt is merged. Could you merged them? virt-top and ocaml site patches only depend on the public API of libvirt. but the testing of them may depend on the qemu-driver-site patch of libvirt, which is sent today. Thanks, Lai On 02/08/2012 04:59 PM, Lai Jiangshan wrote: Old virt-top -1 is not correct, its output is generated by guess: use average usage for pinned physical CPUs. example(old virt-top -1): PHYCPU %CPU rhel6 Windows 00.6 0.1= 0.5= 10.6 0.1= 0.5=# 20.6 0.1= 0.5= 30.6 0.1=# 0.5= The output almost makes no sense(all the value are just average, not real). This is new implement, it use cpuacct cgroup to gain *real* physical usages via cpuacct cgroup by virDomainGetCPUStats() API. new result: PHYCPU %CPU rhel6 Windows 01.3 0.3 1.0 12.3 0.3 2.0 22.2 0.5 1.7 32.5 0.4 2.1 PHYCPU %CPU rhel6 Windows 01.7 0.4 1.3 13.6 1.0 2.7 21.6 0.4 1.2 34.8 3.1 1.7 Note: average flag(=) is dropped, there is not average value in here. Note: running flag(#) is dropped, because if the value is not empty, it means the guest was once running in the physical CPU in this period between updates. Changed from V3: use new virDomainGetCPUStats() libvirt-API. add a new function find_usages_from_stats() to gain cpu usages. Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- virt-top/virt_top.ml | 80 +++-- 1 files changed, 31 insertions(+), 49 deletions(-) diff --git a/virt-top/virt_top.ml b/virt-top/virt_top.ml index ef5ac67..f7bd072 100644 --- a/virt-top/virt_top.ml +++ b/virt-top/virt_top.ml @@ -446,14 +446,14 @@ let collect, clear_pcpu_display_data = let last_info = Hashtbl.create 13 in let last_time = ref (Unix.gettimeofday ()) in - (* Save vcpuinfo structures across redraws too (only for pCPU display). *) - let last_vcpu_info = Hashtbl.create 13 in + (* Save pcpu_usages structures across redraws too (only for pCPU display). *) + let last_pcpu_usages = Hashtbl.create 13 in let clear_pcpu_display_data () = -(* Clear out vcpu_info used by PCPUDisplay display_mode +(* Clear out pcpu_info used by PCPUDisplay display_mode * when we switch back to TaskDisplay mode. *) -Hashtbl.clear last_vcpu_info +Hashtbl.clear last_pcpu_usages in let collect (conn, _, _, _, _, node_info, _, _) = @@ -652,22 +652,28 @@ let collect, clear_pcpu_display_data = (try let domid = rd.rd_domid in let maplen = C.cpumaplen nr_pcpus in + let cpu_stats = D.get_cpu_stats rd.rd_dom nr_pcpus in + let rec find_usages_from_stats = (function +| (cpu_time, D.TypedFieldUInt64(usages)) :: _ - usages +| (_, _) :: params - find_usages_from_stats params +| [] - 0L) in + let pcpu_usages = Array.map find_usages_from_stats cpu_stats in let maxinfo = rd.rd_info.D.nr_virt_cpu in let nr_vcpus, vcpu_infos, cpumaps = D.get_vcpus rd.rd_dom maxinfo maplen in - (* Got previous vcpu_infos for this domain? *) - let prev_vcpu_infos = -try Some (Hashtbl.find last_vcpu_info domid) + (* Got previous pcpu_usages for this domain? *) + let prev_pcpu_usages = +try Some (Hashtbl.find last_pcpu_usages domid) with Not_found - None in - (* Update last_vcpu_info. *) - Hashtbl.replace last_vcpu_info domid vcpu_infos; - - (match prev_vcpu_infos with - | Some prev_vcpu_infos - when Array.length prev_vcpu_infos = Array.length vcpu_infos - - Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) + (* Update last_pcpu_usages. *) + Hashtbl.replace last_pcpu_usages domid pcpu_usages; + + (match prev_pcpu_usages with + | Some prev_pcpu_usages + when Array.length prev_pcpu_usages = Array.length pcpu_usages - + Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen) | _ - None (* ignore missing / unequal length prev_vcpu_infos *) ); with @@ -680,37 +686,15 @@ let collect, clear_pcpu_display_data = (* Rearrange the data into a matrix. Major axis (down) is * pCPUs. Minor axis
[libvirt] [PATCH 6/5 V5] GetCPUStats: fix overflow test
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt.c |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 8035add..a55d823 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -18541,7 +18541,7 @@ int virDomainGetCPUStats(virDomainPtr domain, (start_cpu == -1 ncpus != 1) || ((params == NULL) != (nparams == 0)) || (ncpus == 0 params != NULL) || -ncpus UINT_MAX / nparams) { +(nparams ncpus UINT_MAX / nparams)) { virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); goto error; } -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/5 V5] qemu driver for virDomainGetCPUstats using cpuacct cgroup.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com * Now, only cpu_time is supported. * cpuacct cgroup is used for providing percpu cputime information. * include/libvirt/libvirt.h.in - defines VIR_DOMAIN_CPU_STATS_CPUTIME * src/qemu/qemu.conf - take care of cpuacct cgroup. * src/qemu/qemu_conf.c - take care of cpuacct cgroup. * src/qemu/qemu_driver.c - added an interface * src/util/cgroup.c/h- added interface for getting percpu cputime Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |3 +- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 157 src/util/cgroup.c |6 ++ src/util/cgroup.h |1 + 5 files changed, 168 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 95428c1..db07b8a 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -166,6 +166,7 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - cpuacct - use for CPUs statistics. # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: @@ -177,7 +178,7 @@ # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e95c7a5..a709cbf 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -318,7 +318,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0cac6a5..7aa9d33 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12036,6 +12036,162 @@ cleanup: return ret; } + +/* qemuDomainGetCPUStats() with start_cpu == -1 */ +static int qemuDomainGetTotalcpuStats(virDomainPtr domain ATTRIBUTE_UNUSED, + virCgroupPtr group, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ +unsigned long long cpu_time; +int param_idx = 0; +int ret; + +virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); + +if (nparams == 0) /* return supprted number of params */ +return 1; +/* entry 0 is cputime */ +ret = virCgroupGetCpuacctUsage(group, cpu_time); +if (ret 0) { +virReportSystemError(-ret, %s, _(unable to get cpu account)); +return -1; +} + +virTypedParameterAssign(params[param_idx], VIR_DOMAIN_CPU_STATS_CPUTIME, +VIR_TYPED_PARAM_ULLONG, cpu_time); +return param_idx + 1; +} + +static int qemuDomainGetPercpuStats(virDomainPtr domain, +virCgroupPtr group, +virTypedParameterPtr params, +unsigned int nparams, +int start_cpu, +unsigned int ncpus, +unsigned int flags) +{ +virBitmapPtr map = NULL; +int rv = -1; +int i, max_id; +char *pos; +char *buf = NULL; +virTypedParameterPtr ent; +int param_idx; + +virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); +/* return the number of supported params ? */ +if (nparams == 0 ncpus != 0) +return 1; /* only cpu_time is supported */ + +/* return percpu cputime in index 0 */ +param_idx = 0; +/* to parse account file, we need present cpu map */ +map = nodeGetCPUmap(domain-conn, max_id, present); +if (!map) +return rv; + +if (ncpus == 0) { /* returns max cpu ID */ +rv = max_id; +goto cleanup; +} +/* we get percpu cputime accoutning info. */ +if (virCgroupGetCpuacctPercpuUsage(group, buf)) +goto cleanup; +pos = buf; + +if (max_id start_cpu + ncpus - 1) +max_id = start_cpu + ncpus - 1; + +for (i = 0; i = max_id; i++) { +bool exist; +unsigned long long cpu_time; + +if (virBitmapGetBit(map, i, exist) 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, _(bitmap parse error)); +goto cleanup; +} +if (!exist
[libvirt] [PATCH 5/5 V5] cpu-accts command shows cpu accounting information of a domain.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Total: cpu_time15.8 CPU0: cpu_time8.6 CPU1: cpu_time3.5 CPU2: cpu_time2.4 CPU3: cpu_time1.3 --- tools/virsh.c | 116 +++ tools/virsh.pod |5 ++ 2 files changed, 121 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 66ba61c..e9d2f86 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5385,6 +5385,121 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * cputime command + */ +static const vshCmdInfo info_cpu_accts[] = { +{help, N_(show domain cpu accounting information)}, +{desc, N_(Returns information about the domain's cpu accounting)}, +{NULL, NULL}, +}; + +static const vshCmdOptDef opts_cpu_accts[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL}, +}; + +static bool +cmdCPUAccts(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +virTypedParameterPtr params = NULL; +bool ret = true; +int i, j, pos, cpu, nparams; +int max_id; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +/* get max cpu id on the node */ +max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0); +if (max_id 0) { +vshPrint(ctl, max id %d\n, max_id); +goto cleanup; +} +/* get supported num of parameter for total statistics */ +nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0); +if (nparams 0) +goto cleanup; + +if (VIR_ALLOC_N(params, nparams)) { +virReportOOMError(); +goto cleanup; +} +/* passing start_cpu == -1 gives us domain's total status */ +nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, 0); +if (nparams 0) +goto cleanup; + +vshPrint(ctl, Total:\n); +for (i = 0; i nparams; i++) { +vshPrint(ctl, \t%-15s , params[i].field); +switch (params[i].type) { +case VIR_TYPED_PARAM_ULLONG: +/* Now all UULONG param is used for time accounting in ns */ +vshPrint(ctl, %.1lf\n, + params[i].value.ul / 10.0); +break; +default: +vshError(ctl, %s, _(API mismatch?)); +goto cleanup; +} +} +VIR_FREE(params); +/* get percpu information */ +nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0); +if (nparams 0) +goto cleanup; + +cpu = 0; +while (cpu = max_id) { +int ncpus = 128; + +if (cpu + ncpus - 1 max_id) /* id starts from 0. */ +ncpus = max_id + 1 - cpu; + +if (VIR_ALLOC_N(params, nparams * ncpus)) { +virReportOOMError(); +goto cleanup; +} + +if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0) 0) +goto cleanup; + +for (i = 0; i ncpus; i++) { +if (params[i * nparams].type == 0) +continue; +vshPrint(ctl, CPU%d:\n, cpu + i); + +for (j = 0; j nparams; j++) { +pos = i * nparams + j; +if (params[pos].type == 0) +continue; +vshPrint(ctl, \t%-15s , params[pos].field); +switch(params[j].type) { +case VIR_TYPED_PARAM_ULLONG: +vshPrint(ctl, %.1lf\n, +params[pos].value.ul / 10.0); +break; +default: +vshError(ctl, %s, _(API mismatch?)); +goto cleanup; +} +} +} +cpu += ncpus; +/* Note: If we handle string type, it should be freed */ +VIR_FREE(params); +} +cleanup: +VIR_FREE(params); +virDomainFree(dom); +return ret; +} + +/* * inject-nmi command */ static const vshCmdInfo info_inject_nmi[] = { @@ -16441,6 +16556,7 @@ static const vshCmdDef domManagementCmds[] = { #endif {cpu-baseline, cmdCPUBaseline, opts_cpu_baseline, info_cpu_baseline, 0}, {cpu-compare, cmdCPUCompare, opts_cpu_compare, info_cpu_compare, 0}, +{cpu-accts, cmdCPUAccts, opts_cpu_accts, info_cpu_accts, 0}, {create, cmdCreate, opts_create, info_create, 0}, {define, cmdDefine, opts_define, info_define, 0}, {desc, cmdDesc, opts_desc, info_desc, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index bd79b4c..2b2f70b 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -762,6 +762,11 @@ Provide the maximum number of virtual CPUs supported for a guest VM on this connection. If provided, the Itype parameter must be a valid type attribute for the domain element of XML. +=item Bcpu-accts Idomain + +Provide cpu accounting information of a domain. The domain should +be running. + =item Bmigrate [I--live]
[libvirt] [PATCH 3/5 V5] add nodeGetCPUmap() for getting available CPU IDs in a bitmap.
From: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com add virBitmapParseCommaSeparetedFormat() for parsing bitmap in comma separeted ascii format. This format of bitmap is used in Linux sysfs and cpuset. Changelog: - fixed typos. - fixed string scan routine. Signed-off-by: KAMEZAWA Hiroyuki kamezawa.hir...@jp.fujitsu.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt_private.syms |2 + src/nodeinfo.c | 51 src/nodeinfo.h |4 +++ src/util/bitmap.c| 65 ++ src/util/bitmap.h|6 +++- 5 files changed, 127 insertions(+), 1 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d6ad36c..14b144f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -17,6 +17,7 @@ virBitmapFree; virBitmapGetBit; virBitmapSetBit; virBitmapString; +virBitmapParseCommaSeparatedFormat; # buf.h @@ -797,6 +798,7 @@ nodeGetCellsFreeMemory; nodeGetFreeMemory; nodeGetInfo; nodeGetMemoryStats; +nodeGetCPUmap; # nwfilter_conf.h diff --git a/src/nodeinfo.c b/src/nodeinfo.c index e0b66f7..b0ebb88 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -47,6 +47,7 @@ #include count-one-bits.h #include intprops.h #include virfile.h +#include bitmap.h #define VIR_FROM_THIS VIR_FROM_NONE @@ -569,6 +570,33 @@ int linuxNodeGetMemoryStats(FILE *meminfo, cleanup: return ret; } + +/* + * Linux maintains cpu bit map. For example, if cpuid=5's flag is not set + * and max cpu is 7. The map file shows 0-4,6-7. This functin parses + * it and returns bitmap. + */ +static virBitmapPtr linuxParseCPUmap(int *max_cpuid, const char *path) +{ +char str[1024]; +FILE *fp; +virBitmapPtr map = NULL; + +fp = fopen(path, r); +if (!fp) { +virReportSystemError(errno, _(cannot open %s), path); +goto cleanup; +} +if (fgets(str, sizeof(str), fp) == NULL) { +virReportSystemError(errno, _(cannot read from %s), path); +goto cleanup; +} +map = virBitmapParseCommaSeparatedFormat(str, max_cpuid); + +cleanup: +VIR_FORCE_FCLOSE(fp); +return map; +} #endif int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) { @@ -712,6 +740,29 @@ int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED, #endif } +virBitmapPtr nodeGetCPUmap(virConnectPtr conn ATTRIBUTE_UNUSED, + int *max_id, + const char *mapname) +{ +#ifdef __linux__ +char *path; +virBitmapPtr map; + +if (virAsprintf(path, CPU_SYS_PATH /%s, mapname) 0) { +virReportOOMError(); +return NULL; +} + +map = linuxParseCPUmap(max_id, path); +VIR_FREE(path); +return map; +#else + nodeReportError(VIR_ERR_NO_SUPPORT, %s, + _(node cpumap not implemented on this platform)); + return -1; +#endif +} + #if HAVE_NUMACTL # if LIBNUMA_API_VERSION = 1 # define NUMA_MAX_N_CPUS 4096 diff --git a/src/nodeinfo.h b/src/nodeinfo.h index 4766152..7f26b77 100644 --- a/src/nodeinfo.h +++ b/src/nodeinfo.h @@ -26,6 +26,7 @@ # include libvirt/libvirt.h # include capabilities.h +# include bitmap.h int nodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo); int nodeCapsInitNUMA(virCapsPtr caps); @@ -46,4 +47,7 @@ int nodeGetCellsFreeMemory(virConnectPtr conn, int maxCells); unsigned long long nodeGetFreeMemory(virConnectPtr conn); +virBitmapPtr nodeGetCPUmap(virConnectPtr conn, + int *max_id, + const char *mapname); #endif /* __VIR_NODEINFO_H__*/ diff --git a/src/util/bitmap.c b/src/util/bitmap.c index 8c326c4..6dead3b 100644 --- a/src/util/bitmap.c +++ b/src/util/bitmap.c @@ -33,6 +33,11 @@ #include bitmap.h #include memory.h #include buf.h +#include c-ctype.h +#include util.h +#include virterror_internal.h + +#define VIR_FROM_THIS VIR_FROM_NONE struct _virBitmap { @@ -180,3 +185,63 @@ char *virBitmapString(virBitmapPtr bitmap) return virBufferContentAndReset(buf); } + +/** + * virBitmapParseCommaSeparatedFormat: + * + * When bitmap is printed in ascii format, especially in Linux, + * comma-separated format is sometimes used. For example, a bitmap 1011 is + * represetned as 0-4,6-7. This function parses comma-separated format + * and returns virBitmap. Found max bit is returned, too. + * + * This functions stops if characters other than digits, ',', '-' are + * found. This function will be useful for parsing linux's bitmap. + */ +virBitmapPtr virBitmapParseCommaSeparatedFormat(char *buf, int *max_bit) +{ +char *pos = buf; +virBitmapPtr map = NULL; +int val, x; + +/* at first, find the highest number */ +val = 0; +while ((*pos != '\n') (*pos != 0)) { +if (c_isdigit(*pos)) { +virStrToLong_i(pos, pos, 10, val); +} else if ((*pos
[libvirt] [PATCH 1/1 V4] ocaml-libvirt: add D.get_cpu_stats() API to ocaml-libvirt
Changed from V3: use new virDomainGetCPUStats() libvirt-API. use C code to construct the typed_param list array Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- libvirt/libvirt.ml |8 libvirt/libvirt.mli | 11 + libvirt/libvirt_c_oneoffs.c | 91 +++ 3 files changed, 110 insertions(+), 0 deletions(-) diff --git a/libvirt/libvirt.ml b/libvirt/libvirt.ml index fc29264..08c501b 100644 --- a/libvirt/libvirt.ml +++ b/libvirt/libvirt.ml @@ -343,6 +343,13 @@ struct | SchedFieldInt64 of int64 | SchedFieldUInt64 of int64 | SchedFieldFloat of float | SchedFieldBool of bool + type typed_param = string * typed_param_value + and typed_param_value = +| TypedFieldInt32 of int32 | TypedFieldUInt32 of int32 +| TypedFieldInt64 of int64 | TypedFieldUInt64 of int64 +| TypedFieldFloat of float | TypedFieldBool of bool +| TypedFieldString of string + type migrate_flag = Live type memory_flag = Virtual @@ -419,6 +426,7 @@ struct external set_vcpus : [`W] t - int - unit = ocaml_libvirt_domain_set_vcpus external pin_vcpu : [`W] t - int - string - unit = ocaml_libvirt_domain_pin_vcpu external get_vcpus : [`R] t - int - int - int * vcpu_info array * string = ocaml_libvirt_domain_get_vcpus + external get_cpu_stats : [`R] t - int - typed_param list array = ocaml_libvirt_domain_get_cpu_stats external get_max_vcpus : [`R] t - int = ocaml_libvirt_domain_get_max_vcpus external attach_device : [`W] t - xml - unit = ocaml_libvirt_domain_attach_device external detach_device : [`W] t - xml - unit = ocaml_libvirt_domain_detach_device diff --git a/libvirt/libvirt.mli b/libvirt/libvirt.mli index 7bda889..31f2ca2 100644 --- a/libvirt/libvirt.mli +++ b/libvirt/libvirt.mli @@ -439,6 +439,13 @@ sig | SchedFieldInt64 of int64 | SchedFieldUInt64 of int64 | SchedFieldFloat of float | SchedFieldBool of bool + type typed_param = string * typed_param_value + and typed_param_value = +| TypedFieldInt32 of int32 | TypedFieldUInt32 of int32 +| TypedFieldInt64 of int64 | TypedFieldUInt64 of int64 +| TypedFieldFloat of float | TypedFieldBool of bool +| TypedFieldString of string + type migrate_flag = Live type memory_flag = Virtual @@ -586,6 +593,10 @@ sig for a domain. See the libvirt documentation for details of the array and bitmap returned from this function. *) + val get_cpu_stats : [`R] t - int - typed_param list array +(** [get_pcpu_stats dom nr_pcpu] returns the physic CPU stats + for a domain. See the libvirt documentation for details. +*) val get_max_vcpus : [`R] t - int (** Returns the maximum number of vCPUs supported for this domain. *) val attach_device : [`W] t - xml - unit diff --git a/libvirt/libvirt_c_oneoffs.c b/libvirt/libvirt_c_oneoffs.c index d87dd21..a0c74be 100644 --- a/libvirt/libvirt_c_oneoffs.c +++ b/libvirt/libvirt_c_oneoffs.c @@ -604,6 +604,97 @@ ocaml_libvirt_domain_get_vcpus (value domv, value maxinfov, value maplenv) CAMLreturn (rv); } +CAMLprim value +ocaml_libvirt_domain_get_cpu_stats (value domv, value nr_pcpusv) +{ + CAMLparam2 (domv, nr_pcpusv); + CAMLlocal5 (cpustats, param_head, param_node, typed_param, typed_param_value); + virDomainPtr dom = Domain_val (domv); + virConnectPtr conn = Connect_domv (domv); + int nr_pcpus = Int_val (nr_pcpusv); + virTypedParameterPtr params; + int r, cpu, ncpus, nparams, i, j, pos; + + /* get percpu information */ + NONBLOCKING (nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0)); + CHECK_ERROR (nparams 0, conn, virDomainGetCPUStats); + + if ((params = malloc(sizeof(*params) * nparams * 128)) == NULL) +caml_failwith ((char *)__FUNCTION__); + + cpustats = caml_alloc (nr_pcpus, 0); /* cpustats: array of params(list of typed_param) */ + cpu = 0; + while (cpu nr_pcpus) { +ncpus = nr_pcpus - cpu 128 ? 128 : nr_pcpus - cpu; + +NONBLOCKING (r = virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, 0)); +CHECK_ERROR (r 0, conn, virDomainGetCPUStats); + +for (i = 0; i ncpus; i++) { + /* list of typed_param: single linked list of param_nodes */ + param_head = Val_emptylist; /* param_head: the head param_node of list of typed_param */ + + if (params[i * nparams].type == 0) { +Store_field(cpustats, cpu + i, param_head); +continue; + } + + for (j = nparams - 1; j = 0; j--) { +pos = i * nparams + j; + if (params[pos].type == 0) +continue; + +param_node = caml_alloc(2, 0); /* param_node: typed_param, next param_node */ +Store_field(param_node, 1, param_head); +param_head = param_node; + +typed_param = caml_alloc(2, 0); /* typed_param: field name(string), typed_param_value */ +Store_field(param_node, 0, typed_param); +Store_field(typed_param, 0
[libvirt] [PATCH 1/1 V4] virt-top: correct virt-top -1 command via cpuacct cgroup
Old virt-top -1 is not correct, its output is generated by guess: use average usage for pinned physical CPUs. example(old virt-top -1): PHYCPU %CPU rhel6 Windows 00.6 0.1= 0.5= 10.6 0.1= 0.5=# 20.6 0.1= 0.5= 30.6 0.1=# 0.5= The output almost makes no sense(all the value are just average, not real). This is new implement, it use cpuacct cgroup to gain *real* physical usages via cpuacct cgroup by virDomainGetCPUStats() API. new result: PHYCPU %CPU rhel6 Windows 01.3 0.3 1.0 12.3 0.3 2.0 22.2 0.5 1.7 32.5 0.4 2.1 PHYCPU %CPU rhel6 Windows 01.7 0.4 1.3 13.6 1.0 2.7 21.6 0.4 1.2 34.8 3.1 1.7 Note: average flag(=) is dropped, there is not average value in here. Note: running flag(#) is dropped, because if the value is not empty, it means the guest was once running in the physical CPU in this period between updates. Changed from V3: use new virDomainGetCPUStats() libvirt-API. add a new function find_usages_from_stats() to gain cpu usages. Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- virt-top/virt_top.ml | 80 +++-- 1 files changed, 31 insertions(+), 49 deletions(-) diff --git a/virt-top/virt_top.ml b/virt-top/virt_top.ml index ef5ac67..f7bd072 100644 --- a/virt-top/virt_top.ml +++ b/virt-top/virt_top.ml @@ -446,14 +446,14 @@ let collect, clear_pcpu_display_data = let last_info = Hashtbl.create 13 in let last_time = ref (Unix.gettimeofday ()) in - (* Save vcpuinfo structures across redraws too (only for pCPU display). *) - let last_vcpu_info = Hashtbl.create 13 in + (* Save pcpu_usages structures across redraws too (only for pCPU display). *) + let last_pcpu_usages = Hashtbl.create 13 in let clear_pcpu_display_data () = -(* Clear out vcpu_info used by PCPUDisplay display_mode +(* Clear out pcpu_info used by PCPUDisplay display_mode * when we switch back to TaskDisplay mode. *) -Hashtbl.clear last_vcpu_info +Hashtbl.clear last_pcpu_usages in let collect (conn, _, _, _, _, node_info, _, _) = @@ -652,22 +652,28 @@ let collect, clear_pcpu_display_data = (try let domid = rd.rd_domid in let maplen = C.cpumaplen nr_pcpus in +let cpu_stats = D.get_cpu_stats rd.rd_dom nr_pcpus in +let rec find_usages_from_stats = (function + | (cpu_time, D.TypedFieldUInt64(usages)) :: _ - usages + | (_, _) :: params - find_usages_from_stats params + | [] - 0L) in +let pcpu_usages = Array.map find_usages_from_stats cpu_stats in let maxinfo = rd.rd_info.D.nr_virt_cpu in let nr_vcpus, vcpu_infos, cpumaps = D.get_vcpus rd.rd_dom maxinfo maplen in -(* Got previous vcpu_infos for this domain? *) -let prev_vcpu_infos = - try Some (Hashtbl.find last_vcpu_info domid) +(* Got previous pcpu_usages for this domain? *) +let prev_pcpu_usages = + try Some (Hashtbl.find last_pcpu_usages domid) with Not_found - None in -(* Update last_vcpu_info. *) -Hashtbl.replace last_vcpu_info domid vcpu_infos; - -(match prev_vcpu_infos with - | Some prev_vcpu_infos - when Array.length prev_vcpu_infos = Array.length vcpu_infos - - Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) +(* Update last_pcpu_usages. *) +Hashtbl.replace last_pcpu_usages domid pcpu_usages; + +(match prev_pcpu_usages with + | Some prev_pcpu_usages + when Array.length prev_pcpu_usages = Array.length pcpu_usages - + Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen) | _ - None (* ignore missing / unequal length prev_vcpu_infos *) ); with @@ -680,37 +686,15 @@ let collect, clear_pcpu_display_data = (* Rearrange the data into a matrix. Major axis (down) is * pCPUs. Minor axis (right) is domains. At each node we store: * cpu_time (on this pCPU only, nanosecs), -* average? (if set, then cpu_time is an average because the -* vCPU is pinned to more than one pCPU) -* running? (if set, we were instantaneously running on this pCPU) *) - let empty_node = (0L, false, false) in - let pcpus = Array.make_matrix nr_pcpus nr_doms empty_node
Re: [libvirt] [PATCH V4 1/5] Add new public API virDomainGetCPUStats()
Hi, Eric Are any problem/suggestion with 3/5 4/5 5/5 of V4 patchset? If not, I will rebase them and resend them with tiny fixes applied. Thanks, Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/5 V3] virsh: Enable the pcpuinfo command in virsh
This command gets information about the physical CPUs. Example: # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 93 +++ tools/virsh.pod |5 +++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index c511e2a..e8b9221 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4702,6 +4702,98 @@ cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd) } /* + * pcpuinfo command + */ +static const vshCmdInfo info_pcpuinfo[] = { +{help, N_(detailed domain pcpu information)}, +{desc, N_(Returns basic information about the domain's physical CPUs.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_pcpuinfo[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdPcpuinfo(vshControl *ctl, const vshCmd *cmd) +{ +virDomainInfo info; +virDomainPtr dom; +virNodeInfo nodeinfo; +virVcpuInfoPtr cpuinfo; +unsigned char *cpumaps; +int ncpus, maxcpu; +size_t cpumaplen; +bool ret = true; +int n, m; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (virNodeGetInfo(ctl-conn, nodeinfo) != 0) { +virDomainFree(dom); +return false; +} + +if (virDomainGetInfo(dom, info) != 0) { +virDomainFree(dom); +return false; +} + +cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu); +maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo); +cpumaplen = VIR_CPU_MAPLEN(maxcpu); +cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen); + +if ((ncpus = virDomainGetVcpus(dom, + cpuinfo, info.nrVirtCpu, + cpumaps, cpumaplen)) = 0) { +unsigned long long *usages; +int nr_usages = maxcpu; + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto fail; +} + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, 0) 0) { +VIR_FREE(usages); +goto fail; +} + +for (n = 0; n MIN(maxcpu, nr_usages); n++) { +vshPrint(ctl, %-15s %d\n, _(CPU:), n); +for (m = 0; m ncpus; m++) { +if (cpuinfo[m].cpu == n) { +vshPrint(ctl, %-15s %d\n, _(Curr VCPU:), m); +break; +} +} +if (m == ncpus) { +vshPrint(ctl, %-15s %s\n, _(Curr VCPU:), _(-)); +} +vshPrint(ctl, %-15s %.1lf\n\n, _(Usage:), + usages[n] / 10.0); +} +VIR_FREE(usages); +goto cleanup; +} + +fail: +ret = false; + +cleanup: +VIR_FREE(cpumaps); +VIR_FREE(cpuinfo); +virDomainFree(dom); +return ret; +} + +/* * vcpupin command */ static const vshCmdInfo info_vcpupin[] = { @@ -15952,6 +16044,7 @@ static const vshCmdDef domManagementCmds[] = { {migrate-getspeed, cmdMigrateGetMaxSpeed, opts_migrate_getspeed, info_migrate_getspeed, 0}, {numatune, cmdNumatune, opts_numatune, info_numatune, 0}, +{pcpuinfo, cmdPcpuinfo, opts_pcpuinfo, info_pcpuinfo, 0}, {reboot, cmdReboot, opts_reboot, info_reboot, 0}, {reset, cmdReset, opts_reset, info_reset, 0}, {restore, cmdRestore, opts_restore, info_restore, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index c88395b..8d9cceb 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1296,6 +1296,11 @@ Thus, this command always takes exactly zero or two flags. Returns basic information about the domain virtual CPUs, like the number of vCPUs, the running time, the affinity to physical processors. +=item Bpcpuinfo Idomain-id + +Returns information about the physical CPUs of the domain, like the usage of +CPUs, the current attached vCPUs. + =item Bvcpupin Idomain-id [Ivcpu] [Icpulist] [[I--live] [I--config] | [I--current]] -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/1 V3] virt-top: correct virt-top -1 command via cpuacct cgroup
Old virt-top -1 is not correct, its output is generated by guess: use average usage for pinned physical CPUs. example(old virt-top -1): PHYCPU %CPU rhel6 Windows 00.6 0.1= 0.5= 10.6 0.1= 0.5=# 20.6 0.1= 0.5= 30.6 0.1=# 0.5= The output almost makes no sense(all the value are just average, not real). This is new implement, it use cpuacct cgroup to gain *real* physical usages via cpuacct cgroup by virDomainGetPcpusUsage() API. new result: PHYCPU %CPU rhel6 Windows 03.3 2.90.3 11.7 1.10.6 23.5 1.81.6 33.4 1.61.8 PHYCPU %CPU rhel6 Windows 01.2 0.80.4 11.6 1.6 22.2 1.70.5 33.0 2.50.5 Note: average flag(=) is dropped, there is not average value in here. Note: running flag(#) is dropped, because if the value is not empty, it means the guest was once running in the physical CPU in this period between updates. Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- virt-top/virt_top.ml | 75 + 1 files changed, 26 insertions(+), 49 deletions(-) diff --git a/virt-top/virt_top.ml b/virt-top/virt_top.ml index ef5ac67..2556b77 100644 --- a/virt-top/virt_top.ml +++ b/virt-top/virt_top.ml @@ -446,14 +446,14 @@ let collect, clear_pcpu_display_data = let last_info = Hashtbl.create 13 in let last_time = ref (Unix.gettimeofday ()) in - (* Save vcpuinfo structures across redraws too (only for pCPU display). *) - let last_vcpu_info = Hashtbl.create 13 in + (* Save pcpu_usages structures across redraws too (only for pCPU display). *) + let last_pcpu_usages = Hashtbl.create 13 in let clear_pcpu_display_data () = -(* Clear out vcpu_info used by PCPUDisplay display_mode +(* Clear out pcpu_info used by PCPUDisplay display_mode * when we switch back to TaskDisplay mode. *) -Hashtbl.clear last_vcpu_info +Hashtbl.clear last_pcpu_usages in let collect (conn, _, _, _, _, node_info, _, _) = @@ -652,22 +652,23 @@ let collect, clear_pcpu_display_data = (try let domid = rd.rd_domid in let maplen = C.cpumaplen nr_pcpus in +let pcpu_usages = D.get_pcpu_usages rd.rd_dom nr_pcpus in let maxinfo = rd.rd_info.D.nr_virt_cpu in let nr_vcpus, vcpu_infos, cpumaps = D.get_vcpus rd.rd_dom maxinfo maplen in -(* Got previous vcpu_infos for this domain? *) -let prev_vcpu_infos = - try Some (Hashtbl.find last_vcpu_info domid) +(* Got previous pcpu_usages for this domain? *) +let prev_pcpu_usages = + try Some (Hashtbl.find last_pcpu_usages domid) with Not_found - None in -(* Update last_vcpu_info. *) -Hashtbl.replace last_vcpu_info domid vcpu_infos; - -(match prev_vcpu_infos with - | Some prev_vcpu_infos - when Array.length prev_vcpu_infos = Array.length vcpu_infos - - Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) +(* Update last_pcpu_usages. *) +Hashtbl.replace last_pcpu_usages domid pcpu_usages; + +(match prev_pcpu_usages with + | Some prev_pcpu_usages + when Array.length prev_pcpu_usages = Array.length pcpu_usages - + Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen) | _ - None (* ignore missing / unequal length prev_vcpu_infos *) ); with @@ -680,37 +681,15 @@ let collect, clear_pcpu_display_data = (* Rearrange the data into a matrix. Major axis (down) is * pCPUs. Minor axis (right) is domains. At each node we store: * cpu_time (on this pCPU only, nanosecs), -* average? (if set, then cpu_time is an average because the -* vCPU is pinned to more than one pCPU) -* running? (if set, we were instantaneously running on this pCPU) *) - let empty_node = (0L, false, false) in - let pcpus = Array.make_matrix nr_pcpus nr_doms empty_node in + let pcpus = Array.make_matrix nr_pcpus nr_doms 0L in List.iteri ( - fun di (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) - + fun di (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen
[libvirt] [PATCH 1/5 V3] libvirt: Add new public API virDomainGetPcpusUsage
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |5 python/generator.py |1 + src/driver.h |7 + src/libvirt.c| 51 ++ src/libvirt_public.syms |5 5 files changed, 69 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index e436f3c..167e89f 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3606,6 +3606,11 @@ int virConnectSetKeepAlive(virConnectPtr conn, int interval, unsigned int count); +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index 6fee3a4..0311004 100755 --- a/python/generator.py +++ b/python/generator.py @@ -421,6 +421,7 @@ skip_impl = ( 'virDomainGetBlockIoTune', 'virDomainSetInterfaceParameters', 'virDomainGetInterfaceParameters', +'virDomainGetPcpusUsage', # not implemented yet ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index 24636a4..2a3c46d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -794,6 +794,12 @@ typedef int int *nparams, unsigned int flags); +typedef int +(*virDrvDomainGetPcpusUsage)(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + /** * _virDriver: * @@ -962,6 +968,7 @@ struct _virDriver { virDrvNodeSuspendForDuration nodeSuspendForDuration; virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; +virDrvDomainGetPcpusUsage domainGetPcpusUsage; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index a540424..bd19bf5 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17882,3 +17882,54 @@ error: virDispatchError(dom-conn); return -1; } + +/** + * virDomainGetPcpusUsage: + * @dom: pointer to domain object + * @usages: returned physical cpu usages + * @nr_usages: length of @usages + * @flags: flags to control the operation + * + * Get the cpu usages for every physical cpu since the domain started (in nanoseconds). + * + * Returns 0 if success, -1 on error + */ +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, usages=%p, nr_usages=%d, flags=%x, + usages, (nr_usages) ? *nr_usages : -1, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} + +if (nr_usages == NULL *nr_usages != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); +goto error; +} + +conn = dom-conn; + +if (conn-driver-domainGetPcpusUsage) { +int ret; +ret = conn-driver-domainGetPcpusUsage(dom, usages, nr_usages, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ca7216..15d944c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -516,4 +516,9 @@ LIBVIRT_0.9.9 { virDomainSetNumaParameters; } LIBVIRT_0.9.8; +LIBVIRT_0.9.10 { +global: +virDomainGetPcpusUsage; +} LIBVIRT_0.9.9; + # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 0/5 0/1 0/1 V3] Add new public API virDomainGetPcpusUsage() and pcpuinfo command in virsh
virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (See the last patch in this series). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physical CPUs, such as the usage of CPUs, the current attached vCPUs. # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Changed from V2: Simple cleanup Add python implementation of virDomainGetPcpusUsage() Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com Patch for libvirt(5 patches): daemon/remote.c | 68 include/libvirt/libvirt.h.in|5 ++ python/generator.py |1 + python/libvirt-override-api.xml |6 +++ python/libvirt-override.c | 33 ++ src/driver.h|7 +++ src/libvirt.c | 51 + src/libvirt_public.syms |5 ++ src/qemu/qemu.conf |5 +- src/qemu/qemu_conf.c|3 +- src/qemu/qemu_driver.c | 74 +++ src/remote/remote_driver.c | 51 + src/remote/remote_protocol.x| 17 +++- src/remote_protocol-structs | 13 + src/util/cgroup.c |7 +++ src/util/cgroup.h |1 + tools/virsh.c | 93 +++ tools/virsh.pod |5 ++ 18 files changed, 441 insertions(+), 4 deletions(-) Patch for ocaml-libvirt (1 patch): libvirt/libvirt.ml |1 + libvirt/libvirt.mli |4 libvirt/libvirt_c_oneoffs.c | 25 + 3 files changed, 30 insertions(+), 0 deletions(-) Patch for virt-top (1 patch): virt-top/virt_top.ml | 75 + 1 files changed, 26 insertions(+), 49 deletions(-) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/1 V3] ocaml-libvirt: and virDomainGetPcpusUsage() API to ocaml-libvirt
Acked-by: Richard W.M. Jones rjo...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- libvirt/libvirt.ml |1 + libvirt/libvirt.mli |4 libvirt/libvirt_c_oneoffs.c | 25 + 3 files changed, 30 insertions(+), 0 deletions(-) diff --git a/libvirt/libvirt.ml b/libvirt/libvirt.ml index fc29264..a8720a9 100644 --- a/libvirt/libvirt.ml +++ b/libvirt/libvirt.ml @@ -419,6 +419,7 @@ struct external set_vcpus : [`W] t - int - unit = ocaml_libvirt_domain_set_vcpus external pin_vcpu : [`W] t - int - string - unit = ocaml_libvirt_domain_pin_vcpu external get_vcpus : [`R] t - int - int - int * vcpu_info array * string = ocaml_libvirt_domain_get_vcpus + external get_pcpu_usages : [`R] t - int - int64 array = ocaml_libvirt_domain_get_pcpu_usages external get_max_vcpus : [`R] t - int = ocaml_libvirt_domain_get_max_vcpus external attach_device : [`W] t - xml - unit = ocaml_libvirt_domain_attach_device external detach_device : [`W] t - xml - unit = ocaml_libvirt_domain_detach_device diff --git a/libvirt/libvirt.mli b/libvirt/libvirt.mli index 7bda889..63bf830 100644 --- a/libvirt/libvirt.mli +++ b/libvirt/libvirt.mli @@ -586,6 +586,10 @@ sig for a domain. See the libvirt documentation for details of the array and bitmap returned from this function. *) + val get_pcpu_usages : [`R] t - int - int64 array +(** [get_pcpu_usages dom nr_pcpu] returns the physical CPU usages + for a domain. See the libvirt documentation for details. +*) val get_max_vcpus : [`R] t - int (** Returns the maximum number of vCPUs supported for this domain. *) val attach_device : [`W] t - xml - unit diff --git a/libvirt/libvirt_c_oneoffs.c b/libvirt/libvirt_c_oneoffs.c index d87dd21..68d5ecc 100644 --- a/libvirt/libvirt_c_oneoffs.c +++ b/libvirt/libvirt_c_oneoffs.c @@ -604,6 +604,31 @@ ocaml_libvirt_domain_get_vcpus (value domv, value maxinfov, value maplenv) CAMLreturn (rv); } +CAMLprim value +ocaml_libvirt_domain_get_pcpu_usages (value domv, value nr_pcpusv) +{ + CAMLparam2 (domv, nr_pcpusv); + CAMLlocal1 (usagev); + virDomainPtr dom = Domain_val (domv); + virConnectPtr conn = Connect_domv (domv); + int nr_pcpus = Int_val (nr_pcpusv); + unsigned long long pcpu_usages[nr_pcpus]; + int r, i; + + memset (pcpu_usages, 0, sizeof(pcpu_usages[0]) * nr_pcpus); + + NONBLOCKING (r = virDomainGetPcpusUsage (dom, pcpu_usages, nr_pcpus, 0)); + CHECK_ERROR (r == -1, conn, virDomainGetPcpusUsage); + + /* Copy the pcpu_usages. */ + usagev = caml_alloc (nr_pcpus, 0); + for (i = 0; i nr_pcpus; ++i) { +Store_field (usagev, i, caml_copy_int64 ((int64_t)pcpu_usages[i])); + } + + CAMLreturn (usagev); +} + #ifdef HAVE_WEAK_SYMBOLS #ifdef HAVE_VIRDOMAINMIGRATE extern virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 5/5 V3] python: implement virDomainGetPcpusUsage
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- python/generator.py |2 +- python/libvirt-override-api.xml |6 ++ python/libvirt-override.c | 33 + 3 files changed, 40 insertions(+), 1 deletions(-) diff --git a/python/generator.py b/python/generator.py index 0311004..cf75c44 100755 --- a/python/generator.py +++ b/python/generator.py @@ -421,7 +421,7 @@ skip_impl = ( 'virDomainGetBlockIoTune', 'virDomainSetInterfaceParameters', 'virDomainGetInterfaceParameters', -'virDomainGetPcpusUsage', # not implemented yet +'virDomainGetPcpusUsage', ) qemu_skip_impl = ( diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 704fee9..69bb159 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -421,5 +421,11 @@ arg name='flags' type='unsigned int' info='an ORapos;ed set of virDomainMemoryFlags'/ return type='char *' info='the returned buffer or None in case of error'/ /function +function name='virDomainGetPcpusUsage' file='python' + infoGet the cpu usages for every physical cpu since the domain started (in nanoseconds)./info + arg name='dom' type='virDomainPtr' info='pointer to the domain'/ + arg name='flags' type='unsigned int' info='an ORapos;ed set of virDomainMemoryFlags'/ + return type='unsigned long long *' info='the tuple of the cpu usages'/ +/function /symbols /api diff --git a/python/libvirt-override.c b/python/libvirt-override.c index d2aad0f..ad4646d 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -5108,6 +5108,38 @@ cleanup: return py_retval; } +static PyObject * +libvirt_virDomainGetPcpusUsage(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { +PyObject *py_retval = NULL; +int c_retval; +virDomainPtr domain; +PyObject *pyobj_domain; +unsigned long long usages[32]; +int i, nr_usages = sizeof(usages) / sizeof(usages[0]); +unsigned int flags; + +if (!PyArg_ParseTuple(args, (char *)Oi:virDomainGetPcpusUsage, pyobj_domain, + flags)) +return NULL; + +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +LIBVIRT_BEGIN_ALLOW_THREADS; +c_retval = virDomainGetPcpusUsage(domain, usages, nr_usages, flags); +LIBVIRT_END_ALLOW_THREADS; + +if (c_retval 0) +return VIR_PY_NONE; + +if ((py_retval = PyTuple_New(nr_usages)) == NULL) +return VIR_PY_NONE; +for (i = 0; i nr_usages; i++) +PyTuple_SetItem(py_retval, i, PyLong_FromLongLong(usages[i])); + +return py_retval; +} + / * * * The registration stuff * @@ -5206,6 +5238,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainMigrateGetMaxSpeed, libvirt_virDomainMigrateGetMaxSpeed, METH_VARARGS, NULL}, {(char *) virDomainBlockPeek, libvirt_virDomainBlockPeek, METH_VARARGS, NULL}, {(char *) virDomainMemoryPeek, libvirt_virDomainMemoryPeek, METH_VARARGS, NULL}, +{(char *) virDomainGetPcpusUsage, libvirt_virDomainGetPcpusUsage, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/5 V3] remote: mplement new API virDomainGetPcpusUsage for remote driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- daemon/remote.c | 68 ++ src/remote/remote_driver.c | 51 +++ src/remote/remote_protocol.x | 17 ++- src/remote_protocol-structs | 13 4 files changed, 148 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index a28a754..9b0206f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2084,6 +2084,74 @@ cleanup: return rv; } +static int +remoteDispatchDomainGetPcpusUsage(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_pcpus_usage_args *args, + remote_domain_get_pcpus_usage_ret *ret) +{ +int i; +virDomainPtr dom = NULL; +int rv = -1; +unsigned long long *usages; +int nr_usages = args-nr_usages; +struct daemonClientPrivate *priv = +virNetServerClientGetPrivateData(client); + +if (!priv-conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (nr_usages REMOTE_DOMAIN_PCPUS_USAGE_PARAMETERS_MAX) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(nr_usages too large)); +goto cleanup; +} + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(priv-conn, args-dom))) +goto cleanup; + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, args-flags) 0) +goto cleanup; + +ret-nr_usages = nr_usages; + +/* + * In this case, we need to send back the number of parameters + * supported + */ +if (args-nr_usages == 0) { +goto success; +} + +ret-usages.usages_len = MIN(args-nr_usages, nr_usages); +if (VIR_ALLOC_N(ret-usages.usages_val, ret-usages.usages_len) 0) { +virReportOOMError(); +goto cleanup; +} + +for (i = 0; i ret-usages.usages_len; i++) { +ret-usages.usages_val[i] = usages[i]; +} + +success: +rv = 0; + +cleanup: +if (rv 0) +virNetMessageSaveError(rerr); +VIR_FREE(usages); +if (dom) +virDomainFree(dom); +return rv; +} #ifdef HAVE_SASL /* diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e28840b..9e2f931 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2305,6 +2305,56 @@ done: return rv; } +static int remoteDomainGetPcpusUsage(virDomainPtr domain, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +int i; +int rv = -1; +remote_domain_get_pcpus_usage_args args; +remote_domain_get_pcpus_usage_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.nr_usages = *nr_usages; +args.flags = flags; + +memset(ret, 0, sizeof(ret)); + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_PCPUS_USAGE, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_args, + (char *) args, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret) == -1) { +goto done; +} + +/* Handle the case when the caller does not know the number of parameters + * and is asking for the number of parameters supported + */ +if (*nr_usages == 0) { +*nr_usages = ret.nr_usages; +goto cleanup; +} + +for (i = 0; i MIN(*nr_usages, ret.usages.usages_len); i++) { +usages[i] = ret.usages.usages_val[i]; +} +*nr_usages = ret.nr_usages; + +rv = 0; + +cleanup: +xdr_free ((xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret); +done: +remoteDriverUnlock(priv); +return rv; +} + /*--*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -4750,6 +4800,7 @@ static virDriver remote_driver = { .domainGetBlockIoTune = remoteDomainGetBlockIoTune, /* 0.9.8 */ .domainSetNumaParameters = remoteDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ +.domainGetPcpusUsage = remoteDomainGetPcpusUsage, /* 0.9.10 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ca739ff..3c1ab41 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -131,6 +131,9 @@ const REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX = 16; /* Upper limit on list of numa
[libvirt] [PATCH 3/5 V3] qemu: implement new API virDomainGetPcpusUsage for qemu driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |5 ++- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 74 src/util/cgroup.c |7 src/util/cgroup.h |1 + 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 4ec5e6c..5f75b3e 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -158,18 +158,19 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - 'cpuacct' - use for CPUs' account # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: # # mkdir /dev/cgroup -# mount -t cgroup -o devices,cpu,memory,blkio,cpuset none /dev/cgroup +# mount -t cgroup -o devices,cpu,memory,blkio,cpuset,cpuacct none /dev/cgroup # # They can be mounted anywhere, and different controllers # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index bc0a646..4775638 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 712f1fc..e90e185 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11180,6 +11180,79 @@ cleanup: return ret; } +static int +qemuGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +struct qemud_driver *driver = dom-conn-privateData; +virCgroupPtr group = NULL; +virDomainObjPtr vm = NULL; +char *pos, *raw; +unsigned long long val; +int nr_cpus = 0; +int ret = -1; +int rc; +bool isActive; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); + +vm = virDomainFindByUUID(driver-domains, dom-uuid); + +if (vm == NULL) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(No such domain %s), dom-uuid); +goto cleanup; +} + +isActive = virDomainObjIsActive(vm); + +if (!isActive) { +qemuReportError(VIR_ERR_OPERATION_INVALID, %s, +_(domain is not running)); +goto cleanup; +} + +if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPUACCT)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(cgroup CPUACCT controller is not mounted)); +goto cleanup; +} + +if (virCgroupForDomain(driver-cgroup, vm-def-name, group, 0) != 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(cannot find cgroup for domain %s), vm-def-name); +goto cleanup; +} + +rc = virCgroupGetCpuacctPcpusUsage(group, raw); +if (rc != 0) { +virReportSystemError(-rc, %s, _(unable to get cpu account)); +goto cleanup; +} + +pos = raw; +while (virStrToLong_ull(pos, pos, 10, val) = 0) { +if (nr_cpus *nr_usages) { +usages[nr_cpus] = val; +} +nr_cpus++; +} + +VIR_FREE(raw); +*nr_usages = nr_cpus; +ret = 0; + +cleanup: +virCgroupFree(group); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} static virDomainPtr qemuDomainAttach(virConnectPtr conn, unsigned int pid, @@ -11983,6 +12056,7 @@ static virDriver qemuDriver = { .domainGetNumaParameters = qemuDomainGetNumaParameters, /* 0.9.9 */ .domainGetInterfaceParameters = qemuDomainGetInterfaceParameters, /* 0.9.9 */ .domainSetInterfaceParameters = qemuDomainSetInterfaceParameters, /* 0.9.9 */ +.domainGetPcpusUsage = qemuGetPcpusUsage, /* 0.9.10 */ }; diff --git a/src/util/cgroup.c b/src/util/cgroup.c index 25f2691..114eeb5 100644 --- a/src/util/cgroup.c +++ b/src/util/cgroup.c @@ -1554,6 +1554,13 @@ int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage) cpuacct.usage, usage); } +int virCgroupGetCpuacctPcpusUsage(virCgroupPtr group, char **usage) +{ +return virCgroupGetValueStr(group
Re: [libvirt] [PATCH 0/4 0/1 0/1 V2] Add new public API virDomainGetPcpusUsage() and pcpuinfo command in virsh
On 01/05/2012 12:55 AM, Eric Blake wrote: On 01/03/2012 09:09 PM, Lai Jiangshan wrote: virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (See the last patch in this series). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physic CPUs, such as the usage of CPUs, the current attached vCPUs. I think this is invasive enough that we should defer it until after the 0.9.9 release, but it's definitely on my list of things to review and not forgotten. Ping. I'm glad to see your comments. Thanks, Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 1/4 V2] libvirt: Add new public API virDomainGetPcpusUsage
On 01/05/2012 10:23 PM, Richard W.M. Jones wrote: On Wed, Jan 04, 2012 at 12:09:18PM +0800, Lai Jiangshan wrote: +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); It'd be useful to get feedback from libvirt folk about whether this API is reasonable. +/** + * virDomainGetPcpusUsage: + * @dom: pointer to domain object + * @usages: returned physic cpu usages 'physical' + * @nr_usages: length of @usages + * @flags: flags to control the operation + * + * Get the cpu usages per every physic cpu 'physical' I think this needs to describe what exactly is returned. Nanoseconds since the domain started, I assume? Yes. I will apply your comments in next version which will be deferred until 0.9.9 released as Eric suggested. Thank you very much. Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/4 V2] libvirt: Add new public API virDomainGetPcpusUsage
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |5 python/generator.py |1 + src/driver.h |7 + src/libvirt.c| 51 ++ src/libvirt_public.syms |5 5 files changed, 69 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index ad6fcce..fc92143 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3600,6 +3600,11 @@ int virConnectSetKeepAlive(virConnectPtr conn, int interval, unsigned int count); +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index 6fee3a4..0311004 100755 --- a/python/generator.py +++ b/python/generator.py @@ -421,6 +421,7 @@ skip_impl = ( 'virDomainGetBlockIoTune', 'virDomainSetInterfaceParameters', 'virDomainGetInterfaceParameters', +'virDomainGetPcpusUsage', # not implemented yet ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index ec4abf3..2cf408a 100644 --- a/src/driver.h +++ b/src/driver.h @@ -793,6 +793,12 @@ typedef int int *nparams, unsigned int flags); +typedef int +(*virDrvDomainGetPcpusUsage)(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + /** * _virDriver: * @@ -961,6 +967,7 @@ struct _virDriver { virDrvNodeSuspendForDuration nodeSuspendForDuration; virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; +virDrvDomainGetPcpusUsage domainGetPcpusUsage; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index feb3ca6..79ff2df 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17867,3 +17867,54 @@ error: virDispatchError(dom-conn); return -1; } + +/** + * virDomainGetPcpusUsage: + * @dom: pointer to domain object + * @usages: returned physic cpu usages + * @nr_usages: length of @usages + * @flags: flags to control the operation + * + * Get the cpu usages per every physic cpu + * + * Returns 0 if success, -1 on error + */ +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, usages=%p, nr_usages=%d, flags=%x, + usages, (nr_usages) ? *nr_usages : -1, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} + +if (nr_usages == NULL *nr_usages != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); +goto error; +} + +conn = dom-conn; + +if (conn-driver-domainGetPcpusUsage) { +int ret; +ret = conn-driver-domainGetPcpusUsage(dom, usages, nr_usages, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ca7216..15d944c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -516,4 +516,9 @@ LIBVIRT_0.9.9 { virDomainSetNumaParameters; } LIBVIRT_0.9.8; +LIBVIRT_0.9.10 { +global: +virDomainGetPcpusUsage; +} LIBVIRT_0.9.9; + # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/4 V2] virsh: Enable the pcpuinfo command in virsh
This command gets information about the physic CPUs. Example: # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 93 +++ tools/virsh.pod |5 +++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 0bc0519..f6fac24 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4578,6 +4578,98 @@ cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd) } /* + * pcpuinfo command + */ +static const vshCmdInfo info_pcpuinfo[] = { +{help, N_(detailed domain pcpu information)}, +{desc, N_(Returns basic information about the domain physic CPUs.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_pcpuinfo[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdPcpuinfo(vshControl *ctl, const vshCmd *cmd) +{ +virDomainInfo info; +virDomainPtr dom; +virNodeInfo nodeinfo; +virVcpuInfoPtr cpuinfo; +unsigned char *cpumaps; +int ncpus, maxcpu; +size_t cpumaplen; +bool ret = true; +int n, m; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (virNodeGetInfo(ctl-conn, nodeinfo) != 0) { +virDomainFree(dom); +return false; +} + +if (virDomainGetInfo(dom, info) != 0) { +virDomainFree(dom); +return false; +} + +cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu); +maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo); +cpumaplen = VIR_CPU_MAPLEN(maxcpu); +cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen); + +if ((ncpus = virDomainGetVcpus(dom, + cpuinfo, info.nrVirtCpu, + cpumaps, cpumaplen)) = 0) { +unsigned long long *usages; +int nr_usages = maxcpu; + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto fail; +} + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, 0) 0) { +VIR_FREE(usages); +goto fail; +} + +for (n = 0; n MIN(maxcpu, nr_usages); n++) { +vshPrint(ctl, %-15s %d\n, _(CPU:), n); +for (m = 0; m ncpus; m++) { +if (cpuinfo[m].cpu == n) { +vshPrint(ctl, %-15s %d\n, _(Curr VCPU:), m); +break; +} +} +if (m == ncpus) { +vshPrint(ctl, %-15s %s\n, _(Curr VCPU:), _(-)); +} +vshPrint(ctl, %-15s %.1lf\n\n, _(Usage:), + usages[n] / 10.0); +} +VIR_FREE(usages); +goto cleanup; +} + +fail: +ret = false; + +cleanup: +VIR_FREE(cpumaps); +VIR_FREE(cpuinfo); +virDomainFree(dom); +return ret; +} + +/* * vcpupin command */ static const vshCmdInfo info_vcpupin[] = { @@ -15813,6 +15905,7 @@ static const vshCmdDef domManagementCmds[] = { {migrate-getspeed, cmdMigrateGetMaxSpeed, opts_migrate_getspeed, info_migrate_getspeed, 0}, {numatune, cmdNumatune, opts_numatune, info_numatune, 0}, +{pcpuinfo, cmdPcpuinfo, opts_pcpuinfo, info_pcpuinfo, 0}, {reboot, cmdReboot, opts_reboot, info_reboot, 0}, {reset, cmdReset, opts_reset, info_reset, 0}, {restore, cmdRestore, opts_restore, info_restore, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 138f886..5a8b2e6 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1286,6 +1286,11 @@ Thus, this command always takes exactly zero or two flags. Returns basic information about the domain virtual CPUs, like the number of vCPUs, the running time, the affinity to physical processors. +=item Bpcpuinfo Idomain-id + +Returns information about the physic CPUs of the domain, like the usage of +CPUs, the current attached vCPUs. + =item Bvcpupin Idomain-id [Ivcpu] [Icpulist] [[I--live] [I--config] | [I--current]] -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/4 V2] remote: mplement new API virDomainGetPcpusUsage for remote driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- daemon/remote.c | 68 ++ src/remote/remote_driver.c | 51 +++ src/remote/remote_protocol.x | 17 ++- src/remote_protocol-structs | 13 4 files changed, 148 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index a28a754..9b0206f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2084,6 +2084,74 @@ cleanup: return rv; } +static int +remoteDispatchDomainGetPcpusUsage(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_pcpus_usage_args *args, + remote_domain_get_pcpus_usage_ret *ret) +{ +int i; +virDomainPtr dom = NULL; +int rv = -1; +unsigned long long *usages; +int nr_usages = args-nr_usages; +struct daemonClientPrivate *priv = +virNetServerClientGetPrivateData(client); + +if (!priv-conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (nr_usages REMOTE_DOMAIN_PCPUS_USAGE_PARAMETERS_MAX) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(nr_usages too large)); +goto cleanup; +} + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(priv-conn, args-dom))) +goto cleanup; + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, args-flags) 0) +goto cleanup; + +ret-nr_usages = nr_usages; + +/* + * In this case, we need to send back the number of parameters + * supported + */ +if (args-nr_usages == 0) { +goto success; +} + +ret-usages.usages_len = MIN(args-nr_usages, nr_usages); +if (VIR_ALLOC_N(ret-usages.usages_val, ret-usages.usages_len) 0) { +virReportOOMError(); +goto cleanup; +} + +for (i = 0; i ret-usages.usages_len; i++) { +ret-usages.usages_val[i] = usages[i]; +} + +success: +rv = 0; + +cleanup: +if (rv 0) +virNetMessageSaveError(rerr); +VIR_FREE(usages); +if (dom) +virDomainFree(dom); +return rv; +} #ifdef HAVE_SASL /* diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 7580477..7dd3876 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2305,6 +2305,56 @@ done: return rv; } +static int remoteDomainGetPcpusUsage(virDomainPtr domain, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +int i; +int rv = -1; +remote_domain_get_pcpus_usage_args args; +remote_domain_get_pcpus_usage_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.nr_usages = *nr_usages; +args.flags = flags; + +memset(ret, 0, sizeof(ret)); + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_PCPUS_USAGE, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_args, + (char *) args, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret) == -1) { +goto done; +} + +/* Handle the case when the caller does not know the number of parameters + * and is asking for the number of parameters supported + */ +if (*nr_usages == 0) { +*nr_usages = ret.nr_usages; +goto cleanup; +} + +for (i = 0; i MIN(*nr_usages, ret.usages.usages_len); i++) { +usages[i] = ret.usages.usages_val[i]; +} +*nr_usages = ret.nr_usages; + +rv = 0; + +cleanup: +xdr_free ((xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret); +done: +remoteDriverUnlock(priv); +return rv; +} + /*--*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -4739,6 +4789,7 @@ static virDriver remote_driver = { .domainGetBlockIoTune = remoteDomainGetBlockIoTune, /* 0.9.8 */ .domainSetNumaParameters = remoteDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ +.domainGetPcpusUsage = remoteDomainGetPcpusUsage, /* 0.9.10 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ca739ff..d38206d 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -131,6 +131,9 @@ const REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX = 16; /* Upper limit on list of numa
[libvirt] [PATCH 1/1 V2] ocaml-libvirt: and virDomainGetPcpusUsage() API to ocaml-libvirt
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- libvirt/libvirt.ml |1 + libvirt/libvirt.mli |4 libvirt/libvirt_c_oneoffs.c | 25 + 3 files changed, 30 insertions(+), 0 deletions(-) diff --git a/libvirt/libvirt.ml b/libvirt/libvirt.ml index fc29264..a8720a9 100644 --- a/libvirt/libvirt.ml +++ b/libvirt/libvirt.ml @@ -419,6 +419,7 @@ struct external set_vcpus : [`W] t - int - unit = ocaml_libvirt_domain_set_vcpus external pin_vcpu : [`W] t - int - string - unit = ocaml_libvirt_domain_pin_vcpu external get_vcpus : [`R] t - int - int - int * vcpu_info array * string = ocaml_libvirt_domain_get_vcpus + external get_pcpu_usages : [`R] t - int - int64 array = ocaml_libvirt_domain_get_pcpu_usages external get_max_vcpus : [`R] t - int = ocaml_libvirt_domain_get_max_vcpus external attach_device : [`W] t - xml - unit = ocaml_libvirt_domain_attach_device external detach_device : [`W] t - xml - unit = ocaml_libvirt_domain_detach_device diff --git a/libvirt/libvirt.mli b/libvirt/libvirt.mli index 7bda889..63bf830 100644 --- a/libvirt/libvirt.mli +++ b/libvirt/libvirt.mli @@ -586,6 +586,10 @@ sig for a domain. See the libvirt documentation for details of the array and bitmap returned from this function. *) + val get_pcpu_usages : [`R] t - int - int64 array +(** [get_pcpu_usages dom nr_pcpu] returns the physic CPU usages + for a domain. See the libvirt documentation for details. +*) val get_max_vcpus : [`R] t - int (** Returns the maximum number of vCPUs supported for this domain. *) val attach_device : [`W] t - xml - unit diff --git a/libvirt/libvirt_c_oneoffs.c b/libvirt/libvirt_c_oneoffs.c index d87dd21..68d5ecc 100644 --- a/libvirt/libvirt_c_oneoffs.c +++ b/libvirt/libvirt_c_oneoffs.c @@ -604,6 +604,31 @@ ocaml_libvirt_domain_get_vcpus (value domv, value maxinfov, value maplenv) CAMLreturn (rv); } +CAMLprim value +ocaml_libvirt_domain_get_pcpu_usages (value domv, value nr_pcpusv) +{ + CAMLparam2 (domv, nr_pcpusv); + CAMLlocal1 (usagev); + virDomainPtr dom = Domain_val (domv); + virConnectPtr conn = Connect_domv (domv); + int nr_pcpus = Int_val (nr_pcpusv); + unsigned long long pcpu_usages[nr_pcpus]; + int r, i; + + memset (pcpu_usages, 0, sizeof(pcpu_usages[0]) * nr_pcpus); + + NONBLOCKING (r = virDomainGetPcpusUsage (dom, pcpu_usages, nr_pcpus, 0)); + CHECK_ERROR (r == -1, conn, virDomainGetPcpusUsage); + + /* Copy the pcpu_usages. */ + usagev = caml_alloc (nr_pcpus, 0); + for (i = 0; i nr_pcpus; ++i) { +Store_field (usagev, i, caml_copy_int64 ((int64_t)pcpu_usages[i])); + } + + CAMLreturn (usagev); +} + #ifdef HAVE_WEAK_SYMBOLS #ifdef HAVE_VIRDOMAINMIGRATE extern virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 0/4 0/1 0/1 V2] Add new public API virDomainGetPcpusUsage() and pcpuinfo command in virsh
virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (See the last patch in this series). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physic CPUs, such as the usage of CPUs, the current attached vCPUs. # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Patch for libvirt(4 patches): daemon/remote.c | 68 ++ include/libvirt/libvirt.h.in |5 ++ python/generator.py |1 + src/driver.h |7 +++ src/libvirt.c| 51 +++ src/libvirt_public.syms |5 ++ src/qemu/qemu.conf |5 +- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 74 + src/remote/remote_driver.c | 51 +++ src/remote/remote_protocol.x | 17 +++- src/remote_protocol-structs | 13 ++ src/util/cgroup.c|7 +++ src/util/cgroup.h|1 + tools/virsh.c| 93 ++ tools/virsh.pod |5 ++ 16 files changed, 402 insertions(+), 4 deletions(-) Patch for ocaml-libvirt (1 patch): libvirt/libvirt.ml |1 + libvirt/libvirt.mli |4 libvirt/libvirt_c_oneoffs.c | 25 + 3 files changed, 30 insertions(+), 0 deletions(-) Patch for virt-top (1 patch): virt-top/virt_top.ml | 75 + 1 files changed, 26 insertions(+), 49 deletions(-) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/4 V2] qemu: implement new API virDomainGetPcpusUsage for qemu driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |5 ++- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 74 src/util/cgroup.c |7 src/util/cgroup.h |1 + 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 4ec5e6c..5f75b3e 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -158,18 +158,19 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - 'cpuacct' - use for CPUs' account # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: # # mkdir /dev/cgroup -# mount -t cgroup -o devices,cpu,memory,blkio,cpuset none /dev/cgroup +# mount -t cgroup -o devices,cpu,memory,blkio,cpuset,cpuacct none /dev/cgroup # # They can be mounted anywhere, and different controllers # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index bc0a646..4775638 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 82bab67..3d023eb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11194,6 +11194,79 @@ cleanup: return ret; } +static int +qemuGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +struct qemud_driver *driver = dom-conn-privateData; +virCgroupPtr group = NULL; +virDomainObjPtr vm = NULL; +char *pos, *raw; +unsigned long long val; +int nr_cpus = 0; +int ret = -1; +int rc; +bool isActive; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); + +vm = virDomainFindByUUID(driver-domains, dom-uuid); + +if (vm == NULL) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(No such domain %s), dom-uuid); +goto cleanup; +} + +isActive = virDomainObjIsActive(vm); + +if (!isActive) { +qemuReportError(VIR_ERR_OPERATION_INVALID, %s, +_(domain is not running)); +goto cleanup; +} + +if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPUACCT)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(cgroup CPUACCT controller is not mounted)); +goto cleanup; +} + +if (virCgroupForDomain(driver-cgroup, vm-def-name, group, 0) != 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(cannot find cgroup for domain %s), vm-def-name); +goto cleanup; +} + +rc = virCgroupGetCpuacctPcpusUsage(group, raw); +if (rc != 0) { +virReportSystemError(-rc, %s, _(unable to get cpu account)); +goto cleanup; +} + +pos = raw; +while (virStrToLong_ull(pos, pos, 10, val) = 0) { +if (nr_cpus *nr_usages) { +usages[nr_cpus] = val; +} +nr_cpus++; +} + +VIR_FREE(raw); +*nr_usages = nr_cpus; +ret = 0; + +cleanup: +virCgroupFree(group); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} static virDomainPtr qemuDomainAttach(virConnectPtr conn, unsigned int pid, @@ -11997,6 +12070,7 @@ static virDriver qemuDriver = { .domainGetNumaParameters = qemuDomainGetNumaParameters, /* 0.9.9 */ .domainGetInterfaceParameters = qemuDomainGetInterfaceParameters, /* 0.9.9 */ .domainSetInterfaceParameters = qemuDomainSetInterfaceParameters, /* 0.9.9 */ +.domainGetPcpusUsage = qemuGetPcpusUsage, /* 0.9.10 */ }; diff --git a/src/util/cgroup.c b/src/util/cgroup.c index 25f2691..114eeb5 100644 --- a/src/util/cgroup.c +++ b/src/util/cgroup.c @@ -1554,6 +1554,13 @@ int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage) cpuacct.usage, usage); } +int virCgroupGetCpuacctPcpusUsage(virCgroupPtr group, char **usage) +{ +return virCgroupGetValueStr(group
[libvirt] [PATCH 1/1 V2] virt-top: correct virt-top -1 command via cpuacct cgroup
Old virt-top -1 is not correct, its output is generated by guess: use average usage for pinned physic CPUs. example(old virt-top -1): PHYCPU %CPU rhel6 Windows 00.6 0.1= 0.5= 10.6 0.1= 0.5=# 20.6 0.1= 0.5= 30.6 0.1=# 0.5= The output almost makes no sense(all the value are just average, not real). This is new implement, it use cpuacct cgroup to gain *real* physic usages via cpuacct cgroup by virDomainGetPcpusUsage() API. new result: PHYCPU %CPU rhel6 Windows 03.3 2.90.3 11.7 1.10.6 23.5 1.81.6 33.4 1.61.8 PHYCPU %CPU rhel6 Windows 01.2 0.80.4 11.6 1.6 22.2 1.70.5 33.0 2.50.5 Note: average flag(=) is dropped, there is not average value in here. Note: running flag(#) is dropped, because if the value is not empty, it means the guest was once running in the physic CPU in this period between updates. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- virt-top/virt_top.ml | 75 + 1 files changed, 26 insertions(+), 49 deletions(-) diff --git a/virt-top/virt_top.ml b/virt-top/virt_top.ml index ef5ac67..2556b77 100644 --- a/virt-top/virt_top.ml +++ b/virt-top/virt_top.ml @@ -446,14 +446,14 @@ let collect, clear_pcpu_display_data = let last_info = Hashtbl.create 13 in let last_time = ref (Unix.gettimeofday ()) in - (* Save vcpuinfo structures across redraws too (only for pCPU display). *) - let last_vcpu_info = Hashtbl.create 13 in + (* Save pcpu_usages structures across redraws too (only for pCPU display). *) + let last_pcpu_usages = Hashtbl.create 13 in let clear_pcpu_display_data () = -(* Clear out vcpu_info used by PCPUDisplay display_mode +(* Clear out pcpu_info used by PCPUDisplay display_mode * when we switch back to TaskDisplay mode. *) -Hashtbl.clear last_vcpu_info +Hashtbl.clear last_pcpu_usages in let collect (conn, _, _, _, _, node_info, _, _) = @@ -652,22 +652,23 @@ let collect, clear_pcpu_display_data = (try let domid = rd.rd_domid in let maplen = C.cpumaplen nr_pcpus in +let pcpu_usages = D.get_pcpu_usages rd.rd_dom nr_pcpus in let maxinfo = rd.rd_info.D.nr_virt_cpu in let nr_vcpus, vcpu_infos, cpumaps = D.get_vcpus rd.rd_dom maxinfo maplen in -(* Got previous vcpu_infos for this domain? *) -let prev_vcpu_infos = - try Some (Hashtbl.find last_vcpu_info domid) +(* Got previous pcpu_usages for this domain? *) +let prev_pcpu_usages = + try Some (Hashtbl.find last_pcpu_usages domid) with Not_found - None in -(* Update last_vcpu_info. *) -Hashtbl.replace last_vcpu_info domid vcpu_infos; - -(match prev_vcpu_infos with - | Some prev_vcpu_infos - when Array.length prev_vcpu_infos = Array.length vcpu_infos - - Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) +(* Update last_pcpu_usages. *) +Hashtbl.replace last_pcpu_usages domid pcpu_usages; + +(match prev_pcpu_usages with + | Some prev_pcpu_usages + when Array.length prev_pcpu_usages = Array.length pcpu_usages - + Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen) | _ - None (* ignore missing / unequal length prev_vcpu_infos *) ); with @@ -680,37 +681,15 @@ let collect, clear_pcpu_display_data = (* Rearrange the data into a matrix. Major axis (down) is * pCPUs. Minor axis (right) is domains. At each node we store: * cpu_time (on this pCPU only, nanosecs), -* average? (if set, then cpu_time is an average because the -* vCPU is pinned to more than one pCPU) -* running? (if set, we were instantaneously running on this pCPU) *) - let empty_node = (0L, false, false) in - let pcpus = Array.make_matrix nr_pcpus nr_doms empty_node in + let pcpus = Array.make_matrix nr_pcpus nr_doms 0L in List.iteri ( - fun di (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos, - cpumaps, maplen) - + fun di (domid, name, nr_vcpus, vcpu_infos, pcpu_usages, + prev_pcpu_usages, cpumaps, maplen) - (* Which pCPUs can this dom run
[libvirt] [PATCH] virsh: move version command to virsh group
Trivial patch, move version command to virsh commands group. It has no any related with any domain. It may connect to the daemon, so the flag is 0 but not VSH_CMD_FLAG_NOCONNECT. --- diff --git a/tools/virsh.c b/tools/virsh.c index 02f2e0d..0166bc6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -15641,7 +15641,6 @@ static const vshCmdDef domManagementCmds[] = { {vcpucount, cmdVcpucount, opts_vcpucount, info_vcpucount, 0}, {vcpuinfo, cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo, 0}, {vcpupin, cmdVcpuPin, opts_vcpupin, info_vcpupin, 0}, -{version, cmdVersion, opts_version, info_version, 0}, {vncdisplay, cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0}, {NULL, NULL, NULL, NULL, 0} }; @@ -15810,6 +15809,7 @@ static const vshCmdDef secretCmds[] = { }; static const vshCmdDef virshCmds[] = { +{version, cmdVersion, opts_version, info_version, 0}, {cd, cmdCd, opts_cd, info_cd, VSH_CMD_FLAG_NOCONNECT}, {echo, cmdEcho, opts_echo, info_echo, VSH_CMD_FLAG_NOCONNECT}, {exit, cmdQuit, NULL, info_quit, VSH_CMD_FLAG_NOCONNECT}, -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 0/4] Add new public API virDomainGetPcpusUsage and pcpuinfo command in virsh
Hi, Eric On 12/08/2011 11:44 AM, Eric Blake wrote: On 12/07/2011 08:40 PM, Lai Jiangshan wrote: virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (still require virt-top site patch). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physic CPUs, such as the usage of CPUs, the current attached vCPUs. Meta-question - is this the time taken _by just the guest in question_ on the host's physical CPU, or is it the cumulative CPU time taken by all process on the physical CPU, in which case this would be better named as virNodeGetCpusUsage()? It's per-domain usage. And if it is per-domain, then what's the difference between physical cpu usage and virtual cpu usage the API tell us how many cpu utilization the whole guest occupies for every physic CPUs. I don't know what is virtual cpu usage means. (that is, can we even tell the overhead of the hypervisor, and should we be exposing that overhead to the user)? it is not exposed to the normal user. the administrator can use virt-top -1 or virsh pcpuinfo to get cpus usage, to find out which physic cpu is busy, to change the cpu bound of (new) guests, to find a proper physic cpu and offline it, or to do nothing, just to observe the stat of the guests. I/(I will) very appreciate for your reply. Thanks, Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 0/4] Add new public API virDomainGetPcpusUsage and pcpuinfo command in virsh
On 12/08/2011 11:44 AM, Eric Blake wrote: On 12/07/2011 08:40 PM, Lai Jiangshan wrote: virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (still require virt-top site patch). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physic CPUs, such as the usage of CPUs, the current attached vCPUs. Meta-question - is this the time taken _by just the guest in question_ on the host's physical CPU, or is it the cumulative CPU time taken by The time is used by the guest in question on the host's physical CPUs. per-domain and per physical CPU all process on the physical CPU, in which case this would be better named as virNodeGetCpusUsage()? And if it is per-domain, then what's the difference between physical cpu usage and virtual cpu usage (that is, can we even tell the overhead of the hypervisor, and should we be exposing that overhead to the user)? I did not catch your mean, could you explain more? Thanks, Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/4] remote: implement new API virDomainGetPcpusUsage for remote driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- daemon/remote.c | 68 ++ src/remote/remote_driver.c | 51 +++ src/remote/remote_protocol.x | 17 ++- src/remote_protocol-structs | 13 4 files changed, 148 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e1d208c..c154892 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2022,6 +2022,74 @@ cleanup: return rv; } +static int +remoteDispatchDomainGetPcpusUsage(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_pcpus_usage_args *args, + remote_domain_get_pcpus_usage_ret *ret) +{ +int i; +virDomainPtr dom = NULL; +int rv = -1; +unsigned long long *usages; +int nr_usages = args-nr_usages; +struct daemonClientPrivate *priv = +virNetServerClientGetPrivateData(client); + +if (!priv-conn) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(connection not open)); +goto cleanup; +} + +if (nr_usages REMOTE_DOMAIN_PCPUS_USAGE_PARAMETERS_MAX) { +virNetError(VIR_ERR_INTERNAL_ERROR, %s, _(nr_usages too large)); +goto cleanup; +} + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto cleanup; +} + +if (!(dom = get_nonnull_domain(priv-conn, args-dom))) +goto cleanup; + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, args-flags) 0) +goto cleanup; + +ret-nr_usages = nr_usages; + +/* + * In this case, we need to send back the number of parameters + * supported + */ +if (args-nr_usages == 0) { +goto success; +} + +ret-usages.usages_len = MIN(args-nr_usages, nr_usages); +if (VIR_ALLOC_N(ret-usages.usages_val, ret-usages.usages_len) 0) { +virReportOOMError(); +goto cleanup; +} + +for (i = 0; i ret-usages.usages_len; i++) { +ret-usages.usages_val[i] = usages[i]; +} + +success: +rv = 0; + +cleanup: +if (rv 0) +virNetMessageSaveError(rerr); +VIR_FREE(usages); +if (dom) +virDomainFree(dom); +return rv; +} #ifdef HAVE_SASL /* diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 556c90c..1a4c129 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2255,6 +2255,56 @@ done: return rv; } +static int remoteDomainGetPcpusUsage(virDomainPtr domain, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +int i; +int rv = -1; +remote_domain_get_pcpus_usage_args args; +remote_domain_get_pcpus_usage_ret ret; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain(args.dom, domain); +args.nr_usages = *nr_usages; +args.flags = flags; + +memset(ret, 0, sizeof(ret)); + +if (call(domain-conn, priv, 0, REMOTE_PROC_DOMAIN_GET_PCPUS_USAGE, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_args, + (char *) args, + (xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret) == -1) { +goto done; +} + +/* Handle the case when the caller does not know the number of parameters + * and is asking for the number of parameters supported + */ +if (*nr_usages == 0) { +*nr_usages = ret.nr_usages; +goto cleanup; +} + +for (i = 0; i MIN(*nr_usages, ret.usages.usages_len); i++) { +usages[i] = ret.usages.usages_val[i]; +} +*nr_usages = ret.nr_usages; + +rv = 0; + +cleanup: +xdr_free ((xdrproc_t) xdr_remote_domain_get_pcpus_usage_ret, + (char *) ret); +done: +remoteDriverUnlock(priv); +return rv; +} + /*--*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -4677,6 +4727,7 @@ static virDriver remote_driver = { .nodeSuspendForDuration = remoteNodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = remoteDomainSetBlockIoTune, /* 0.9.8 */ .domainGetBlockIoTune = remoteDomainGetBlockIoTune, /* 0.9.8 */ +.domainGetPcpusUsage = remoteDomainGetPcpusUsage, /* 0.9.9 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 509a20b..e6b38ac 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -128,6 +128,9 @@ const REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX = 16; /* Upper limit on list of blockio tuning parameters
[libvirt] [PATCH 0/4] Add new public API virDomainGetPcpusUsage and pcpuinfo command in virsh
virt-top -1 can call virDomainGetPcpusUsage() periodically and get the CPU activities per CPU. (still require virt-top site patch). virsh is also added a pcpuinfo command which calls virDomainGetPcpusUsage(), it gets information about the physic CPUs, such as the usage of CPUs, the current attached vCPUs. # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Lai Jiangshan (4): Add new public API virDomainGetPcpusUsage remote: support new API virDomainGetPcpusUsage for remote driver qemu: implemnt new API virDomainGetPcpusUsage for qemu driver Enable the pcpuinfo command in virsh daemon/remote.c | 68 ++ include/libvirt/libvirt.h.in |5 ++ python/generator.py |1 + src/driver.h |7 +++ src/libvirt.c| 51 +++ src/libvirt_public.syms |5 ++ src/qemu/qemu.conf |5 +- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 74 + src/remote/remote_driver.c | 51 +++ src/remote/remote_protocol.x | 17 +++- src/remote_protocol-structs | 13 ++ src/util/cgroup.c|7 +++ src/util/cgroup.h|1 + tools/virsh.c| 93 ++ tools/virsh.pod |5 ++ 16 files changed, 402 insertions(+), 4 deletions(-) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/4] qemu: implement new API virDomainGetPcpusUsage for qemu driver
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu.conf |5 ++- src/qemu/qemu_conf.c |3 +- src/qemu/qemu_driver.c | 74 src/util/cgroup.c |7 src/util/cgroup.h |1 + 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index c3f264f..beb0123 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -158,18 +158,19 @@ # - 'memory' - use for memory tunables # - 'blkio' - use for block devices I/O tunables # - 'cpuset' - use for CPUs and memory nodes +# - 'cpuacct' - use for CPUs' account # # NB, even if configured here, they won't be used unless # the administrator has mounted cgroups, e.g.: # # mkdir /dev/cgroup -# mount -t cgroup -o devices,cpu,memory,blkio,cpuset none /dev/cgroup +# mount -t cgroup -o devices,cpu,memory,blkio,cpuset,cpuacct none /dev/cgroup # # They can be mounted anywhere, and different controllers # can be mounted in different locations. libvirt will detect # where they are located. # -# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset ] +# cgroup_controllers = [ cpu, devices, memory, blkio, cpuset, cpuacct ] # This is the basic set of devices allowed / required by # all virtual machines. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 3766119..bf8d77a 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, (1 VIR_CGROUP_CONTROLLER_DEVICES) | (1 VIR_CGROUP_CONTROLLER_MEMORY) | (1 VIR_CGROUP_CONTROLLER_BLKIO) | -(1 VIR_CGROUP_CONTROLLER_CPUSET); +(1 VIR_CGROUP_CONTROLLER_CPUSET) | +(1 VIR_CGROUP_CONTROLLER_CPUACCT); } for (i = 0 ; i VIR_CGROUP_CONTROLLER_LAST ; i++) { if (driver-cgroupControllers (1 i)) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ed90c66..648910e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10748,6 +10748,79 @@ cleanup: return ret; } +static int +qemuGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +struct qemud_driver *driver = dom-conn-privateData; +virCgroupPtr group = NULL; +virDomainObjPtr vm = NULL; +char *pos, *raw; +unsigned long long val; +int nr_cpus = 0; +int ret = -1; +int rc; +bool isActive; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); + +vm = virDomainFindByUUID(driver-domains, dom-uuid); + +if (vm == NULL) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(No such domain %s), dom-uuid); +goto cleanup; +} + +isActive = virDomainObjIsActive(vm); + +if (!isActive) { +qemuReportError(VIR_ERR_OPERATION_INVALID, %s, +_(domain is not running)); +goto cleanup; +} + +if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPUACCT)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(cgroup CPUACCT controller is not mounted)); +goto cleanup; +} + +if (virCgroupForDomain(driver-cgroup, vm-def-name, group, 0) != 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +_(cannot find cgroup for domain %s), vm-def-name); +goto cleanup; +} + +rc = virCgroupGetCpuacctPcpusUsage(group, raw); +if (rc != 0) { +virReportSystemError(-rc, %s, _(unable to get cpu account)); +goto cleanup; +} + +pos = raw; +while (virStrToLong_ull(pos, pos, 10, val) = 0) { +if (nr_cpus *nr_usages) { +usages[nr_cpus] = val; +} +nr_cpus++; +} + +VIR_FREE(raw); +*nr_usages = nr_cpus; +ret = 0; + +cleanup: +virCgroupFree(group); +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} static virDomainPtr qemuDomainAttach(virConnectPtr conn, unsigned int pid, @@ -11589,6 +11662,7 @@ static virDriver qemuDriver = { .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ .domainGetBlockIoTune = qemuDomainGetBlockIoTune, /* 0.9.8 */ +.domainGetPcpusUsage = qemuGetPcpusUsage, /* 0.9.9 */ }; diff --git a/src/util/cgroup.c b/src/util/cgroup.c index b4d3d8b..3d281f9 100644 --- a/src/util/cgroup.c +++ b/src/util/cgroup.c @@ -1522,6 +1522,13 @@ int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage) cpuacct.usage, usage); } +int virCgroupGetCpuacctPcpusUsage(virCgroupPtr group, char **usage) +{ +return virCgroupGetValueStr(group, +VIR_CGROUP_CONTROLLER_CPUACCT
[libvirt] [PATCH 4/4] Enable the pcpuinfo command in virsh
This command gets information about the physic CPUs. Example: # virsh pcpuinfo rhel6 CPU:0 Curr VCPU: - Usage: 47.3 CPU:1 Curr VCPU: 1 Usage: 46.8 CPU:2 Curr VCPU: 0 Usage: 52.7 CPU:3 Curr VCPU: - Usage: 44.1 Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 93 +++ tools/virsh.pod |5 +++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 0fccf88..4a3833c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -4012,6 +4012,98 @@ cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd) } /* + * pcpuinfo command + */ +static const vshCmdInfo info_pcpuinfo[] = { +{help, N_(detailed domain pcpu information)}, +{desc, N_(Returns basic information about the domain physic CPUs.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_pcpuinfo[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL} +}; + +static bool +cmdPcpuinfo(vshControl *ctl, const vshCmd *cmd) +{ +virDomainInfo info; +virDomainPtr dom; +virNodeInfo nodeinfo; +virVcpuInfoPtr cpuinfo; +unsigned char *cpumaps; +int ncpus, maxcpu; +size_t cpumaplen; +bool ret = true; +int n, m; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (virNodeGetInfo(ctl-conn, nodeinfo) != 0) { +virDomainFree(dom); +return false; +} + +if (virDomainGetInfo(dom, info) != 0) { +virDomainFree(dom); +return false; +} + +cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu); +maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo); +cpumaplen = VIR_CPU_MAPLEN(maxcpu); +cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen); + +if ((ncpus = virDomainGetVcpus(dom, + cpuinfo, info.nrVirtCpu, + cpumaps, cpumaplen)) = 0) { +unsigned long long *usages; +int nr_usages = maxcpu; + +if (VIR_ALLOC_N(usages, nr_usages) 0) { +virReportOOMError(); +goto fail; +} + +if (virDomainGetPcpusUsage(dom, usages, nr_usages, 0) 0) { +VIR_FREE(usages); +goto fail; +} + +for (n = 0; n MIN(maxcpu, nr_usages); n++) { +vshPrint(ctl, %-15s %d\n, _(CPU:), n); +for (m = 0; m ncpus; m++) { +if (cpuinfo[m].cpu == n) { +vshPrint(ctl, %-15s %d\n, _(Curr VCPU:), m); +break; +} +} +if (m == ncpus) { +vshPrint(ctl, %-15s %s\n, _(Curr VCPU:), _(-)); +} +vshPrint(ctl, %-15s %.1lf\n\n, _(Usage:), + usages[n] / 10.0); +} +VIR_FREE(usages); +goto cleanup; +} + +fail: +ret = false; + +cleanup: +VIR_FREE(cpumaps); +VIR_FREE(cpuinfo); +virDomainFree(dom); +return ret; +} + +/* * vcpupin command */ static const vshCmdInfo info_vcpupin[] = { @@ -15130,6 +15222,7 @@ static const vshCmdDef domManagementCmds[] = { opts_migrate_setspeed, info_migrate_setspeed, 0}, {migrate-getspeed, cmdMigrateGetMaxSpeed, opts_migrate_getspeed, info_migrate_getspeed, 0}, +{pcpuinfo, cmdPcpuinfo, opts_pcpuinfo, info_pcpuinfo, 0}, {reboot, cmdReboot, opts_reboot, info_reboot, 0}, {reset, cmdReset, opts_reset, info_reset, 0}, {restore, cmdRestore, opts_restore, info_restore, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 5131ade..241e0dc 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1217,6 +1217,11 @@ Thus, this command always takes exactly zero or two flags. Returns basic information about the domain virtual CPUs, like the number of vCPUs, the running time, the affinity to physical processors. +=item Bpcpuinfo Idomain-id + +Returns information about the physic CPUs of the domain, like the usage of +CPUs, the current attached vCPUs. + =item Bvcpupin Idomain-id [Ivcpu] [Icpulist] [[I--live] [I--config] | [I--current]] -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 1/4] Add new public API virDomainGetPcpusUsage
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |5 python/generator.py |1 + src/driver.h |7 + src/libvirt.c| 51 ++ src/libvirt_public.syms |5 5 files changed, 69 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d01d1bc..2ec6b6b 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3487,6 +3487,11 @@ int virConnectSetKeepAlive(virConnectPtr conn, int interval, unsigned int count); +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index 88c52b9..a39244f 100755 --- a/python/generator.py +++ b/python/generator.py @@ -416,6 +416,7 @@ skip_impl = ( 'virDomainBlockStatsFlags', 'virDomainSetBlockIoTune', 'virDomainGetBlockIoTune', +'virDomainGetPcpusUsage', # not implemented yet ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index 941ff51..3d54370 100644 --- a/src/driver.h +++ b/src/driver.h @@ -770,6 +770,12 @@ typedef int int *nparams, unsigned int flags); +typedef int +(*virDrvDomainGetPcpusUsage)(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags); + /** * _virDriver: * @@ -934,6 +940,7 @@ struct _virDriver { virDrvNodeSuspendForDuration nodeSuspendForDuration; virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; +virDrvDomainGetPcpusUsage domainGetPcpusUsage; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 68074e7..06a019c 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17599,3 +17599,54 @@ error: virDispatchError(dom-conn); return -1; } + +/** + * virDomainGetPcpusUsage: + * @dom: pointer to domain object + * @usages: returned physic cpu usages + * @nr_usages: length of @usages + * @flags: flags to control the operation + * + * Get the cpu usages per every physic cpu + * + * Returns 0 if success, -1 on error + */ +int virDomainGetPcpusUsage(virDomainPtr dom, + unsigned long long *usages, + int *nr_usages, + unsigned int flags) +{ +virConnectPtr conn; + +VIR_DOMAIN_DEBUG(dom, usages=%p, nr_usages=%d, flags=%x, + usages, (nr_usages) ? *nr_usages : -1, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN (dom)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} + +if (nr_usages == NULL *nr_usages != 0) { +virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); +goto error; +} + +conn = dom-conn; + +if (conn-driver-domainGetPcpusUsage) { +int ret; +ret = conn-driver-domainGetPcpusUsage(dom, usages, nr_usages, flags); +if (ret 0) +goto error; +return ret; +} + +virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(dom-conn); +return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 164039a..05f8d9e 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -508,4 +508,9 @@ LIBVIRT_0.9.8 { virNodeSuspendForDuration; } LIBVIRT_0.9.7; +LIBVIRT_0.9.9 { +global: +virDomainGetPcpusUsage; +} LIBVIRT_0.9.8; + # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] problem of escaped scancodes
Hi, Daniel P. Berrange, Our user found that some keycode can not be handled well when they try to map other keycode to xt_kbd keycode, when xt_kbd keycode is an escaped scancode. The xt_kbd keycode http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv are come from x86_keycodes[] of linux/drivers/char/keyboard.c. And x86_keycodes[] are: static const unsigned short x86_keycodes[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,118, 86, 87, 88,115,120,119,121,112,123, 92, 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339, 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349, 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355, 103,104,105,275,287,279,258,106,274,107,294,364,358,363,362,361, 291,108,381,281,290,272,292,305,280, 99,112,257,306,359,113,114, 264,117,271,374,379,265,266, 93, 94, 95, 85,259,375,260, 90,116, 377,109,111,277,278,282,283,295,296,297,299,300,301,293,303,307, 308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330, 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 }; There is no keycode in range [128, 256] in this table, which means all escaped scancode e0 ?? are encoded as 0x100 | ?? in this table. Example, LeftCtrl is 0x1d, and RightCtrl is escaped: e0 1d, RightCtrl is encoded as 0x100 | 0x1d(=0x11d=285) in this table. The code also tell me the same information: code = x86_keycodes[keycode]; if (!code) return -1; if (code 0x100) put_queue(vc, 0xe0); put_queue(vc, (code 0x7f) | up_flag); But some other place, escaped scancode e0 ?? are encoded as 0x80 | ??, this encoding is more commonly used, and qemu uses this encoding, RightCtrl is encoded as 0x80 | 0x1d(=0x9d=157) in qemu: { 0x9d, ctrl_r }, keycode = keycodes[i]; if (keycode 0x80) kbd_put_keycode(0xe0); kbd_put_keycode(keycode 0x7f); The problem: keymaps.csv uses the first encoding while qemu uses the second one, it causes virsh send-key command can't work when it sends escaped scancodes. Example: When do virsh send-key domain KEY_RIGHTCTRL, qemu will receive keycode=285 which is not expected. So I suggest to change keymaps.csv. Covert the old KEY_RIGHTCTRL,97,RightControl,0x3e,,,88,,285,228,VK_RCONTROL,0xa3,0x65,0x65 to new KEY_RIGHTCTRL,97,RightControl,0x3e,,,88,,157,228,VK_RCONTROL,0xa3,0x65,0x65 (ditto for other escaped scancodes) Or just add the new line to keymaps.csv followed by the old line, and make 285 and 157 can work at the same time. Thought? Thanks, Lai. -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH ] send-key: Implement Python API
ping. On 07/21/2011 05:21 PM, Lai Jiangshan wrote: Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +PyObject *py_retval; +virDomainPtr domain; +PyObject *pyobj_domain; +PyObject *pyobj_list; +int codeset; +int holdtime; +unsigned int flags; +int ret; +int i; +unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; +unsigned int nkeycodes; + +if (!PyArg_ParseTuple(args, (char *)OiiOii:virDomainSendKey, + pyobj_domain, codeset, holdtime, pyobj_list, + nkeycodes, flags)) { +DEBUG(%s failed to parse tuple\n, __FUNCTION__); +return VIR_PY_INT_FAIL; +} +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +if (!PyList_Check(pyobj_list)) { +return VIR_PY_INT_FAIL; +} + +if (nkeycodes != PyList_Size(pyobj_list) || +nkeycodes VIR_DOMAIN_SEND_KEY_MAX_KEYS) { +return VIR_PY_INT_FAIL; +} + +for (i = 0; i nkeycodes; i++) { +keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); +} + +LIBVIRT_BEGIN_ALLOW_THREADS; +ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); +LIBVIRT_END_ALLOW_THREADS; + +DEBUG(virDomainSendKey ret=%d\n, ret); + +py_retval = libvirt_intWrap(ret); +return py_retval; +} + / * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames, libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) virDomainRevertToSnapshot, libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, +{(char *) virDomainSendKey, libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH ] send-key: Implement Python API
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ +PyObject *py_retval; +virDomainPtr domain; +PyObject *pyobj_domain; +PyObject *pyobj_list; +int codeset; +int holdtime; +unsigned int flags; +int ret; +int i; +unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; +unsigned int nkeycodes; + +if (!PyArg_ParseTuple(args, (char *)OiiOii:virDomainSendKey, + pyobj_domain, codeset, holdtime, pyobj_list, + nkeycodes, flags)) { +DEBUG(%s failed to parse tuple\n, __FUNCTION__); +return VIR_PY_INT_FAIL; +} +domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + +if (!PyList_Check(pyobj_list)) { +return VIR_PY_INT_FAIL; +} + +if (nkeycodes != PyList_Size(pyobj_list) || +nkeycodes VIR_DOMAIN_SEND_KEY_MAX_KEYS) { +return VIR_PY_INT_FAIL; +} + +for (i = 0; i nkeycodes; i++) { +keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); +} + +LIBVIRT_BEGIN_ALLOW_THREADS; +ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); +LIBVIRT_END_ALLOW_THREADS; + +DEBUG(virDomainSendKey ret=%d\n, ret); + +py_retval = libvirt_intWrap(ret); +return py_retval; +} + / * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) virDomainGetJobInfo, libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) virDomainSnapshotListNames, libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) virDomainRevertToSnapshot, libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, +{(char *) virDomainSendKey, libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] virsh: add custom readline generator
On 06/28/2011 05:29 PM, Michal Privoznik wrote: On 27.06.2011 21:39, Eric Blake wrote: On 06/27/2011 12:06 PM, Michal Privoznik wrote: That is, if you have command-based custom generators, then each command has to repeat parsing functionality, then call back to common list generators; whereas if you have option-based custom generators, then you have fewer callbacks because all the smarts are tied to well-typed options, and after all, it is the type of each option that determines which list to generate, more than the type of the command that includes the option. I was thinking about same feature and started to work on it during this weekend. But I ran into a problem. Basically, what I intended to implement, is: 1.) expand struct vshCmdDef for a char *(*callback) (const char *text, int state). But for real benefit, we need connection object, so we could e.g. list only inactive networks for 'net-start' command. And this is the problem, because we would have to make this object global (since readline functions does not allow passing any opaque value). As gross as it is, a global object isn't entirely bad - virsh is single-threaded. And even if we want to make virsh threaded at some point, I still think we can get away with adding a thread-local access scheme. 2.) expand each command definition with its own completer. So e.g. for start commands we could only list inactive domains, networks, pools, whatever. For destroy only active objects. And so on. So maybe we want both - an option-based completer that generates the initial list, and a command-based completer that can then prune out irrelevant options? Well, I would say that is the highest goal to be achieved. Although that implies parsing during options generation. But I don't think I get it. Option-based completer generates list for given option, command-based completer generates list for given command (entries from this list are options). Take detach-interface for instance. This have some options: --mac: here we want option-based completer to list mac addresses of a NICs of a domain specified by --domain: since this could be left out we want here command-base completer to either list all domains, or list only those domains which have a NIC. So then we get: # detach-interface TABTAB --mac --persistent domain1 domain2 # detach-interface domain1 --mac TABTAB 00:11:22:33:44:55 01:23:45:67:89 My point is - I can't see any pruning here. But maybe I've chosen wrong example. However, this example shows we need both types of completer. Michal Hi, All Are their any progress now? Thanks, Lai -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/4 V5] util: add virtkey
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname); int virKeycodeValueTranslate(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |6 ++ src/Makefile.am | 12 +++- src/libvirt_private.syms |5 ++ src/util/virkey.c | 145 + src/util/virkeycode-mapgen.py | 45 + src/util/virkeycode.h | 35 ++ 6 files changed, 247 insertions(+), 1 deletions(-) create mode 100644 src/util/virkey.c create mode 100644 src/util/virkeycode-mapgen.py create mode 100644 src/util/virkeycode.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 40ce0fc..6afd591 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1788,6 +1788,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, +VIR_KEYCODE_SET_OSX= 5, +VIR_KEYCODE_SET_XT_KBD = 6, +VIR_KEYCODE_SET_USB= 7, +VIR_KEYCODE_SET_WIN32 = 8, + +VIR_KEYCODE_SET_LAST, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 2a6b0e4..87117de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,17 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/xml.c util/xml.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/virkey.c util/virkeycode.h util/virkeymaps.h + +EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virkeycode-mapgen.py + +$(srcdir)/util/virkeymaps.h: $(srcdir)/util/keymaps.csv\ + $(srcdir)/util/virkeycode-mapgen.py + python $(srcdir)/util/virkeycode-mapgen.py $(srcdir)/util/keymaps.csv $@ + +$(srcdir)/util/virkey.c: $(srcdir)/util/virkeycode.h $(srcdir)/util/virkeymaps.h EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3e3b1dd..e9ae018 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1097,6 +1097,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virkeycode.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virKeycodeValueFromString; +virKeycodeValueTranslate; # xml.h virXMLParseFileHelper; diff --git a/src/util/virkey.c b/src/util/virkey.c new file mode 100644 index 000..e94768a --- /dev/null +++ b/src/util/virkey.c @@ -0,0 +1,145 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include config.h +#include virkeycode.h +#include string.h +#include stddef.h + +#define getfield(object, field_type, field_offset) \ +(*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { +const char *linux_name; +const char *os_x_name; +const char *win32_name; +unsigned short linux_keycode; +unsigned short os_x; +unsigned short atset1; +unsigned short atset2; +unsigned short atset3; +unsigned short xt; +unsigned short xt_kbd; +unsigned short usb; +unsigned short win32; +}; + +#define VIRT_KEY_INTERNAL +#include virkeymaps.h + +static unsigned int codeOffset[] = { +[VIR_KEYCODE_SET_LINUX] = +offsetof(struct keycode, linux_keycode), +[VIR_KEYCODE_SET_XT] = +offsetof(struct keycode, xt), +[VIR_KEYCODE_SET_ATSET1] = +offsetof(struct keycode, atset1), +[VIR_KEYCODE_SET_ATSET2] = +offsetof(struct keycode, atset2), +[VIR_KEYCODE_SET_ATSET3] = +offsetof(struct keycode, atset3
[libvirt] [PATCH 0/4 V5] Add support for send keys to guest
Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Daniel P. Berrange (1): util: Add keymaps.csv Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in |6 + src/Makefile.am | 14 +- src/libvirt_private.syms |5 + src/qemu/qemu_driver.c| 71 +++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h |5 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h |5 + src/qemu/qemu_monitor_text.c | 49 + src/qemu/qemu_monitor_text.h |5 + src/util/keymaps.csv | 463 + src/util/virkey.c | 145 + src/util/virkeycode-mapgen.py | 45 src/util/virkeycode.h | 35 +++ tools/virsh.c | 94 + tools/virsh.pod |4 + 16 files changed, 976 insertions(+), 2 deletions(-) create mode 100644 src/util/keymaps.csv create mode 100644 src/util/virkey.c create mode 100644 src/util/virkeycode-mapgen.py create mode 100644 src/util/virkeycode.h -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/4 V5] qemu:send-key: Implement the driver methods
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu_driver.c | 71 ++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h |5 +++ src/qemu/qemu_monitor_json.c | 15 + src/qemu/qemu_monitor_json.h |5 +++ src/qemu/qemu_monitor_text.c | 49 + src/qemu/qemu_monitor_text.h |5 +++ 7 files changed, 167 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index cd65bce..f1ecebf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ #include configmake.h #include threadpool.h #include locking/lock_manager.h +#include virkeycode.h #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1849,6 +1850,75 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +/* translate the keycode to XT_KBD for qemu driver */ +if (codeset != VIR_KEYCODE_SET_XT_KBD) { +int i; +int keycode; + +for (i = 0; i nkeycodes; i++) { +keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); +if (keycode 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, can not translate +keycode %u of %s codeset to xt_kbd codeset +keycode, keycodes[i], +virKeycodeSetTypeToString(codeset)); +return -1; +} +keycodes[i] = keycode; +} +} + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) 0) +goto cleanup; + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +ignore_value(qemuDomainObjEnterMonitorWithDriver(driver, vm)); +ret = qemuMonitorSendKey(priv-mon, holdtime, keycodes, nkeycodes); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(driver, vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8653,6 +8723,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainSendKey = qemuDomainSendKey, /* 0.9.4 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3a30a15..016f8f0 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2408,6 +2408,23 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ +int ret; + +VIR_DEBUG(mon=%p, holdtime=%u, nkeycodes=%u, + mon, holdtime, nkeycodes); + +if (mon-json) +ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); +else +ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); +return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index f246d21..0419147 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -447,6 +447,11 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int
[libvirt] [PATCH 1/4 V5] util: Add keymaps.csv
From: Daniel P. Berrange berra...@redhat.com Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver. Signed-off-by: Daniel P. Berrange berra...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/Makefile.am |2 +- src/util/keymaps.csv | 463 ++ 2 files changed, 464 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv diff --git a/src/Makefile.am b/src/Makefile.am index f4ff489..2a6b0e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ AM_CFLAGS = $(DRIVER_MODULE_CFLAGS) \ $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -EXTRA_DIST = $(conf_DATA) +EXTRA_DIST = $(conf_DATA) util/keymaps.csv BUILT_SOURCES = diff --git a/src/util/keymaps.csv b/src/util/keymaps.csv new file mode 100644 index 000..c447f46 --- /dev/null +++ b/src/util/keymaps.csv @@ -0,0 +1,463 @@ +Linux Name,Linux Keycode,OS-X Name,OS-X Keycode,AT set1 keycode,AT set2 keycode,AT set3 keycode,XT,XT KBD,USB Keycodes,Win32 Name,Win32 Keycode,Xwin XT,Xfree86 KBD XT +KEY_RESERVED,0 +KEY_ESC,1,Escape,0x35,1,118,8,1,1,41,VK_ESCAPE,0x1b,1,1 +KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2 +KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3 +KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4 +KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5 +KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6 +KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7 +KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8 +KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9 +KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10 +KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11 +KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12 +KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13 +KEY_BACKSPACE,14,Delete,0x33,14,102,102,14,14,42,VK_BACK,0x08,14,14 +KEY_TAB,15,Tab,0x30,15,13,13,15,15,43,VK_TAB,0x09,15,15 +KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16 +KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17 +KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18 +KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19 +KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20 +KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21 +KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22 +KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23 +KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24 +KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26 +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27 +KEY_ENTER,28,Return,0x24,28,90,90,28,28,40,VK_RETURN,0x0d,28,28 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_LCONTROL,0xa2,29,29 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_CONTROL,0x11,29,29 +KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30 +KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31 +KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32 +KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33 +KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34 +KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35 +KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36 +KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37 +KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_2,0xbf,40,40 +KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41 +KEY_LEFTSHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_LSHIFT,0xa0,42,42 +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43 +KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44 +KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45 +KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46 +KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47 +KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48 +KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49 +KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50 +KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51 +KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52 +KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53 +KEY_RIGHTSHIFT,54,RightShift,0x3c,54,89,89,54,54,229,VK_RSHIFT,0xa1,54,54 +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,55,124,126,55,55,85,VK_MULTIPLY,0x6a,55,55 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_LMENU,0xa4,56,56 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_MENU,0x12,56,56 +KEY_SPACE,57,Space
[libvirt] [PATCH 3/4 V5] send-key: Expose the new API in virsh
Also support string names for the linux keycode(auto detect). Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com Acked-by: Daniel P. Berrange berra...@redhat.com --- tools/virsh.c | 94 +++ tools/virsh.pod |4 ++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ca92f0c..e9294f8 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include threads.h #include command.h #include count-one-bits.h +#include virkeycode.h static char *progname; @@ -3447,6 +3448,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * send-key command + */ +static const vshCmdInfo info_send_key[] = { +{help, N_(Send keycodes to the guest)}, +{desc, N_(Send keycodes to the guest, the keycodes must be integers\n +Examples:\n\n +virsh # send-key domain 37 18 21\n +virsh # send-key domain KEY_RIGHTCTRL KEY_C\n +virsh # send-key domain --codeset xt 37 18 21\n +virsh # send-key domain --holdtime 1000 0x15 18 0xf\n +)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{codeset, VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_(the codeset of keycodes, default:linux)}, +{holdtime, VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_(the time (in millsecond) how long the keys will be held)}, +{keycode, VSH_OT_ARGV, VSH_OFLAG_REQ, N_(the key code)}, +{NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ +long val; +char *endptr; + +val = strtol(key_name, endptr, 0); +if (*endptr != '\0' || val 0x || val = 0) + return -1; + +return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = false; +const char *codeset_option; +int codeset; +int holdtime; +int count = 0; +const vshCmdOpt *opt = NULL; +int keycode; +unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (vshCommandOptString(cmd, codeset, codeset_option) = 0) +codeset_option = linux; + +if (vshCommandOptInt(cmd, holdtime, holdtime) = 0) +holdtime = 0; + +codeset = virKeycodeSetTypeFromString(codeset_option); +if ((int)codeset 0) { +vshError(ctl, _(unknown codeset: '%s'), codeset_option); +goto cleanup; +} + +while ((opt = vshCommandOptArgv(cmd, opt))) { +if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { +vshError(ctl, _(too many keycodes)); +goto cleanup; +} + +if ((keycode = get_integer_keycode(opt-data)) = 0) { +if ((keycode = virKeycodeValueFromString(codeset, opt-data)) = 0) { +vshError(ctl, _(invalid keycode: '%s'), opt-data); +goto cleanup; +} +} + +keycodes[count] = keycode; +count++; +} + +if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) 0)) +ret = true; + +cleanup: +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -12005,6 +12098,7 @@ static const vshCmdDef domManagementCmds[] = { {dumpxml, cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {edit, cmdEdit, opts_edit, info_edit, 0}, {inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, +{send-key, cmdSendKey, opts_send_key, info_send_key}, {managedsave, cmdManagedSave, opts_managedsave, info_managedsave, 0}, {managedsave-remove, cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 9cd2853..60ae8ff 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -309,6 +309,10 @@ running Bvirsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. +=item Bsend-key Idomain-id optional I--codeset Bcodeset optional I--holdtime Bholdtime Bkeycode... + +Send keys to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 0/4 V4] Add support for send keys to guest
Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Daniel P. Berrange (1): util: Add keymaps.csv Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in |8 + src/Makefile.am | 11 +- src/libvirt_private.syms |5 + src/qemu/qemu_driver.c | 71 +++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h |5 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h |5 + src/qemu/qemu_monitor_text.c | 49 + src/qemu/qemu_monitor_text.h |5 + src/util/keymaps.csv | 463 ++ src/util/virtkey.c | 117 +++ src/util/virtkey.h | 41 src/util/virtkeymap-gen.py | 47 + tools/virsh.c| 94 + tools/virsh.pod |4 + 16 files changed, 956 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h create mode 100644 src/util/virtkeymap-gen.py -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/4 V4] util: add virtkey
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |8 +++ src/Makefile.am | 11 - src/libvirt_private.syms |5 ++ src/util/virtkey.c | 117 ++ src/util/virtkey.h | 41 +++ src/util/virtkeymap-gen.py | 47 + 6 files changed, 228 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h create mode 100644 src/util/virtkeymap-gen.py diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d5a7105..acfe9d9 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1778,6 +1778,14 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, +VIR_KEYCODE_SET_OSX= 5, +VIR_KEYCODE_SET_XT_KBD = 6, +VIR_KEYCODE_SET_USB= 7, +VIR_KEYCODE_SET_WIN32 = 8, +VIR_KEYCODE_SET_XWIN_XT= 9, +VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, + +VIR_KEYCODE_SET_LAST, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 39f0cf8..90b0743 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,16 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/xml.c util/xml.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/virtkey.c util/virtkey.h \ + util/virtkeymaps.c + +EXTRA_DIST += $(srcdir)/util/virtkeymaps.c $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virtkeymap-gen.py + +$(srcdir)/util/virtkeymaps.c: $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virtkeymap-gen.py + python $(srcdir)/util/virtkeymap-gen.py $(srcdir)/util/keymaps.csv $@ EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3237d18..6611471 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1094,6 +1094,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virtkey.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virParseKeyName; +virTranslateKeyCode; # xml.h virXMLParseFileHelper; diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 000..1eb3c62 --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,117 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include virtkey.h +#include string.h +#include stddef.h + +#define getfield(object, field_type, field_offset) \ +(*(typeof(field_type) *)((char *)(object) + field_offset)) + +static unsigned int codeOffset[] = { +[VIR_KEYCODE_SET_LINUX] = +offsetof(struct keycode, linux_keycode), +[VIR_KEYCODE_SET_XT] = +offsetof(struct keycode, xt), +[VIR_KEYCODE_SET_ATSET1] = +offsetof(struct keycode, atset1), +[VIR_KEYCODE_SET_ATSET2] = +offsetof(struct keycode, atset2), +[VIR_KEYCODE_SET_ATSET3] = +offsetof(struct keycode, atset3), +[VIR_KEYCODE_SET_OSX] = +offsetof(struct keycode, os_x), +[VIR_KEYCODE_SET_XT_KBD] = +offsetof(struct keycode, xt_kbd), +[VIR_KEYCODE_SET_USB] = +offsetof(struct keycode, usb), +[VIR_KEYCODE_SET_WIN32] = +offsetof(struct keycode, win32), +[VIR_KEYCODE_SET_XWIN_XT] = +offsetof(struct keycode, xwin_xt), +[VIR_KEYCODE_SET_XFREE86_KBD_XT] = +offsetof(struct keycode, xfree86_kbd_xt), +}; + +VIR_ENUM_IMPL(virKeycodeSet, VIR_KEYCODE_SET_LAST, +linux, +xt, +atset1, +atset2, +atset3, +os_x, +xt_kbd, +usb, +win32, +xwin_xt, +xfree86_kbd_xt); + +static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ +int i; + +for (i = 0; i virtKeycodesSize; i++) { +const char *name = getfield(virtKeycodes + i, const char *, name_offset); + +if (name !strcmp(name, keyname
[libvirt] [PATCH 1/4 V4] util: Add keymaps.csv
From: Daniel P. Berrange berra...@redhat.com Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver. Signed-off-by: Daniel P. Berrange berra...@redhat.com Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/util/keymaps.csv | 463 ++ 1 files changed, 463 insertions(+), 0 deletions(-) create mode 100644 src/util/keymaps.csv diff --git a/src/util/keymaps.csv b/src/util/keymaps.csv new file mode 100644 index 000..c447f46 --- /dev/null +++ b/src/util/keymaps.csv @@ -0,0 +1,463 @@ +Linux Name,Linux Keycode,OS-X Name,OS-X Keycode,AT set1 keycode,AT set2 keycode,AT set3 keycode,XT,XT KBD,USB Keycodes,Win32 Name,Win32 Keycode,Xwin XT,Xfree86 KBD XT +KEY_RESERVED,0 +KEY_ESC,1,Escape,0x35,1,118,8,1,1,41,VK_ESCAPE,0x1b,1,1 +KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2 +KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3 +KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4 +KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5 +KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6 +KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7 +KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8 +KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9 +KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10 +KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11 +KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12 +KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13 +KEY_BACKSPACE,14,Delete,0x33,14,102,102,14,14,42,VK_BACK,0x08,14,14 +KEY_TAB,15,Tab,0x30,15,13,13,15,15,43,VK_TAB,0x09,15,15 +KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16 +KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17 +KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18 +KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19 +KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20 +KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21 +KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22 +KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23 +KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24 +KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26 +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27 +KEY_ENTER,28,Return,0x24,28,90,90,28,28,40,VK_RETURN,0x0d,28,28 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_LCONTROL,0xa2,29,29 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_CONTROL,0x11,29,29 +KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30 +KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31 +KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32 +KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33 +KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34 +KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35 +KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36 +KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37 +KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_2,0xbf,40,40 +KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41 +KEY_LEFTSHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_LSHIFT,0xa0,42,42 +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43 +KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44 +KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45 +KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46 +KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47 +KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48 +KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49 +KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50 +KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51 +KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52 +KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53 +KEY_RIGHTSHIFT,54,RightShift,0x3c,54,89,89,54,54,229,VK_RSHIFT,0xa1,54,54 +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,55,124,126,55,55,85,VK_MULTIPLY,0x6a,55,55 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_LMENU,0xa4,56,56 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_MENU,0x12,56,56 +KEY_SPACE,57,Space,0x31,57,41,41,57,57,44,VK_SPACE,0x20,57,57 +KEY_CAPSLOCK,58,CapsLock,0x39,58,88,20,58,58,57,VK_CAPITAL,0x14,58,58 +KEY_F1,59,F1,0x7a,59,5,7,59,59,58,VK_F1,0x70,59,59 +KEY_F2,60,F2,0x78,60,6,15,60,60,59,VK_F2,0x71,60,60 +KEY_F3,61,F3,0x63,61,4,23,61,61,60,VK_F3,0x72,61,61 +KEY_F4,62,F4,0x76,62,12,31,62,62,61,VK_F4,0x73,62,62 +KEY_F5,63,F5,0x60,63,3,39,63,63,62,VK_F5,0x74,63,63 +KEY_F6,64
[libvirt] [PATCH 4/4 V4] qemu:send-key: Implement the driver methods
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu_driver.c | 71 ++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h |5 +++ src/qemu/qemu_monitor_json.c | 15 + src/qemu/qemu_monitor_json.h |5 +++ src/qemu/qemu_monitor_text.c | 49 + src/qemu/qemu_monitor_text.h |5 +++ 7 files changed, 167 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0ea182d..c93885b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ #include configmake.h #include threadpool.h #include locking/lock_manager.h +#include virtkey.h #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1843,6 +1844,75 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +/* translate the keycode to XT_KBD for qemu driver */ +if (codeset != VIR_KEYCODE_SET_XT_KBD) { +int i; +int keycode; + +for (i = 0; i nkeycodes; i++) { +keycode = virTranslateKeyCode(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); +if (keycode 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, can not translate +keycode %u of %s codeset to xt_kbd codeset +keycode, keycodes[i], +virKeycodeSetTypeToString(codeset)); +return -1; +} +keycodes[i] = keycode; +} +} + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorSendKey(priv-mon, holdtime, keycodes, nkeycodes); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8646,6 +8716,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainSendKey = qemuDomainSendKey, /* 0.9.3 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index e593642..35fb999 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2398,6 +2398,23 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ +int ret; + +VIR_DEBUG(mon=%p, holdtime=%u, nkeycodes=%u, + mon, holdtime, nkeycodes); + +if (mon-json) +ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); +else +ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); +return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 893f3e9..73a95f1 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -444,6 +444,11 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + /** * When running two dd process
Re: [libvirt] [PATCH 1/3 V3] lib: add virtkey
On 06/24/2011 09:18 PM, Daniel P. Berrange wrote: On Fri, Jun 24, 2011 at 02:33:29PM +0800, Lai Jiangshan wrote: Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetName(virKeycodeSet codeset); virKeycodeSet virParseKeycodeSet(const char *name); These should just be done using the standard VIR_ENUM_DECL/IMPL macros. diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3f634e6..2f2efe7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1815,6 +1815,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, +VIR_KEYCODE_SET_OSX= 5, +VIR_KEYCODE_SET_XT_KBD = 6, +VIR_KEYCODE_SET_USB= 7, +VIR_KEYCODE_SET_WIN32 = 8, +VIR_KEYCODE_SET_XWIN_XT= 9, +VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, } virKeycodeSet; IMHO, we don't really need to include the XT_KBD, XWIN_XT or XFREE86_KBD_XT codesets, since these are all special purpose sets which are just derived from the based XT set. Lets just stick to the core interesting sets. So add OSX, USB and WIN32 only. I found qemu monitor just accept XT_KBD, not XT, maybe I'm wrong. diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 000..48fbfcc --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,633 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include config.h +#include string.h +#include stddef.h +#include libvirt/libvirt.h +#include virtkey.h + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define getfield(object, field_type, field_offset) \ +(*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { +const char *linux_name; +const char *os_x_name; +const char *win32_name; +unsigned short linux_keycode; +unsigned short xt; +unsigned short atset1; +unsigned short atset2; +unsigned short atset3; +unsigned short os_x; +unsigned short xt_kbd; +unsigned short usb; +unsigned short win32; +unsigned short xwin_xt; +unsigned short xfree86_kbd_xt; +}; + +/* + * generated from http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv + * script: + * + * #!/bin/python + * import sys + * import re + * + * for line in sys.stdin.xreadlines(): + * a = re.match(([^,]*), * 13 + ([^,]*)$, line[0:-1]).groups() + * b = + * for i in (0,2,10,1,7,4,5,6,3,8,9,11,12,13): + * if i in (0, 2, 10): + * b = b + (a[i] and ('' + a[i] + '') or 'NULL') + ',' + * else: + * b = b + (a[i] or '0') + ',' + * print { + b + }, + */ One of the goals of having the keymap data in the CSV file was that it makes it trivially updatable across apps using it, without making code changes. In fact my goal is to actually put 'keymaps.csv' and 'keymaps.pl' into a separate shared package at some point. So rather than hardcoding this giant array in libvirt, just include the GTK-VNC keymaps.csv and keymaps.pl file as-is, and run them to generate the mapping tables for combinations we need. NB, keymaps.pl will need to be updated to be able to output a table for doing string-keycode mapping since it doesn't do that yet. For the plain keycode-keycode mappings though just use its currently functionality. I didn't find separate git repository for keymaps.csv. Should I copy keymaps.csv to libvirt? keymaps.pl need to be run O(N*N) times and it will generate O(N*N) tables for different translating, I think that 1 table is the best, even the table are bigger. +const char *virKeycodeSetName(virKeycodeSet codeset) +{ +int i = (int)codeset; + +if (i 0 || i = ARRAY_SIZE(codesetInfo)) +return UNKNOWN; + +return codesetInfo[i].name; +} + +virKeycodeSet virParseKeycodeSet(const char *name) +{ +int i; + +for (i = 0; i ARRAY_SIZE(codesetInfo); i++) { +if (!strcmp(codesetInfo[i].name, name)) +return (virKeycodeSet)i; +} + +return (virKeycodeSet)-1; +} These just get replaced by VIR_ENUM_IMPL Will do, thanks, +static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ +int i; + +for (i = 0; i ARRAY_SIZE(keycodes); i++) { +const char *name = getfield(keycodes + i, const char *, name_offset); + +if (name !strcmp(name, keyname)) +return getfield(keycodes + i, unsigned short, code_offset); +} + +return -1
[libvirt] [PATCH 0/3 V3] Add support for send keys to guest
Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Lai Jiangshan (3): lib: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in |6 + src/Makefile.am |3 +- src/libvirt_private.syms |5 + src/qemu/qemu_driver.c | 51 src/qemu/qemu_monitor.c | 37 +++ src/qemu/qemu_monitor.h |6 + src/qemu/qemu_monitor_json.c | 15 + src/qemu/qemu_monitor_json.h |5 + src/qemu/qemu_monitor_text.c | 49 src/qemu/qemu_monitor_text.h |5 + src/util/virtkey.c | 633 ++ src/util/virtkey.h | 21 ++ tools/virsh.c| 94 +++ tools/virsh.pod |4 + 14 files changed, 933 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/3 V3] send-key: Expose the new API in virsh
Also support string names for the linux keycode(auto detect). Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 93 +++ tools/virsh.pod |4 ++ 2 files changed, 97 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index a1e2f83..d7fb8a6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3263,6 +3263,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * send-key command + */ +static const vshCmdInfo info_send_key[] = { +{help, N_(Send keycodes to the guest)}, +{desc, N_(Send keycodes to the guest, the keycodes must be integers\n +Examples:\n\n +virsh # send-key domain 37 18 21\n +virsh # send-key domain KEY_RIGHTCTRL KEY_C\n +virsh # send-key domain --codeset xt 37 18 21\n +virsh # send-key domain --holdtime 1000 0x15 18 0xf\n +)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{codeset, VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_(the codeset of keycodes, default:linux)}, +{holdtime, VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_(the time (in millsecond) how long the keys will be held)}, +{keycode, VSH_OT_ARGV, VSH_OFLAG_REQ, N_(the key code)}, +{NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ +long val; +char *endptr; + +val = strtol(key_name, endptr, 0); +if (*endptr != '\0' || val 0x || val = 0) + return -1; + +return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = false; +const char *codeset_option; +int codeset; +int holdtime; +int count = 0; +const vshCmdOpt *opt = NULL; +int keycode; +unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (vshCommandOptString(cmd, codeset, codeset_option) = 0) +codeset_option = linux; + +if (vshCommandOptInt(cmd, holdtime, holdtime) = 0) +holdtime = 0; + +codeset = virParseKeycodeSet(codeset_option); +if ((int)codeset 0) { +vshError(ctl, _(unknown codeset: '%s'), codeset_option); +goto cleanup; +} + +while ((opt = vshCommandOptArgv(cmd, opt))) { +if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { +vshError(ctl, _(too many keycodes)); +goto cleanup; +} + +if ((keycode = get_integer_keycode(opt-data)) = 0) { +if ((keycode = virParseKeyName(codeset, opt-data)) = 0) { +vshError(ctl, _(invalid keycode: '%s'), opt-data); +goto cleanup; +} +} + +keycodes[count] = keycode; +count++; +} + +if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) 0)) +ret = true; + +cleanup: +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -11672,6 +11764,7 @@ static const vshCmdDef domManagementCmds[] = { {dumpxml, cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {edit, cmdEdit, opts_edit, info_edit, 0}, {inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, +{send-key, cmdSendKey, opts_send_key, info_send_key}, {managedsave, cmdManagedSave, opts_managedsave, info_managedsave, 0}, {managedsave-remove, cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 023ab42..50c4800 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -308,6 +308,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item Bsend-key Idomain-id optional I--codeset Bcodeset optional I--holdtime Bholdtime Bkeycode... + +Send keys to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/3 V3] qemu:send-key: Implement the driver methods
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu_driver.c | 51 ++ src/qemu/qemu_monitor.c | 37 ++ src/qemu/qemu_monitor.h |6 + src/qemu/qemu_monitor_json.c | 15 src/qemu/qemu_monitor_json.h |5 src/qemu/qemu_monitor_text.c | 49 src/qemu/qemu_monitor_text.h |5 7 files changed, 168 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 01587e8..994d7bd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1761,6 +1761,56 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorSendKey(priv-mon, codeset, holdtime, keycodes, nkeycodes); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8436,6 +8486,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainSendKey = qemuDomainSendKey, /* 0.9.3 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.3 */ .domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.3 */ .domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.3 */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 89a3f64..e14703b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -36,6 +36,7 @@ #include memory.h #include logging.h #include files.h +#include virtkey.h #define VIR_FROM_THIS VIR_FROM_QEMU @@ -2369,6 +2370,42 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ +int ret; + +VIR_DEBUG(mon=%p, codeset=%s(%u), holdtime=%u, nkeycodes=%u, + mon, virKeycodeSetName(codeset), codeset, holdtime, nkeycodes); + +if (codeset != VIR_KEYCODE_SET_XT_KBD) { +int i; +int keycode; + +for (i = 0; i nkeycodes; i++) { +keycode = virTranslateKeyCode(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); +if (keycode 0) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, can not translate +keycode %u of %s codeset to xt_kbd codeset +keycode, keycodes[i], +virKeycodeSetName(codeset)); +return -1; +} +keycodes[i] = keycode; +} +} + +if (mon-json) +ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); +else +ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); +return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3bb0269..ed80c4b 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -462,6 +462,12 @@ int qemuMonitorBlockPull(qemuMonitorPtr mon, virDomainBlockPullInfoPtr info, int
[libvirt] [PATCH 1/3 V3] lib: add virtkey
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetName(virKeycodeSet codeset); virKeycodeSet virParseKeycodeSet(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |6 + src/Makefile.am |3 +- src/libvirt_private.syms |5 + src/util/virtkey.c | 633 ++ src/util/virtkey.h | 21 ++ tools/virsh.c|1 + 6 files changed, 668 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3f634e6..2f2efe7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1815,6 +1815,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, +VIR_KEYCODE_SET_OSX= 5, +VIR_KEYCODE_SET_XT_KBD = 6, +VIR_KEYCODE_SET_USB= 7, +VIR_KEYCODE_SET_WIN32 = 8, +VIR_KEYCODE_SET_XWIN_XT= 9, +VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 4f9bfc9..e34ea74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,8 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/xml.c util/xml.h \ util/virtaudit.c util/virtaudit.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.hi \ + util/virtkey.c util/virtkey.h EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 09b0159..151e256 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1059,6 +1059,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virtkey.h +virKeycodeSetName; +virParseKeycodeSet; +virParseKeyName; +virTranslateKeyCode; # xml.h virXMLParseFileHelper; diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 000..48fbfcc --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,633 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include config.h +#include string.h +#include stddef.h +#include libvirt/libvirt.h +#include virtkey.h + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define getfield(object, field_type, field_offset) \ +(*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { +const char *linux_name; +const char *os_x_name; +const char *win32_name; +unsigned short linux_keycode; +unsigned short xt; +unsigned short atset1; +unsigned short atset2; +unsigned short atset3; +unsigned short os_x; +unsigned short xt_kbd; +unsigned short usb; +unsigned short win32; +unsigned short xwin_xt; +unsigned short xfree86_kbd_xt; +}; + +/* + * generated from http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv + * script: + * + * #!/bin/python + * import sys + * import re + * + * for line in sys.stdin.xreadlines(): + * a = re.match(([^,]*), * 13 + ([^,]*)$, line[0:-1]).groups() + * b = + * for i in (0,2,10,1,7,4,5,6,3,8,9,11,12,13): + * if i in (0, 2, 10): + * b = b + (a[i] and ('' + a[i] + '') or 'NULL') + ',' + * else: + * b = b + (a[i] or '0') + ',' + * print { + b + }, + */ +static struct keycode keycodes[] = { +{ KEY_RESERVED,NULL,NULL,0,0,0,0,0,0,0,0,0,0,0,}, +{ KEY_ESC,Escape,VK_ESCAPE,1,1,1,118,8,0x35,1,41,0x1b,1,1,}, +{ KEY_1,ANSI_1,VK_1,2,2,2,22,22,0x12,2,30,0x31,2,2,}, +{ KEY_2,ANSI_2,VK_2,3,3,3,30,30,0x13,3,31,0x32,3,3,}, +{ KEY_3,ANSI_3,VK_3,4,4,4,38,38,0x14,4,32,0x33,4,4,}, +{ KEY_4,ANSI_4,VK_4,5,5,5,37,37,0x15,5,33,0x34,5,5,}, +{ KEY_5,ANSI_5,VK_5,6,6,6,46,46,0x17,6,34,0x35,6,6,}, +{ KEY_6,ANSI_6,VK_6,7,7,7,54,54,0x16,7,35,0x36,7,7,}, +{ KEY_7,ANSI_7,VK_7,8,8,8,61,61,0x1a,8,36,0x37,8,8,}, +{ KEY_8,ANSI_8,VK_8,9,9,9,62,62,0x1c,9,37,0x38,9,9,}, +{ KEY_9,ANSI_9,VK_9,10,10,10,70,70,0x19,10,38,0x39,10,10,}, +{ KEY_0,ANSI_0,VK_0,11,11,11,69,69,0x1d,11,39,0x30,11,11,}, +{ KEY_MINUS,ANSI_Minus,VK_OEM_MINUS,12,12,12,78,78,0x1b,12,45,0xbd,12,12,}, +{ KEY_EQUAL,ANSI_Equal,VK_OEM_PLUS
[libvirt] [PATCH] virsh: add custom readline generator
Custom readline generator will help for some usecase. Also add a custom readline generator for the help command. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- diff --git a/tools/virsh.c b/tools/virsh.c index fcd254d..51e43c1 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -13575,7 +13575,7 @@ vshCloseLogFile(vshControl *ctl) * (i.e. STATE == 0), then we start at the top of the list. */ static char * -vshReadlineCommandGenerator(const char *text, int state) +vshReadlineCmdAndGrpGenerator(const char *text, int state, int grpname) { static int grp_list_index, cmd_list_index, len; const char *name; @@ -13604,8 +13604,13 @@ vshReadlineCommandGenerator(const char *text, int state) return vshStrdup(NULL, name); } } else { +name = grp[grp_list_index].keyword; cmd_list_index = 0; grp_list_index++; + +if (grpname STREQLEN(name, text, len)) +return vshStrdup(NULL, name); + } } @@ -13614,10 +13619,45 @@ vshReadlineCommandGenerator(const char *text, int state) } static char * +vshReadlineCommandGenerator(const char *text, int state) +{ + return vshReadlineCmdAndGrpGenerator(text, state, 0); +} + +static char * +vshReadlineHelpOptionGenerator(const char *text, int state) +{ + return vshReadlineCmdAndGrpGenerator(text, state, 1); +} + +struct vshCustomReadLine { + const char *name; + char *(*CustomReadLineOptionGenerator)(const char *text, int state); +}; + +struct vshCustomReadLine customeReadLine[] = { + { help, vshReadlineHelpOptionGenerator }, + { NULL, NULL } +}; + +static struct vshCustomReadLine *vshCustomReadLineSearch(const char *name) +{ +struct vshCustomReadLine *ret = customeReadLine; + +for (ret = customeReadLine; ret-name; ret++) { +if (STREQ(ret-name, name)) +return ret; +} + +return NULL; +} + +static char * vshReadlineOptionsGenerator(const char *text, int state) { static int list_index, len; static const vshCmdDef *cmd = NULL; +static const struct vshCustomReadLine *rl = NULL; const char *name; if (!state) { @@ -13632,6 +13672,7 @@ vshReadlineOptionsGenerator(const char *text, int state) memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); cmd = vshCmddefSearch(cmdname); +rl = vshCustomReadLineSearch(cmdname); list_index = 0; len = strlen(text); VIR_FREE(cmdname); @@ -13640,6 +13681,9 @@ vshReadlineOptionsGenerator(const char *text, int state) if (!cmd) return NULL; +if (rl) +return rl-CustomReadLineOptionGenerator(text, state); + if (!cmd-opts) return NULL; -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 05/10 V2] [PATCH 05/13] send-key: Defining the public API
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- include/libvirt/libvirt.h.in |7 +++ include/libvirt/virtkeys.h | 22 ++ python/generator.py |1 + src/libvirt_public.syms |5 + 4 files changed, 35 insertions(+), 0 deletions(-) create mode 100644 include/libvirt/virtkeys.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..94da205 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2673,6 +2673,13 @@ typedef struct _virTypedParameter virMemoryParameter; */ typedef virMemoryParameter *virMemoryParameterPtr; +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 000..854594a --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,22 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { +LIBVIRT_KEYCODE_LINUX = 0, +LIBVIRT_KEYCODE_XT = 1, +LIBVIRT_KEYCODE_ATSET1 = 2, +LIBVIRT_KEYCODE_ATSET2 = 3, +LIBVIRT_KEYCODE_ATSET3 = 4, +}; + +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 7c38fdd..57373f0 100755 --- a/python/generator.py +++ b/python/generator.py @@ -356,6 +356,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainSendKey', ) diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..ddfed44 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -450,4 +450,9 @@ LIBVIRT_0.9.2 { virInterfaceChangeRollback; } LIBVIRT_0.9.0; +LIBVIRT_0.9.3 { +global: +virDomainSendKey; +} LIBVIRT_0.9.2; + # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 02/10 V2] improve the iteration of VSH_OT_ARGV options
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 47 --- 1 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 61eb11e..638029c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -280,7 +280,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD-def-opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ +while (opt) { + if (opt-def opt-def-type == VSH_OT_ARGV) + break; + opt = opt-next; +} + +return opt; +} + +#define for_each_variable_arg(cmd, opt) \ +for (opt = __variable_arg(cmd-opts); opt; opt = __variable_arg(opt-next)) + #define VSH_BYID (1 1) #define VSH_BYUUID (1 2) @@ -10341,6 +10361,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; +const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -10349,10 +10370,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, xml)) xml = true; -while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { +for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q; +arg = opt-data; if (count) virBufferAddChar(buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11803,27 +11825,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; } -/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name be last in the - * list of supported options in CMD-def-opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ -vshCmdOpt *opt = cmd-opts; - -while (opt) { -if (opt-def opt-def-type == VSH_OT_ARGV) { -if (count-- == 0) -return opt-data; -} -opt = opt-next; -} -return NULL; -} - /* Determine whether CMD-opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */ -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 00/10 V2] Add support for send keys to guest
Add API virDomainSendKey() and virsh send-key command. PATCH 01~04 prepare PATCH 05~10 Add support for send keys to guest Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Some usage-improvment patches will be sent later(after these 10 are applied) these usage-improvment patches does not touch any APIs nor change the behaviors: support KEY_XXX names for the linux keycode for virsh command(auto detect), translate keycodes between different codesets, ...etc. Lai Jiangshan (10): allow name for VSH_OT_ARGV options improve the iteration of VSH_OT_ARGV options add VSH_OFLAG_REQ_OPT options remote generator: Handle (unsigned) int arrays send-key: Defining the public API send-key: Defining the internal API send-key: Implementing the public API send-key: Implementing the remote protocol send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods daemon/remote_generator.pl | 17 include/libvirt/libvirt.h.in |7 ++ include/libvirt/virtkeys.h | 22 + python/generator.py |1 + src/driver.h |8 ++ src/libvirt.c| 63 +++ src/libvirt_public.syms |5 + src/qemu/qemu_driver.c | 50 src/qemu/qemu_monitor.c | 27 +++ src/qemu/qemu_monitor.h |6 ++ src/qemu/qemu_monitor_json.c | 15 src/qemu/qemu_monitor_json.h |5 + src/qemu/qemu_monitor_text.c | 47 +++ src/qemu/qemu_monitor_text.h |5 + src/remote/remote_driver.c |1 + src/remote/remote_protocol.x | 16 - src/remote_protocol-structs | 11 +++ tools/virsh.c| 175 +++--- tools/virsh.pod |4 + 19 files changed, 455 insertions(+), 30 deletions(-) create mode 100644 include/libvirt/virtkeys.h -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 04/10 V2] remote generator: Handle (unsigned) int arrays
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- daemon/remote_generator.pl | 17 + 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 632972c..532fe63 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -407,6 +407,13 @@ elsif ($opt_b) { } push(@args_list, args-$2.$2_len); +} elsif ($args_member =~ m/^(?:unsigned )?int (\S+)\S+;/) { +if (! @args_list) { +push(@args_list, conn); +} + +push(@args_list, args-$1.$1_val); +push(@args_list, args-$1.$1_len); } elsif ($args_member =~ m/^remote_typed_param (\S+)(\S+);/) { push(@vars_list, virTypedParameterPtr $1 = NULL); push(@vars_list, int n$1); @@ -985,6 +992,16 @@ elsif ($opt_k) { push(@setters_list, args.$arg_name.${arg_name}_val = (char *)$arg_name;); push(@setters_list, args.$arg_name.${arg_name}_len = ${arg_name}len;); push(@args_check_list, { name = \$arg_name\, arg = ${arg_name}len, limit = $limit }); +} elsif ($args_member =~ m/^((?:unsigned )?int) (\S+)(\S+);/) { +my $type_name = $1; +my $arg_name = $2; +my $limit = $3; + +push(@args_list, ${type_name} *$arg_name); +push(@args_list, unsigned int ${arg_name}len); +push(@setters_list, args.$arg_name.${arg_name}_val = $arg_name;); +push(@setters_list, args.$arg_name.${arg_name}_len = ${arg_name}len;); +push(@args_check_list, { name = \$arg_name\, arg = ${arg_name}len, limit = $limit }); } elsif ($args_member =~ m/^remote_typed_param (\S+)(\S+);/) { push(@args_list, virTypedParameterPtr $1); push(@args_list, int n$1); -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 09/10 V2] send-key: Expose the new API in virsh
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 102 +++ tools/virsh.pod |4 ++ 2 files changed, 106 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d13c12b..7b5847f 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include signal.h #include poll.h +#include libvirt/virtkeys.h + #include libxml/parser.h #include libxml/tree.h #include libxml/xpath.h @@ -3182,6 +3184,105 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * send-key command + */ +static const vshCmdInfo info_send_key[] = { +{help, N_(Send keycodes to the guest)}, +{desc, N_(Send keycodes to the guest, the keycodes must be integers\n +Examples:\n\n +virsh # send-key domain 37 18 21\n +virsh # send-key domain --holdtime 1000 0x15 18 0xf\n +)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{codeset, VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_(the codeset of keycodes, default:linux)}, +{holdtime, VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_(the time (in millsecond) how long the keys will be held)}, +{keycode, VSH_OT_ARGV, VSH_OFLAG_REQ, N_(the key code)}, +{NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ +long val; +char *endptr; + +val = strtol(key_name, endptr, 0); +if (*endptr != '\0' || val 255 || val = 0) + return -1; + +return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = false; +const char *codeset_option; +int codeset; +int holdtime; +int count = 0; +const vshCmdOpt *opt; +int keycode; +unsigned int keycodes[MAX_SEND_KEY]; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (vshCommandOptString(cmd, codeset, codeset_option) = 0) +codeset_option = default; + +if (vshCommandOptInt(cmd, holdtime, holdtime) = 0) +holdtime = 0; + +if (STREQ(codeset_option, default) || STREQ(codeset_option, linux)) { +codeset = LIBVIRT_KEYCODE_LINUX; +} else if (STREQ(codeset_option, xt)) { +codeset = LIBVIRT_KEYCODE_XT; +} else if (STREQ(codeset_option, atset1)) { +codeset = LIBVIRT_KEYCODE_ATSET1; +} else if (STREQ(codeset_option, atset2)) { +codeset = LIBVIRT_KEYCODE_ATSET2; +} else if (STREQ(codeset_option, atset3)) { +codeset = LIBVIRT_KEYCODE_ATSET3; +} else { +vshError(ctl, _(unknown codeset: '%s'), codeset_option); +goto free_domain; +} + +for_each_variable_arg(cmd, opt) { +if (count == MAX_SEND_KEY) { +vshError(ctl, _(too many keycode)); +goto free_domain; +} + +if ((keycode = get_integer_keycode(opt-data)) 0) +goto get_keycode; + +vshError(ctl, _(invalid keycode: '%s'), opt-data); +goto free_domain; + +get_keycode: +keycodes[count] = keycode; +count++; +} + +if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) 0)) +ret = true; + +free_domain: +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -11095,6 +11196,7 @@ static const vshCmdDef domManagementCmds[] = { {dumpxml, cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {edit, cmdEdit, opts_edit, info_edit, 0}, {inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, +{send-key, cmdSendKey, opts_send_key, info_send_key}, {managedsave, cmdManagedSave, opts_managedsave, info_managedsave, 0}, {managedsave-remove, cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 7ed3003..03b1418 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -296,6 +296,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item Bsend-key Idomain-id I--codeset Bcodeset I--holdtime Bholdtime Bkeycode... + +Send keys to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 06/10 V2] send-key: Defining the internal API
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/driver.h |8 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 5df798a..2e335a1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -542,6 +542,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); +typedef int +(*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -749,6 +756,7 @@ struct _virDriver { virDrvDomainMigratePerform3domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3domainMigrateConfirm3; +virDrvDomainSendKey domainSendKey; }; typedef int -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 01/10 V2] allow name for VSH_OT_ARGV options
A name will improve the usege, example # virsh help echo NAME echo - echo arguments SYNOPSIS echo [--shell] [--xml] [string]... DESCRIPTION Echo back arguments, possibly with quoting. OPTIONS --shell escape for shell use --xmlescape for XML use string arguments to echo [string]... is added to SYNOPSIS. string arguments to echo is added to OPTIONS. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 19 +-- 1 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d98be1c..61eb11e 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ -VSH_OT_ARGV /* remaining arguments, opt-name should be */ +VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType; /* @@ -10328,7 +10328,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {shell, VSH_OT_BOOL, 0, N_(escape for shell use)}, {xml, VSH_OT_BOOL, 0, N_(escape for XML use)}, -{, VSH_OT_ARGV, 0, N_(arguments to echo)}, +{string, VSH_OT_ARGV, 0, N_(arguments to echo)}, {NULL, 0, 0, NULL} }; @@ -11379,6 +11379,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _(option --%s already seen), name); return NULL; } +if (opt-type == VSH_OT_ARGV) { +vshError(ctl, _(variable argument %s + should not be used with --%s), name, name); +return NULL; +} *opts_seen |= 1 i; return opt; } @@ -11427,7 +11432,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = def-opts[i]; vshError(ctl, - opt-type == VSH_OT_DATA ? + opt-type == VSH_OT_DATA || opt-type == VSH_OT_ARGV ? _(command '%s' requires %s option) : _(command '%s' requires --%s option), def-name, opt-name); @@ -11535,7 +11540,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ -fmt = _([string]...); +fmt = (opt-flag VSH_OFLAG_REQ) ? _(%s...) + : _([%s]...); break; default: assert(0); @@ -11575,7 +11581,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ -continue; +snprintf(buf, sizeof(buf), _(%s), opt-name); +break; default: assert(0); } @@ -13012,7 +13019,7 @@ vshReadlineOptionsGenerator(const char *text, int state) list_index++; -if (opt-type == VSH_OT_DATA) +if (opt-type == VSH_OT_DATA || opt-type == VSH_OT_ARGV) /* ignore non --option */ continue; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 07/10] send-key: Implementing the public API
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt.c | 63 + 1 files changed, 63 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index cbe1926..112f690 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -26,6 +26,8 @@ #include libxml/uri.h #include getpass.h +#include libvirt/virtkeys.h + #ifdef HAVE_WINSOCK2_H # include winsock2.h #endif @@ -6511,6 +6513,67 @@ error: } /** + * virDomainSendKey: + * @domain:pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ +virConnectPtr conn; +VIR_DOMAIN_DEBUG(domain, codeset=%u,holdtime=%u,nkeycodes=%u,flags=%u, + codeset, holdtime, nkeycodes, flags); + +virResetLastError(); + +if (nkeycodes == 0 || nkeycodes MAX_SEND_KEY) { +virLibDomainError(VIR_ERR_OPERATION_INVALID, __FUNCTION__); +virDispatchError(NULL); +return -1; +} + +if (!VIR_IS_CONNECTED_DOMAIN(domain)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +if (domain-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +conn = domain-conn; + +if (conn-driver-domainSendKey) { +int ret; +ret = conn-driver-domainSendKey(domain, codeset, holdtime, + keycodes, nkeycodes, flags); +if (ret 0) +goto error; +return ret; +} + +virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(domain-conn); +return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 08/10 V2] send-key: Implementing the remote protocol
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/remote/remote_driver.c |1 + src/remote/remote_protocol.x | 16 +++- src/remote_protocol-structs | 11 +++ 3 files changed, 27 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ +.domainSendKey = remoteDomainSendKey, /* 0.9.3 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256; +/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -811,6 +816,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; }; +struct remote_domain_send_key_args { +remote_nonnull_domain dom; +unsigned int codeset; +unsigned int holdtime; +unsigned int keycodesREMOTE_SEND_KEY_MAX; +unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; unsigned int nvcpus; @@ -2297,7 +2310,8 @@ enum remote_procedure { REMOTE_PROC_INTERFACE_CHANGE_COMMIT = 221, /* autogen autogen */ REMOTE_PROC_INTERFACE_CHANGE_ROLLBACK = 222, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS_FLAGS = 223, /* skipgen autogen */ -REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224 /* skipgen skipgen */ +REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224, /* skipgen skipgen */ +REMOTE_PROC_DOMAIN_SEND_KEY = 225 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 1d90dd5..dd70c24 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1550,3 +1550,14 @@ struct remote_message_header { u_int serial; remote_message_status status; }; + +struct remote_domain_send_key_args { +remote_nonnull_domain dom; +unsigned int codeset; +unsigned int holdtime; +struct { +unsigned int keycodes_len; +unsigned int * keycodes_val; +} keycodes; +unsigned int flags; +}; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 10/10 V2] qemu:send-key: Implement the driver methods
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu_driver.c | 50 ++ src/qemu/qemu_monitor.c | 27 ++ src/qemu/qemu_monitor.h |6 + src/qemu/qemu_monitor_json.c | 15 src/qemu/qemu_monitor_json.h |5 src/qemu/qemu_monitor_text.c | 47 +++ src/qemu/qemu_monitor_text.h |5 7 files changed, 155 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2957467..1678deb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1764,6 +1764,55 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorSendKey(priv-mon, codeset, holdtime, nkeycodes, keycodes); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8092,6 +8141,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ +.domainSendKey = qemuDomainSendKey, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..faab819 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include logging.h #include files.h +#include libvirt/virtkeys.h + #define VIR_FROM_THIS VIR_FROM_QEMU #define DEBUG_IO 0 @@ -2357,6 +2359,31 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ +int ret; + +VIR_DEBUG(mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u, + mon, codeset, holdtime, nkeycodes); + +if (codeset != LIBVIRT_KEYCODE_XT) { +qemuReportError(VIR_ERR_NO_SUPPORT, +qemu monitor can not support the codeset: %d, +codeset); +return -1; +} + +if (mon-json) +ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); +else +ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); +return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 910865b..64a6320 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -441,6 +441,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 75adf66..538e98f 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2619,6 +2619,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes
[libvirt] [PATCH 03/10 V2] add VSH_OFLAG_REQ_OPT options
A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option. It will help us use optional arguement with VSH_OT_ARGV argument. Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c |7 +++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 638029c..d13c12b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0,/* without flags */ VSH_OFLAG_REQ = (1 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 1), /* empty string option allowed */ +VSH_OFLAG_REQ_OPT = (1 2), /* --optionname required */ }; /* dummy */ @@ -11375,6 +11376,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } +if (opt-flag VSH_OFLAG_REQ_OPT) { +if (opt-flag VSH_OFLAG_REQ) +*opts_required |= 1 i; +continue; +} + *opts_need_arg |= 1 i; if (opt-flag VSH_OFLAG_REQ) { if (optional) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 00/13] Add support for send keys to guest
On 05/26/2011 12:36 AM, Daniel P. Berrange wrote: On Wed, May 25, 2011 at 05:37:42PM +0800, Lai Jiangshan wrote: Add API virDomainSendKey() and virsh send-key command. # virsh help send-key NAME send-key - Send keycodes to the guest SYNOPSIS send-key domain [--codeset string] [--holdtime number] keycode... DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the xt:keystring codeset or the KEY_* strings listed below for the linux codeset. Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) defaultlinux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but keycode... must be the qemu-style key strings I was thinking we'd just use the Linux keycode set in the API, but I guess if the client app already has things in XT codeset, it isn't too nice to force them to convert to Linux keycodes, only for libvirt to convert them straight back for QEMU. So I think it was a good idea to add different codesets. I don't think that 'driver_default' makes sense though. For that to be usable, the person invoking the API must somehow know what the driver default codeset is. If they know that, then they can trivially just specify that already, so 'driver_default' doesn't seem to add any benefit. OK, it will be removed. As for 'xt:keystring', if you think it is worth being able to use key strings, then perhaps we should have 2 apis. One API that takes a list of keycodes as ints, and one API that takes a list of keycodes as strings. I don't think it is a good idea to add a second API, virDomainSendKey() is not used directly by human, integer is enough. 2 apis will make the user of the lib confused. virsh send-key command is a human interface, I think it is good if it accepts strings, so I allow KEY_XXX strings. And this command should wrap all things(convert strings to integers). If you deny 'xt:keystring', I can remove the patch, but KEY_XXX strings will still stay. This would avoid the need for every client application to maintain a mapping table of strings - ints. eg, all the mapping tables would be contained within libvirt. qemu monitor don't require ints--names conversions, it just needs very trivial conversions ints--0xNN, this conversion will not need when qmp send-key is implemented. I don't know other hypervisors, but I think virDomainSendKey() taking a list of keycode as ints is the best api for all hypervisors. Examples: virsh # send-key domain 37 18 21 virsh # send-key domain --holdtime 1000 0x15 18 0xf virsh # send-key domain KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key domain --codeset xt:keystring alt-sysrq h KEY_XXX strings for the linux codeset: For virsh, perhaps it should default to always using the strings for the keys, and only use integers if given a special flag. I think most admins using virsh would just be using strings. The integer keycodes are mostly useful for apps using the API directly. eg, perhaps virsh # send-key domain KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key domain --codeset linux KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key domain --codeset xt alt-sysrq h virsh # send-key domain --num 37 18 21 virsh # send-key domain --num --holdtime 1000 0x15 18 0xf virsh # send-key domain --num --codeset linux --holdtime 1000 0x15 18 0xf It is good for me, but I may want to wrap the strings-ints conversions in virsh instead of in hypervisors. . . . OPTIONS [--domain] string domain name, id or uuid --codeset string the codeset of keycodes, default:linux --numthe keys are specified as integer values, --holdtime number the time (in millsecond) how long the keys will be held keycodethe key string (or value if --num is set) Daniel -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 05/13] send-key: Defining the public API
On 05/26/2011 12:43 AM, Daniel P. Berrange wrote: On Wed, May 25, 2011 at 05:37:47PM +0800, Lai Jiangshan wrote: Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- include/libvirt/libvirt.h.in |7 +++ include/libvirt/virtkeys.h | 23 +++ python/generator.py |1 + src/libvirt_public.syms |1 + 4 files changed, 32 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom, int virDomainInjectNMI(virDomainPtr domain, unsigned int flags); +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + This looks fine. As mentioned earlier we might like to *also* have a variant which takes strings, to make life easier for virsh, or similar use cases +int virDomainSendKeyStr(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned char **keycodestrs, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 000..eb07129 --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,23 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { +LIBVIRT_KEYCODE_LINUX = 0, +LIBVIRT_KEYCODE_DRIVER_DEFAULT = 1, +LIBVIRT_KEYCODE_XT = 2, +LIBVIRT_KEYCODE_ATSET1 = LIBVIRT_KEYCODE_XT, +LIBVIRT_KEYCODE_ATSET2 = 3, +LIBVIRT_KEYCODE_ATSET3 = 4, +}; If we're going to have constants for XT and ATSET1 then we probably want to have them separate values. If not then we should just kill one of them. As mentioned in the initial message, I think 'driver default' isn't useful, so I'd go with: +enum libvirt_keycode_set { +LIBVIRT_KEYCODE_LINUX = 0, +LIBVIRT_KEYCODE_XT = 1, +LIBVIRT_KEYCODE_ATSET1 = 2 +LIBVIRT_KEYCODE_ATSET2 = 3, +LIBVIRT_KEYCODE_ATSET3 = 4, +}; Sorry, That's my fault, I thought xt and atset1 are the same codeset. Thank you for correcting me. Thanks, Lai. + +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 1741bba..3d57bf9 100755 --- a/python/generator.py +++ b/python/generator.py @@ -355,6 +355,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainSendKey', ) diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..9e8a37c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; +virDomainSendKey; } LIBVIRT_0.9.0; # define new API here using predicted next version number Daniel -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 08/13] send-key: Implementing the remote protocol
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- src/remote/remote_driver.c |1 + src/remote/remote_protocol.x | 19 ++- src/remote_protocol-structs | 11 +++ 3 files changed, 30 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1691dab..6614250 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6868,6 +6868,7 @@ static virDriver remote_driver = { .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = remoteDomainSetSchedulerParametersFlags, /* 0.9.2 */ +.domainSendKey = remoteDomainSendKey, /* 0.9.2 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f0da95d..61504c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,14 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256; +/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16; + +/* define dynamic array's base type for unsigned int */ +typedef unsigned int u_int_DABT; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -838,6 +846,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; }; +struct remote_domain_send_key_args { +remote_nonnull_domain dom; +unsigned int codeset; +unsigned int holdtime; +u_int_DABT keycodesREMOTE_SEND_KEY_MAX; +unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; int nvcpus; @@ -2291,7 +2307,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3 = 216, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_FINISH3 = 217, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3 = 218, /* skipgen skipgen */ -REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219 /* skipgen skipgen */ +REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219, /* skipgen skipgen */ +REMOTE_PROC_DOMAIN_SEND_KEY = 220 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 414b4d5..814821a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1553,3 +1553,14 @@ struct remote_message_header { u_int serial; remote_message_status status; }; + +struct remote_domain_send_key_args { +remote_nonnull_domain dom; +unsigned int codeset; +unsigned int holdtime; +struct { +unsigned int keycodes_len; +unsigned int * keycodes_val; +} keycodes; +unsigned int flags; +}; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 09/13] send-key: Expose the new API in virsh
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- tools/virsh.c | 103 +++ tools/virsh.pod |4 ++ 2 files changed, 107 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 80cffac..505a821 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include signal.h #include poll.h +#include libvirt/virtkeys.h + #include libxml/parser.h #include libxml/tree.h #include libxml/xpath.h @@ -3010,6 +3012,106 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * send-key command + */ +static const vshCmdInfo info_send_key[] = { +{help, N_(Send keycodes to the guest)}, +{desc, N_(Send keycodes to the guest, the keycodes must be integers\n +Examples:\n\n +virsh # send-key domain 37 18 21\n +virsh # send-key domain --holdtime 1000 0x15 18 0xf\n +{NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{codeset, VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_(the codeset of keycodes, default:linux)}, +{holdtime, VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_(the time (in millsecond) how long the keys will be held)}, +{keycode, VSH_OT_ARGV, VSH_OFLAG_REQ, N_(the key code)}, +{NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ +long val; +char *endptr; + +val = strtol(key_name, endptr, 0); +if (*endptr != '\0' || val 255 || val = 0) + return -1; + +return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = false; +const char *codeset_option; +int codeset; +int holdtime; +int count = 0; +const vshCmdOpt *opt; +int keycode; +unsigned int keycodes[MAX_SEND_KEY]; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (vshCommandOptString(cmd, codeset, codeset_option) = 0) +codeset_option = default; + +if (vshCommandOptInt(cmd, holdtime, holdtime) = 0) +holdtime = 0; + +if (STREQ(codeset_option, default) || STREQ(codeset_option, linux)) { +codeset = LIBVIRT_KEYCODE_LINUX; +} else if (STREQ(codeset_option, diriver_default)) { +codeset = LIBVIRT_KEYCODE_DRIVER_DEFAULT; +} else if (STREQ(codeset_option, xt)) { +codeset = LIBVIRT_KEYCODE_XT; +} else if (STREQ(codeset_option, atset1)) { +codeset = LIBVIRT_KEYCODE_ATSET1; +} else if (STREQ(codeset_option, atset2)) { +codeset = LIBVIRT_KEYCODE_ATSET2; +} else if (STREQ(codeset_option, atset3)) { +codeset = LIBVIRT_KEYCODE_ATSET3; +} else { +vshError(ctl, _(unknown codeset: '%s'), codeset_option); +goto free_domain; +} + +for_each_variable_arg(cmd, opt) { +if (count == MAX_SEND_KEY) { +vshError(ctl, _(too many keycode)); +goto free_domain; +} + +if ((keycode = get_integer_keycode(opt-data)) 0) +goto get_keycode; + +vshError(ctl, _(invalid keycode: '%s'), opt-data); +goto free_domain; + +get_keycode: +keycodes[count] = keycode; +count++; +} + +if (!(virDomainSendKey(dom, codeset, holdtime, count, keycodes, 0) 0)) +ret = true; + +free_domain: +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -10807,6 +10909,7 @@ static const vshCmdDef domManagementCmds[] = { {dumpxml, cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {edit, cmdEdit, opts_edit, info_edit, 0}, {inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, +{send-key, cmdSendKey, opts_send_key, info_send_key}, {managedsave, cmdManagedSave, opts_managedsave, info_managedsave, 0}, {managedsave-remove, cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index ef01f41..beef608 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -294,6 +294,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item Bsend-key Idomain-id I--codeset Bcodeset I--holdtime Bholdtime Bkeycode... + +Send keys to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 04/13] remote_generator: support general dynamic array
It will allow us use dynamic_array_basic_type member_nameMAX for remote protocol and avoid so many manual coding. For avoiding ambiguity, dynamic_array_basic_type must have a _DABT suffix. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- daemon/remote_generator.pl | 18 ++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index d21f959..6905ff3 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -343,6 +343,12 @@ elsif ($opt_b) { } push(@args_list, args-$2.$2_len); +} elsif ($args_member =~ m/^(\S+_DABT) (\S+)\S+;/) { # normal dynamic array +if (! @args_list) { +push(@args_list, conn); +} +push(@args_list, args-$2.$2_len); +push(@args_list, ($1 *)args-$2.$2_val); } elsif ($args_member =~ m/\S+;/ or $args_member =~ m/\[\S+\];/) { # just make all other array types fail die unhandled type for argument value: $args_member; @@ -838,6 +844,18 @@ elsif ($opt_k) { push(@setters_list, args.$arg_name.${arg_name}_val = (char *)$arg_name;); push(@setters_list, args.$arg_name.${arg_name}_len = ${arg_name}len;); push(@args_check_list, { name = \$arg_name\, arg = ${arg_name}len, limit = $limit }); +} elsif ($args_member =~ m/^(\S+_DABT) (\S+)(\S+);/) { # normal dynamic array +my $type_name = $1; +my $arg_name = $2; +my $limit = $3; + +push(@args_list, unsigned int n${arg_name}); +push(@args_list, ${type_name} *$arg_name); + +push(@setters_list, args.$arg_name.${arg_name}_len = n${arg_name};); +push(@setters_list, args.$arg_name.${arg_name}_val = (${type_name} *)$arg_name;); + +push(@args_check_list, { name = \$arg_name\, arg = n${arg_name}, limit = $limit }); } elsif ($args_member =~ m/^(unsigned )?(int|hyper) (\S+);/) { my $type_name; my $arg_name = $3; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 11/13] send-key: support KEY_XXX names for the linux keycode
It make send-key command more friendly for user. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- include/libvirt/virtkeys.h | 253 tools/virsh.c | 21 2 files changed, 274 insertions(+), 0 deletions(-) diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h index eb07129..7893450 100644 --- a/include/libvirt/virtkeys.h +++ b/include/libvirt/virtkeys.h @@ -20,4 +20,257 @@ enum libvirt_keycode_set { #define MAX_SEND_KEY 16 +#define ITEM_KEYCODE(KEY_XXX) VIRT##KEY_XXX +#define KEYCODES() \ +keycode(KEY_ESC, 1)\ +keycode(KEY_1, 2)\ +keycode(KEY_2, 3)\ +keycode(KEY_3, 4)\ +keycode(KEY_4, 5)\ +keycode(KEY_5, 6)\ +keycode(KEY_6, 7)\ +keycode(KEY_7, 8)\ +keycode(KEY_8, 9)\ +keycode(KEY_9, 10)\ +keycode(KEY_0, 11)\ +keycode(KEY_MINUS, 12)\ +keycode(KEY_EQUAL, 13)\ +keycode(KEY_BACKSPACE, 14)\ +keycode(KEY_TAB, 15)\ +keycode(KEY_Q, 16)\ +keycode(KEY_W, 17)\ +keycode(KEY_E, 18)\ +keycode(KEY_R, 19)\ +keycode(KEY_T, 20)\ +keycode(KEY_Y, 21)\ +keycode(KEY_U, 22)\ +keycode(KEY_I, 23)\ +keycode(KEY_O, 24)\ +keycode(KEY_P, 25)\ +keycode(KEY_LEFTBRACE, 26)\ +keycode(KEY_RIGHTBRACE, 27)\ +keycode(KEY_ENTER, 28)\ +keycode(KEY_LEFTCTRL,29)\ +keycode(KEY_A, 30)\ +keycode(KEY_S, 31)\ +keycode(KEY_D, 32)\ +keycode(KEY_F, 33)\ +keycode(KEY_G, 34)\ +keycode(KEY_H, 35)\ +keycode(KEY_J, 36)\ +keycode(KEY_K, 37)\ +keycode(KEY_L, 38)\ +keycode(KEY_SEMICOLON, 39)\ +keycode(KEY_APOSTROPHE, 40)\ +keycode(KEY_GRAVE, 41)\ +keycode(KEY_LEFTSHIFT, 42)\ +keycode(KEY_BACKSLASH, 43)\ +keycode(KEY_Z, 44)\ +keycode(KEY_X, 45)\ +keycode(KEY_C, 46)\ +keycode(KEY_V, 47)\ +keycode(KEY_B, 48)\ +keycode(KEY_N, 49)\ +keycode(KEY_M, 50)\ +keycode(KEY_COMMA, 51)\ +keycode(KEY_DOT, 52)\ +keycode(KEY_SLASH, 53)\ +keycode(KEY_RIGHTSHIFT, 54)\ +keycode(KEY_KPASTERISK, 55)\ +keycode(KEY_LEFTALT, 56)\ +keycode(KEY_SPACE, 57)\ +keycode(KEY_CAPSLOCK,58)\ +keycode(KEY_F1, 59)\ +keycode(KEY_F2, 60)\ +keycode(KEY_F3, 61)\ +keycode(KEY_F4, 62)\ +keycode(KEY_F5, 63)\ +keycode(KEY_F6, 64)\ +keycode(KEY_F7, 65)\ +keycode(KEY_F8, 66)\ +keycode(KEY_F9, 67)\ +keycode(KEY_F10, 68)\ +keycode(KEY_NUMLOCK, 69)\ +keycode(KEY_SCROLLLOCK, 70)\ +keycode(KEY_KP7, 71)\ +keycode(KEY_KP8, 72)\ +keycode(KEY_KP9, 73)\ +keycode(KEY_KPMINUS, 74)\ +keycode(KEY_KP4, 75)\ +keycode(KEY_KP5, 76)\ +keycode(KEY_KP6, 77)\ +keycode(KEY_KPPLUS, 78)\ +keycode(KEY_KP1, 79)\ +keycode(KEY_KP2, 80)\ +keycode(KEY_KP3, 81)\ +keycode(KEY_KP0, 82)\ +keycode(KEY_KPDOT, 83)\ +keycode(KEY_ZENKAKUHANKAKU, 85)\ +keycode(KEY_102ND, 86)\ +keycode(KEY_F11, 87)\ +keycode(KEY_F12, 88)\ +keycode(KEY_RO, 89)\ +keycode(KEY_KATAKANA,90)\ +keycode(KEY_HIRAGANA,91)\ +keycode(KEY_HENKAN, 92)\ +keycode(KEY_KATAKANAHIRAGANA,93)\ +keycode(KEY_MUHENKAN,94)\ +keycode(KEY_KPJPCOMMA, 95)\ +keycode(KEY_KPENTER, 96)\ +keycode(KEY_RIGHTCTRL, 97)\ +keycode(KEY_KPSLASH, 98)\ +keycode(KEY_SYSRQ, 99)\ +keycode(KEY_RIGHTALT,100)\ +keycode(KEY_LINEFEED,101)\ +keycode(KEY_HOME,102)\ +keycode(KEY_UP, 103)\ +keycode
[libvirt] [PATCH 03/13] add VSH_OFLAG_REQ_OPT options
A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option. It will help us use optional arguement with VSH_OT_ARGV argument. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- tools/virsh.c |7 +++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2e27535..80cffac 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0,/* without flags */ VSH_OFLAG_REQ = (1 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 1), /* empty string option allowed */ +VSH_OFLAG_REQ_OPT = (1 2), /* --optionname required */ }; /* dummy */ @@ -11080,6 +11081,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } +if (opt-flag VSH_OFLAG_REQ_OPT) { +if (opt-flag VSH_OFLAG_REQ) +*opts_required |= 1 i; +continue; +} + *opts_need_arg |= 1 i; if (opt-flag VSH_OFLAG_REQ) { if (optional) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 01/13] allow name for VSH_OT_ARGV options
A name will improve the usege, example # virsh help echo NAME echo - echo arguments SYNOPSIS echo [--shell] [--xml] [string]... DESCRIPTION Echo back arguments, possibly with quoting. OPTIONS --shell escape for shell use --xmlescape for XML use string arguments to echo [string]... is added to SYNOPSIS. string arguments to echo is added to OPTIONS. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- tools/virsh.c | 19 +-- 1 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index de49489..c358580 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ -VSH_OT_ARGV /* remaining arguments, opt-name should be */ +VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType; /* @@ -10046,7 +10046,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {shell, VSH_OT_BOOL, 0, N_(escape for shell use)}, {xml, VSH_OT_BOOL, 0, N_(escape for XML use)}, -{, VSH_OT_ARGV, 0, N_(arguments to echo)}, +{string, VSH_OT_ARGV, 0, N_(arguments to echo)}, {NULL, 0, 0, NULL} }; @@ -11084,6 +11084,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _(option --%s already seen), name); return NULL; } +if (opt-type == VSH_OT_ARGV) { +vshError(ctl, _(variable argument %s + should not be used with --%s), name, name); +return NULL; +} *opts_seen |= 1 i; return opt; } @@ -11132,7 +11137,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = def-opts[i]; vshError(ctl, - opt-type == VSH_OT_DATA ? + opt-type == VSH_OT_DATA || opt-type == VSH_OT_ARGV ? _(command '%s' requires %s option) : _(command '%s' requires --%s option), def-name, opt-name); @@ -11240,7 +11245,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ -fmt = _([string]...); +fmt = (opt-flag VSH_OFLAG_REQ) ? _(%s...) + : _([%s]...); break; default: assert(0); @@ -11280,7 +11286,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ -continue; +snprintf(buf, sizeof(buf), _(%s), opt-name); +break; default: assert(0); } @@ -12693,7 +12700,7 @@ vshReadlineOptionsGenerator(const char *text, int state) list_index++; -if (opt-type == VSH_OT_DATA) +if (opt-type == VSH_OT_DATA || opt-type == VSH_OT_ARGV) /* ignore non --option */ continue; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 06/13] send-key: Defining the internal API
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- src/driver.h |8 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 450dd53..70a30d9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -535,6 +535,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); +typedef int +(*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -738,6 +745,7 @@ struct _virDriver { virDrvDomainMigratePerform3domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3domainMigrateConfirm3; +virDrvDomainSendKey domainSendKey; }; typedef int -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 10/13] qemu:send-key: Implement the driver methods
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- src/qemu/qemu_driver.c | 50 ++ src/qemu/qemu_monitor.c | 28 +++ src/qemu/qemu_monitor.h |6 + src/qemu/qemu_monitor_json.c | 15 src/qemu/qemu_monitor_json.h |5 src/qemu/qemu_monitor_text.c | 48 src/qemu/qemu_monitor_text.h |5 7 files changed, 157 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 691965d..f7e21bf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1753,6 +1753,55 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorSendKey(priv-mon, codeset, holdtime, nkeycodes, keycodes); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -7746,6 +7795,7 @@ static virDriver qemuDriver = { .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = qemuSetSchedulerParametersFlags, /* 0.9.2 */ +.domainSendKey = qemuDomainSendKey, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5186f99..c0688fd 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include logging.h #include files.h +#include libvirt/virtkeys.h + #define VIR_FROM_THIS VIR_FROM_QEMU #define DEBUG_IO 0 @@ -2294,6 +2296,32 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ +int ret; + +VIR_DEBUG(mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u, + mon, codeset, holdtime, nkeycodes); + +if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT +|| codeset == LIBVIRT_KEYCODE_XT)) { +qemuReportError(VIR_ERR_NO_SUPPORT, +qemu monitor can not support the codeset: %d, +codeset); +return -1; +} + +if (mon-json) +ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); +else +ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); +return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 05c3359..76a849a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -435,6 +435,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2d8a390..a547f1d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2615,6 +2615,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon
[libvirt] [PATCH 00/13] Add support for send keys to guest
Add API virDomainSendKey() and virsh send-key command. # virsh help send-key NAME send-key - Send keycodes to the guest SYNOPSIS send-key domain [--codeset string] [--holdtime number] keycode... DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the xt:keystring codeset or the KEY_* strings listed below for the linux codeset. Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) defaultlinux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but keycode... must be the qemu-style key strings Examples: virsh # send-key domain 37 18 21 virsh # send-key domain --holdtime 1000 0x15 18 0xf virsh # send-key domain KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key domain --codeset xt:keystring alt-sysrq h KEY_XXX strings for the linux codeset: . . . OPTIONS [--domain] string domain name, id or uuid --codeset string the codeset of keycodes, default:linux --holdtime number the time (in millsecond) how long the keys will be held keycodethe key code PATCH 01~04 prepare PATCH 05~10 Add support for send keys to guest PATCH 11~13 improve usage of send keys. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Lai Jiangshan (13): allow name for VSH_OT_ARGV options improve the iteration of VSH_OT_ARGV options add VSH_OFLAG_REQ_OPT options remote_generator: support general dynamic array send-key: Defining the public API send-key: Defining the internal API send-key: Implementing the public API send-key: Implementing the remote protocol send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods send-key: support KEY_XXX names for the linux keycode qemu,send-key: map linux keycode to xt keycode virsh,send-key: add --codeset xt:keystring support daemon/remote_generator.pl | 18 ++ include/libvirt/libvirt.h.in |7 + include/libvirt/virtkeys.h | 276 python/generator.py |1 + src/driver.h |8 + src/libvirt.c| 54 ++ src/libvirt_public.syms |1 + src/qemu/qemu_driver.c | 50 + src/qemu/qemu_monitor.c | 75 src/qemu/qemu_monitor.h |6 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h |5 + src/qemu/qemu_monitor_text.c | 48 + src/qemu/qemu_monitor_text.h |5 + src/remote/remote_driver.c |1 + src/remote/remote_protocol.x | 19 ++- src/remote_protocol-structs | 11 + tools/virsh.c| 415 +++--- tools/virsh.pod |4 + 19 files changed, 989 insertions(+), 30 deletions(-) create mode 100644 include/libvirt/virtkeys.h -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 13/13] virsh, send-key: add --codeset xt:keystring support
It make send-key command more friendly for user. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- tools/virsh.c | 224 - 1 files changed, 221 insertions(+), 3 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 3bccc08..18ef4bb 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3017,11 +3017,24 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) static const vshCmdInfo info_send_key[] = { {help, N_(Send keycodes to the guest)}, {desc, N_(Send keycodes to the guest, the keycodes must be integers\n +or the qemu-style key strings for the \xt:keystring\ codeset\n or the KEY_* strings listed below for the \linux\ codeset.\n\n +Available codeset:\n +linux the keycodes specified in \n +/usr/include/linux/input.h(default)\n +defaultlinux codeset will be used\n +driver_default the hypervisor default codeset will be used\n +xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards\n +atset1 set1 scancode of standard AT keyboards and PS/2 keyboards\n +atset2 set2 scancode of standard AT keyboards and PS/2 keyboards\n +atset3 set3 scancode of standard AT keyboards and PS/2 keyboards\n +xt:keystring XT scancode, but keycode... must be the qemu-style key strings\n +\n Examples:\n\n virsh # send-key domain 37 18 21\n virsh # send-key domain --holdtime 1000 0x15 18 0xf\n -virsh # send-eky domain KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n +virsh # send-key domain KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n +virsh # send-key domain --codeset xt:keystring alt-sysrq h\n \n KEY_XXX strings for the \linux\ codeset:\n #define keycode(var, value) #var = #value \n @@ -3062,6 +3075,178 @@ static int get_integer_keycode(const char *key_name) return val; } + +typedef struct { +int keycode; +const char *name; +} KeyDef; + +static const KeyDef key_defs[] = { +{ 0x2a, shift }, +{ 0x36, shift_r }, + +{ 0x38, alt }, +{ 0xb8, alt_r }, +{ 0x64, altgr }, +{ 0xe4, altgr_r }, +{ 0x1d, ctrl }, +{ 0x9d, ctrl_r }, + +{ 0xdd, menu }, + +{ 0x01, esc }, + +{ 0x02, 1 }, +{ 0x03, 2 }, +{ 0x04, 3 }, +{ 0x05, 4 }, +{ 0x06, 5 }, +{ 0x07, 6 }, +{ 0x08, 7 }, +{ 0x09, 8 }, +{ 0x0a, 9 }, +{ 0x0b, 0 }, +{ 0x0c, minus }, +{ 0x0d, equal }, +{ 0x0e, backspace }, + +{ 0x0f, tab }, +{ 0x10, q }, +{ 0x11, w }, +{ 0x12, e }, +{ 0x13, r }, +{ 0x14, t }, +{ 0x15, y }, +{ 0x16, u }, +{ 0x17, i }, +{ 0x18, o }, +{ 0x19, p }, +{ 0x1a, bracket_left }, +{ 0x1b, bracket_right }, +{ 0x1c, ret }, + +{ 0x1e, a }, +{ 0x1f, s }, +{ 0x20, d }, +{ 0x21, f }, +{ 0x22, g }, +{ 0x23, h }, +{ 0x24, j }, +{ 0x25, k }, +{ 0x26, l }, +{ 0x27, semicolon }, +{ 0x28, apostrophe }, +{ 0x29, grave_accent }, + +{ 0x2b, backslash }, +{ 0x2c, z }, +{ 0x2d, x }, +{ 0x2e, c }, +{ 0x2f, v }, +{ 0x30, b }, +{ 0x31, n }, +{ 0x32, m }, +{ 0x33, comma }, +{ 0x34, dot }, +{ 0x35, slash }, + +{ 0x37, asterisk }, + +{ 0x39, spc }, +{ 0x3a, caps_lock }, +{ 0x3b, f1 }, +{ 0x3c, f2 }, +{ 0x3d, f3 }, +{ 0x3e, f4 }, +{ 0x3f, f5 }, +{ 0x40, f6 }, +{ 0x41, f7 }, +{ 0x42, f8 }, +{ 0x43, f9 }, +{ 0x44, f10 }, +{ 0x45, num_lock }, +{ 0x46, scroll_lock }, + +{ 0xb5, kp_divide }, +{ 0x37, kp_multiply }, +{ 0x4a, kp_subtract }, +{ 0x4e, kp_add }, +{ 0x9c, kp_enter }, +{ 0x53, kp_decimal }, +{ 0x54, sysrq }, + +{ 0x52, kp_0 }, +{ 0x4f, kp_1 }, +{ 0x50, kp_2 }, +{ 0x51, kp_3 }, +{ 0x4b, kp_4 }, +{ 0x4c, kp_5 }, +{ 0x4d, kp_6 }, +{ 0x47, kp_7 }, +{ 0x48, kp_8 }, +{ 0x49, kp_9 }, + +{ 0x56, }, + +{ 0x57, f11 }, +{ 0x58, f12 }, + +{ 0xb7, print }, + +{ 0xc7, home }, +{ 0xc9, pgup }, +{ 0xd1, pgdn }, +{ 0xcf, end }, + +{ 0xcb, left }, +{ 0xc8, up }, +{ 0xd0, down }, +{ 0xcd, right }, + +{ 0xd2, insert }, +{ 0xd3, delete }, + +{ 0xf0, stop }, +{ 0xf1, again }, +{ 0xf2, props }, +{ 0xf3, undo }, +{ 0xf4, front }, +{ 0xf5, copy }, +{ 0xf6, open }, +{ 0xf7, paste }, +{ 0xf8, find }, +{ 0xf9, cut }, +{ 0xfa, lf }, +{ 0xfb, help }, +{ 0xfc, meta_l }, +{ 0xfd, meta_r }, +{ 0xfe, compose }, + +{ 0, NULL
[libvirt] [PATCH 05/13] send-key: Defining the public API
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- include/libvirt/libvirt.h.in |7 +++ include/libvirt/virtkeys.h | 23 +++ python/generator.py |1 + src/libvirt_public.syms |1 + 4 files changed, 32 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom, int virDomainInjectNMI(virDomainPtr domain, unsigned int flags); +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 000..eb07129 --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,23 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { +LIBVIRT_KEYCODE_LINUX = 0, +LIBVIRT_KEYCODE_DRIVER_DEFAULT = 1, +LIBVIRT_KEYCODE_XT = 2, +LIBVIRT_KEYCODE_ATSET1 = LIBVIRT_KEYCODE_XT, +LIBVIRT_KEYCODE_ATSET2 = 3, +LIBVIRT_KEYCODE_ATSET3 = 4, +}; + +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 1741bba..3d57bf9 100755 --- a/python/generator.py +++ b/python/generator.py @@ -355,6 +355,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', +'virDomainSendKey', ) diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..9e8a37c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; +virDomainSendKey; } LIBVIRT_0.9.0; # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 02/13] improve the iteration of VSH_OT_ARGV options
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- tools/virsh.c | 47 --- 1 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index c358580..2e27535 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -277,7 +277,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD-def-opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ +while (opt) { + if (opt-def opt-def-type == VSH_OT_ARGV) + break; + opt = opt-next; +} + +return opt; +} + +#define for_each_variable_arg(cmd, opt) \ +for (opt = __variable_arg(cmd-opts); opt; opt = __variable_arg(opt-next)) + #define VSH_BYID (1 1) #define VSH_BYUUID (1 2) @@ -10059,6 +10079,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; +const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -10067,10 +10088,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, xml)) xml = true; -while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { +for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q; +arg = opt-data; if (count) virBufferAddChar(buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11484,27 +11506,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; } -/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name be last in the - * list of supported options in CMD-def-opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ -vshCmdOpt *opt = cmd-opts; - -while (opt) { -if (opt-def opt-def-type == VSH_OT_ARGV) { -if (count-- == 0) -return opt-data; -} -opt = opt-next; -} -return NULL; -} - /* Determine whether CMD-opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */ -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 07/13] send-key: Implementing the public API
Signed-off-by: Lai Jiangshan la...@fujitsu.com --- src/libvirt.c | 54 ++ 1 files changed, 54 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..8246975 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6080,6 +6080,60 @@ error: } /** + * virDomainSendKey: + * @domain:pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ +virConnectPtr conn; +VIR_DOMAIN_DEBUG(domain, flags=%u, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN(domain)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return -1; +} +if (domain-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +conn = domain-conn; + +if (conn-driver-domainSendKey) { +int ret; +ret = conn-driver-domainSendKey(domain, codeset, holdtime, + nkeycodes, keycodes, flags); +if (ret 0) +goto error; +return ret; +} + +virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(domain-conn); +return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 12/13] qemu, send-key: map linux keycode to xt keycode
It allows us use linux keycode for qemu driver. Signed-off-by: Lai Jiangshan la...@fujitsu.com --- src/qemu/qemu_monitor.c | 49 ++- 1 files changed, 48 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c0688fd..f6cdff1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2296,6 +2296,41 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +unsigned int linux2xt_keycode[256] = { +/* 00: */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, +/* 08: */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +/* 10: */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, +/* 18: */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, +/* 20: */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, +/* 28: */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, +/* 30: */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +/* 38: */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, +/* 40: */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, +/* 48: */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, +/* 50: */ 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x57, +/* 58: */ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60: */ 0x00, 0x9d, 0xb5, 0x54, 0xb8, 0x00, 0xc7, 0xc8, +/* 68: */ 0xc9, 0xcb, 0xcd, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, +/* 70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 80: */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, +/* 88: */ 0xf8, 0xf9, 0xfb, 0xdd, 0x00, 0x00, 0x00, 0x00, +/* 90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* d0: */ 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, +/* d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + int qemuMonitorSendKey(qemuMonitorPtr mon, unsigned int codeset, unsigned int holdtime, @@ -2307,7 +2342,19 @@ int qemuMonitorSendKey(qemuMonitorPtr mon, VIR_DEBUG(mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u, mon, codeset, holdtime, nkeycodes); -if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT +if (codeset == LIBVIRT_KEYCODE_LINUX) { +int i; + +for (i = 0; i nkeycodes; i++) { +if (keycodes[i] = 256 || !linux2xt_keycode[keycodes[i]]) { +qemuReportError(VIR_ERR_INTERNAL_ERROR, +can not map the linux keycode:%d to XT keycode, +keycodes[i]); +return -1; +} +keycodes[i] = linux2xt_keycode[keycodes[i]]; +} +} else if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT || codeset == LIBVIRT_KEYCODE_XT)) { qemuReportError(VIR_ERR_NO_SUPPORT, qemu monitor can not support the codeset: %d, -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 0/6 V4] Add support for injecting NMI to guest
This patch series implements a feature of injecting NMI to guest, which is accessible via new virDomainInjectNMI API and 'inject-nmi' command in virsh. Lai Jiangshan (6): inject-nmi: Defining the public API inject-nmi: Defining the internal API inject-nmi: Implementing the public API inject-nmi: Implementing the remote protocol inject-nmi: Expose the new API in virsh qemu,inject-nmi: Implement the driver methods daemon/remote_generator.pl |2 +- include/libvirt/libvirt.h.in |2 + src/driver.h |4 +++ src/esx/esx_driver.c |1 + src/libvirt.c| 44 + src/libvirt_public.syms |5 src/libxl/libxl_driver.c |1 + src/lxc/lxc_driver.c |1 + src/openvz/openvz_driver.c |1 + src/phyp/phyp_driver.c |3 +- src/qemu/qemu_driver.c | 45 ++ src/qemu/qemu_monitor.c | 14 + src/qemu/qemu_monitor.h |2 + src/qemu/qemu_monitor_json.c | 27 + src/qemu/qemu_monitor_json.h |1 + src/qemu/qemu_monitor_text.c | 28 ++ src/qemu/qemu_monitor_text.h |1 + src/remote/remote_driver.c |1 + src/remote/remote_protocol.x |8 ++- src/remote_protocol-structs |4 +++ src/test/test_driver.c |1 + src/uml/uml_driver.c |1 + src/vbox/vbox_tmpl.c |1 + src/vmware/vmware_driver.c |1 + src/xen/xen_driver.c |1 + src/xen/xen_driver.h |1 + src/xen/xen_hypervisor.c |1 + src/xen/xen_inotify.c|1 + src/xen/xend_internal.c |1 + src/xen/xm_internal.c|1 + src/xen/xs_internal.c|1 + src/xenapi/xenapi_driver.c |1 + tools/virsh.c| 35 tools/virsh.pod |4 +++ 34 files changed, 243 insertions(+), 3 deletions(-) -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/6 V4] inject-nmi: Implementing the public API
--- src/libvirt.c | 44 1 files changed, 44 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index abacf85..f468b61 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5217,6 +5217,50 @@ error: } /** + * virDomainInjectNMI: + * @domain: pointer to domain object, or NULL for Domain0 + * @flags: the flags for controlling behaviours + * + * Send NMI to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainInjectNMI(virDomainPtr domain, unsigned int flags) +{ +virConnectPtr conn; +VIR_DOMAIN_DEBUG(domain, flags=%u, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN(domain)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return (-1); +} +if (domain-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +conn = domain-conn; + +if (conn-driver-domainInjectNMI) { +int ret; +ret = conn-driver-domainInjectNMI(domain, flags); +if (ret 0) +goto error; +return ret; +} + +virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(domain-conn); +return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 6/6 V4] qemu, inject-nmi: Implement the driver methods
--- src/qemu/qemu_driver.c | 46 +- src/qemu/qemu_monitor.c | 14 src/qemu/qemu_monitor.h |2 + src/qemu/qemu_monitor_json.c | 27 src/qemu/qemu_monitor_json.h |1 + src/qemu/qemu_monitor_text.c | 28 + src/qemu/qemu_monitor_text.h |1 + 7 files changed, 118 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fa4a2bd..bec0e35 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1705,6 +1705,50 @@ static int qemudDomainSetMaxMemory(virDomainPtr dom, unsigned long memory) return qemudDomainSetMemoryFlags(dom, memory, VIR_DOMAIN_MEM_MAXIMUM); } +static int qemuDomainInjectNMI(virDomainPtr domain, unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorInjectNMI(priv-mon); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -7192,7 +7236,7 @@ static virDriver qemuDriver = { qemuDomainSnapshotDelete, /* domainSnapshotDelete */ qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainOpenConsole, /* domainOpenConsole */ -NULL, /* domainInjectNMI */ +qemuDomainInjectNMI, /* domainInjectNMI */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f89038e..9f0f20d 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2228,3 +2228,17 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply); return ret; } + + +int qemuMonitorInjectNMI(qemuMonitorPtr mon) +{ +int ret; + +VIR_DEBUG(mon=%p, mon); + +if (mon-json) +ret = qemuMonitorJSONInjectNMI(mon); +else +ret = qemuMonitorTextInjectNMI(mon); +return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c90219b..b84e230 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -423,6 +423,8 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, char **reply, bool hmp); +int qemuMonitorInjectNMI(qemuMonitorPtr mon); + /** * When running two dd process and using redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 20a78e1..04ef077 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2513,3 +2513,30 @@ cleanup: return ret; } + +int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon) +{ +int ret; +virJSONValuePtr cmd; +virJSONValuePtr reply = NULL; + +cmd = qemuMonitorJSONMakeCommand(inject-nmi, NULL); +if (!cmd) +return -1; + +if ((ret = qemuMonitorJSONCommand(mon, cmd, reply)) 0) +goto cleanup; + +if (qemuMonitorJSONHasError(reply, CommandNotFound) +qemuMonitorCheckHMP(mon, inject-nmi)) { +VIR_DEBUG0(inject-nmi command not found, trying HMP); +ret = qemuMonitorTextInjectNMI(mon); +} else { +ret = qemuMonitorJSONCheckError(cmd, reply); +} + +cleanup: +virJSONValueFree(cmd); +virJSONValueFree(reply); +return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 086f0e1..f2dc4d2 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -204,4 +204,5 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, char **reply_str, bool hmp); +int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 53781c8..1a15d49 100644 --- a/src/qemu/qemu_monitor_text.c +++
[libvirt] [PATCH 1/6 V4] inject-nmi: Defining the public API
--- include/libvirt/libvirt.h.in |2 ++ src/libvirt_public.syms |5 + 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5783303..0e1e27a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2519,6 +2519,8 @@ int virDomainOpenConsole(virDomainPtr dom, virStreamPtr st, unsigned int flags); +int virDomainInjectNMI(virDomainPtr domain, unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4aed41..ababf39 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -436,4 +436,9 @@ LIBVIRT_0.9.0 { virStorageVolUpload; } LIBVIRT_0.8.8; +LIBVIRT_0.9.2 { +global: +virDomainInjectNMI; +} LIBVIRT_0.9.0; + # define new API here using predicted next version number -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 5/6 V4] inject-nmi: Expose the new API in virsh
--- tools/virsh.c | 35 +++ tools/virsh.pod |4 2 files changed, 39 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2b16714..c2dabd7 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2912,6 +2912,40 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * inject-nmi command + */ +static const vshCmdInfo info_inject_nmi[] = { +{help, N_(Inject NMI to the guest)}, +{desc, N_(Inject NMI to the guest domain.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_inject_nmi[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL} +}; + + +static bool +cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = true; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (virDomainInjectNMI(dom, 0) 0) +ret = false; + +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -10720,6 +10754,7 @@ static const vshCmdDef domManagementCmds[] = { {setmaxmem, cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, {setmem, cmdSetmem, opts_setmem, info_setmem}, {setvcpus, cmdSetvcpus, opts_setvcpus, info_setvcpus}, +{inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi}, {shutdown, cmdShutdown, opts_shutdown, info_shutdown}, {start, cmdStart, opts_start, info_start}, {suspend, cmdSuspend, opts_suspend, info_suspend}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 2a708f6..f317c57 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -290,6 +290,10 @@ running Bvirsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. +=item Binject-nmi Idomain-id + +Inject NMI to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/6 V4] inject-nmi: Implementing the remote protocol
--- daemon/remote_generator.pl |2 +- src/remote/remote_driver.c |2 +- src/remote/remote_protocol.x |8 +++- src/remote_protocol-structs |4 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 062ccc1..74fa769 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -35,7 +35,7 @@ sub name_to_ProcName { @elems = map { $_ =~ s/Nwfilter/NWFilter/; $_ =~ s/Xml/XML/; $_ =~ s/Uri/URI/; $_ =~ s/Uuid/UUID/; $_ =~ s/Id/ID/; $_ =~ s/Mac/MAC/; $_ =~ s/Cpu/CPU/; $_ =~ s/Os/OS/; - $_ } @elems; + $_ =~ s/Nmi/NMI/; $_ } @elems; join , @elems } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c6beb3d..0a1afde 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6493,7 +6493,7 @@ static virDriver remote_driver = { remoteDomainSnapshotDelete, /* domainSnapshotDelete */ remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainOpenConsole, /* domainOpenConsole */ -NULL, /* domainInjectNMI */ +remoteDomainInjectNMI, /* domainInjectNMI */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c706c36..cdb8369 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -817,6 +817,11 @@ struct remote_domain_undefine_args { remote_nonnull_domain dom; }; +struct remote_domain_inject_nmi_args { +remote_nonnull_domain dom; +unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; int nvcpus; @@ -2176,7 +2181,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_BLKIO_PARAMETERS = 206, REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207, REMOTE_PROC_STORAGE_VOL_UPLOAD = 208, -REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209 +REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209, +REMOTE_PROC_DOMAIN_INJECT_NMI = 210 /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index f904c4d..8a5ade0 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1435,3 +1435,7 @@ struct remote_message_header { u_int serial; remote_message_status status; }; +struct remote_domain_inject_nmi_args { +remote_nonnull_domain dom; +unsigned int flags; +}; -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] libvirt, logging: cleanup VIR_DEBUG0() VIR_INFO0() VIR_WARN0() VIR_ERROR0()
Is this OK? fmt... #define high_level_api(fmt...) low_level_api(fmt) On 05/10/2011 11:29 PM, Eric Blake wrote: On 05/10/2011 08:52 AM, Eric Blake wrote: On 05/09/2011 03:24 AM, Lai Jiangshan wrote: These VIR_0 APIs make us confused, use the non-0-suffix APIs instead. How these coversions works? The magic is using ##. #define high_levle_api(fmt, ...) low_levle_api(fmt, ##__VA_ARGS__) When __VA_ARGS__ is empty, ## will swallow the , in fmt, to avoid compile error. ##__VA_ARGS__ is a GNU extension; it does not work on all C99 compilers. I'm worried that this is not portable. But, we CAN do: #ifdef __GNUC__ # define high_level(fmt, ...) low_level(fmt, ##__VA_ARGS__) #else # define PP_NARG(...) \ PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) # define PP_NARG_(...) \ PP_ARG_N(__VA_ARGS__) # define PP_ARG_N( \ _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ _61,_62,_63,N,...) N # define PP_RSEQ_N() \ 63,62,61,60, \ 59,58,57,56,55,54,53,52,51,50, \ 49,48,47,46,45,44,43,42,41,40, \ 39,38,37,36,35,34,33,32,31,30, \ 29,28,27,26,25,24,23,22,21,20, \ 19,18,17,16,15,14,13,12,11,10, \ 9,8,7,6,5,4,3,2,1,0 # define VIR_DEBUG(...) VIR_DEBUG ## PP_NARG(__VA_ARGS__) (__VA_ARGS__) # define VIR_DEBUG1(xxx) xxx # define VIR_DEBUG2(xxx, xxx) xxx ... # define VIR_DEBUG63(xxx, ..., xxx) xxx #endif suitably expanded (and we can also place a cap on the maximum number of arguments we ever expect to see in VIR_DEBUG, rather than going all the way to the C99 limit of 63). Thanks to: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5 for the idea. Yeah, it's gross, but we limit the grossness to one header, and the rest of the code is more maintainable as a result. -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 0/6 V3] Add support for injecting NMI to guest
On 04/21/2011 02:42 PM, Lai Jiangshan wrote: This patch series implements a feature of injecting NMI to guest, which is accessible via new virDomainInjectNMI API and 'inject-nmi' command in virsh. Lai Jiangshan (6): inject-nmi: Defining the public API inject-nmi: Defining the internal API inject-nmi: Implementing the public API inject-nmi: Implementing the remote protocol inject-nmi: Expose the new API in virsh qemu,inject-nmi: Implement the driver methods Hi, Eric and Daniel: In qemu community, Luiz have accepted the inject-nmi patches, could you accept these patches for libvirt? Thanks, Lai. -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 2/6 V3] inject-nmi: Defining the internal API
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/driver.h |4 src/esx/esx_driver.c |1 + src/libxl/libxl_driver.c |1 + src/lxc/lxc_driver.c |1 + src/openvz/openvz_driver.c |1 + src/phyp/phyp_driver.c |3 ++- src/qemu/qemu_driver.c |1 + src/remote/remote_driver.c |1 + src/test/test_driver.c |1 + src/uml/uml_driver.c |1 + src/vbox/vbox_tmpl.c |1 + src/vmware/vmware_driver.c |1 + src/xen/xen_driver.c |1 + src/xen/xen_driver.h |1 + src/xen/xen_hypervisor.c |1 + src/xen/xen_inotify.c |1 + src/xen/xend_internal.c|1 + src/xen/xm_internal.c |1 + src/xen/xs_internal.c |1 + src/xenapi/xenapi_driver.c |1 + 20 files changed, 24 insertions(+), 1 deletions(-) diff --git a/src/driver.h b/src/driver.h index a8b79e6..94cc37d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -515,6 +515,9 @@ typedef int virStreamPtr st, unsigned int flags); +typedef int +(*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); + /** * _virDriver: @@ -639,6 +642,7 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvQemuDomainMonitorCommand qemuDomainMonitorCommand; virDrvDomainOpenConsole domainOpenConsole; +virDrvDomainInjectNMI domainInjectNMI; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 50c631b..5b9edea 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4672,6 +4672,7 @@ static virDriver esxDriver = { esxDomainSnapshotDelete, /* domainSnapshotDelete */ NULL,/* qemuDomainMonitorCommand */ NULL,/* domainOpenConsole */ +NULL,/* domainInjectNMI */ }; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 247d78e..bc6d0d9 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -2748,6 +2748,7 @@ static virDriver libxlDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; static virStateDriver libxlStateDriver = { diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index e905302..fbbe803 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2906,6 +2906,7 @@ static virDriver lxcDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ lxcDomainOpenConsole, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 4af28e9..96f82bb 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1667,6 +1667,7 @@ static virDriver openvzDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index bb0e0ac..33848ff 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -3794,7 +3794,8 @@ static virDriver phypDriver = { NULL, /* domainRevertToSnapshot */ NULL, /* domainSnapshotDelete */ NULL, /* qemuMonitorCommand */ -NULL, /* domainOpenConsole */ +NULL, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; static virStorageDriver phypStorageDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f6e503a..ac1adbb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7031,6 +7031,7 @@ static virDriver qemuDriver = { qemuDomainSnapshotDelete, /* domainSnapshotDelete */ qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainOpenConsole, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e30780c..4655338 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -11298,6 +11298,7 @@ static virDriver remote_driver = { remoteDomainSnapshotDelete, /* domainSnapshotDelete */ remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainOpenConsole, /* domainOpenConsole */ +NULL, /* domainInjectNMI */ }; static virNetworkDriver network_driver = { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 0978214..f3df6a5 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5447,6 +5447,7 @@ static virDriver testDriver = { NULL, /* domainSnapshotDelete */ NULL
[libvirt] [PATCH 0/6 V3] Add support for injecting NMI to guest
This patch series implements a feature of injecting NMI to guest, which is accessible via new virDomainInjectNMI API and 'inject-nmi' command in virsh. Lai Jiangshan (6): inject-nmi: Defining the public API inject-nmi: Defining the internal API inject-nmi: Implementing the public API inject-nmi: Implementing the remote protocol inject-nmi: Expose the new API in virsh qemu,inject-nmi: Implement the driver methods daemon/remote.c | 28 + daemon/remote_dispatch_args.h |1 + daemon/remote_dispatch_prototypes.h |8 ++ daemon/remote_dispatch_table.h |5 include/libvirt/libvirt.h.in|2 + src/driver.h|4 +++ src/esx/esx_driver.c|1 + src/libvirt.c | 44 ++ src/libvirt_public.syms |5 src/libxl/libxl_driver.c|1 + src/lxc/lxc_driver.c|1 + src/openvz/openvz_driver.c |1 + src/phyp/phyp_driver.c |3 +- src/qemu/qemu_driver.c | 45 +++ src/qemu/qemu_monitor.c | 14 +++ src/qemu/qemu_monitor.h |2 + src/qemu/qemu_monitor_json.c| 27 + src/qemu/qemu_monitor_json.h|1 + src/qemu/qemu_monitor_text.c| 20 +++ src/qemu/qemu_monitor_text.h|1 + src/remote/remote_driver.c | 25 +++ src/remote/remote_protocol.c| 11 src/remote/remote_protocol.h| 10 +++ src/remote/remote_protocol.x|8 +- src/remote_protocol-structs |4 +++ src/test/test_driver.c |1 + src/uml/uml_driver.c|1 + src/vbox/vbox_tmpl.c|1 + src/vmware/vmware_driver.c |1 + src/xen/xen_driver.c|1 + src/xen/xen_driver.h|1 + src/xen/xen_hypervisor.c|1 + src/xen/xen_inotify.c |1 + src/xen/xend_internal.c |1 + src/xen/xm_internal.c |1 + src/xen/xs_internal.c |1 + src/xenapi/xenapi_driver.c |1 + tools/virsh.c | 35 +++ tools/virsh.pod |4 +++ 39 files changed, 321 insertions(+), 2 deletions(-) -- 1.7.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 3/6 V3] inject-nmi: Implementing the public API
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/libvirt.c | 44 1 files changed, 44 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 9e6784b..32446d1 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5214,6 +5214,50 @@ error: } /** + * virDomainInjectNMI: + * @domain: pointer to domain object, or NULL for Domain0 + * @flags: the flags for controlling behaviours + * + * Send NMI to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainInjectNMI(virDomainPtr domain, unsigned int flags) +{ +virConnectPtr conn; +VIR_DOMAIN_DEBUG(domain, flags=%u, flags); + +virResetLastError(); + +if (!VIR_IS_CONNECTED_DOMAIN(domain)) { +virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); +virDispatchError(NULL); +return (-1); +} +if (domain-conn-flags VIR_CONNECT_RO) { +virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); +goto error; +} + +conn = domain-conn; + +if (conn-driver-domainInjectNMI) { +int ret; +ret = conn-driver-domainInjectNMI(domain, flags); +if (ret 0) +goto error; +return ret; +} + +virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: +virDispatchError(domain-conn); +return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 4/6 V3] inject-nmi: Implementing the remote protocol
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- daemon/remote.c | 28 daemon/remote_dispatch_args.h |1 + daemon/remote_dispatch_prototypes.h |8 daemon/remote_dispatch_table.h |5 + src/remote/remote_driver.c | 26 +- src/remote/remote_protocol.c| 11 +++ src/remote/remote_protocol.h| 10 ++ src/remote/remote_protocol.x|8 +++- src/remote_protocol-structs |4 9 files changed, 99 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 54fef64..9ae232e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3288,6 +3288,34 @@ no_memory: } static int +remoteDispatchDomainInjectNmi(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_inject_nmi_args *args, + void *ret ATTRIBUTE_UNUSED) +{ +virDomainPtr dom; +int rv = -1; + +if (!(dom = get_nonnull_domain(conn, args-dom))) +goto cleanup; + +if (virDomainInjectNMI(dom, args-flags) == -1) +goto cleanup; + +rv = 0; + +cleanup: +if (rv 0) +remoteDispatchError(rerr); +if (dom) +virDomainFree(dom); +return rv; +} + +static int remoteDispatchDomainSetVcpus(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index f9537d7..4887d7c 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -178,3 +178,4 @@ remote_domain_migrate_set_max_speed_args val_remote_domain_migrate_set_max_speed_args; remote_storage_vol_upload_args val_remote_storage_vol_upload_args; remote_storage_vol_download_args val_remote_storage_vol_download_args; +remote_domain_inject_nmi_args val_remote_domain_inject_nmi_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index 18bf41d..3de6046 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -338,6 +338,14 @@ static int remoteDispatchDomainHasManagedSaveImage( remote_error *err, remote_domain_has_managed_save_image_args *args, remote_domain_has_managed_save_image_ret *ret); +static int remoteDispatchDomainInjectNmi( +struct qemud_server *server, +struct qemud_client *client, +virConnectPtr conn, +remote_message_header *hdr, +remote_error *err, +remote_domain_inject_nmi_args *args, +void *ret); static int remoteDispatchDomainInterfaceStats( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index b39f7c2..dacafdb 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -1052,3 +1052,8 @@ .args_filter = (xdrproc_t) xdr_remote_storage_vol_download_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* DomainInjectNmi = 210 */ +.fn = (dispatch_fn) remoteDispatchDomainInjectNmi, +.args_filter = (xdrproc_t) xdr_remote_domain_inject_nmi_args, +.ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 4655338..58eb682 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2933,6 +2933,30 @@ done: } static int +remoteDomainInjectNMI(virDomainPtr domain, unsigned int flags) +{ +int rv = -1; +remote_domain_inject_nmi_args args; +struct private_data *priv = domain-conn-privateData; + +remoteDriverLock(priv); + +make_nonnull_domain (args.dom, domain); +args.flags = flags; + +if (call (domain-conn, priv, 0, REMOTE_PROC_DOMAIN_INJECT_NMI, + (xdrproc_t) xdr_remote_domain_inject_nmi_args, (char *) args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) +goto done; + +rv = 0; + +done: +remoteDriverUnlock(priv); +return rv; +} + +static int remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus) { int rv = -1; @@ -11298,7 +11322,7 @@ static virDriver remote_driver = { remoteDomainSnapshotDelete, /* domainSnapshotDelete */ remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainOpenConsole, /* domainOpenConsole */ -NULL, /* domainInjectNMI */ +remoteDomainInjectNMI, /* domainInjectNMI */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 5604371..3ff0b1f 100644 --- a/src/remote
[libvirt] [PATCH 5/6 V3] inject-nmi: Expose the new API in virsh
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- tools/virsh.c | 35 +++ tools/virsh.pod |4 2 files changed, 39 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 9ac27b3..54fd6bf 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2912,6 +2912,40 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * inject-nmi command + */ +static const vshCmdInfo info_inject_nmi[] = { +{help, N_(Inject NMI to the guest)}, +{desc, N_(Inject NMI to the guest domain.)}, +{NULL, NULL} +}; + +static const vshCmdOptDef opts_inject_nmi[] = { +{domain, VSH_OT_DATA, VSH_OFLAG_REQ, N_(domain name, id or uuid)}, +{NULL, 0, 0, NULL} +}; + + +static bool +cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) +{ +virDomainPtr dom; +int ret = true; + +if (!vshConnectionUsability(ctl, ctl-conn)) +return false; + +if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) +return false; + +if (virDomainInjectNMI(dom, 0) 0) +ret = false; + +virDomainFree(dom); +return ret; +} + +/* * setmemory command */ static const vshCmdInfo info_setmem[] = { @@ -10730,6 +10764,7 @@ static const vshCmdDef domManagementCmds[] = { {setmaxmem, cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, {setmem, cmdSetmem, opts_setmem, info_setmem}, {setvcpus, cmdSetvcpus, opts_setvcpus, info_setvcpus}, +{inject-nmi, cmdInjectNMI, opts_inject_nmi, info_inject_nmi}, {shutdown, cmdShutdown, opts_shutdown, info_shutdown}, {start, cmdStart, opts_start, info_start}, {suspend, cmdSuspend, opts_suspend, info_suspend}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 2a708f6..f317c57 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -290,6 +290,10 @@ running Bvirsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. +=item Binject-nmi Idomain-id + +Inject NMI to the guest + =item Bshutdown The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
[libvirt] [PATCH 6/6 V3] qemu, inject-nmi: Implement the driver methods
Signed-off-by: Lai Jiangshan la...@cn.fujitsu.com --- src/qemu/qemu_driver.c | 46 +- src/qemu/qemu_monitor.c | 14 src/qemu/qemu_monitor.h |2 + src/qemu/qemu_monitor_json.c | 27 src/qemu/qemu_monitor_json.h |1 + src/qemu/qemu_monitor_text.c | 20 ++ src/qemu/qemu_monitor_text.h |1 + 7 files changed, 110 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac1adbb..548e657 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1702,6 +1702,50 @@ static int qemudDomainSetMaxMemory(virDomainPtr dom, unsigned long memory) return qemudDomainSetMemoryFlags(dom, memory, VIR_DOMAIN_MEM_MAXIMUM); } +static int qemuDomainInjectNMI(virDomainPtr domain, unsigned int flags) +{ +struct qemud_driver *driver = domain-conn-privateData; +virDomainObjPtr vm = NULL; +int ret = -1; +qemuDomainObjPrivatePtr priv; + +virCheckFlags(0, -1); + +qemuDriverLock(driver); +vm = virDomainFindByUUID(driver-domains, domain-uuid); +if (!vm) { +char uuidstr[VIR_UUID_STRING_BUFLEN]; +virUUIDFormat(domain-uuid, uuidstr); +qemuReportError(VIR_ERR_NO_DOMAIN, +_(no domain with matching uuid '%s'), uuidstr); +goto cleanup; +} + +if (!virDomainObjIsActive(vm)) { +qemuReportError(VIR_ERR_OPERATION_INVALID, +%s, _(domain is not running)); +goto cleanup; +} + +priv = vm-privateData; + +if (qemuDomainObjBeginJobWithDriver(driver, vm) 0) +goto cleanup; +qemuDomainObjEnterMonitorWithDriver(driver, vm); +ret = qemuMonitorInjectNMI(priv-mon); +qemuDomainObjExitMonitorWithDriver(driver, vm); +if (qemuDomainObjEndJob(vm) == 0) { +vm = NULL; +goto cleanup; +} + +cleanup: +if (vm) +virDomainObjUnlock(vm); +qemuDriverUnlock(driver); +return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -7031,7 +7075,7 @@ static virDriver qemuDriver = { qemuDomainSnapshotDelete, /* domainSnapshotDelete */ qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainOpenConsole, /* domainOpenConsole */ -NULL, /* domainInjectNMI */ +qemuDomainInjectNMI, /* domainInjectNMI */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 2d28f8d..5ed41e1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2228,3 +2228,17 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply); return ret; } + + +int qemuMonitorInjectNMI(qemuMonitorPtr mon) +{ +int ret; + +VIR_DEBUG(mon=%p, mon); + +if (mon-json) +ret = qemuMonitorJSONInjectNMI(mon); +else +ret = qemuMonitorTextInjectNMI(mon); +return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c90219b..b84e230 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -423,6 +423,8 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, char **reply, bool hmp); +int qemuMonitorInjectNMI(qemuMonitorPtr mon); + /** * When running two dd process and using redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 20a78e1..04ef077 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2513,3 +2513,30 @@ cleanup: return ret; } + +int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon) +{ +int ret; +virJSONValuePtr cmd; +virJSONValuePtr reply = NULL; + +cmd = qemuMonitorJSONMakeCommand(inject-nmi, NULL); +if (!cmd) +return -1; + +if ((ret = qemuMonitorJSONCommand(mon, cmd, reply)) 0) +goto cleanup; + +if (qemuMonitorJSONHasError(reply, CommandNotFound) +qemuMonitorCheckHMP(mon, inject-nmi)) { +VIR_DEBUG0(inject-nmi command not found, trying HMP); +ret = qemuMonitorTextInjectNMI(mon); +} else { +ret = qemuMonitorJSONCheckError(cmd, reply); +} + +cleanup: +virJSONValueFree(cmd); +virJSONValueFree(reply); +return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 086f0e1..f2dc4d2 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -204,4 +204,5 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, char **reply_str, bool hmp); +int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 53781c8..2ce871c 100644 --- a/src