Hi guys, I'm working on a pre migration checks implementation. The patch files attached provide a framework to execute pre-migration-checks before the domain migration. ================================================================ 1 - FRAMEWORK OVERVIEW ================================================================ Pre-Migration-Checks are enabled by adding a "--pmc" option in "virsh migrate" command $ virsh migrate --pmc ... In this way, before the execution of function virDomainMigrate(), an array of "checks" are performed. Return value of every check set if the migration will continue or not. ================================================================ 2 - FRAMEWORK STRUCTURE ================================================================ Every check is defined in the file libvirt/src/util/pmc.c Check list is defined by object "chklist". This is a struct defined as static virPMCCheckList chklist = { .count = 2, .check = {&chk1,&chk2,NULL} }; where "count" define the current array size, and "check" is an array of pointer to virPMCCheck object. virPMCCheck is a stuct defined as struct _virPMCCheck { const char *name; virPMCCheckRun run; }; where "name" is the name of the check needed for debugging messages, and "run" is a pointer to check function. ================================================================ 3 - HOW TO ADD NEW CHECK ================================================================ Adding new check is very simple! You have to define a check funcion as static int pmcCheckFooCheck(virDomainPtr domain, virConnectPtr dconn) { /* do something */ return CHECK_SUCCESS } then you have to define a "check-hook" as static virPMCCheck chk3 = { .name = "this is fooCheck", .run = pmcCheckFooCheck }; and update the object "chklist" static virPMCCheckList chklist = { .count = 3, /* previous value was 2 */ .check = {&chk1,&chk2,&chk3,NULL} /* previous content not * include a pointer to * chk3 object */ }; ================================================================ 4 - POSSIBLE ENHANCHEMENT ================================================================ 1. Adding infos in _virPMCCheck struct about check security level. Example: if a check with level CRITICAL return CHECK_FAIL, migration process is stopped, 2. Implementing framework not as util, but as driver. For example this can permit to define new checks by xml file. 3. Define external checks loadable by shared library. ================================================================ 5 - PATCH FILE DESCRIPTION ================================================================ file: datatypes.h.patch desc: define new type virPMCCheckRun define struct _virPMCCheck define struct _virPMCCheckList file: libvirt.c.patch desc: impement public API virDomainMigratePMC() file: libvirt.h.in.patch desc: impement public API virDomainMigratePMC() prototype define type virPMCCheck define type virPMCCheckPtr define type virPMCCheckList define type virPMCCheckListPtr define macro PMC_LIST_SIZE file: libvirt_public.syms desc: export public API virDomainMigratePMC() file: pmc.c.patch, pmc.h.patch desc: define and implements pre migration checks and some auxiliary functions. file: virsh.c.patch desc: add option "pmc" to virsh migrate command ================================================================ 6 - NOTES ================================================================ I start to implement this framework on git sources, but the latest sources produce a regress on my code. For this reason I applied my code on latest stable release of libvirt (0.8.1). What do you think about that? Is this a good approach to implement pre migration checks? Have you a suggestions? There is a possibility to include this patch in future libvirt distributions? Waiting for feedback... Paolo Smiraglia -- PAOLO SMIRAGLIA http://portale.isf.polito.it/paolo-smiraglia
diff --git a/libvirt/src/datatypes.h b/libvirt-pmc/src/datatypes.h index bbeb7cf..1510bc2 100644 --- a/libvirt/src/datatypes.h +++ b/libvirt-pmc/src/datatypes.h @@ -393,4 +393,22 @@ virDomainSnapshotPtr virGetDomainSnapshot(virDomainPtr domain, const char *name); int virUnrefDomainSnapshot(virDomainSnapshotPtr pool); +/** + * PMC + */ + +typedef int (*virPMCCheckRun) (virDomainPtr,virConnectPtr); + +struct _virPMCCheck +{ + const char *name; + virPMCCheckRun run; +}; + +struct _virPMCCheckList +{ + unsigned int count; + virPMCCheckPtr check[PMC_LIST_SIZE]; +}; + #endif
diff --git a/libvirt/src/libvirt.c b/libvirt-pmc/src/libvirt.c index 028115c..cce2060 100644 --- a/libvirt/src/libvirt.c +++ b/libvirt-pmc/src/libvirt.c @@ -42,6 +42,8 @@ #include "util.h" #include "memory.h" +#include "pmc.h" + #ifndef WITH_DRIVER_MODULES # ifdef WITH_TEST # include "test/test_driver.h" @@ -12779,3 +12781,112 @@ virDomainSnapshotFree(virDomainSnapshotPtr snapshot) } return 0; } + +/** + * virDomainMigratePMC: + * + * @domain: a domain object + * @dconn: destination host (a connection object) + * @flags: flags + * @dname: (optional) rename domain to this at destination + * @uri: (optional) dest hostname/URI as seen from the source host + * @bandwidth: (optional) specify migration bandwidth limit in Mbps + * + * Function load an object wich contains a pre migration checks list. + * Every pre migration check present in the list is executed, and return + * value is checked: + * + * ~ if check return CHECK_SUCCESS, check list scanning continue + * ~ if check return CHECK_WARNING, function shows to user a menu' + * where it can select the action (TERMINATE, CONTINUE) + * ~ if check return CHECK_FAIL, function return value -1 and + * migration process is terminated + * + */ + +int +virDomainMigratePMC(virDomainPtr domain, + virConnectPtr dconn, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + virConnectPtr conn = NULL; + virPMCCheckPtr check = NULL; + virPMCCheckListPtr chklist = NULL; + int i = -1; + int chkret = -1; + char sel = 0; + + DEBUG("domain=%p, dconn=%p, flags=%lu, dname=%s, uri=%s, bandwidth=%lu\n", + domain, dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if ( VIR_ALLOC(chklist) < 0 ) + { + virReportOOMError(); + return -1; + } + + /* load internal pre migration check list */ + + chklist = virPMCGetList(); + if ( !chklist ) + { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + /* execute all checks defined into the list */ + + for ( i = 0 ; i < chklist->count ; i++ ) + { + check = chklist->check[i]; + if( check != NULL ) + { + printf("Running check - %s\n",check->name); + + chkret = check->run(domain,dconn); + + switch(chkret) + { + case CHECK_SUCCESS: + printf("\nCheck - %s: SUCCESS!\n",check->name); + break; + case CHECK_FAIL: + printf("\nCheck - %s: FAIL!\n",check->name); + goto check_fail; + break; + case CHECK_WARNING: + printf("\nCheck - %s: WARNING!\n",check->name); + if( (sel = warningMenu()) == TERMINATE ) + goto check_fail; + break; + } + } + } + + return 0; + +check_fail: + return -1; + +error: + virDispatchError(conn); + return -1; +}
diff --git a/libvirt/include/libvirt/libvirt.h.in b/libvirt-pmc/include/libvirt/libvirt.h.in index f296d16..eb7cb99 100644 --- a/libvirt/include/libvirt/libvirt.h.in +++ b/libvirt-pmc/include/libvirt/libvirt.h.in @@ -426,6 +426,11 @@ int virDomainMigrateSetMaxDowntime (virDomainPtr domain, unsigned long long downtime, unsigned int flags); +/*pmc*/ +int virDomainMigratePMC (virDomainPtr domain, virConnectPtr dconn, + unsigned long flags, const char *dname, + const char *uri, unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance @@ -2272,4 +2277,15 @@ int virNWFilterGetUUIDString (virNWFilterPtr nwfilter, char * virNWFilterGetXMLDesc (virNWFilterPtr nwfilter, int flags); +/* + * PMC + */ +#define PMC_LIST_SIZE 128 + +typedef struct _virPMCCheck virPMCCheck; +typedef virPMCCheck *virPMCCheckPtr; + +typedef struct _virPMCCheckList virPMCCheckList; +typedef virPMCCheckList *virPMCCheckListPtr; + #endif /* __VIR_VIRLIB_H__ */
diff --git a/libvirt/src/libvirt_public.syms b/libvirt-pmc/src/libvirt_public.syms index 81465d3..719f504 100644 --- a/libvirt/src/libvirt_public.syms +++ b/libvirt-pmc/src/libvirt_public.syms @@ -397,6 +397,7 @@ LIBVIRT_0.8.0 { LIBVIRT_0.8.1 { global: virDomainGetBlockInfo; + virDomainMigratePMC; } LIBVIRT_0.8.0; # .... define new API here using predicted next version number ....
diff --git a/libvirt-pmc/src/util/pmc.c b/libvirt-pmc/src/util/pmc.c new file mode 100644 index 0000000..a279197 --- /dev/null +++ b/libvirt-pmc/src/util/pmc.c @@ -0,0 +1,280 @@ +#include <config.h> +#include <stdio.h> + +#include "pmc.h" +#include "datatypes.h" +#include "virterror_internal.h" +#include "internal.h" +#include "util.h" +#include "logging.h" + +#define MAX_NETWORK_LIST 128 + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virLibPMCError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +/*-------------------- various functions --------------------*/ + +/** + * splitVersion + * + * Convert (unsigned long) version provided by libvirtVersion() in array + * where: + * array[0] = major number + * array[1] = minor number + * array[2] = release + */ +static void +splitVersion(unsigned long ver, unsigned int *split) +{ + int maj = ver/1000000; + int min = (ver - maj*1000000)/1000; + int rel = ver - maj*1000000 - min*1000; + split[0] = maj; + split[1] = min; + split[2] = rel; + return; +} + +/** + * warningMenu + * + * Print a menù to select the action when check return value + * CHECK_WARNING. The availables choise are: + * T - terminate migrationt process + * C - continue (at your own risk) migration process + */ +int +warningMenu(void) +{ + char sel = 0; + int retval = TERMINATE; + + printf("C - continue (at your own risk) migration process\n"); + printf("T - terminate migration process\n"); + printf("Action [default: T]: "); + + sel = getchar(); + + switch(sel) + { + case 't': + case 'T': + break; + case 'c': + case 'C': + retval = CONTINUE; + break; + default: + printf("Wrong selectopn! Migration will be terminated.\n"); + retval = TERMINATE; + break; + } + + return retval; +} + +/*-------------------- checks implementation --------------------*/ + +/** + * pmcCheckVersion + * + * Perform the comparison between local and remote libvirt version. + * + * If libvirt version are not equals, check return CHECK_WARNING, else + * check return CHECK_SUCCESS. + * + * The defined comparison algorithm is only a draft! It can be improved + * using a compatibility table or similar. + */ + +static int +pmcCheckVersion(virDomainPtr domain, + virConnectPtr dconn) +{ + virConnectPtr conn = NULL; + unsigned long localLibVersion = 0; + + unsigned long remoteLibVersion = 0; + + int res = -1; + unsigned int lver[3] = {0}; + unsigned int rver[3] = {0}; + + virResetLastError(); + + /* check params */ + if (domain == NULL || dconn == NULL) { + virLibPMCError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + /* check domain */ + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { + virLibPMCError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibPMCError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + /* check remote connection */ + if (!VIR_IS_CONNECT(dconn)) { + virLibPMCError(VIR_ERR_INVALID_CONN, __FUNCTION__); + goto error; + } + if (dconn->flags & VIR_CONNECT_RO) { + virLibPMCError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + /* get local infos */ + + res = conn->driver->libvirtVersion(conn,&localLibVersion); + if (res < 0) + { + virLibPMCError(VIR_ERR_INTERNAL_ERROR,__FUNCTION__); + goto error; + } + + + /* get remote infos */ + + res = dconn->driver->libvirtVersion(dconn,&remoteLibVersion); + if (res < 0) + { + virLibPMCError(VIR_ERR_INTERNAL_ERROR,__FUNCTION__); + goto error; + } + + /* check */ + + if( localLibVersion == remoteLibVersion ) + return CHECK_SUCCESS; + else + { + splitVersion(localLibVersion,lver); + splitVersion(remoteLibVersion,rver); + printf(" WARNING: local libvirt version (%d.%d.%d) is not equal to remote libvirt version (%d.%d.%d)\n", + lver[0],lver[1],lver[2], + rver[0],rver[1],rver[2]); + return CHECK_WARNING; + } + +error: + virDispatchError(domain->conn); + return INTERNAL_ERROR; +} + +/** + * pmcCheckHypervisorType + * + * Perform the comparison between hypervisor type. Check return value + * CHECK_SUCCESS if local and remote hypervisor type are equals, else + * CHECK_FAIL. + * + * TODO + * Check can be improved by adding the comparison between + * hypervisor versions if local and remote type is the same. + */ +static int +pmcCheckHypervisorType(virDomainPtr domain, + virConnectPtr dconn) +{ + virConnectPtr conn = NULL; + const char *localHypervisor = NULL; + const char *remoteHypervisor = NULL; + + virResetLastError(); + + /* check params */ + if (domain == NULL || dconn == NULL) { + virLibPMCError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + /* check domain */ + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { + virLibPMCError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibPMCError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + /* check remote connection */ + if (!VIR_IS_CONNECT(dconn)) { + virLibPMCError(VIR_ERR_INVALID_CONN, __FUNCTION__); + goto error; + } + + if (dconn->flags & VIR_CONNECT_RO) { + virLibPMCError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + /* get local hypervisor type */ + if (conn->driver->name) + localHypervisor = conn->driver->name; + else + { + virLibPMCError(VIR_ERR_INTERNAL_ERROR,__FUNCTION__); + goto error; + } + + /* get remote hypervisor type */ + if (dconn->driver->name) + remoteHypervisor = dconn->driver->name; + else + { + virLibPMCError(VIR_ERR_INTERNAL_ERROR,__FUNCTION__); + goto error; + } + + /* check */ + + if( STRNEQ(localHypervisor,remoteHypervisor) ) + { + printf(" ERROR: local hypervisor (%s) is not equal to remote hypervisor (%s)\n", + localHypervisor,remoteHypervisor); + return CHECK_FAIL; + } + + + return CHECK_SUCCESS; + +error: + virDispatchError(domain->conn); + return INTERNAL_ERROR; +} + +/*--------------- pre migration check interface ---------------*/ + +static virPMCCheck chk1 = { + .name = "compare libvirt version", + .run = pmcCheckVersion +}; + +static virPMCCheck chk2 = { + .name = "compare hypervisor type", + .run = pmcCheckHypervisorType +}; + +static virPMCCheckList chklist = { + .count = 2, + .check = {&chk1,&chk2,NULL} +}; + +virPMCCheckListPtr virPMCGetList(void) +{ + return &chklist; +}
diff --git a/libvirt-pmc/src/util/pmc.h b/libvirt-pmc/src/util/pmc.h new file mode 100644 index 0000000..cff48cc --- /dev/null +++ b/libvirt-pmc/src/util/pmc.h @@ -0,0 +1,18 @@ +#ifndef PMC_H +#define PMC_H + +#include "internal.h" + +#define CHECK_SUCCESS 0 +#define CHECK_FAIL 1 +#define CHECK_WARNING 2 + +#define TERMINATE 3 +#define CONTINUE 4 + +#define INTERNAL_ERROR -1 + +virPMCCheckListPtr virPMCGetList(void); +int warningMenu(void); + +#endif /* PMC_H */
diff --git a/libvirt/tools/virsh.c b/libvirt-pmc/tools/virsh.c index eb11a78..9f473b9 100644 --- a/libvirt/tools/virsh.c +++ b/libvirt-pmc/tools/virsh.c @@ -2844,6 +2844,7 @@ static const vshCmdInfo info_migrate[] = { }; static const vshCmdOptDef opts_migrate[] = { + {"pmc", VSH_OT_BOOL, 0, N_("enable pre migration checks")}, {"live", VSH_OT_BOOL, 0, N_("live migration")}, {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")}, {"direct", VSH_OT_BOOL, 0, N_("direct migration")}, @@ -2866,6 +2867,14 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) const char *migrateuri; const char *dname; int flags = 0, found, ret = FALSE; + + /** + * pre migration checks + */ + int enable_checks = FALSE; + virConnectPtr dconn = NULL; + /** + */ if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) return FALSE; @@ -2882,6 +2891,10 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) migrateuri = vshCommandOptString (cmd, "migrateuri", NULL); dname = vshCommandOptString (cmd, "dname", NULL); + + /* checking for pre migration check activation */ + if (vshCommandOptBool(cmd,"pmc")) + enable_checks = TRUE; if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; @@ -2912,11 +2925,30 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) ret = TRUE; } else { /* For traditional live migration, connect to the destination host directly. */ - virConnectPtr dconn = NULL; + //virConnectPtr dconn = NULL; //moved to line 2875 virDomainPtr ddom = NULL; dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); if (!dconn) goto done; + + /** + * Before migration if enable_checks variable assume value TRUE + * pre migration checks are executed + */ + + if( enable_checks == TRUE ) + { + int checkres = -1; + + checkres = virDomainMigratePMC(dom,dconn,flags, + dname,migrateuri,0); + if( checkres < 0 ) + { + printf("Pre migration checks fails: migration process will be terminated.\n"); + ret = TRUE; + goto check_fail; + } + } ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); if (ddom) { @@ -2924,7 +2956,11 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) ret = TRUE; } virConnectClose (dconn); + goto done; } + + check_fail: + virConnectClose (dconn); done: if (dom) virDomainFree (dom);
-- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list