提交 a7a82f98 编写于 作者: E Eduardo Otubo 提交者: Daniel Veillard

First version of the Power Hypervisor driver

Features supported:
- Connects to HMC/VIOS or IVM systems.
- Life cycle commands (resume and shutdown).
- dumpxml
- 'list' and 'list --all'

What is being implemented:
- better and centralized control for UUID
- definexml
- CPU management commands

* src/domain_conf.c src/domain_conf.h: first version of the driver
* configure.in src/Makefile.am include/libvirt/virterror.h
  src/domain_conf.[ch] src/libvirt.c src/virterror.c: glue the driver
  in the general framework
上级 521ac517
......@@ -187,6 +187,10 @@ AC_ARG_WITH([uml],
[ --with-uml add UML support (on)],[],[with_uml=yes])
AC_ARG_WITH([openvz],
[ --with-openvz add OpenVZ support (on)],[],[with_openvz=yes])
AC_ARG_WITH([libssh],
[ --with-libssh=[PFX] libssh location],[],[with_libssh=yes])
AC_ARG_WITH([phyp],
[ --with-phyp=[PFX] add PHYP support (on)],[with_phyp=yes],[with_phyp=check])
AC_ARG_WITH([vbox],
[ --with-vbox add VirtualBox support (on)],[],[with_vbox=yes])
AC_ARG_WITH([lxc],
......@@ -776,7 +780,50 @@ AM_CONDITIONAL([HAVE_NUMACTL], [test "$with_numactl" != "no"])
AC_SUBST([NUMACTL_CFLAGS])
AC_SUBST([NUMACTL_LIBS])
if test "$with_libssh" != "yes" -a "$with_libssh" != "no"; then
libssh_path="$with_libssh"
elif test "$with_libssh" = "yes"; then
libssh_path="/usr/local/lib/"
elif test "$with_libssh" = "no"; then
with_phyp="no";
fi
if test "$with_phyp" = "check"; then
AC_CHECK_LIB([ssh],[ssh_new],[
LIBSSH_LIBS="$LIBSSH_LIBS -lssh -L$libssh_path"
AC_SUBST([LIBSSH_LIBS])],[
with_phyp="no"
with_libssh="no";
],[])
if test "$with_phyp" != "no"; then
AC_CHECK_HEADERS([libssh/libssh.h],[
with_phyp="yes"
LIBSSH_CFLAGS="-I/usr/local/include/libssh"
AC_SUBST([LIBSSH_CFLAGS])
AC_DEFINE_UNQUOTED([WITH_PHYP], 1,
[whether IBM HMC / IVM driver is enabled])
],[
with_phyp="no"
with_libssh="no";
],[])
fi
elif test "$with_phyp" = "yes"; then
AC_CHECK_LIB([ssh],[ssh_new],[
LIBSSH_LIBS="$LIBSSH_LIBS -lssh -L$libssh_path"
AC_SUBST([LIBSSH_LIBS])],[
AC_MSG_ERROR([You must install the libssh to compile Phype driver.])
])
AC_CHECK_HEADERS([libssh/libssh.h],[
LIBSSH_CFLAGS="-I/usr/local/include/libssh"
AC_SUBST([LIBSSH_CFLAGS])],[
AC_MSG_ERROR([Cannot find libssh headers.Is libssh installed ?])
],[])
AC_DEFINE_UNQUOTED([WITH_PHYP], 1,
[whether IBM HMC / IVM driver is enabled])
fi
AM_CONDITIONAL([WITH_PHYP],[test "$with_phyp" = "yes"])
dnl libcap-ng
AC_ARG_WITH([capng],
......@@ -1518,6 +1565,7 @@ AC_MSG_NOTICE([ UML: $with_uml])
AC_MSG_NOTICE([ OpenVZ: $with_openvz])
AC_MSG_NOTICE([ VBox: $with_vbox])
AC_MSG_NOTICE([ LXC: $with_lxc])
AC_MSG_NOTICE([ PHYP: $with_phyp])
AC_MSG_NOTICE([ ONE: $with_one])
AC_MSG_NOTICE([ ESX: $with_esx])
AC_MSG_NOTICE([ Test: $with_test])
......@@ -1556,6 +1604,11 @@ AC_MSG_NOTICE([])
AC_MSG_NOTICE([Libraries])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS])
if test "$with_libssh" != "no" ; then
AC_MSG_NOTICE([ libssh: $LIBSSH_CFLAGS $LIBSSH_LIBS])
else
AC_MSG_NOTICE([ libssh: no])
fi
AC_MSG_NOTICE([ gnutls: $GNUTLS_CFLAGS $GNUTLS_LIBS])
if test "$with_sasl" != "no" ; then
AC_MSG_NOTICE([ sasl: $SASL_CFLAGS $SASL_LIBS])
......
......@@ -123,9 +123,10 @@
<p>
Jim Mereying was maintaining a CVS to git mirror on
<a href="http://git.et.redhat.com/?p=libvirt.git">git.et.redhat.com</a>.
Existing users should migrate to the new git server, as the
old one will be deprecated and turned into a mirror of the
libvirt.org one. It's available as:
Existing users should migrate to the new libvirt.org git server, as the
old one is now deprecated. For the sake of old links including now-
rewritten SHA1s, we'll leave the old repository on-line for some time.
It is available as:
</p>
<pre>
......
......@@ -53,19 +53,20 @@ typedef enum {
VIR_FROM_REMOTE, /* Error from remote driver */
VIR_FROM_OPENVZ, /* Error from OpenVZ driver */
VIR_FROM_XENXM, /* Error at Xen XM layer */
VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */
VIR_FROM_STATS_LINUX,/* Error in the Linux Stats code */
VIR_FROM_LXC, /* Error from Linux Container driver */
VIR_FROM_STORAGE, /* Error from storage driver */
VIR_FROM_NETWORK, /* Error from network config */
VIR_FROM_DOMAIN, /* Error from domain config */
VIR_FROM_UML, /* Error at the UML driver */
VIR_FROM_NODEDEV, /* Error from node device monitor */
VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */
VIR_FROM_XEN_INOTIFY,/* Error from xen inotify layer */
VIR_FROM_SECURITY, /* Error from security framework */
VIR_FROM_VBOX, /* Error from VirtualBox driver */
VIR_FROM_INTERFACE, /* Error when operating on an interface */
VIR_FROM_ONE, /* Error from OpenNebula driver */
VIR_FROM_ESX, /* Error from ESX driver */
VIR_FROM_PHYP, /* Error from IBM power hypervisor */
} virErrorDomain;
......
......@@ -6,6 +6,7 @@ INCLUDES = \
-I@top_srcdir@/include \
-I@top_srcdir@/qemud \
$(LIBXML_CFLAGS) \
$(LIBSSH_CFLAGS) \
$(XEN_CFLAGS) \
$(SELINUX_CFLAGS) \
$(DRIVER_MODULE_CFLAGS) \
......@@ -129,6 +130,9 @@ LXC_CONTROLLER_SOURCES = \
veth.c veth.h \
cgroup.c cgroup.h
PHYP_DRIVER_SOURCES = \
phyp/phyp_driver.c phyp/phyp_driver.h
OPENVZ_DRIVER_SOURCES = \
openvz_conf.c openvz_conf.h \
openvz_driver.c openvz_driver.h
......@@ -295,6 +299,18 @@ endif
libvirt_driver_xen_la_SOURCES = $(XEN_DRIVER_SOURCES)
endif
if WITH_PHYP
if WITH_DRIVER_MODULES
mod_LTLIBRARIES += libvirt_driver_phyp.la
else
noinst_LTLIBRARIES += libvirt_driver_phyp.la
libvirt_la_LIBADD += libvirt_driver_phyp.la
endif
libvirt_driver_phyp_la_LDFLAGS = $(LIBSSH_LIBS)
libvirt_driver_phyp_la_CFLAGS = $(LIBSSH_CFLAGS)
libvirt_driver_phyp_la_SOURCES = $(PHYP_DRIVER_SOURCES)
endif
if WITH_OPENVZ
if WITH_DRIVER_MODULES
mod_LTLIBRARIES += libvirt_driver_openvz.la
......@@ -511,6 +527,7 @@ EXTRA_DIST += \
$(UML_DRIVER_SOURCES) \
$(ONE_DRIVER_SOURCES) \
$(OPENVZ_DRIVER_SOURCES) \
$(PHYP_DRIVER_SOURCES) \
$(VBOX_DRIVER_SOURCES) \
$(ESX_DRIVER_SOURCES) \
$(NETWORK_DRIVER_SOURCES) \
......
......@@ -57,7 +57,8 @@ VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
"vmware",
"hyperv",
"vbox",
"one")
"one",
"phyp")
VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST,
"fd",
......
......@@ -55,6 +55,7 @@ enum virDomainVirtType {
VIR_DOMAIN_VIRT_HYPERV,
VIR_DOMAIN_VIRT_VBOX,
VIR_DOMAIN_VIRT_ONE,
VIR_DOMAIN_VIRT_PHYP,
VIR_DOMAIN_VIRT_LAST,
};
......
......@@ -23,6 +23,7 @@ typedef enum {
VIR_DRV_VBOX = 8,
VIR_DRV_ONE = 9,
VIR_DRV_ESX = 10,
VIR_DRV_PHYP = 11,
} virDrvNo;
......
......@@ -55,6 +55,9 @@
#ifdef WITH_OPENVZ
#include "openvz_driver.h"
#endif
#ifdef WITH_PHYP
#include "phyp/phyp_driver.h"
#endif
#ifdef WITH_VBOX
#include "vbox/vbox_driver.h"
#endif
......@@ -323,6 +326,9 @@ virInitialize(void)
#ifdef WITH_OPENVZ
if (openvzRegister() == -1) return -1;
#endif
#ifdef WITH_PHYP
if (phypRegister() == -1) return -1;
#endif
#ifdef WITH_VBOX
if (vboxRegister() == -1) return -1;
#endif
......@@ -896,6 +902,10 @@ virGetVersion(unsigned long *libVer, const char *type,
if (STRCASEEQ(type, "LXC"))
*typeVer = LIBVIR_VERSION_NUMBER;
#endif
#if WITH_PHYP
if (STRCASEEQ(type, "phyp"))
*typeVer = LIBVIR_VERSION_NUMBER;
#endif
#if WITH_OPENVZ
if (STRCASEEQ(type, "OpenVZ"))
*typeVer = LIBVIR_VERSION_NUMBER;
......
/*
* Copyright IBM Corp. 2009
*
* phyp_driver.c: ssh layer to access Power Hypervisors
*
* Authors:
* Eduardo Otubo <otubo at linux.vnet.ibm.com>
*
* 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 <sys/types.h>
#include <limits.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <libssh/libssh.h>
#include "internal.h"
#include "util.h"
#include "datatypes.h"
#include "buf.h"
#include "memory.h"
#include "logging.h"
#include "driver.h"
#include "libvirt/libvirt.h"
#include "virterror_internal.h"
#include "uuid.h"
#include "domain_conf.h"
#include "phyp_driver.h"
#define VIR_FROM_THIS VIR_FROM_PHYP
/*
* URI: phyp://user@[hmc|ivm]/managed_system
* */
static virDrvOpenStatus
phypOpen(virConnectPtr conn,
virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED)
{
SSH_SESSION *session;
ConnectionData *connection_data;
char string[strlen(conn->uri->path)];
uuid_dbPtr uuid_db = NULL;
if (VIR_ALLOC(uuid_db) < 0)
virReportOOMError(conn);
if (VIR_ALLOC(connection_data) < 0)
virReportOOMError(conn);
if (!conn || !conn->uri)
return VIR_DRV_OPEN_DECLINED;
if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "phyp"))
return VIR_DRV_OPEN_DECLINED;
if (conn->uri->server == NULL) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Missing server name in phyp:// URI"));
return VIR_DRV_OPEN_ERROR;
}
if (conn->uri->path == NULL) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Missing path name in phyp:// URI"));
return VIR_DRV_OPEN_ERROR;
}
if (escape_specialcharacters(conn->uri->path, string) == -1) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Error parsing 'path'. Invalid characters."));
return VIR_DRV_OPEN_ERROR;
}
if ((session = openSSHSession(conn, auth)) == NULL) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Error while opening SSH session."));
return VIR_DRV_OPEN_ERROR;
}
conn->uri->path = string;
connection_data->session = session;
connection_data->auth = auth;
uuid_db->nlpars = 0;
uuid_db->lpars = NULL;
conn->privateData = uuid_db;
conn->networkPrivateData = connection_data;
init_uuid_db(conn);
return VIR_DRV_OPEN_SUCCESS;
}
static int
phypClose(virConnectPtr conn)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
ssh_disconnect(ssh_session);
VIR_FREE(connection_data);
return 0;
}
SSH_SESSION *
openSSHSession(virConnectPtr conn, virConnectAuthPtr auth)
{
SSH_SESSION *session;
SSH_OPTIONS *opt;
char *user = conn->uri->user;
char *host = conn->uri->server;
int ssh_auth = 0;
char *banner;
int port = 22;
char *password;
if (conn->uri->port)
port = conn->uri->port;
session = ssh_new();
opt = ssh_options_new();
/*setting some ssh options */
ssh_options_set_host(opt, host);
ssh_options_set_port(opt, port);
ssh_options_set_username(opt, user);
ssh_set_options(session, opt);
/*starting ssh connection */
if (ssh_connect(session)) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR,
NULL, NULL, NULL, 0, 0, "%s",
_("Connection failed."));
ssh_disconnect(session);
ssh_finalize();
goto err;
}
/*trying to use pub key */
if ((ssh_auth =
ssh_userauth_autopubkey(session, NULL)) == SSH_AUTH_ERROR) {
VIR_WARN("%s", "Authentication with public key failed.");
}
if ((banner = ssh_get_issue_banner(session))) {
VIR_INFO("%s", banner);
VIR_FREE(banner);
}
if (ssh_auth != SSH_AUTH_SUCCESS) {
int i;
int hasPassphrase = 0;
int auth_check = 0;
virConnectCredential creds[] = {
{VIR_CRED_PASSPHRASE, "password", "Password", NULL, NULL, 0},
};
if (!auth || !auth->cb) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("No authentication callback provided."));
goto err;
}
for (i = 0; i < auth->ncredtype; i++) {
if (auth->credtype[i] == VIR_CRED_PASSPHRASE)
hasPassphrase = 1;
}
if (!hasPassphrase) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Required credentials are not supported."));
goto err;
}
int res =
(auth->cb) (creds, ARRAY_CARDINALITY(creds), auth->cbdata);
if (res < 0) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Unable to fetch credentials."));
goto err;
}
if (creds[0].result)
password = creds[0].result;
else {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s",
"Unable to get password certificate.",
ssh_get_error(session));
ssh_disconnect(session);
goto err;
}
char *username = user;
auth_check = ssh_userauth_password(session, username, password);
memset(password, 0, strlen(password));
if (auth_check != SSH_AUTH_SUCCESS) {
virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s",
"Authentication failed.",
ssh_get_error(session));
ssh_disconnect(session);
goto err;
} else
goto exit;
} else
goto exit;
err:
return NULL;
exit:
return session;
}
/* this functions is the layer that manipulates the ssh channel itself
* and executes the commands on the remote machine */
static char *
phypExec(SSH_SESSION * session, char *cmd, int *exit_status,
virConnectPtr conn)
{
CHANNEL *channel = channel_new(session);
virBuffer tex_ret = VIR_BUFFER_INITIALIZER;
char buf[4096] = { 0 };
int ret = 0;
if (channel_open_session(channel) == SSH_ERROR) {
virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Unable to open a SSH channel."));
goto err;
}
if (channel_request_exec(channel, cmd) == SSH_ERROR) {
virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Unable to execute remote command."));
goto err;
}
if (channel_send_eof(channel) == SSH_ERROR) {
virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP,
VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s",
_("Unable to send EOF."));
goto err;
}
while (channel && channel_is_open(channel)) {
ret = channel_read(channel, buf, sizeof(buf), 0);
if (ret < 0)
goto err;
if (ret == 0) {
channel_send_eof(channel);
if (channel_get_exit_status(channel) == -1)
goto err;
if (channel_close(channel) == SSH_ERROR)
goto err;
channel_free(channel);
channel = NULL;
goto exit;
}
virBufferAdd(&tex_ret, (const char *) &buf, ret);
}
err:
(*exit_status) = SSH_CMD_ERR;
char *cleanup_buf = virBufferContentAndReset(&tex_ret);
VIR_FREE(cleanup_buf);
return NULL;
exit:
if (virBufferError(&tex_ret)) {
virReportOOMError(conn);
return NULL;
}
return virBufferContentAndReset(&tex_ret);
}
/* return the lpar_id given a name and a managed system name */
static int
phypGetLparID(SSH_SESSION * ssh_session, const char *managed_system,
const char *name, virConnectPtr conn)
{
int exit_status = 0;
int lpar_id = 0;
char *char_ptr;
char *cmd;
if (virAsprintf(&cmd,
"lssyscfg -r lpar -m %s --filter lpar_names=%s -F lpar_id",
managed_system, name) < 0) {
virReportOOMError(conn);
goto err;
}
const char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (exit_status < 0 || tex_ret == NULL)
goto err;
if (virStrToLong_i(tex_ret, &char_ptr, 10, &lpar_id) == -1)
goto err;
VIR_FREE(cmd);
return lpar_id;
err:
VIR_FREE(cmd);
return -1;
}
/* return the lpar name given a lpar_id and a managed system name */
static char *
phypGetLparNAME(SSH_SESSION * ssh_session, const char *managed_system,
unsigned int lpar_id, virConnectPtr conn)
{
char *cmd;
int exit_status = 0;
if (virAsprintf(&cmd,
"lssyscfg -r lpar -m %s --filter lpar_ids=%d -F name",
managed_system, lpar_id) < 0) {
virReportOOMError(conn);
goto err;
}
char *lpar_name = phypExec(ssh_session, cmd, &exit_status, conn);
if (lpar_name == NULL)
goto err;
char *char_ptr = strchr(lpar_name, '\n');
if (char_ptr)
*char_ptr = '\0';
if (exit_status < 0 || lpar_name == NULL)
goto err;
VIR_FREE(cmd);
return lpar_name;
err:
VIR_FREE(cmd);
return NULL;
}
/* Search into the uuid_db for a lpar_uuid given a lpar_id
* and a managed system name
*
* return: 0 - record found
* -1 - not found
* */
int
phypGetLparUUID(unsigned char *uuid, int lpar_id, virConnectPtr conn)
{
uuid_dbPtr uuid_db = conn->privateData;
lparPtr *lpars = uuid_db->lpars;
unsigned int i = 0;
for (i = 0; i < uuid_db->nlpars; i++) {
if (lpars[i]->id == lpar_id) {
memmove(uuid, lpars[i]->uuid, VIR_UUID_BUFLEN);
return 0;
}
}
return -1;
}
/*
* type:
* 0 - maxmem
* 1 - memory
* */
unsigned long
phypGetLparMem(virConnectPtr conn, const char *managed_system, int lpar_id,
int type)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
char *char_ptr;
int memory = 0;
int exit_status = 0;
if (type != 1 && type != 0)
goto err;
if (type) {
if (virAsprintf(&cmd,
"lshwres -m %s -r mem --level lpar -F curr_mem --filter lpar_ids=%d",
managed_system, lpar_id) < 0) {
virReportOOMError(conn);
goto err;
}
} else {
if (virAsprintf(&cmd,
"lshwres -m %s -r mem --level lpar -F curr_max_mem --filter lpar_ids=%d",
managed_system, lpar_id) < 0) {
virReportOOMError(conn);
goto err;
}
}
char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (tex_ret == NULL)
goto err;
char *mem_char_ptr = strchr(tex_ret, '\n');
if (mem_char_ptr)
*mem_char_ptr = '\0';
if (exit_status < 0)
goto err;
if (virStrToLong_i(tex_ret, &char_ptr, 10, &memory) == -1)
goto err;
VIR_FREE(cmd);
return memory;
err:
VIR_FREE(cmd);
return 0;
}
unsigned long
phypGetLparCPU(virConnectPtr conn, const char *managed_system, int lpar_id)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
int exit_status = 0;
int vcpus = 0;
if (virAsprintf(&cmd,
"lshwres -m %s -r proc --level lpar -F curr_procs --filter lpar_ids=%d",
managed_system, lpar_id) < 0) {
virReportOOMError(conn);
goto err;
}
char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (tex_ret == NULL)
goto err;
char *char_ptr = strchr(tex_ret, '\n');
if (char_ptr)
*char_ptr = '\0';
if (virStrToLong_i(tex_ret, &char_ptr, 10, &vcpus) == -1)
goto err;
if (exit_status < 0)
goto err;
VIR_FREE(cmd);
return (unsigned long) vcpus;
err:
VIR_FREE(cmd);
return 0;
}
int
phypGetRemoteSlot(virConnectPtr conn, const char *managed_system,
const char *lpar_name)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
char *char_ptr;
int remote_slot = 0;
int exit_status = 0;
if (virAsprintf(&cmd,
"lshwres -m %s -r virtualio --rsubtype scsi -F remote_slot_num --filter lpar_names=%s",
managed_system, lpar_name) < 0) {
virReportOOMError(conn);
goto err;
}
char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (tex_ret == NULL)
goto err;
char *char_ptr2 = strchr(tex_ret, '\n');
if (char_ptr2)
*char_ptr2 = '\0';
if (exit_status < 0)
goto err;
if (virStrToLong_i(tex_ret, &char_ptr, 10, &remote_slot) == -1)
goto err;
VIR_FREE(cmd);
return remote_slot;
err:
VIR_FREE(cmd);
return 0;
}
char *
phypGetBackingDevice(virConnectPtr conn, const char *managed_system,
char *lpar_name)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
int remote_slot = 0;
int exit_status = 0;
if ((remote_slot =
phypGetRemoteSlot(conn, managed_system, lpar_name)) == 0)
goto err;
if (virAsprintf(&cmd,
"lshwres -m %s -r virtualio --rsubtype scsi -F backing_devices --filter slots=%d",
managed_system, remote_slot) < 0) {
virReportOOMError(conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (ret == NULL)
goto err;
/* here is a little trick to deal returns of this kind:
*
* 0x8100000000000000//lv01
*
* the information we really need is only lv01, so we
* need to skip a lot of things on the string.
* */
char *backing_device = strchr(ret, '/');
if (backing_device) {
backing_device++;
if (backing_device[0] == '/')
backing_device++;
else
goto err;
} else {
backing_device = ret;
}
char *char_ptr = strchr(backing_device, '\n');
if (char_ptr)
*char_ptr = '\0';
if (exit_status < 0 || backing_device == NULL)
goto err;
VIR_FREE(cmd);
return backing_device;
err:
VIR_FREE(cmd);
return NULL;
}
int
phypGetLparState(virConnectPtr conn, unsigned int lpar_id)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
int exit_status = 0;
char *char_ptr = NULL;
char *managed_system = conn->uri->path;
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
if (virAsprintf(&cmd,
"lssyscfg -r lpar -m %s -F state --filter lpar_ids=%d",
managed_system, lpar_id) < 0) {
virReportOOMError(conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (ret == NULL)
goto err;
char_ptr = strchr(ret, '\n');
if (char_ptr)
*char_ptr = '\0';
if (exit_status < 0 || ret == NULL)
goto err;
VIR_FREE(cmd);
if (STREQ(ret, "Running"))
return VIR_DOMAIN_RUNNING;
else if (STREQ(ret, "Not Activated"))
return VIR_DOMAIN_SHUTOFF;
else if (STREQ(ret, "Shutting Down"))
return VIR_DOMAIN_SHUTDOWN;
else
goto err;
err:
VIR_FREE(cmd);
return VIR_DOMAIN_NOSTATE;
}
int
phypDiskType(virConnectPtr conn, char *backing_device)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *cmd;
int exit_status = 0;
if (virAsprintf(&cmd,
"ioscli lssp -field name type -fmt , -all|grep %s|sed -e 's/^.*,//g'",
backing_device) < 0) {
virReportOOMError(conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (ret == NULL)
goto err;
char *char_ptr = strchr(ret, '\n');
if (char_ptr)
*char_ptr = '\0';
if (exit_status < 0 || ret == NULL)
goto err;
VIR_FREE(cmd);
if (STREQ(ret, "LVPOOL"))
return VIR_DOMAIN_DISK_TYPE_BLOCK;
else if (STREQ(ret, "FBPOOL"))
return VIR_DOMAIN_DISK_TYPE_FILE;
else
goto err;
err:
VIR_FREE(cmd);
return -1;
}
/* This is a generic function that won't be used directly by
* libvirt api. The function returns the number of domains
* in different states: Running, Not Activated and all:
*
* type: 0 - Running
* 1 - Not Activated
* * - All
* */
static int
phypNumDomainsGeneric(virConnectPtr conn, unsigned int type)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
int exit_status = 0;
int ndom = 0;
char *char_ptr;
char *cmd;
char *managed_system = conn->uri->path;
const char *state;
if (type == 0)
state = "|grep Running";
else if (type == 1)
state = "|grep \"Not Activated\"";
else
state = " ";
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
if (virAsprintf(&cmd,
"lssyscfg -r lpar -m %s -F lpar_id,state %s |grep -c ^[0-9]*",
managed_system, state) < 0) {
virReportOOMError(conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (exit_status < 0 || ret == NULL)
goto err;
if (virStrToLong_i(ret, &char_ptr, 10, &ndom) == -1)
goto err;
VIR_FREE(cmd);
return ndom;
err:
VIR_FREE(cmd);
return 0;
}
static int
phypNumDefinedDomains(virConnectPtr conn)
{
return phypNumDomainsGeneric(conn, 1);
}
static int
phypNumDomains(virConnectPtr conn)
{
return phypNumDomainsGeneric(conn, 0);
}
/* This is a generic function that won't be used directly by
* libvirt api. The function returns the ids of domains
* in different states: Running, and all:
*
* type: 0 - Running
* * - all
* */
static int
phypListDomainsGeneric(virConnectPtr conn, int *ids, int nids,
unsigned int type)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *managed_system = conn->uri->path;
int exit_status = 0;
int got = 0;
char *char_ptr;
unsigned int i = 0, j = 0;
char id_c[10];
char *cmd;
const char *state;
if (type == 0)
state = "|grep Running";
else
state = " ";
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
memset(id_c, 0, 10);
if (virAsprintf
(&cmd,
"lssyscfg -r lpar -m %s -F lpar_id,state %s | sed -e 's/,.*$//g'",
managed_system, state) < 0) {
virReportOOMError(conn);
goto err;
}
char *domains = phypExec(ssh_session, cmd, &exit_status, conn);
/* I need to parse the textual return in order to get the domains */
if (exit_status < 0 || domains == NULL || got == 0)
goto err;
else {
while (got < nids) {
if (domains[i] == '\n') {
if (virStrToLong_i(id_c, &char_ptr, 10, &ids[got]) == -1)
return 0;
memset(id_c, 0, 10);
j = 0;
got++;
} else {
id_c[j] = domains[i];
j++;
}
i++;
}
}
VIR_FREE(cmd);
return got;
err:
VIR_FREE(cmd);
return 0;
}
static int
phypListDomains(virConnectPtr conn, int *ids, int nids)
{
return phypListDomainsGeneric(conn, ids, nids, 0);
}
static int
phypListDefinedDomains(virConnectPtr conn, char **const names, int nnames)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *managed_system = conn->uri->path;
int exit_status = 0;
int got = 0;
char *char_ptr = NULL;
char *cmd;
char *domains;
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
if (virAsprintf
(&cmd,
"lssyscfg -r lpar -m %s -F name,state | grep \"Not Activated\" | sed -e 's/,.*$//g'",
managed_system) < 0) {
virReportOOMError(conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, conn);
if (VIR_ALLOC(domains) < 0)
virReportOOMError(conn);
domains = strdup(ret);
if (!domains)
goto err;
char *char_ptr2 = NULL;
/* I need to parse the textual return in order to get the domains */
if (exit_status < 0 || domains == NULL)
goto err;
else {
while (got < nnames) {
char_ptr2 = strchr(domains, '\n');
if (char_ptr2) {
*char_ptr2 = '\0';
if (!strdup(domains))
goto err;
names[got] = strdup(domains);
char_ptr2++;
domains = char_ptr2;
got++;
}
}
}
VIR_FREE(domains);
VIR_FREE(cmd);
VIR_FREE(ret);
return got;
err:
VIR_FREE(domains);
VIR_FREE(ret);
return 0;
}
static virDomainPtr
phypDomainLookupByName(virConnectPtr conn, const char *lpar_name)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
virDomainPtr dom = NULL;
int lpar_id = 0;
char *managed_system = conn->uri->path;
unsigned char *lpar_uuid = NULL;
if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0)
virReportOOMError(dom->conn);
/* need to shift one byte in order to remove the first "/" of uri component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char *char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
lpar_id = phypGetLparID(ssh_session, managed_system, lpar_name, conn);
if (lpar_id < 0)
goto err;
if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1)
goto err;
dom = virGetDomain(conn, lpar_name, lpar_uuid);
if (dom)
dom->id = lpar_id;
VIR_FREE(lpar_uuid);
return dom;
err:
VIR_FREE(lpar_uuid);
return NULL;
}
static virDomainPtr
phypDomainLookupByID(virConnectPtr conn, int lpar_id)
{
ConnectionData *connection_data = conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
virDomainPtr dom = NULL;
char *managed_system = conn->uri->path;
int exit_status = 0;
unsigned char *lpar_uuid = NULL;
if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0)
virReportOOMError(dom->conn);
/* need to shift one byte in order to remove the first "/" of uri component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char *char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
char *lpar_name = phypGetLparNAME(ssh_session, managed_system, lpar_id,
conn);
if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1)
goto err;
if (exit_status < 0)
goto err;
dom = virGetDomain(conn, lpar_name, lpar_uuid);
if (dom)
dom->id = lpar_id;
VIR_FREE(lpar_name);
VIR_FREE(lpar_uuid);
return dom;
err:
VIR_FREE(lpar_name);
VIR_FREE(lpar_uuid);
return NULL;
}
static char *
phypDomainDumpXML(virDomainPtr dom, int flags)
{
ConnectionData *connection_data = dom->conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
virDomainDefPtr def = NULL;
char *ret = NULL;
char *managed_system = dom->conn->uri->path;
unsigned char *lpar_uuid = NULL;
if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0)
virReportOOMError(dom->conn);
if (VIR_ALLOC(def) < 0)
virReportOOMError(dom->conn);
/* need to shift one byte in order to remove the first "/" of uri component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char *char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
def->virtType = VIR_DOMAIN_VIRT_PHYP;
def->id = dom->id;
char *lpar_name = phypGetLparNAME(ssh_session, managed_system, def->id,
dom->conn);
if (lpar_name == NULL) {
VIR_ERROR("%s", "Unable to determine domain's name.");
goto err;
}
if (phypGetLparUUID(lpar_uuid, dom->id, dom->conn) == -1) {
VIR_ERROR("%s", "Unable to generate random uuid.");
goto err;
}
if (!memcpy(def->uuid, lpar_uuid, VIR_UUID_BUFLEN)) {
VIR_ERROR("%s", "Unable to generate random uuid.");
goto err;
}
if ((def->maxmem =
phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) {
VIR_ERROR("%s", "Unable to determine domain's max memory.");
goto err;
}
if ((def->memory =
phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) {
VIR_ERROR("%s", "Unable to determine domain's memory.");
goto err;
}
if ((def->vcpus =
phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) {
VIR_ERROR("%s", "Unable to determine domain's CPU.");
goto err;
}
ret = virDomainDefFormat(dom->conn, def, flags);
err:
VIR_FREE(def);
return ret;
}
static int
phypDomainResume(virDomainPtr dom)
{
ConnectionData *connection_data = dom->conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *managed_system = dom->conn->uri->path;
int exit_status = 0;
char *char_ptr = NULL;
char *cmd;
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
if (virAsprintf
(&cmd,
"chsysstate -m %s -r lpar -o on --id %d -f %s",
managed_system, dom->id, dom->name) < 0) {
virReportOOMError(dom->conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, dom->conn);
err:
VIR_FREE(cmd);
VIR_FREE(ret);
return 0;
}
static int
phypDomainShutdown(virDomainPtr dom)
{
ConnectionData *connection_data = dom->conn->networkPrivateData;
SSH_SESSION *ssh_session = connection_data->session;
char *managed_system = dom->conn->uri->path;
int exit_status = 0;
char *char_ptr = NULL;
char *cmd;
/* need to shift one byte in order to remove the first "/" of URI component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
if (virAsprintf
(&cmd,
"chsysstate -m %s -r lpar -o shutdown --id %d",
managed_system, dom->id) < 0) {
virReportOOMError(dom->conn);
goto err;
}
char *ret = phypExec(ssh_session, cmd, &exit_status, dom->conn);
err:
VIR_FREE(cmd);
VIR_FREE(ret);
return 0;
}
static int
phypDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info)
{
char *managed_system = dom->conn->uri->path;
/* need to shift one byte in order to remove the first "/" of uri component */
if (managed_system[0] == '/')
managed_system++;
/* here we are handling only the first component of the path,
* so skipping the second:
* */
char *char_ptr = strchr(managed_system, '/');
if (char_ptr)
*char_ptr = '\0';
info->state = phypGetLparState(dom->conn, dom->id);
if ((info->maxMem =
phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0)
VIR_WARN("%s", "Unable to determine domain's max memory.");
if ((info->memory =
phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0)
VIR_WARN("%s", "Unable to determine domain's memory.");
if ((info->nrVirtCpu =
phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0)
VIR_WARN("%s", "Unable to determine domain's CPU.");
return 0;
}
virDriver phypDriver = {
VIR_DRV_PHYP,
"PHYP",
phypOpen, /* open */
phypClose, /* close */
NULL, /* supports_feature */
NULL, /* type */
NULL, /* version */
NULL, /* getHostname */
NULL, /* getMaxVcpus */
NULL, /* nodeGetInfo */
NULL, /* getCapabilities */
phypListDomains, /* listDomains */
phypNumDomains, /* numOfDomains */
NULL, /* domainCreateXML */
phypDomainLookupByID, /* domainLookupByID */
NULL, /* domainLookupByUUID */
phypDomainLookupByName, /* domainLookupByName */
NULL, /* domainSuspend */
phypDomainResume, /* domainResume */
phypDomainShutdown, /* domainShutdown */
NULL, /* domainReboot */
NULL, /* domainDestroy */
NULL, /* domainGetOSType */
NULL, /* domainGetMaxMemory */
NULL, /* domainSetMaxMemory */
NULL, /* domainSetMemory */
phypDomainGetInfo, /* domainGetInfo */
NULL, /* domainSave */
NULL, /* domainRestore */
NULL, /* domainCoreDump */
NULL, /* domainSetVcpus */
NULL, /* domainPinVcpu */
NULL, /* domainGetVcpus */
NULL, /* domainGetMaxVcpus */
NULL, /* domainGetSecurityLabel */
NULL, /* nodeGetSecurityModel */
phypDomainDumpXML, /* domainDumpXML */
NULL, /* domainXmlFromNative */
NULL, /* domainXmlToNative */
phypListDefinedDomains, /* listDefinedDomains */
phypNumDefinedDomains, /* numOfDefinedDomains */
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
NULL, /* domainAttachDevice */
NULL, /* domainDetachDevice */
NULL, /* domainGetAutostart */
NULL, /* domainSetAutostart */
NULL, /* domainGetSchedulerType */
NULL, /* domainGetSchedulerParameters */
NULL, /* domainSetSchedulerParameters */
NULL, /* domainMigratePrepare */
NULL, /* domainMigratePerform */
NULL, /* domainMigrateFinish */
NULL, /* domainBlockStats */
NULL, /* domainInterfaceStats */
NULL, /* domainBlockPeek */
NULL, /* domainMemoryPeek */
NULL, /* nodeGetCellsFreeMemory */
NULL, /* getFreeMemory */
NULL, /* domainEventRegister */
NULL, /* domainEventDeregister */
NULL, /* domainMigratePrepare2 */
NULL, /* domainMigrateFinish2 */
NULL, /* nodeDeviceDettach */
NULL, /* nodeDeviceReAttach */
NULL, /* nodeDeviceReset */
};
int
phypRegister(void)
{
virRegisterDriver(&phypDriver);
return 0;
}
void
init_uuid_db(virConnectPtr conn)
{
uuid_dbPtr uuid_db;
int nids = 0;
int *ids = NULL;
unsigned int i = 0;
if ((nids = phypNumDomainsGeneric(conn, 2)) == 0)
goto exit;
if (VIR_ALLOC_N(ids, nids) < 0)
virReportOOMError(conn);
if (VIR_ALLOC(uuid_db) < 0)
virReportOOMError(conn);
if (phypListDomainsGeneric(conn, ids, nids, 1) == 0)
goto exit;
uuid_db = conn->privateData;
uuid_db->nlpars = nids;
if (VIR_ALLOC_N(uuid_db->lpars, uuid_db->nlpars) >= 0) {
for (i = 0; i < uuid_db->nlpars; i++) {
if (VIR_ALLOC(uuid_db->lpars[i]) < 0)
virReportOOMError(conn);
uuid_db->lpars[i]->id = ids[i];
if (virUUIDGenerate(uuid_db->lpars[i]->uuid) < 0)
VIR_WARN("%s %d", "Unable to generate UUID for domain",
ids[i]);
}
}
exit:
VIR_FREE(ids);
return;
}
int
escape_specialcharacters(char *src, char *dst)
{
size_t len = strlen(src);
char temp_buffer[len];
unsigned int i = 0, j = 0;
if (len == 0)
return -1;
for (i = 0; i < len; i++) {
switch (src[i]) {
case '&': case ';': case '`': case '@':
case '"': case '|': case '*': case '?':
case '~': case '<': case '>': case '^':
case '(': case ')': case '[': case ']':
case '{': case '}': case '$': case '%':
case '#': case '\\': case '\n': case '\r':
case '\t':
continue;
default:
temp_buffer[j] = src[i];
j++;
}
}
temp_buffer[j] = '\0';
if (strncpy(dst, temp_buffer, j) == NULL)
return -1;
return 0;
}
#include <config.h>
#include <libssh/libssh.h>
#define LPAR_EXEC_ERR -1
#define SSH_CONN_ERR -2 /* error while trying to connect to remote host */
#define SSH_CMD_ERR -3 /* error while trying to execute the remote cmd */
typedef struct _ConnectionData ConnectionData;
typedef ConnectionData *ConnectionDataPtr;
struct _ConnectionData {
SSH_SESSION *session;
virConnectAuthPtr auth;
};
/* This is the lpar (domain) struct that relates
* the ID with UUID generated by the API
* */
typedef struct _lpar lpar_t;
typedef lpar_t *lparPtr;
struct _lpar {
unsigned char uuid[VIR_UUID_BUFLEN];
int id;
};
/* Struct that holds how many lpars (domains) we're
* handling and a pointer to an array of lpar structs
* */
typedef struct _uuid_db uuid_db_t;
typedef uuid_db_t *uuid_dbPtr;
struct _uuid_db {
int nlpars;
lparPtr *lpars;
};
int phypGetLparUUID(unsigned char *uuid, int lpar_id, virConnectPtr conn);
void init_uuid_db(virConnectPtr conn);
int phypRegister(void);
void stripPath(char *striped_path, char *path);
void stripNewline(char *striped_string, char *string);
int buffer_add_u8(struct buffer_struct *buffer, u8 data);
int phypGetLparState(virConnectPtr conn, unsigned int lpar_id);
unsigned long phypGetLparMem(virConnectPtr conn,
const char *managed_system, int lpar_id,
int type);
unsigned long phypGetLparCPU(virConnectPtr conn,
const char *managed_system, int lpar_id);
int phypGetRemoteSlot(virConnectPtr conn, const char *managed_system,
const char *lpar_name);
char *phypGetBackingDevice(virConnectPtr conn, const char *managed_system,
char *lpar_name);
int phypDiskType(virConnectPtr conn, char *backing_device);
SSH_SESSION *openSSHSession(virConnectPtr conn, virConnectAuthPtr auth);
int escape_specialcharacters(char *src, char *dst);
......@@ -124,6 +124,9 @@ static const char *virErrorDomainName(virErrorDomain domain) {
case VIR_FROM_CONF:
dom = "Config ";
break;
case VIR_FROM_PHYP:
dom = "IBM power hypervisor ";
break;
case VIR_FROM_OPENVZ:
dom = "OpenVZ ";
break;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册