From eblima at linux.vnet.ibm.com Fri Mar 2 17:45:20 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Fri, 02 Mar 2012 14:45:20 -0300 Subject: [Libvirt-cim] [PATCH 2/2] ComputerSystem: Reboot/Shutdown state changes as jobs In-Reply-To: <1329767001-23314-3-git-send-email-eblima@linux.vnet.ibm.com> References: <1329767001-23314-1-git-send-email-eblima@linux.vnet.ibm.com> <1329767001-23314-3-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <4F510730.7090907@linux.vnet.ibm.com> On 02/20/2012 05:43 PM, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > For Reboot and Shutdown, th RequestStateChange method returns immediately with > return code 0 (successful) even though the state change is still not completed. > > According to the DMTF specification DSP1052 (Computer System Profile) the > RequestStateChange() method should return 0x1000 and a corresponding job > reference in the return parameters which can be polled for completion. > > Signed-off-by: Eduardo Lima (Etrunko) > --- > schema/ComputerSystem.mof | 9 ++ > src/Virt_ComputerSystem.c | 300 +++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 299 insertions(+), 10 deletions(-) > > diff --git a/schema/ComputerSystem.mof b/schema/ComputerSystem.mof > index 10cb8c4..886c085 100644 > --- a/schema/ComputerSystem.mof > +++ b/schema/ComputerSystem.mof > @@ -1,5 +1,14 @@ > // Copyright IBM Corp. 2007 > > +class Xen_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > +class KVM_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > +class LXC_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > [Description ( > "A class derived from CIM_ComputerSystem to represent " > "the Xen virtual machines/domains running on the system."), > diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c > index e6c7e55..dee4ef7 100644 > --- a/src/Virt_ComputerSystem.c > +++ b/src/Virt_ComputerSystem.c [snip] > + > +static CMPI_THREAD_RETURN state_change_thread(void *data) > +{ > + CMPIStatus s; > + CMPIInstance *inst = NULL; > + state_change_job_t *job = (state_change_job_t *) data; > + virConnectPtr conn = NULL; > + virDomainPtr dom = NULL; > + virDomainInfo info; > + int job_cb = -1; > + > + /* Set job state */ > + CBAttachThread(_BROKER, job->context); > + CU_DEBUG("State change job %s started", job->uuid); > + job->status = CIM_JOB_STATE_RUNNING; > + > + inst = CBGetInstance(_BROKER, job->context, job->obj_path, NULL, &s); > + if ((inst == NULL) || (s.rc != CMPI_RC_OK)) { > + CU_DEBUG("Failed to get job instance (%i)", s.rc); > + return NULL; > + } > + > + CMSetProperty(inst, "JobState", > + (CMPIValue *)&(job->status), CMPI_uint16); > + CMSetProperty(inst, "Status", > + (CMPIValue *) "Running", CMPI_chars); > + > + /* Connect to domain event callback */ > + conn = connect_by_classname(_BROKER, CLASSNAME(job->obj_path), &s); > + if (conn == NULL) { > + CU_DEBUG("Unable to connect to '%s' hypervisor", > + CLASSNAME(job->obj_path)); > + goto out; > + } > + > + dom = virDomainLookupByName(conn, job->dom_name); > + if (dom == NULL) { > + CU_DEBUG("Unable to get domain '%s'", job->dom_name); > + goto out; > + } > + > + if (virDomainGetInfo(dom, &info) != 0) { > + CU_DEBUG("Unable to get domain info for '%s'", job->dom_name); > + goto out; > + } > + > + if (events_registered == false) { > + events_registered = true; > + virEventRegisterDefaultImpl(); > + } > + > + if (job->dom_state == CIM_STATE_REBOOT) { > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > + VIR_DOMAIN_EVENT_ID_REBOOT, > + VIR_DOMAIN_EVENT_CALLBACK(state_change_reboot_cb), > + job, NULL); > + > + if (job_cb == -1) { > + CU_DEBUG("Unable to connect domain reboot callback"); > + goto out; > + } > + > + s = state_change_reboot(dom, &info); > + > + if (s.rc != CMPI_RC_OK) { > + CU_DEBUG("Unable to trigger domain reboot: '%s'", > + CMGetCharPtr(s.msg)); > + goto out; > + } > + } else if (job->dom_state == CIM_STATE_SHUTDOWN) { > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > + VIR_DOMAIN_EVENT_ID_REBOOT, > + VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb), > + job, NULL); > + Sharad has just found another issue here, for shutdown the event is VIR_DOMAIN_EVENT_ID_LIFECYCLE, and not VIR_DOMAIN_EVENT_ID_REBOOT. -- Eduardo de Barros Lima Software Engineer, Open Virtualization Linux Technology Center - IBM/Brazil eblima at br.ibm.com From orquidea.peramor at gmail.com Fri Mar 2 19:20:51 2012 From: orquidea.peramor at gmail.com (Orquidea Salt mas) Date: Fri, 2 Mar 2012 20:20:51 +0100 Subject: [Libvirt-cim] =?iso-8859-1?q?Reb=E9late_by_self-management=2C_fir?= =?iso-8859-1?q?st_project_of_free_software_by_which_we_bet_all_/_R?= =?iso-8859-1?q?eb=E9late_por_la_autogesti=F3n=2C_primer_proyecto_d?= =?iso-8859-1?q?e_software_libre_por_el_que_apostamos_todas?= Message-ID: Ingl?s : Many already we have contributed to the first project of free software dedicated to self-management in this campaign of collective financing, it collaborates and it spreads!/ Beginning campaign collective financing http://www.goteo.org/project/rebelaos-publicacion-por-la-autogestion?lang=en Login to enter with user of social networks and for would register in Goteo : http://www.goteo.org/user/login?lang=en Rebelaos! Publication by self-management A massive publication that floods the public transport, the work centers, the parks, the consumption centers, by means of distribution of 500,000 gratuitous units, acting simultaneously in all sides and nowhere. We announce the main tool of a vestibule Web for the management of self-sustaining resources by means of Drupal, in addition in the publication there will be an article dedicated to free software, hardware, It is being prepared in ingl?s, the machinery You can see more details in the index of the publication https://n-1.cc/pg/file/read/1151902/indexresumen-de-los-contenidos-pdf . A computer system that allows us to share resources in all the scopes of our life so that we do not have to generate means different for each subject nor for each territory. A point of contact digitalis to generate projects of life outside Capitalism and to margin of the State. A tool to spread and to impel the social transformation through the resources that will set out in their contents around self-management, the autoorganizaci?n, the disobedience and the collective action. In which the capitalist system goes to the collapse, in a while immersed in a deep systemic crisis (ecological, political and economic, but mainly of values), where individual and collective of people they are being lacking of his fundamental rights, is necessary to develop a horizontal collective process where all the human beings we pruned to interact in equality of conditions and freedom. To interact means to relate to us (as much human as economically), to communicate to us, to cover our basic needs, to generate and to protect communal properties, to know and to provide collective solutions us problematic that our lives interfere. We want abrir a breach within normality in the monotonous life state-capitalist, a day anyone, that finally will not be any day. By means of this publication we try: - To drive a horizontal collective process where all and all we pruned to interact in equality of conditions and freedom. - To create communications network between the people it jeopardize with the change and arranged to act. - To find collective solutions to problematic that our lives interfere - To facilitate the access to resources that make possible self-management. - To participate in the construction of networks of mutual support, generated horizontals, asamblearias and from the base. - To publish all this information in an attractive format stops to facilitate the access to all the society. There are 15 days remaining for the upcoming March 15, the day that will come Rebelaos!, Magazine for the selfmanagement Today, we issue the cover of Rebelaos! (Castilian version) that can be displayed on the following link: https://n-1.cc/pg/file/read/1200503/portada-15-de-marzo-rebelaos The contents of the store owners to us by 15 March. Do you? Do you keep on 15 March? In addition, we have over 200 distribution nodes, distributed throughout the Spanish state. Check the map: https://afinidadrebelde.crowdmap.com/ On the other hand, the funding campaign continues to move and still have 12 days to collect the remaining 6,000 euros. We can all make a bit for all the grains of sand become a great beach on March 15. You can access the co-financing campaign: http://www.goteo.org/project/rebelaos-publicacion-por-la-autogestion Rebel Affinity group www.rebelaos.net ------------------------------------------------------------------------------- Castellano: Muchos ya hemos aportado al primer proyecto de software libre dedicado a la la financiaci?n colectiva, colabora y diffunde !!!!! Inicio campa?a financiaci?n colectiva goteo.org www.goteo.org/project/rebelaos-publicacion-por-la-autogestion Link para registrarse en Goteo y acceder a redes sociales para colaborar en la difus?n http://www.goteo.org/user/login ?Rebelaos! Publicaci?n por la autogesti?n Una publicaci?n masiva que inunde el transporte p?blico, los centros de trabajo, los parques, los centros de consumo, mediante la distribuci?n de 500.000 ejemplares gratuitos, actuando simult?neamente en todos lados y en ninguna parte. Anunciamos la herramienta principal de un portal web para la gesti?n de recursos autogestionados mediante Drupal, adem?s en la publicaci?n habr? un art?culo dedicado al software libre, el hardware, la maquinaria... Puedes ver m?s detalles en el ?ndice de la publicaci?n https://n-1.cc/pg/file/read/1151902/indexresumen-de-los-contenidos-pdf Un sistema inf?rmatico que nos permita compartir recursos en todos los ?mbitos de nuestra vida de forma que no tengamos que generar un medio distinto para cada tema ni para cada territorio. Un punto de encuentro digital para generar proyectos de vida fuera del capitalismo y al margen del Estado. Una herramienta para difundir e impulsar la transformaci?n social a trav?s de los recursos que se propondr?n en sus contenidos en torno a la autogesti?n, la autoorganizaci?n, la desobediencia y la acci?n colectiva. En un momento en que el sistema capitalista se dirige al colapso, inmerso en una profunda crisis sist?mica (ecol?gica, pol?tica y econ?mica, pero principalmente de valores), donde individuos y colectivos de personas est?n siendo desprovistos de sus derechos fundamentales, es necesario desarrollar un proceso colectivo horizontal donde todos los seres humanos podamos interactuar en igualdad de condiciones y en libertad. Interactuar significa relacionarnos (tanto humana como econ?micamente), comunicarnos, cubrir nuestras necesidades b?sicas, generar y proteger bienes comunes, conocernos y dar soluciones colectivas a problem?ticas que interfieren nuestras vidas. Queremos abrir una brecha dentro de la normalidad en la mon?tona vida estatal-capitalista, un d?a cualquiera, que finalmente no ser? cualquier d?a. Mediante esta publicaci?n pretendemos: - Impulsar un proceso colectivo horizontal donde todos y todas podamos interactuar en igualdad de condiciones y en libertad. - Crear red de comunicaciones entre las personas comprometidas con el cambio y dispuestas a actuar. - Encontrar soluciones colectivas a problem?ticas que interfieren nuestras vidas. - Facilitar el acceso a recursos que posibiliten la autogesti?n. - Participar en la construcci?n de redes de apoyo mutuo, horizontales, asamblearias y generadas desde la base. - Publicar toda esta informaci?n en un formato atractivo para facilitar el acceso a toda la sociedad. Son 15 los d?as que restan para el pr?ximo 15 de marzo, d?a en el que ver? la luz ?Rebelaos!, publicaci?n por la autogesti?n. Hoy, hacemos p?blica la portada de ?Rebelaos! (versi?n en castellano) que pod?is visualizar en el siguiente enlace: https://n-1.cc/pg/file/read/1200503/portada-15-de-marzo-rebelaos El contenido de los titulares nos los guardamos para el 15 de marzo. ?Y t?? ?Te guardas el 15 de marzo? Adem?s, ya hemos superado los 200 nodos de distribuci?n, repartidos por todo el estado espa?ol. Ver el mapa: https://afinidadrebelde.crowdmap.com/ Por otro lado, la campa?a de financiaci?n contin?a avanzando y todav?a quedan 12 d?as para reunir los 6.000 euros que restan. Todas podemos aportar un poco para que todos los granitos de arena se conviertan en una gran playa el 15 de marzo. Pod?is acceder a la campa?a de cofinanciaci?n en: http://www.goteo.org/project/rebelaos-publicacion-por-la-autogestion Colectivo Afinidad Rebelde www.rebelaos.net From eblima at linux.vnet.ibm.com Tue Mar 6 17:45:55 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 6 Mar 2012 14:45:55 -0300 Subject: [Libvirt-cim] [PATCH] Fix possible memory leaks Message-ID: <1331055955-28998-1-git-send-email-eblima@linux.vnet.ibm.com> From: "Eduardo Lima (Etrunko)" Another Coverity scan report provided by Red Hat: https://bugzilla.redhat.com/show_bug.cgi?id=750418#c10 Error: RESOURCE_LEAK: Virt_AppliedFilterList.c:608: alloc_arg: Calling allocation function "get_dominfo" on "dominfo". device_parsing.c:1251: alloc_arg: "get_dominfo_from_xml" allocates memory that is stored into "*dominfo". device_parsing.c:1205: alloc_fn: Storage is returned from allocation function "calloc". device_parsing.c:1205: var_assign: Assigning: "*dominfo" = "calloc(1UL, 216UL)". device_parsing.c:1209: noescape: Variable "*dominfo" is not freed or pointed-to in function "_get_dominfo". device_parsing.c:1168:57: noescape: "_get_dominfo" does not free or save its pointer parameter "dominfo". Virt_AppliedFilterList.c:644: leaked_storage: Variable "dominfo" going out of scope leaks the storage it points to. Error: RESOURCE_LEAK: Virt_AppliedFilterList.c:485: alloc_arg: Calling allocation function "get_dominfo" on "dominfo". device_parsing.c:1251: alloc_arg: "get_dominfo_from_xml" allocates memory that is stored into "*dominfo". device_parsing.c:1205: alloc_fn: Storage is returned from allocation function "calloc". device_parsing.c:1205: var_assign: Assigning: "*dominfo" = "calloc(1UL, 216UL)". device_parsing.c:1209: noescape: Variable "*dominfo" is not freed or pointed-to in function "_get_dominfo". device_parsing.c:1168:57: noescape: "_get_dominfo" does not free or save its pointer parameter "dominfo". Virt_AppliedFilterList.c:529: leaked_storage: Variable "dominfo" going out of scope leaks the storage it points to. Error: RESOURCE_LEAK: misc_util.c:275: alloc_arg: Calling allocation function "get_domain_list" on "list". cs_util_instance.c:52: alloc_fn: Storage is returned from allocation function "calloc". cs_util_instance.c:52: var_assign: Assigning: "list" = "calloc(n_names + n_ids, 8UL)". cs_util_instance.c:112: var_assign: Assigning: "*_list" = "list". misc_util.c:277: leaked_storage: Variable "list" going out of scope leaks the storage it points to. Signed-off-by: Eduardo Lima (Etrunko) --- libxkutil/misc_util.c | 6 +++--- src/Virt_AppliedFilterList.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libxkutil/misc_util.c b/libxkutil/misc_util.c index 61893c3..2d149ae 100644 --- a/libxkutil/misc_util.c +++ b/libxkutil/misc_util.c @@ -267,14 +267,14 @@ bool match_pn_to_cn(const char *pn, const char *cn) uint64_t allocated_memory(virConnectPtr conn) { - virDomainPtr *list; + virDomainPtr *list = NULL; int count; int i; uint64_t memory = 0; count = get_domain_list(conn, &list); if (count <= 0) - return 0; + goto end; for (i = 0; i < count; i ++) { virDomainPtr dom = list[i]; @@ -286,8 +286,8 @@ uint64_t allocated_memory(virConnectPtr conn) virDomainFree(dom); } + end: free(list); - return memory; } diff --git a/src/Virt_AppliedFilterList.c b/src/Virt_AppliedFilterList.c index 538adf4..0dfe6a3 100644 --- a/src/Virt_AppliedFilterList.c +++ b/src/Virt_AppliedFilterList.c @@ -521,7 +521,7 @@ static CMPIStatus CreateInstance( free(net_name); cleanup_filters(&filter, 1); - cleanup_virt_devices(&device, 1); + cleanup_dominfo(&dominfo); virDomainFree(dom); virConnectClose(conn); @@ -636,7 +636,7 @@ static CMPIStatus DeleteInstance( free(net_name); cleanup_filters(&filter, 1); - cleanup_virt_devices(&device, 1); + cleanup_dominfo(&dominfo); virDomainFree(dom); virConnectClose(conn); -- 1.7.7.6 From eblima at linux.vnet.ibm.com Tue Mar 6 17:55:53 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 6 Mar 2012 14:55:53 -0300 Subject: [Libvirt-cim] [PATCH 3/3] ComputerSystem: Reboot state change also a job In-Reply-To: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331056553-29221-4-git-send-email-eblima@linux.vnet.ibm.com> From: "Eduardo Lima (Etrunko)" As it happens for Shutdown, the RequestStateChange method returns immediately with return code 0 (successful) even though the state change is still not completed. Change the current job implementation to support reboot as well. For this we need the libvirt domain events APIs and event loop implementation, which are only available in libvirt version 0.9.0 or newer. Signed-off-by: Eduardo Lima (Etrunko) --- src/Virt_ComputerSystem.c | 90 ++++++++++++++++++++++++++++++++++++++------- 1 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c index 778809d..04584f3 100644 --- a/src/Virt_ComputerSystem.c +++ b/src/Virt_ComputerSystem.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -61,6 +62,8 @@ struct _state_change_job { uint16_t status; /* job status */ }; +static bool events_registered = false; + /* Set the "Name" property of an instance from a domain */ static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance) { @@ -1203,14 +1206,12 @@ static CMPIStatus __state_change(const char *name, s = state_change_enable(dom, &info); else if (state == CIM_STATE_DISABLED) s = state_change_disable(dom, &info); - else if (state == CIM_STATE_SHUTDOWN) - s.rc = CIM_SVPC_RETURN_JOB_STARTED; else if (state == CIM_STATE_PAUSED) s = state_change_pause(dom, &info); - else if (state == CIM_STATE_REBOOT) - s = state_change_reboot(dom, &info); else if (state == CIM_STATE_RESET) s = state_change_reset(dom, &info); + else if (state == CIM_STATE_SHUTDOWN || state == CIM_STATE_REBOOT) + s.rc = CIM_SVPC_RETURN_JOB_STARTED; else cu_statusf(_BROKER, &s, CMPI_RC_ERR_NOT_SUPPORTED, @@ -1333,6 +1334,25 @@ static CMPIStatus create_state_change_job(const CMPIObjectPath *ref, return s; } +static void state_change_reboot_cb(virConnectPtr conn, + virDomainPtr dom, + void *data) +{ + state_change_job_t *job = (state_change_job_t *) data; + job->status = CIM_JOB_STATE_COMPLETED; +} + +static void state_change_shutdown_cb(virConnectPtr conn, + virDomainPtr dom, + int event, + int detail, + void *data) +{ + state_change_job_t *job = (state_change_job_t *) data; + if (event == VIR_DOMAIN_EVENT_SHUTDOWN) + job->status = CIM_JOB_STATE_COMPLETED; +} + static CMPI_THREAD_RETURN state_change_thread(void *data) { CMPIStatus s; @@ -1341,8 +1361,10 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) virConnectPtr conn = NULL; virDomainPtr dom = NULL; virDomainInfo info; + int job_cb = -1; - if (job->dom_state != CIM_STATE_SHUTDOWN) { + if (job->dom_state != CIM_STATE_SHUTDOWN && + job->dom_state != CIM_STATE_REBOOT) { CU_DEBUG("Unrecognized state '%d'", job->dom_state); goto end; } @@ -1382,17 +1404,56 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) goto out; } - s = state_change_shutdown(dom, &info); - if (s.rc != CMPI_RC_OK) { - CU_DEBUG("Unable to trigger domain shutdown: '%s'", - CMGetCharPtr(s.msg)); - goto out; + if (events_registered == false) { + events_registered = true; + virEventRegisterDefaultImpl(); + } + + if (job->dom_state == CIM_STATE_REBOOT) { + job_cb = virConnectDomainEventRegisterAny(conn, NULL, + VIR_DOMAIN_EVENT_ID_REBOOT, + VIR_DOMAIN_EVENT_CALLBACK(state_change_reboot_cb), + job, NULL); + + if (job_cb == -1) { + CU_DEBUG("Unable to connect domain reboot callback"); + goto out; + } + + s = state_change_reboot(dom, &info); + + if (s.rc != CMPI_RC_OK) { + CU_DEBUG("Unable to trigger domain reboot: '%s'", + CMGetCharPtr(s.msg)); + goto out; + } + } else if (job->dom_state == CIM_STATE_SHUTDOWN) { + job_cb = virConnectDomainEventRegisterAny(conn, NULL, + VIR_DOMAIN_EVENT_ID_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb), + job, NULL); + + if (job_cb == -1) { + CU_DEBUG("Unable to connect domain shutdown callback"); + goto out; + } + + s = state_change_shutdown(dom, &info); + + if (s.rc != CMPI_RC_OK) { + CU_DEBUG("Unable to trigger domain shutdown: '%s'", + CMGetCharPtr(s.msg)); + goto out; + } } /* Wait for operation (shutdown/reboot) to complete */ - while (info.state != VIR_DOMAIN_SHUTOFF) { - usleep(100 * 1000); - virDomainGetInfo(dom, &info); + while (job->status == CIM_JOB_STATE_RUNNING) { + if (virEventRunDefaultImpl() < 0) { + virErrorPtr err = virGetLastError(); + CU_DEBUG("Failed to run event loop: %s\n", + err && err->message ? err->message : "Unknown error"); + } } CU_DEBUG("Job completed"); @@ -1405,6 +1466,8 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) (CMPIValue *) "Completed", CMPI_chars); } + virConnectDomainEventDeregisterAny(conn, job_cb); + out: virDomainFree(dom); virConnectClose(conn); @@ -1412,7 +1475,6 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) CBDetachThread(_BROKER, job->context); free(job->dom_name); free(job); - end: return NULL; } -- 1.7.7.6 From eblima at linux.vnet.ibm.com Tue Mar 6 17:55:50 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 6 Mar 2012 14:55:50 -0300 Subject: [Libvirt-cim] [PATCHv2 0/3] Make Shutdown and Reboot state changes jobs Message-ID: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> From: "Eduardo Lima (Etrunko)" This code is based on the VSMigrationService for the job creation and on ComputerSystemIndication for the job thread, which will listen for the respective domain event to mark the job as finished. I am still not able to test this feature in my environment, every call to CBGetInstance fails with code error 100. This is happening in other places of the codebase, not only the ComputerSystem provider, but also ElementConfors, for instance. Please use the test case send in previous email for reference. Differences from v1: - Fix conditional causing Reqstate not being set - Fix domain event id for shutdown - Now instead of using the domain events for both shutdown and reboot, there is an intermediate patch, that will deal with the shutdown case by polling for the domain state until it reflects the desired state. This workaround won't work for the reboot case, because is is not possible to monitor the reboot event if not using the domain event callbacks. The third patch of the series contains the complete solution for both cases. Eduardo Lima (Etrunko) (3): VSMigrationService: Move job state definitions to svpc_types.h ComputerSystem: Make Shutdown state change a job ComputerSystem: Reboot state change also a job schema/ComputerSystem.mof | 9 ++ src/Virt_ComputerSystem.c | 304 +++++++++++++++++++++++++++++++++++++++-- src/Virt_VSMigrationService.c | 14 +-- src/svpc_types.h | 13 ++ 4 files changed, 321 insertions(+), 19 deletions(-) -- 1.7.7.6 From eblima at linux.vnet.ibm.com Tue Mar 6 17:55:52 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 6 Mar 2012 14:55:52 -0300 Subject: [Libvirt-cim] [PATCH 2/3] ComputerSystem: Make Shutdown state change a job In-Reply-To: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331056553-29221-3-git-send-email-eblima@linux.vnet.ibm.com> From: "Eduardo Lima (Etrunko)" For Shutdown, the RequestStateChange method returns immediately with return code 0 (successful) even though the state change is still not completed. According to the DMTF specification DSP1052 (Computer System Profile) the RequestStateChange() method should return 0x1000 and a corresponding job reference in the return parameters which can be polled for completion. Signed-off-by: Eduardo Lima (Etrunko) --- schema/ComputerSystem.mof | 9 ++ src/Virt_ComputerSystem.c | 236 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 238 insertions(+), 7 deletions(-) diff --git a/schema/ComputerSystem.mof b/schema/ComputerSystem.mof index 10cb8c4..886c085 100644 --- a/schema/ComputerSystem.mof +++ b/schema/ComputerSystem.mof @@ -1,5 +1,14 @@ // Copyright IBM Corp. 2007 +class Xen_ComputerSystemStateChangeJob : CIM_ConcreteJob { +}; + +class KVM_ComputerSystemStateChangeJob : CIM_ConcreteJob { +}; + +class LXC_ComputerSystemStateChangeJob : CIM_ConcreteJob { +}; + [Description ( "A class derived from CIM_ComputerSystem to represent " "the Xen virtual machines/domains running on the system."), diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c index e6c7e55..778809d 100644 --- a/src/Virt_ComputerSystem.c +++ b/src/Virt_ComputerSystem.c @@ -30,23 +30,37 @@ #include #include +#include #include -#include "cs_util.h" #include -#include "misc_util.h" -#include "infostore.h" -#include "device_parsing.h" #include #include #include +#include "cs_util.h" +#include "misc_util.h" +#include "infostore.h" +#include "device_parsing.h" +#include "svpc_types.h" + #include "Virt_ComputerSystem.h" #include "Virt_HostSystem.h" #include "Virt_VirtualSystemSnapshotService.h" + const static CMPIBroker *_BROKER; +typedef struct _state_change_job state_change_job_t; +struct _state_change_job { + char uuid[VIR_UUID_STRING_BUFLEN]; + CMPIContext *context; + CMPIObjectPath *obj_path; + char *dom_name; + uint16_t dom_state; + uint16_t status; /* job status */ +}; + /* Set the "Name" property of an instance from a domain */ static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance) { @@ -1190,7 +1204,7 @@ static CMPIStatus __state_change(const char *name, else if (state == CIM_STATE_DISABLED) s = state_change_disable(dom, &info); else if (state == CIM_STATE_SHUTDOWN) - s = state_change_shutdown(dom, &info); + s.rc = CIM_SVPC_RETURN_JOB_STARTED; else if (state == CIM_STATE_PAUSED) s = state_change_pause(dom, &info); else if (state == CIM_STATE_REBOOT) @@ -1202,6 +1216,9 @@ static CMPIStatus __state_change(const char *name, CMPI_RC_ERR_NOT_SUPPORTED, "State not supported"); + if (s.rc != CMPI_RC_OK && s.rc != CIM_SVPC_RETURN_JOB_STARTED) + goto out; + infostore = infostore_open(dom); if (infostore != NULL) { infostore_set_u64(infostore, "reqstate", (uint64_t)state); @@ -1215,6 +1232,191 @@ static CMPIStatus __state_change(const char *name, return s; } +static CMPIStatus create_state_change_job(const CMPIObjectPath *ref, + const CMPIContext *context, + state_change_job_t **job, + uint16_t state) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + CMPIInstance *job_inst; + CMPIDateTime *start; + CMPIBoolean autodelete = true; + CMPIObjectPath *obj_path; + uuid_t uuid; + char *type = NULL, *cn = NULL, *ns = NULL; + + start = CMNewDateTime(_BROKER, &s); + if ((s.rc != CMPI_RC_OK) || CMIsNullObject(start)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Failed to get job start time"); + goto out; + } + + cn = strdup(CLASSNAME(ref)); + type = get_typed_class(cn, "ComputerSystemStateChangeJob"); + + obj_path = CMNewObjectPath(_BROKER, ns, type, &s); + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Failed to get new object path"); + goto out; + } + + job_inst = CMNewInstance(_BROKER, obj_path, &s); + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(job_inst))) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Failed to get new instance object"); + goto out; + } + + /* Alloc job struct */ + *job = calloc(1, sizeof(**job)); + if (*job == NULL) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Failed to allocate memory for job structure"); + goto out; + } + + (*job)->dom_state = state; + (*job)->status = CIM_JOB_STATE_STARTING; + + uuid_generate(uuid); + uuid_unparse(uuid, (*job)->uuid); + + /* Set Properties */ + CMSetProperty(job_inst, "InstanceID", + (CMPIValue *)(*job)->uuid, CMPI_chars); + CMSetProperty(job_inst, "Name", + (CMPIValue *) "ComputerSystemStateChange", CMPI_chars); + CMSetProperty(job_inst, "StartTime", + (CMPIValue *)&start, CMPI_dateTime); + CMSetProperty(job_inst, "JobState", + (CMPIValue *)&((*job)->status), CMPI_uint16); + CMSetProperty(job_inst, "Status", + (CMPIValue *) "Starting", CMPI_chars); + CMSetProperty(job_inst, "DeleteOnCompletion", + (CMPIValue *)&autodelete, CMPI_boolean); + + obj_path = CMGetObjectPath(job_inst, &s); + if ((obj_path == NULL) || (s.rc != CMPI_RC_OK)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Failed to get path for ComputerSystemStateChangeJob instance"); + goto out; + } + + CMSetNameSpace(obj_path, ns); + + CU_DEBUG("Creating ComputerSystemStateChangeJob instance: %s", + CMGetCharPtr(CMObjectPathToString(obj_path, NULL))); + + obj_path = CBCreateInstance(_BROKER, context, obj_path, job_inst, &s); + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) { + CU_DEBUG("Failed to create ComputerSystemStateChangeJob instance: %i", s.rc); + goto out; + } + + ns = strdup(NAMESPACE(ref)); + CMSetNameSpace(obj_path, ns); + + (*job)->obj_path = obj_path; + (*job)->context = CBPrepareAttachThread(_BROKER, context); + + out: + free(type); + free(cn); + free(ns); + return s; +} + +static CMPI_THREAD_RETURN state_change_thread(void *data) +{ + CMPIStatus s; + CMPIInstance *inst = NULL; + state_change_job_t *job = (state_change_job_t *) data; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + virDomainInfo info; + + if (job->dom_state != CIM_STATE_SHUTDOWN) { + CU_DEBUG("Unrecognized state '%d'", job->dom_state); + goto end; + } + + /* Set job state */ + CBAttachThread(_BROKER, job->context); + CU_DEBUG("State change job %s started", job->uuid); + job->status = CIM_JOB_STATE_RUNNING; + + inst = CBGetInstance(_BROKER, job->context, job->obj_path, NULL, &s); + if ((inst == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Failed to get job instance (%i)", s.rc); + return NULL; + } + + CMSetProperty(inst, "JobState", + (CMPIValue *)&(job->status), CMPI_uint16); + CMSetProperty(inst, "Status", + (CMPIValue *) "Running", CMPI_chars); + + /* Connect to domain event callback */ + conn = connect_by_classname(_BROKER, CLASSNAME(job->obj_path), &s); + if (conn == NULL) { + CU_DEBUG("Unable to connect to '%s' hypervisor", + CLASSNAME(job->obj_path)); + goto out; + } + + dom = virDomainLookupByName(conn, job->dom_name); + if (dom == NULL) { + CU_DEBUG("Unable to get domain '%s'", job->dom_name); + goto out; + } + + if (virDomainGetInfo(dom, &info) != 0) { + CU_DEBUG("Unable to get domain info for '%s'", job->dom_name); + goto out; + } + + s = state_change_shutdown(dom, &info); + if (s.rc != CMPI_RC_OK) { + CU_DEBUG("Unable to trigger domain shutdown: '%s'", + CMGetCharPtr(s.msg)); + goto out; + } + + /* Wait for operation (shutdown/reboot) to complete */ + while (info.state != VIR_DOMAIN_SHUTOFF) { + usleep(100 * 1000); + virDomainGetInfo(dom, &info); + } + + CU_DEBUG("Job completed"); + + /* Set job state */ + if (job->status == CIM_JOB_STATE_COMPLETED) { + CMSetProperty(inst, "JobState", + (CMPIValue *)&(job->status), CMPI_uint16); + CMSetProperty(inst, "Status", + (CMPIValue *) "Completed", CMPI_chars); + } + + out: + virDomainFree(dom); + virConnectClose(conn); + + CBDetachThread(_BROKER, job->context); + free(job->dom_name); + free(job); + + end: + return NULL; +} + static CMPIStatus state_change(CMPIMethodMI *self, const CMPIContext *context, const CMPIResult *results, @@ -1244,7 +1446,8 @@ static CMPIStatus state_change(CMPIMethodMI *self, goto out; } - /* Retain original instance of the guest to use for the PreviousInstance attribute when generating an indication. */ + /* Retain original instance of the guest to use for the PreviousInstance + attribute when generating an indication. */ s = get_domain_by_name(_BROKER, reference, name, &prev_inst); if (s.rc != CMPI_RC_OK || prev_inst == NULL) { cu_statusf(_BROKER, &s, @@ -1256,8 +1459,27 @@ static CMPIStatus state_change(CMPIMethodMI *self, s = __state_change(name, state, reference); - if (s.rc == CMPI_RC_OK) + if (s.rc == CMPI_RC_OK) { rc = 0; + goto out; + } + + if (s.rc == CIM_SVPC_RETURN_JOB_STARTED) { + state_change_job_t *job = NULL; + s = create_state_change_job(reference, context, &job, state); + if (s.rc != CMPI_RC_OK) { + free(job); + goto out; + } + + job->dom_name = strdup(name); + + _BROKER->xft->newThread(state_change_thread, job, 0); + + CMAddArg(argsout, "Job", (CMPIValue *)&(job->obj_path), + CMPI_ref); + rc = s.rc; + } out: CMReturnData(results, &rc, CMPI_uint32); -- 1.7.7.6 From eblima at linux.vnet.ibm.com Tue Mar 6 17:55:51 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 6 Mar 2012 14:55:51 -0300 Subject: [Libvirt-cim] [PATCH 1/3] VSMigrationService: Move job state definitions to svpc_types.h In-Reply-To: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331056553-29221-2-git-send-email-eblima@linux.vnet.ibm.com> From: "Eduardo Lima (Etrunko)" Further job implementations may reuse these values. Signed-off-by: Eduardo Lima (Etrunko) --- src/Virt_VSMigrationService.c | 14 +++++--------- src/svpc_types.h | 13 +++++++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Virt_VSMigrationService.c b/src/Virt_VSMigrationService.c index 76e3d25..a6b5fc0 100644 --- a/src/Virt_VSMigrationService.c +++ b/src/Virt_VSMigrationService.c @@ -52,10 +52,6 @@ #include "config.h" -#define CIM_JOBSTATE_STARTING 3 -#define CIM_JOBSTATE_RUNNING 4 -#define CIM_JOBSTATE_COMPLETE 7 - #define MIGRATE_SHUTDOWN_TIMEOUT 120 #define METHOD_RETURN(r, v) do { \ @@ -1289,19 +1285,19 @@ static CMPI_THREAD_RETURN migration_thread(struct migration_job *job) CBAttachThread(_BROKER, job->context); CU_DEBUG("Migration Job %s started", job->uuid); - migrate_job_set_state(job, CIM_JOBSTATE_RUNNING, 0, "Running"); + migrate_job_set_state(job, CIM_JOB_STATE_RUNNING, 0, "Running"); s = migrate_vs(job); CU_DEBUG("Migration Job %s finished: %i", job->uuid, s.rc); if (s.rc != CMPI_RC_OK) migrate_job_set_state(job, - CIM_JOBSTATE_COMPLETE, + CIM_JOB_STATE_COMPLETED, s.rc, CMGetCharPtr(s.msg)); else migrate_job_set_state(job, - CIM_JOBSTATE_COMPLETE, + CIM_JOB_STATE_COMPLETED, 0, "Completed"); @@ -1361,7 +1357,7 @@ static CMPIInstance *_migrate_job_new_instance(const char *cn, } inst = CMNewInstance(_BROKER, op, &s); - if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(op))) { + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(inst))) { CU_DEBUG("Failed to create instance from ref: %s", CMGetCharPtr(CMObjectPathToString(op, NULL))); return NULL; @@ -1378,7 +1374,7 @@ static CMPIStatus migrate_create_job_instance(const CMPIContext *context, CMPIInstance *jobinst; CMPIDateTime *start; CMPIBoolean autodelete = true; - uint16_t state = CIM_JOBSTATE_STARTING; + uint16_t state = CIM_JOB_STATE_STARTING; char *type = NULL; start = CMNewDateTime(_BROKER, &s); diff --git a/src/svpc_types.h b/src/svpc_types.h index 90bb608..338a7ef 100644 --- a/src/svpc_types.h +++ b/src/svpc_types.h @@ -167,5 +167,18 @@ enum CIM_op_status { CIM_OP_STATUS_POWER_MODE = 18, }; +enum CIM_job_state { + CIM_JOB_STATE_NEW = 2, + CIM_JOB_STATE_STARTING, + CIM_JOB_STATE_RUNNING, + CIM_JOB_STATE_SUSPENDED, + CIM_JOB_STATE_SHUTTING_DOWN, + CIM_JOB_STATE_COMPLETED, + CIM_JOB_STATE_TERMINATED, + CIM_JOB_STATE_KILLED, + CIM_JOB_STATE_EXCEPTION, + CIM_JOB_STATE_SERVICE, + CIM_JOB_STATE_QUERY_PENDING, +}; #endif -- 1.7.7.6 From snmishra at linux.vnet.ibm.com Wed Mar 7 17:12:02 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Wed, 07 Mar 2012 09:12:02 -0800 Subject: [Libvirt-cim] [PATCHv2 0/3] Make Shutdown and Reboot state changes jobs In-Reply-To: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331140322.2597.26.camel@snmishra-desktop.beaverton.ibm.com> On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > This code is based on the VSMigrationService for the job creation and on > ComputerSystemIndication for the job thread, which will listen for the > respective domain event to mark the job as finished. > > I am still not able to test this feature in my environment, every call to > CBGetInstance fails with code error 100. This is happening in other places of > the codebase, not only the ComputerSystem provider, but also ElementConfors, > for instance. Please use the test case send in previous email for reference. Eduardo, It is not clear if you started seeing this error code after applying this patch or were you getting it before. -Sharad > > Differences from v1: > > - Fix conditional causing Reqstate not being set > - Fix domain event id for shutdown > - Now instead of using the domain events for both shutdown and reboot, there is > an intermediate patch, that will deal with the shutdown case by polling for > the domain state until it reflects the desired state. > > This workaround won't work for the reboot case, because is is not possible to > monitor the reboot event if not using the domain event callbacks. The third > patch of the series contains the complete solution for both cases. > > Eduardo Lima (Etrunko) (3): > VSMigrationService: Move job state definitions to svpc_types.h > ComputerSystem: Make Shutdown state change a job > ComputerSystem: Reboot state change also a job > > schema/ComputerSystem.mof | 9 ++ > src/Virt_ComputerSystem.c | 304 +++++++++++++++++++++++++++++++++++++++-- > src/Virt_VSMigrationService.c | 14 +-- > src/svpc_types.h | 13 ++ > 4 files changed, 321 insertions(+), 19 deletions(-) > From snmishra at linux.vnet.ibm.com Wed Mar 7 17:19:10 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Wed, 07 Mar 2012 09:19:10 -0800 Subject: [Libvirt-cim] [PATCH 1/3] VSMigrationService: Move job state definitions to svpc_types.h In-Reply-To: <1331056553-29221-2-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331056553-29221-2-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331140750.2597.28.camel@snmishra-desktop.beaverton.ibm.com> visual code review looks good not applied and tested the patch yet. -Sharad On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > Further job implementations may reuse these values. > > Signed-off-by: Eduardo Lima (Etrunko) > --- > src/Virt_VSMigrationService.c | 14 +++++--------- > src/svpc_types.h | 13 +++++++++++++ > 2 files changed, 18 insertions(+), 9 deletions(-) > > diff --git a/src/Virt_VSMigrationService.c b/src/Virt_VSMigrationService.c > index 76e3d25..a6b5fc0 100644 > --- a/src/Virt_VSMigrationService.c > +++ b/src/Virt_VSMigrationService.c > @@ -52,10 +52,6 @@ > > #include "config.h" > > -#define CIM_JOBSTATE_STARTING 3 > -#define CIM_JOBSTATE_RUNNING 4 > -#define CIM_JOBSTATE_COMPLETE 7 > - > #define MIGRATE_SHUTDOWN_TIMEOUT 120 > > #define METHOD_RETURN(r, v) do { \ > @@ -1289,19 +1285,19 @@ static CMPI_THREAD_RETURN migration_thread(struct migration_job *job) > CBAttachThread(_BROKER, job->context); > > CU_DEBUG("Migration Job %s started", job->uuid); > - migrate_job_set_state(job, CIM_JOBSTATE_RUNNING, 0, "Running"); > + migrate_job_set_state(job, CIM_JOB_STATE_RUNNING, 0, "Running"); > > s = migrate_vs(job); > > CU_DEBUG("Migration Job %s finished: %i", job->uuid, s.rc); > if (s.rc != CMPI_RC_OK) > migrate_job_set_state(job, > - CIM_JOBSTATE_COMPLETE, > + CIM_JOB_STATE_COMPLETED, > s.rc, > CMGetCharPtr(s.msg)); > else > migrate_job_set_state(job, > - CIM_JOBSTATE_COMPLETE, > + CIM_JOB_STATE_COMPLETED, > 0, > "Completed"); > > @@ -1361,7 +1357,7 @@ static CMPIInstance *_migrate_job_new_instance(const char *cn, > } > > inst = CMNewInstance(_BROKER, op, &s); > - if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(op))) { > + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(inst))) { > CU_DEBUG("Failed to create instance from ref: %s", > CMGetCharPtr(CMObjectPathToString(op, NULL))); > return NULL; > @@ -1378,7 +1374,7 @@ static CMPIStatus migrate_create_job_instance(const CMPIContext *context, > CMPIInstance *jobinst; > CMPIDateTime *start; > CMPIBoolean autodelete = true; > - uint16_t state = CIM_JOBSTATE_STARTING; > + uint16_t state = CIM_JOB_STATE_STARTING; > char *type = NULL; > > start = CMNewDateTime(_BROKER, &s); > diff --git a/src/svpc_types.h b/src/svpc_types.h > index 90bb608..338a7ef 100644 > --- a/src/svpc_types.h > +++ b/src/svpc_types.h > @@ -167,5 +167,18 @@ enum CIM_op_status { > CIM_OP_STATUS_POWER_MODE = 18, > }; > > +enum CIM_job_state { > + CIM_JOB_STATE_NEW = 2, > + CIM_JOB_STATE_STARTING, > + CIM_JOB_STATE_RUNNING, > + CIM_JOB_STATE_SUSPENDED, > + CIM_JOB_STATE_SHUTTING_DOWN, > + CIM_JOB_STATE_COMPLETED, > + CIM_JOB_STATE_TERMINATED, > + CIM_JOB_STATE_KILLED, > + CIM_JOB_STATE_EXCEPTION, > + CIM_JOB_STATE_SERVICE, > + CIM_JOB_STATE_QUERY_PENDING, > +}; > > #endif From snmishra at linux.vnet.ibm.com Wed Mar 7 17:50:39 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Wed, 07 Mar 2012 09:50:39 -0800 Subject: [Libvirt-cim] [PATCH 2/3] ComputerSystem: Make Shutdown state change a job In-Reply-To: <1331056553-29221-3-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331056553-29221-3-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331142639.2597.29.camel@snmishra-desktop.beaverton.ibm.com> Visual code inspection looks good. Need to apply and test the patch. -Sharad On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > For Shutdown, the RequestStateChange method returns immediately with > return code 0 (successful) even though the state change is still not completed. > > According to the DMTF specification DSP1052 (Computer System Profile) the > RequestStateChange() method should return 0x1000 and a corresponding job > reference in the return parameters which can be polled for completion. > > Signed-off-by: Eduardo Lima (Etrunko) > --- > schema/ComputerSystem.mof | 9 ++ > src/Virt_ComputerSystem.c | 236 +++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 238 insertions(+), 7 deletions(-) > > diff --git a/schema/ComputerSystem.mof b/schema/ComputerSystem.mof > index 10cb8c4..886c085 100644 > --- a/schema/ComputerSystem.mof > +++ b/schema/ComputerSystem.mof > @@ -1,5 +1,14 @@ > // Copyright IBM Corp. 2007 > > +class Xen_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > +class KVM_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > +class LXC_ComputerSystemStateChangeJob : CIM_ConcreteJob { > +}; > + > [Description ( > "A class derived from CIM_ComputerSystem to represent " > "the Xen virtual machines/domains running on the system."), > diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c > index e6c7e55..778809d 100644 > --- a/src/Virt_ComputerSystem.c > +++ b/src/Virt_ComputerSystem.c > @@ -30,23 +30,37 @@ > #include > #include > > +#include > #include > > -#include "cs_util.h" > #include > -#include "misc_util.h" > -#include "infostore.h" > -#include "device_parsing.h" > #include > #include > #include > > +#include "cs_util.h" > +#include "misc_util.h" > +#include "infostore.h" > +#include "device_parsing.h" > +#include "svpc_types.h" > + > #include "Virt_ComputerSystem.h" > #include "Virt_HostSystem.h" > #include "Virt_VirtualSystemSnapshotService.h" > > + > const static CMPIBroker *_BROKER; > > +typedef struct _state_change_job state_change_job_t; > +struct _state_change_job { > + char uuid[VIR_UUID_STRING_BUFLEN]; > + CMPIContext *context; > + CMPIObjectPath *obj_path; > + char *dom_name; > + uint16_t dom_state; > + uint16_t status; /* job status */ > +}; > + > /* Set the "Name" property of an instance from a domain */ > static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance) > { > @@ -1190,7 +1204,7 @@ static CMPIStatus __state_change(const char *name, > else if (state == CIM_STATE_DISABLED) > s = state_change_disable(dom, &info); > else if (state == CIM_STATE_SHUTDOWN) > - s = state_change_shutdown(dom, &info); > + s.rc = CIM_SVPC_RETURN_JOB_STARTED; > else if (state == CIM_STATE_PAUSED) > s = state_change_pause(dom, &info); > else if (state == CIM_STATE_REBOOT) > @@ -1202,6 +1216,9 @@ static CMPIStatus __state_change(const char *name, > CMPI_RC_ERR_NOT_SUPPORTED, > "State not supported"); > > + if (s.rc != CMPI_RC_OK && s.rc != CIM_SVPC_RETURN_JOB_STARTED) > + goto out; > + > infostore = infostore_open(dom); > if (infostore != NULL) { > infostore_set_u64(infostore, "reqstate", (uint64_t)state); > @@ -1215,6 +1232,191 @@ static CMPIStatus __state_change(const char *name, > return s; > } > > +static CMPIStatus create_state_change_job(const CMPIObjectPath *ref, > + const CMPIContext *context, > + state_change_job_t **job, > + uint16_t state) > +{ > + CMPIStatus s = {CMPI_RC_OK, NULL}; > + CMPIInstance *job_inst; > + CMPIDateTime *start; > + CMPIBoolean autodelete = true; > + CMPIObjectPath *obj_path; > + uuid_t uuid; > + char *type = NULL, *cn = NULL, *ns = NULL; > + > + start = CMNewDateTime(_BROKER, &s); > + if ((s.rc != CMPI_RC_OK) || CMIsNullObject(start)) { > + cu_statusf(_BROKER, &s, > + CMPI_RC_ERR_FAILED, > + "Failed to get job start time"); > + goto out; > + } > + > + cn = strdup(CLASSNAME(ref)); > + type = get_typed_class(cn, "ComputerSystemStateChangeJob"); > + > + obj_path = CMNewObjectPath(_BROKER, ns, type, &s); > + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) { > + cu_statusf(_BROKER, &s, > + CMPI_RC_ERR_FAILED, > + "Failed to get new object path"); > + goto out; > + } > + > + job_inst = CMNewInstance(_BROKER, obj_path, &s); > + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(job_inst))) { > + cu_statusf(_BROKER, &s, > + CMPI_RC_ERR_FAILED, > + "Failed to get new instance object"); > + goto out; > + } > + > + /* Alloc job struct */ > + *job = calloc(1, sizeof(**job)); > + if (*job == NULL) { > + cu_statusf(_BROKER, &s, > + CMPI_RC_ERR_FAILED, > + "Failed to allocate memory for job structure"); > + goto out; > + } > + > + (*job)->dom_state = state; > + (*job)->status = CIM_JOB_STATE_STARTING; > + > + uuid_generate(uuid); > + uuid_unparse(uuid, (*job)->uuid); > + > + /* Set Properties */ > + CMSetProperty(job_inst, "InstanceID", > + (CMPIValue *)(*job)->uuid, CMPI_chars); > + CMSetProperty(job_inst, "Name", > + (CMPIValue *) "ComputerSystemStateChange", CMPI_chars); > + CMSetProperty(job_inst, "StartTime", > + (CMPIValue *)&start, CMPI_dateTime); > + CMSetProperty(job_inst, "JobState", > + (CMPIValue *)&((*job)->status), CMPI_uint16); > + CMSetProperty(job_inst, "Status", > + (CMPIValue *) "Starting", CMPI_chars); > + CMSetProperty(job_inst, "DeleteOnCompletion", > + (CMPIValue *)&autodelete, CMPI_boolean); > + > + obj_path = CMGetObjectPath(job_inst, &s); > + if ((obj_path == NULL) || (s.rc != CMPI_RC_OK)) { > + cu_statusf(_BROKER, &s, > + CMPI_RC_ERR_FAILED, > + "Failed to get path for ComputerSystemStateChangeJob instance"); > + goto out; > + } > + > + CMSetNameSpace(obj_path, ns); > + > + CU_DEBUG("Creating ComputerSystemStateChangeJob instance: %s", > + CMGetCharPtr(CMObjectPathToString(obj_path, NULL))); > + > + obj_path = CBCreateInstance(_BROKER, context, obj_path, job_inst, &s); > + if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) { > + CU_DEBUG("Failed to create ComputerSystemStateChangeJob instance: %i", s.rc); > + goto out; > + } > + > + ns = strdup(NAMESPACE(ref)); > + CMSetNameSpace(obj_path, ns); > + > + (*job)->obj_path = obj_path; > + (*job)->context = CBPrepareAttachThread(_BROKER, context); > + > + out: > + free(type); > + free(cn); > + free(ns); > + return s; > +} > + > +static CMPI_THREAD_RETURN state_change_thread(void *data) > +{ > + CMPIStatus s; > + CMPIInstance *inst = NULL; > + state_change_job_t *job = (state_change_job_t *) data; > + virConnectPtr conn = NULL; > + virDomainPtr dom = NULL; > + virDomainInfo info; > + > + if (job->dom_state != CIM_STATE_SHUTDOWN) { > + CU_DEBUG("Unrecognized state '%d'", job->dom_state); > + goto end; > + } > + > + /* Set job state */ > + CBAttachThread(_BROKER, job->context); > + CU_DEBUG("State change job %s started", job->uuid); > + job->status = CIM_JOB_STATE_RUNNING; > + > + inst = CBGetInstance(_BROKER, job->context, job->obj_path, NULL, &s); > + if ((inst == NULL) || (s.rc != CMPI_RC_OK)) { > + CU_DEBUG("Failed to get job instance (%i)", s.rc); > + return NULL; > + } > + > + CMSetProperty(inst, "JobState", > + (CMPIValue *)&(job->status), CMPI_uint16); > + CMSetProperty(inst, "Status", > + (CMPIValue *) "Running", CMPI_chars); > + > + /* Connect to domain event callback */ > + conn = connect_by_classname(_BROKER, CLASSNAME(job->obj_path), &s); > + if (conn == NULL) { > + CU_DEBUG("Unable to connect to '%s' hypervisor", > + CLASSNAME(job->obj_path)); > + goto out; > + } > + > + dom = virDomainLookupByName(conn, job->dom_name); > + if (dom == NULL) { > + CU_DEBUG("Unable to get domain '%s'", job->dom_name); > + goto out; > + } > + > + if (virDomainGetInfo(dom, &info) != 0) { > + CU_DEBUG("Unable to get domain info for '%s'", job->dom_name); > + goto out; > + } > + > + s = state_change_shutdown(dom, &info); > + if (s.rc != CMPI_RC_OK) { > + CU_DEBUG("Unable to trigger domain shutdown: '%s'", > + CMGetCharPtr(s.msg)); > + goto out; > + } > + > + /* Wait for operation (shutdown/reboot) to complete */ > + while (info.state != VIR_DOMAIN_SHUTOFF) { > + usleep(100 * 1000); > + virDomainGetInfo(dom, &info); > + } > + > + CU_DEBUG("Job completed"); > + > + /* Set job state */ > + if (job->status == CIM_JOB_STATE_COMPLETED) { > + CMSetProperty(inst, "JobState", > + (CMPIValue *)&(job->status), CMPI_uint16); > + CMSetProperty(inst, "Status", > + (CMPIValue *) "Completed", CMPI_chars); > + } > + > + out: > + virDomainFree(dom); > + virConnectClose(conn); > + > + CBDetachThread(_BROKER, job->context); > + free(job->dom_name); > + free(job); > + > + end: > + return NULL; > +} > + > static CMPIStatus state_change(CMPIMethodMI *self, > const CMPIContext *context, > const CMPIResult *results, > @@ -1244,7 +1446,8 @@ static CMPIStatus state_change(CMPIMethodMI *self, > goto out; > } > > - /* Retain original instance of the guest to use for the PreviousInstance attribute when generating an indication. */ > + /* Retain original instance of the guest to use for the PreviousInstance > + attribute when generating an indication. */ > s = get_domain_by_name(_BROKER, reference, name, &prev_inst); > if (s.rc != CMPI_RC_OK || prev_inst == NULL) { > cu_statusf(_BROKER, &s, > @@ -1256,8 +1459,27 @@ static CMPIStatus state_change(CMPIMethodMI *self, > > s = __state_change(name, state, reference); > > - if (s.rc == CMPI_RC_OK) > + if (s.rc == CMPI_RC_OK) { > rc = 0; > + goto out; > + } > + > + if (s.rc == CIM_SVPC_RETURN_JOB_STARTED) { > + state_change_job_t *job = NULL; > + s = create_state_change_job(reference, context, &job, state); > + if (s.rc != CMPI_RC_OK) { > + free(job); > + goto out; > + } > + > + job->dom_name = strdup(name); > + > + _BROKER->xft->newThread(state_change_thread, job, 0); > + > + CMAddArg(argsout, "Job", (CMPIValue *)&(job->obj_path), > + CMPI_ref); > + rc = s.rc; > + } > > out: > CMReturnData(results, &rc, CMPI_uint32); From snmishra at linux.vnet.ibm.com Wed Mar 7 17:51:22 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Wed, 07 Mar 2012 09:51:22 -0800 Subject: [Libvirt-cim] [PATCH 3/3] ComputerSystem: Reboot state change also a job In-Reply-To: <1331056553-29221-4-git-send-email-eblima@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331056553-29221-4-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <1331142682.2597.30.camel@snmishra-desktop.beaverton.ibm.com> Visual inspection looks good. Need to apply and test -Sharad On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > As it happens for Shutdown, the RequestStateChange method returns immediately > with return code 0 (successful) even though the state change is still not > completed. > > Change the current job implementation to support reboot as well. For this we > need the libvirt domain events APIs and event loop implementation, which are > only available in libvirt version 0.9.0 or newer. > > Signed-off-by: Eduardo Lima (Etrunko) > --- > src/Virt_ComputerSystem.c | 90 ++++++++++++++++++++++++++++++++++++++------- > 1 files changed, 76 insertions(+), 14 deletions(-) > > diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c > index 778809d..04584f3 100644 > --- a/src/Virt_ComputerSystem.c > +++ b/src/Virt_ComputerSystem.c > @@ -32,6 +32,7 @@ > > #include > #include > +#include > > #include > #include > @@ -61,6 +62,8 @@ struct _state_change_job { > uint16_t status; /* job status */ > }; > > +static bool events_registered = false; > + > /* Set the "Name" property of an instance from a domain */ > static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance) > { > @@ -1203,14 +1206,12 @@ static CMPIStatus __state_change(const char *name, > s = state_change_enable(dom, &info); > else if (state == CIM_STATE_DISABLED) > s = state_change_disable(dom, &info); > - else if (state == CIM_STATE_SHUTDOWN) > - s.rc = CIM_SVPC_RETURN_JOB_STARTED; > else if (state == CIM_STATE_PAUSED) > s = state_change_pause(dom, &info); > - else if (state == CIM_STATE_REBOOT) > - s = state_change_reboot(dom, &info); > else if (state == CIM_STATE_RESET) > s = state_change_reset(dom, &info); > + else if (state == CIM_STATE_SHUTDOWN || state == CIM_STATE_REBOOT) > + s.rc = CIM_SVPC_RETURN_JOB_STARTED; > else > cu_statusf(_BROKER, &s, > CMPI_RC_ERR_NOT_SUPPORTED, > @@ -1333,6 +1334,25 @@ static CMPIStatus create_state_change_job(const CMPIObjectPath *ref, > return s; > } > > +static void state_change_reboot_cb(virConnectPtr conn, > + virDomainPtr dom, > + void *data) > +{ > + state_change_job_t *job = (state_change_job_t *) data; > + job->status = CIM_JOB_STATE_COMPLETED; > +} > + > +static void state_change_shutdown_cb(virConnectPtr conn, > + virDomainPtr dom, > + int event, > + int detail, > + void *data) > +{ > + state_change_job_t *job = (state_change_job_t *) data; > + if (event == VIR_DOMAIN_EVENT_SHUTDOWN) > + job->status = CIM_JOB_STATE_COMPLETED; > +} > + > static CMPI_THREAD_RETURN state_change_thread(void *data) > { > CMPIStatus s; > @@ -1341,8 +1361,10 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > virConnectPtr conn = NULL; > virDomainPtr dom = NULL; > virDomainInfo info; > + int job_cb = -1; > > - if (job->dom_state != CIM_STATE_SHUTDOWN) { > + if (job->dom_state != CIM_STATE_SHUTDOWN && > + job->dom_state != CIM_STATE_REBOOT) { > CU_DEBUG("Unrecognized state '%d'", job->dom_state); > goto end; > } > @@ -1382,17 +1404,56 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > goto out; > } > > - s = state_change_shutdown(dom, &info); > - if (s.rc != CMPI_RC_OK) { > - CU_DEBUG("Unable to trigger domain shutdown: '%s'", > - CMGetCharPtr(s.msg)); > - goto out; > + if (events_registered == false) { > + events_registered = true; > + virEventRegisterDefaultImpl(); > + } > + > + if (job->dom_state == CIM_STATE_REBOOT) { > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > + VIR_DOMAIN_EVENT_ID_REBOOT, > + VIR_DOMAIN_EVENT_CALLBACK(state_change_reboot_cb), > + job, NULL); > + > + if (job_cb == -1) { > + CU_DEBUG("Unable to connect domain reboot callback"); > + goto out; > + } > + > + s = state_change_reboot(dom, &info); > + > + if (s.rc != CMPI_RC_OK) { > + CU_DEBUG("Unable to trigger domain reboot: '%s'", > + CMGetCharPtr(s.msg)); > + goto out; > + } > + } else if (job->dom_state == CIM_STATE_SHUTDOWN) { > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > + VIR_DOMAIN_EVENT_ID_LIFECYCLE, > + VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb), > + job, NULL); > + > + if (job_cb == -1) { > + CU_DEBUG("Unable to connect domain shutdown callback"); > + goto out; > + } > + > + s = state_change_shutdown(dom, &info); > + > + if (s.rc != CMPI_RC_OK) { > + CU_DEBUG("Unable to trigger domain shutdown: '%s'", > + CMGetCharPtr(s.msg)); > + goto out; > + } > } > > /* Wait for operation (shutdown/reboot) to complete */ > - while (info.state != VIR_DOMAIN_SHUTOFF) { > - usleep(100 * 1000); > - virDomainGetInfo(dom, &info); > + while (job->status == CIM_JOB_STATE_RUNNING) { > + if (virEventRunDefaultImpl() < 0) { > + virErrorPtr err = virGetLastError(); > + CU_DEBUG("Failed to run event loop: %s\n", > + err && err->message ? err->message : "Unknown error"); > + } > } > > CU_DEBUG("Job completed"); > @@ -1405,6 +1466,8 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > (CMPIValue *) "Completed", CMPI_chars); > } > > + virConnectDomainEventDeregisterAny(conn, job_cb); > + > out: > virDomainFree(dom); > virConnectClose(conn); > @@ -1412,7 +1475,6 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > CBDetachThread(_BROKER, job->context); > free(job->dom_name); > free(job); > - > end: > return NULL; > } From eblima at linux.vnet.ibm.com Thu Mar 8 13:07:59 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Thu, 08 Mar 2012 10:07:59 -0300 Subject: [Libvirt-cim] [PATCHv2 0/3] Make Shutdown and Reboot state changes jobs In-Reply-To: <1331140322.2597.26.camel@snmishra-desktop.beaverton.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331140322.2597.26.camel@snmishra-desktop.beaverton.ibm.com> Message-ID: <4F58AF2F.3060305@linux.vnet.ibm.com> On 03/07/2012 02:12 PM, Sharad Mishra wrote: > On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: >> From: "Eduardo Lima (Etrunko)" >> >> This code is based on the VSMigrationService for the job creation and on >> ComputerSystemIndication for the job thread, which will listen for the >> respective domain event to mark the job as finished. >> >> I am still not able to test this feature in my environment, every call to >> CBGetInstance fails with code error 100. This is happening in other places of >> the codebase, not only the ComputerSystem provider, but also ElementConfors, >> for instance. Please use the test case send in previous email for reference. > > Eduardo, It is not clear if you started seeing this error code after > applying this patch or were you getting it before. It already happens with other test cases that will cause the provider to make a call such as CBGetInstance, CBCreateInstance and others from the same family. For example, test ElementConforms/02_reverse.py fails with the same error code 100, with or without this patch applied. I suspect it can be something messed up with my environment. Best regards, Eduardo -- Eduardo de Barros Lima Software Engineer, Open Virtualization Linux Technology Center - IBM/Brazil eblima at br.ibm.com From snmishra at linux.vnet.ibm.com Thu Mar 8 16:20:03 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Thu, 08 Mar 2012 08:20:03 -0800 Subject: [Libvirt-cim] [PATCHv2 0/3] Make Shutdown and Reboot state changes jobs In-Reply-To: <4F58AF2F.3060305@linux.vnet.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331140322.2597.26.camel@snmishra-desktop.beaverton.ibm.com> <4F58AF2F.3060305@linux.vnet.ibm.com> Message-ID: <1331223603.2597.31.camel@snmishra-desktop.beaverton.ibm.com> On Thu, 2012-03-08 at 10:07 -0300, Eduardo Lima (Etrunko) wrote: > On 03/07/2012 02:12 PM, Sharad Mishra wrote: > > On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > >> From: "Eduardo Lima (Etrunko)" > >> > >> This code is based on the VSMigrationService for the job creation and on > >> ComputerSystemIndication for the job thread, which will listen for the > >> respective domain event to mark the job as finished. > >> > >> I am still not able to test this feature in my environment, every call to > >> CBGetInstance fails with code error 100. This is happening in other places of > >> the codebase, not only the ComputerSystem provider, but also ElementConfors, > >> for instance. Please use the test case send in previous email for reference. > > > > Eduardo, It is not clear if you started seeing this error code after > > applying this patch or were you getting it before. > > It already happens with other test cases that will cause the provider to > make a call such as CBGetInstance, CBCreateInstance and others from the > same family. For example, test ElementConforms/02_reverse.py fails with > the same error code 100, with or without this patch applied. I suspect > it can be something messed up with my environment. I have not seen that issue, so I suspect it is your environment. What level of libcmpiutil are you running? -Sharad > > Best regards, Eduardo > > From veillard at redhat.com Fri Mar 9 07:11:10 2012 From: veillard at redhat.com (Daniel Veillard) Date: Fri, 9 Mar 2012 15:11:10 +0800 Subject: [Libvirt-cim] [PATCH] fix spec file for sblim-sfcb and systemctl Message-ID: <20120309071110.GV19825@redhat.com> Right now the spec file is completely tied to tog-pegasus, this should allow the spec file to be compatible with sblim-sfcb too. This also switches to systemctl which is now used in Fedora. I would actually like some feedback on the patch if someone has an environment to test with sblim-sfcb Also I noted that if I built the package with sblim-sfcb installed it failed in the make install phase with the following error: ---------------------------------------------------------- /bin/sh /u/veillard/rpms/BUILD/libvirt-cim-0.6.1/install-sh -c -m 644 -t "/u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim" ./schema/RegisteredProfile.registration ./schema/ElementConformsToProfile.registration ./schema/ReferencedProfile.registration if [[ sfcb != pegasus ]]; then \ sed -i '/^# --/,/^# --!/d' .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/RegisteredProfile.registration .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/ElementConformsToProfile.registration .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/ReferencedProfile.registration; \ fi sed: can't read .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/RegisteredProfile.registration: No such file or directory sed: can't read .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/ElementConformsToProfile.registration: No such file or directory sed: can't read .//u/veillard/rpms/BUILDROOT/libvirt-cim-0.6.1-2.fc18.x86_64/usr/share/libvirt-cim/ReferencedProfile.registration: No such file or directory make[2]: *** [install-data-local] Error 2 make[2]: Leaving directory `/home/veillard/rpms/BUILD/libvirt-cim-0.6.1' make[1]: *** [install-am] Error 2 make[1]: Leaving directory `/home/veillard/rpms/BUILD/libvirt-cim-0.6.1' make: *** [install-recursive] Error 1 ---------------------------------------------------------- Any idea ? thanks, Daniel --- libvirt-cim.spec 2012-02-10 13:49:31.679905149 +0800 +++ libvirt-cim.spec 2012-03-09 14:41:45.617781910 +0800 @@ -12,7 +12,8 @@ Requires: libxml2 >= 2.6.0 Requires: libvirt >= 0.9.0 Requires: unzip -Requires: tog-pegasus +# either tog-pegasus or sblim-sfcb should provide cim-server +Requires: cim-server BuildRequires: libcmpiutil >= 0.5.4 BuildRequires: tog-pegasus-devel BuildRequires: libvirt-devel >= 0.9.0 @@ -85,8 +86,18 @@ %{_datadir}/%{name}/install_base_schema.sh %{_datadir}/%{name} -/etc/init.d/tog-pegasus condrestart +if [ "`systemctl is-active tog-pegasus.service`" = "active" ] +then + systemctl restart tog-pegasus.service +fi + +if [ "`systemctl is-active sblim-sfcb.service`" = "active" ] +then + systemctl restart sblim-sfcb.service +fi +if [ -x /usr/sbin/cimserver ] +then %{_datadir}/%{name}/provider-register.sh -t pegasus \ -n root/virt \ -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true @@ -102,8 +113,29 @@ %{_datadir}/%{name}/provider-register.sh -t pegasus \ -n root/cimv2\ -r %{CIMV2_REG} -m %{CIMV2_MOF} -v >/dev/null 2>&1 || true +fi +if [ -x /usr/sbin/sfcbd ] +then +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/interop \ + -r %{INTEROP_REG} -m %{INTEROP_MOF} -v >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/PG_InterOp \ + -r %{PGINTEROP_REG} -m %{PGINTEROP_MOF} -v >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/cimv2\ + -r %{CIMV2_REG} -m %{CIMV2_MOF} -v >/dev/null 2>&1 || true +fi %preun +if [ -x /usr/sbin/cimserver ] +then %{_datadir}/%{name}/provider-register.sh -d -t pegasus \ -n root/virt \ -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true @@ -116,6 +148,22 @@ %{_datadir}/%{name}/provider-register.sh -d -t pegasus \ -n root/cimv2 \ -r %{CIMV2_REG} -m %{CIMV2_MOF} >/dev/null 2>&1 || true +fi +if [ -x /usr/sbin/sfcbd ] +then +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/interop \ + -r %{INTEROP_REG} -m %{INTEROP_MOF} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/PG_InterOp \ + -r %{PGINTEROP_REG} -m %{PGINTEROP_MOF} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/cimv2 \ + -r %{CIMV2_REG} -m %{CIMV2_MOF} >/dev/null 2>&1 || true +fi %postun -p /sbin/ldconfig -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ From kkaempf at suse.de Fri Mar 9 08:32:58 2012 From: kkaempf at suse.de (Klaus Kaempf) Date: Fri, 9 Mar 2012 09:32:58 +0100 Subject: [Libvirt-cim] [PATCH] fix spec file for sblim-sfcb and systemctl In-Reply-To: <20120309071110.GV19825@redhat.com> References: <20120309071110.GV19825@redhat.com> Message-ID: <20120309083258.GA5592@heron.suse.de> Daniel, thanks for the patch. * Daniel Veillard [Mar 09. 2012 08:12]: > Right now the spec file is completely tied to tog-pegasus, > this should allow the spec file to be compatible with sblim-sfcb > too. This also switches to systemctl which is now used in Fedora. > > I would actually like some feedback on the patch if someone > has an environment to test with sblim-sfcb I'll test later today. For reference, you might want to take a look at the spec file of https://build.opensuse.org/package/files?package=libvirt-cim&project=systemsmanagement%3Awbem which is a pure-sfcb variant. > > Also I noted that if I built the package with sblim-sfcb installed > it failed in the make install phase with the following error: Thats a bug in the Makefile.am which does an incomplete substitution - patch attached. Regards, Klaus --- SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix Imend?rffer, HRB 16746 (AG N?rnberg) Maxfeldstra?e 5, 90409 N?rnberg, Germany -------------- next part -------------- A non-text attachment was scrubbed... Name: fix-sfcb-install.patch Type: text/x-patch Size: 606 bytes Desc: not available URL: From veillard at redhat.com Mon Mar 12 09:18:46 2012 From: veillard at redhat.com (Daniel Veillard) Date: Mon, 12 Mar 2012 17:18:46 +0800 Subject: [Libvirt-cim] [PATCH] fix spec file for sblim-sfcb and systemctl In-Reply-To: <20120309083258.GA5592@heron.suse.de> References: <20120309071110.GV19825@redhat.com> <20120309083258.GA5592@heron.suse.de> Message-ID: <20120312091846.GI19825@redhat.com> On Fri, Mar 09, 2012 at 09:32:58AM +0100, Klaus Kaempf wrote: > Daniel, > > thanks for the patch. > > * Daniel Veillard [Mar 09. 2012 08:12]: > > Right now the spec file is completely tied to tog-pegasus, > > this should allow the spec file to be compatible with sblim-sfcb > > too. This also switches to systemctl which is now used in Fedora. > > > > I would actually like some feedback on the patch if someone > > has an environment to test with sblim-sfcb > > I'll test later today. > For reference, you might want to take a look at the spec file of > https://build.opensuse.org/package/files?package=libvirt-cim&project=systemsmanagement%3Awbem > which is a pure-sfcb variant. > > > > > Also I noted that if I built the package with sblim-sfcb installed > > it failed in the make install phase with the following error: > > Thats a bug in the Makefile.am which does an incomplete substitution - > patch attached. Hi Klaus, thanks for the patch, indeed it fixes the problem! I'm not sure what you are trying to fix in provider-reg.patch, but it seems to make sense to really pass them as 3 separate args instead of one big one. I think the biggest difference I can spot between the two spec files are the fact that you uninstall only the main schemas on %preun while we remove all the ones we registered in %post. Not really significant but I could have overlooked something :-) Chip, eduardo, could you look at those 3 paches ? Mine, the one that Klaus sent and https://build.opensuse.org/package/view_file?file=provider-reg.patch&package=libvirt-cim&project=systemsmanagement%3Awbem&rev=badf80eca4fd06ed9f84a67eafcbc760 thanks again ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ From veillard at redhat.com Tue Mar 13 06:52:34 2012 From: veillard at redhat.com (Daniel Veillard) Date: Tue, 13 Mar 2012 14:52:34 +0800 Subject: [Libvirt-cim] [PATCH] Fix possible memory leaks In-Reply-To: <1331055955-28998-1-git-send-email-eblima@linux.vnet.ibm.com> References: <1331055955-28998-1-git-send-email-eblima@linux.vnet.ibm.com> Message-ID: <20120313065234.GO19825@redhat.com> On Tue, Mar 06, 2012 at 02:45:55PM -0300, Eduardo Lima (Etrunko) wrote: > From: "Eduardo Lima (Etrunko)" > > Another Coverity scan report provided by Red Hat: > https://bugzilla.redhat.com/show_bug.cgi?id=750418#c10 > > Error: RESOURCE_LEAK: > Virt_AppliedFilterList.c:608: alloc_arg: Calling allocation function "get_dominfo" on "dominfo". > device_parsing.c:1251: alloc_arg: "get_dominfo_from_xml" allocates memory that is stored into "*dominfo". > device_parsing.c:1205: alloc_fn: Storage is returned from allocation function "calloc". > device_parsing.c:1205: var_assign: Assigning: "*dominfo" = "calloc(1UL, 216UL)". > device_parsing.c:1209: noescape: Variable "*dominfo" is not freed or pointed-to in function "_get_dominfo". > device_parsing.c:1168:57: noescape: "_get_dominfo" does not free or save its pointer parameter "dominfo". > Virt_AppliedFilterList.c:644: leaked_storage: Variable "dominfo" going out of scope leaks the storage it points to. > > Error: RESOURCE_LEAK: > Virt_AppliedFilterList.c:485: alloc_arg: Calling allocation function "get_dominfo" on "dominfo". > device_parsing.c:1251: alloc_arg: "get_dominfo_from_xml" allocates memory that is stored into "*dominfo". > device_parsing.c:1205: alloc_fn: Storage is returned from allocation function "calloc". > device_parsing.c:1205: var_assign: Assigning: "*dominfo" = "calloc(1UL, 216UL)". > device_parsing.c:1209: noescape: Variable "*dominfo" is not freed or pointed-to in function "_get_dominfo". > device_parsing.c:1168:57: noescape: "_get_dominfo" does not free or save its pointer parameter "dominfo". > Virt_AppliedFilterList.c:529: leaked_storage: Variable "dominfo" going out of scope leaks the storage it points to. > > Error: RESOURCE_LEAK: > misc_util.c:275: alloc_arg: Calling allocation function "get_domain_list" on "list". > cs_util_instance.c:52: alloc_fn: Storage is returned from allocation function "calloc". > cs_util_instance.c:52: var_assign: Assigning: "list" = "calloc(n_names + n_ids, 8UL)". > cs_util_instance.c:112: var_assign: Assigning: "*_list" = "list". > misc_util.c:277: leaked_storage: Variable "list" going out of scope leaks the storage it points to. > > Signed-off-by: Eduardo Lima (Etrunko) > --- > libxkutil/misc_util.c | 6 +++--- > src/Virt_AppliedFilterList.c | 4 ++-- > 2 files changed, 5 insertions(+), 5 deletions(-) > > diff --git a/libxkutil/misc_util.c b/libxkutil/misc_util.c > index 61893c3..2d149ae 100644 > --- a/libxkutil/misc_util.c > +++ b/libxkutil/misc_util.c > @@ -267,14 +267,14 @@ bool match_pn_to_cn(const char *pn, const char *cn) > > uint64_t allocated_memory(virConnectPtr conn) > { > - virDomainPtr *list; > + virDomainPtr *list = NULL; > int count; > int i; > uint64_t memory = 0; > > count = get_domain_list(conn, &list); > if (count <= 0) > - return 0; > + goto end; > > for (i = 0; i < count; i ++) { > virDomainPtr dom = list[i]; > @@ -286,8 +286,8 @@ uint64_t allocated_memory(virConnectPtr conn) > virDomainFree(dom); > } > > + end: > free(list); > - > return memory; > } > > diff --git a/src/Virt_AppliedFilterList.c b/src/Virt_AppliedFilterList.c > index 538adf4..0dfe6a3 100644 > --- a/src/Virt_AppliedFilterList.c > +++ b/src/Virt_AppliedFilterList.c > @@ -521,7 +521,7 @@ static CMPIStatus CreateInstance( > free(net_name); > > cleanup_filters(&filter, 1); > - cleanup_virt_devices(&device, 1); > + cleanup_dominfo(&dominfo); > > virDomainFree(dom); > virConnectClose(conn); > @@ -636,7 +636,7 @@ static CMPIStatus DeleteInstance( > free(net_name); > > cleanup_filters(&filter, 1); > - cleanup_virt_devices(&device, 1); > + cleanup_dominfo(&dominfo); > > virDomainFree(dom); > virConnectClose(conn); Our QE confirmed that this fixed the leak detected, ACK from me :) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ From eblima at linux.vnet.ibm.com Tue Mar 13 17:04:12 2012 From: eblima at linux.vnet.ibm.com (Eduardo Lima (Etrunko)) Date: Tue, 13 Mar 2012 14:04:12 -0300 Subject: [Libvirt-cim] [PATCH] fix spec file for sblim-sfcb and systemctl In-Reply-To: <20120312091846.GI19825@redhat.com> References: <20120309071110.GV19825@redhat.com> <20120309083258.GA5592@heron.suse.de> <20120312091846.GI19825@redhat.com> Message-ID: <4F5F7E0C.8030606@linux.vnet.ibm.com> On 03/12/2012 06:18 AM, Daniel Veillard wrote: > On Fri, Mar 09, 2012 at 09:32:58AM +0100, Klaus Kaempf wrote: >> Daniel, >> >> thanks for the patch. >> >> * Daniel Veillard [Mar 09. 2012 08:12]: >>> Right now the spec file is completely tied to tog-pegasus, >>> this should allow the spec file to be compatible with sblim-sfcb >>> too. This also switches to systemctl which is now used in Fedora. >>> >>> I would actually like some feedback on the patch if someone >>> has an environment to test with sblim-sfcb >> >> I'll test later today. >> For reference, you might want to take a look at the spec file of >> https://build.opensuse.org/package/files?package=libvirt-cim&project=systemsmanagement%3Awbem >> which is a pure-sfcb variant. >> >>> >>> Also I noted that if I built the package with sblim-sfcb installed >>> it failed in the make install phase with the following error: >> >> Thats a bug in the Makefile.am which does an incomplete substitution - >> patch attached. > > Hi Klaus, > > thanks for the patch, indeed it fixes the problem! I'm not sure what > you are trying to fix in provider-reg.patch, but it seems to make sense > to really pass them as 3 separate args instead of one big one. > I think the biggest difference I can spot between the two spec files > are the fact that you uninstall only the main schemas on %preun while > we remove all the ones we registered in %post. Not really significant > but I could have overlooked something :-) > Chip, eduardo, could you look at those 3 paches ? Mine, the one > that Klaus sent and > https://build.opensuse.org/package/view_file?file=provider-reg.patch&package=libvirt-cim&project=systemsmanagement%3Awbem&rev=badf80eca4fd06ed9f84a67eafcbc760 > The problem is that the patch for the spec file actually does not apply, can you resend it against .spec.in, instead of .spec? Anyway, I don't have much to say here, because I am not running with sblim-sfcb in my envioronment. I tested with pegasus and things still work fine. Second patch is also tested and works, but I don't see a reason for that third patch. What is the error it intends to fix? Best regards, Eduardo > thanks again ! > > Daniel > -- Eduardo de Barros Lima Software Engineer, Open Virtualization Linux Technology Center - IBM/Brazil eblima at br.ibm.com From deheller at ptd.net Mon Mar 19 17:21:26 2012 From: deheller at ptd.net (Dave Heller) Date: Mon, 19 Mar 2012 13:21:26 -0400 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py Message-ID: <20120319172126.4222C1AD14AD@proxyz11.mailnet.ptd.net> In the XenKvmLib directory in cimtest, there is a "indication_tester.py" module that is both a command-line program and a component of cimtest. Here is a proposed patch that adds new functionality to the cmdline interface while preserving compatibility with cimtest. The most significant new feature is support for SSL indications. There is also a new --trigger mode to generate a test indication using the SFCB Test_Indication provider. This allows the script to act as a completely stand-alone test tool (for SFCB) without the user having to rely on the "xmltest" files and wbemcat. Below is a complete list of the new features. I am looking for feedback and potentially, the correct process to check into "cimtest". Best regards, Dave Heller New features: - Supports https indications. Adds new options --certFile and --keyFile to specify server certificate and private key for the SSL enabled indication listener. - Supports verification of indication sender (i.e. CIMOM) SSL certificate. New option --trustStore is used to specify the CA certificate file; alternately the --noverify option may be used to disable peer certificate checking. - Adds new option --dest to specify handler destination. This same URL is used to register the subscription (at the CIMOM) and start the indication listener (locally). Via --dest one can specify scheme (http or https), IP or hostname, listener port, and optionally userid:password, all in a single parameter. - New option --interop can be used to override the default interop namespace (i.e root/interop vs. root/PG_InterOp) allowing easy support for both openpegasus and sfcb. - Modifies the default value of existing --ns option (indication namespace); if not specified the indication namespace will default to the same value as the interop namespace. - New option --verbose can be used to display debugging info, such as details (subject, issuer) of the indication sender cert (when peer verification enabled). - Improved error handling (i.e. catch exceptions) for the following operations: starting listener locally, receiving incoming indications, POST operations (i.e. subscribe, unsubscribe) to CIMOM. - New option --trigger can be used to send a request to CIMOM to trigger an indication via the SFCB Test_Indication provider. Limitations: - Not tested on IPv6. - The --trigger option currently only supports the sfcb Test_Indication provider. (It could be made more general by allowing the XML send to support an arbitrary namespace, classname and method call. Open to suggestions here.) --- indication_tester.orig.py 2012-03-05 23:12:08.000000000 -0500 +++ indication_tester.py 2012-03-19 13:12:55.000000000 -0400 @@ -1,18 +1,25 @@ #!/usr/bin/python -# Copyright IBM Corp. 2007 +# Copyright IBM Corp. 2007-2012 # # indication_tester.py - Tool for testing indication subscription and # delivery against a CIMOM # Author: Dan Smith +# SSL support added by Dave Heller import sys from optparse import OptionParser -import BaseHTTPServer +from urlparse import urlparse, urlunparse +from xml.dom.minidom import parse, parseString import httplib import base64 -from xml.dom.minidom import parse, parseString +import errno, os, re +import socket +from SocketServer import BaseServer +from SimpleHTTPServer import SimpleHTTPRequestHandler +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from OpenSSL import SSL -def filter_xml(name, type, ns, sysname): +def filter_xml(name, type, ns, sysname, interopNS): return """ @@ -20,8 +27,8 @@ - - + + @@ -53,9 +60,9 @@ - """ % (sysname, name, type, ns) + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) -def handler_xml(name, port, sysname): +def handler_xml(name, destUrl, sysname, interopNS): return """ @@ -63,8 +70,8 @@ - - + + @@ -81,7 +88,7 @@ %sHandler - http://localhost:%i + %s @@ -89,9 +96,9 @@ - """ % (sysname, name, port) + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) -def subscription_xml(name, sysname): +def subscription_xml(name, sysname, interopNS): return """ @@ -99,8 +106,8 @@ - - + + @@ -167,9 +174,9 @@ - """ % (sysname, name, sysname, name) + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) -def delete_inst_xml(name, type, sysname, inst_name): +def delete_inst_xml(name, type, sysname, inst_name, interopNS): return """ @@ -177,8 +184,8 @@ - - + + @@ -200,9 +207,9 @@ ; - """ % (type, sysname, type, inst_name); + """ % (interopNS[0], interopNS[1], type, sysname, type, inst_name); -def delete_sub_xml(name, sysname): +def delete_sub_xml(name, sysname, interopNS): return """ @@ -210,8 +217,8 @@ - - + + @@ -273,7 +280,45 @@ ; - """ % (sysname, name, sysname, name) + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) + +def trigger_xml(type, interopNS): + return """ + + + + + + + + + + + + + + + + + """ % (interopNS[0], interopNS[1], type) + # FIXME: this should really use indication NS, not interop NS. + +def update_url_port(parsedUrl, port): + # Must manually reconstruct netloc to update the port value. + if isinstance(parsedUrl.username, basestring): + if isinstance(parsedUrl.password, basestring): + netloc = "%s:%s@%s:%s" % (parsedUrl.username, parsedUrl.password, + parsedUrl.hostname, port) + else: + netloc = "%s@%s:%s" % (parsedUrl.username, + parsedUrl.hostname, port) + else: + netloc = "%s:%s" % (parsedUrl.hostname, port) + + # Reassemble url with the updated netloc. return a string. + return urlunparse((parsedUrl.scheme, netloc, + parsedUrl.path, parsedUrl.params, + parsedUrl.query, parsedUrl.fragment)) class CIMIndication: def __init__(self, xmldata): @@ -286,50 +331,135 @@ def __str__(self): return self.name -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): +def socket_handler_wrapper(*parms): + try: + CIMSocketHandler(*parms) + except Exception as e: + print "SSL error: %s" % str(e) + +class CIMSocketHandler(SimpleHTTPRequestHandler): + def setup(self): + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + def do_POST(self): length = self.headers.getheader('content-length') data = self.rfile.read(int(length)) indication = CIMIndication(data) - print "Got indication: %s" % indication + print "Got indication: %s from %s" % (indication, self.client_address) if self.server.print_ind: - print "%s\n\n" % data + print "%s\n" % data self.server.indications.append(indication) + # Silence the unwanted log output from send_response() + realStderr = sys.stderr + sys.stderr = open(os.devnull,'a') + self.send_response(200) + sys.stderr = realStderr + +class SecureHTTPServer(HTTPServer): + def __init__(self, server_address, HandlerClass): + BaseServer.__init__(self, server_address, HandlerClass) + + def verify_cb(conn, cert, errnum, depth, ok): + if options.verbose: + print('Verify peer certificate chain: level %d:' % depth) + print('subject=%s' % cert.get_subject()) + print('issuer =%s' % cert.get_issuer()) + return ok + + ctx = SSL.Context(SSL.SSLv23_METHOD) + #ctx.use_certificate_file(options.certFile) + ctx.use_certificate_chain_file(options.certFile) + ctx.use_privatekey_file(options.keyFile) + + if options.noverify: + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) + else: + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) + ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, + verify_cb) + ctx.load_verify_locations(options.trustStore) + + self.socket = SSL.Connection(ctx, socket.socket(self.address_family, + self.socket_type)) + self.server_bind() + self.server_activate() +# The param defaults allow new options from main() w/o losing compat w/ cimtest. class CIMIndicationSubscription: - def __init__(self, name, typ, ns, print_ind, sysname, port=0): + def __init__(self, name, typ, ns, print_ind, sysname, port=0, + interopNS=('root','PG_InterOp'), destUrl="http://localhost:8000", + triggermode=False): self.name = name self.type = typ self.ns = ns self.sysname = sysname + self.interopNS = interopNS + self.print_ind = print_ind + + # We do not want to open a listener socket in trigger mode. + if triggermode: + self.trigger_xml = trigger_xml(typ, interopNS) + return + + parsedUrl = urlparse(destUrl) + + # Increment the listener port by the offset value. + if isinstance(parsedUrl.port, int): + listenerPort = parsedUrl.port + port + else: + listenerPort = 8000 + port + + destUrl = update_url_port(parsedUrl, listenerPort) + + try: + if parsedUrl.scheme == "http": + self.server = HTTPServer((parsedUrl.hostname, + listenerPort), + CIMSocketHandler) + elif parsedUrl.scheme == "https": + self.server = SecureHTTPServer((parsedUrl.hostname, + listenerPort), + socket_handler_wrapper) + except IOError as e: + print "Error creating listener socket: %s" % str(e) + exit(e.errno) - self.port = 8000 + port - self.server = BaseHTTPServer.HTTPServer(('', self.port), - CIMSocketHandler) self.server.print_ind = print_ind self.server.indications = [] - self.filter_xml = filter_xml(name, typ, ns, sysname) - self.handler_xml = handler_xml(name, self.port, sysname) - self.subscription_xml = subscription_xml(name, sysname) + self.filter_xml = filter_xml(name, typ, ns, sysname, interopNS) + self.handler_xml = handler_xml(name, destUrl, sysname, interopNS) + self.subscription_xml = subscription_xml(name, sysname, interopNS) def __do_cimpost(self, conn, body, method, auth_hdr=None): headers = {"CIMOperation" : "MethodCall", "CIMMethod" : method, - "CIMObject" : "root/PG_Interop", + #"CIMObject" : "root/PG_Interop", + "CIMObject" : "%s/%s" % self.interopNS, "Content-Type" : 'application/xml; charset="utf-8"'} if auth_hdr: headers["Authorization"] = "Basic %s" % auth_hdr - conn.request("POST", "/cimom", body, headers) - resp = conn.getresponse() - if not resp.getheader("content-length"): - raise Exception("Request Failed: %d %s" % - (resp.status, resp.reason)) - - resp.read() + try: + conn.request("POST", "/cimom", body, headers) + resp = conn.getresponse() + if not resp.getheader("content-length"): + raise Exception("Request Failed: %d %s" % + (resp.status, resp.reason)) + except IOError as e: + print "Error connecting to CIMOM: %s" % str(e) + exit(e.errno) + + if self.print_ind: + print "Reply from CIMOM:" + #print resp.msg + print resp.read() + else: + resp.read() def subscribe(self, url, cred=None): self.conn = httplib.HTTPConnection(url) @@ -353,26 +483,39 @@ else: auth_hdr = None - xml = delete_sub_xml(self.name, self.sysname) + xml = delete_sub_xml(self.name, self.sysname, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, - "%sHandler" % self.name) + "%sHandler" % self.name, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) xml = delete_inst_xml(self.name, "Filter", self.sysname, - "%sFilter" % self.name) + "%sFilter" % self.name, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) -def dump_xml(name, typ, ns, sysname): - filter_str = filter_xml(name, typ, ns, sysname) - handler_str = handler_xml(name, 8000, sysname) - subscript_str = subscription_xml(name, sysname) - del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % name) + def trigger(self, url, cred=None): + self.conn = httplib.HTTPConnection(url) + if cred: + (u, p) = cred + auth_hdr = base64.b64encode("%s:%s" % (u, p)) + else: + auth_hdr = None + + self.__do_cimpost(self.conn, self.trigger_xml, + "SendTestIndication", auth_hdr) + +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): + filter_str = filter_xml(name, typ, ns, sysname, interopNS) + handler_str = handler_xml(name, destUrl, sysname, interopNS) + subscript_str = subscription_xml(name, sysname, interopNS) + del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % name, + interopNS) del_handler_str = delete_inst_xml(name, "HandlerCIMXML", sysname, - "%sHandler" % name) - del_subscript_str = delete_sub_xml(name, sysname) + "%sHandler" % name, interopNS) + del_subscript_str = delete_sub_xml(name, sysname, interopNS) + trigger_str = trigger_xml(typ, interopNS) print "CreateFilter:\n%s\n" % filter_str print "DeleteFilter:\n%s\n" % del_filter_str @@ -380,15 +523,20 @@ print "DeleteHandler:\n%s\n" % del_handler_str print "CreateSubscription:\n%s\n" % subscript_str print "DeleteSubscription:\n%s\n" % del_subscript_str + print "Indication trigger:\n%s\n" % trigger_str def main(): usage = "usage: %prog [options] provider\nex: %prog CIM_InstModification" parser = OptionParser(usage) + # FIXME: SecureHTTPServer still relies on this, need a better way. + global options + parser.add_option("-u", "--url", dest="url", default="localhost:5988", help="URL of CIMOM to connect to (host:port)") - parser.add_option("-N", "--ns", dest="ns", default="root/virt", - help="Namespace (default is root/virt)") + parser.add_option("-N", "--ns", dest="ns", + help="Namespace in which the register the indication \ + (default is the same value as the interop namespace)") parser.add_option("-n", "--name", dest="name", default="Test", help="Name for filter, handler, subscription \ (default: Test)") @@ -398,16 +546,41 @@ parser.add_option("-p", "--print-ind", dest="print_ind", default=False, action="store_true", help="Print received indications to stdout.") + parser.add_option("-v", "--verbose", dest="verbose", default=False, + action="store_true", + help="Print addtional debug info.") parser.add_option("-U", "--user", dest="username", default=None, - help="HTTP Auth username") + help="HTTP Auth username (CIMOM)") parser.add_option("-P", "--pass", dest="password", default=None, - help="HTTP Auth password") + help="HTTP Auth password (CIMOM)") parser.add_option("--port", dest="port", default=0, type=int, help="Port increment value (server default: 8000)") + parser.add_option("--dest", dest="destUrl", default="localhost:8000", + help="URL of destination handler \ + (default: http://localhost:8000)") + parser.add_option("--certFile", dest="certFile", default=None, + help="File containing the local certificate to use") + parser.add_option("--keyFile", dest="keyFile", default=None, + help="File containing private key for local cert \ + (if none provided, assume key is in the certFile)") + parser.add_option("--trustStore", dest="trustStore", default=None, + help="File containing trusted certificates for \ + remote endpoint verification") + parser.add_option("--noverify", dest="noverify", default=False, + action="store_true", + help="Skip verification of remote endpoint certificate \ + for incoming https indications") + parser.add_option("-i", "--interop", dest="interop", + default="root/interop", + help="Interop namespace name (default: root/interop)") + parser.add_option("-t", "--trigger", dest="trigger", default=False, + action="store_true", + help="Trigger mode: send a request to CIMOM to trigger \ + an indication via a method call ") (options, args) = parser.parse_args() - if len(args) == 0: + if not options.trigger and len(args)==0: print "Fatal: no indication type provided." sys.exit(1) @@ -421,20 +594,75 @@ else: sysname = options.url + if "/" in options.interop: + options.interopNS = tuple(options.interop.split("/")) + else: + options.interopNS = ("root", options.interop) + + # If no value provided for indication NS, default is same as interopNS. + if not options.ns: + options.ns = "%s/%s" % options.interopNS + + if options.verbose: + print "Interop namespace = %s/%s" % options.interopNS + print "Indication namespace = %s" % options.ns + + # If url does not begin with http or https, assume http. + parsedUrl = urlparse(options.destUrl) + if not re.search(parsedUrl.scheme, "https"): + destUrl = "http://" + options.destUrl + else: + destUrl = options.destUrl + + if parsedUrl.scheme == "https": + if not options.trustStore and not options.noverify: + print "Error: must provide --trustStore or --noverify with https." + sys.exit(1) + elif options.trustStore and options.noverify: + print "Error: options --trustStore and --noverify are exclusive." + sys.exit(1) + if not options.certFile: + print "Error: no certificate file provided." + sys.exit(1) + elif not options.keyFile: + print "No keyFile provided; assuming private key \ + contained in certFile." + options.keyFile = options.certFile + if options.dump: - dump_xml(options.name, args[0], options.ns, sysname) + if isinstance(parsedUrl.port, int): + listenerPort = parsedUrl.port + options.port + else: + listenerPort = 8000 + options.port + + destUrl = update_url_port(parsedUrl, listenerPort) + dump_xml(options.name, args[0], options.ns, sysname, options.interopNS, + destUrl) + sys.exit(0) + + # Trigger mode: currently only supports SFCB Test_Indication provider. + if options.trigger: + classname = "Test_Indication" + sub = CIMIndicationSubscription(options.name, classname, options.ns, + options.print_ind, sysname, options.port, + options.interopNS, destUrl, True) + print "Triggering indication for %s" % classname + sub.trigger(options.url, auth) sys.exit(0) sub = CIMIndicationSubscription(options.name, args[0], options.ns, - options.print_ind, sysname, options.port) + options.print_ind, sysname, options.port, + options.interopNS, destUrl) + + print "Creating subscription for %s" % args[0] sub.subscribe(options.url, auth) print "Watching for %s" % args[0] try: sub.server.serve_forever() - except KeyboardInterrupt,e: - sub.unsubscribe(auth) + except KeyboardInterrupt as e: print "Cancelling subscription for %s" % args[0] + sub.unsubscribe(auth) if __name__=="__main__": sys.exit(main()) From snmishra at linux.vnet.ibm.com Mon Mar 19 21:39:35 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Mon, 19 Mar 2012 14:39:35 -0700 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py In-Reply-To: <20120319172126.4222C1AD14AD@proxyz11.mailnet.ptd.net> References: <20120319172126.4222C1AD14AD@proxyz11.mailnet.ptd.net> Message-ID: <1332193175.5173.19.camel@snmishra-desktop.beaverton.ibm.com> Dave, I assume that this patch is not going to be applied to the existing indication_tester.py in cimtest. In that case, we can add this modified indication_tester.py as a new file at the same location as current indication_tester.py. We do need to give this script a new name. This script is currently not used/invoked by any existing cimtests. Do you want to write tests to use this script? Regards Sharad Mishra On Mon, 2012-03-19 at 13:21 -0400, Dave Heller wrote: > In the XenKvmLib directory in cimtest, there is a "indication_tester.py" > module that is both a command-line program and a component of cimtest. Here > is a proposed patch that adds new functionality to the cmdline interface > while preserving compatibility with cimtest. > > The most significant new feature is support for SSL indications. There is > also a new --trigger mode to generate a test indication using the SFCB > Test_Indication provider. This allows the script to act as a completely > stand-alone test tool (for SFCB) without the user having to rely on the > "xmltest" files and wbemcat. > > Below is a complete list of the new features. I am looking for feedback and > potentially, the correct process to check into "cimtest". > > Best regards, > Dave Heller > > New features: > > - Supports https indications. Adds new options --certFile and --keyFile to > specify server certificate and private key for the SSL enabled indication > listener. > - Supports verification of indication sender (i.e. CIMOM) SSL certificate. > New option --trustStore is used to specify the CA certificate file; > alternately the --noverify option may be used to disable peer certificate > checking. > - Adds new option --dest to specify handler destination. This same URL is > used to register the subscription (at the CIMOM) and start the indication > listener (locally). Via --dest one can specify scheme (http or https), IP or > hostname, listener port, and optionally userid:password, all in a single > parameter. > - New option --interop can be used to override the default interop namespace > (i.e root/interop vs. root/PG_InterOp) allowing easy support for both > openpegasus and sfcb. > - Modifies the default value of existing --ns option (indication namespace); > if not specified the indication namespace will default to the same value as > the interop namespace. > - New option --verbose can be used to display debugging info, such as > details (subject, issuer) of the indication sender cert (when peer > verification enabled). > - Improved error handling (i.e. catch exceptions) for the following > operations: starting listener locally, receiving incoming indications, POST > operations (i.e. subscribe, unsubscribe) to CIMOM. > - New option --trigger can be used to send a request to CIMOM to trigger an > indication via the SFCB Test_Indication provider. > > Limitations: > > - Not tested on IPv6. > - The --trigger option currently only supports the sfcb Test_Indication > provider. (It could be made more general by allowing the XML send to > support an arbitrary namespace, classname and method call. Open to > suggestions here.) > > > --- indication_tester.orig.py 2012-03-05 23:12:08.000000000 -0500 > +++ indication_tester.py 2012-03-19 13:12:55.000000000 -0400 > @@ -1,18 +1,25 @@ > #!/usr/bin/python > -# Copyright IBM Corp. 2007 > +# Copyright IBM Corp. 2007-2012 > # > # indication_tester.py - Tool for testing indication subscription and > # delivery against a CIMOM > # Author: Dan Smith > +# SSL support added by Dave Heller > > import sys > from optparse import OptionParser > -import BaseHTTPServer > +from urlparse import urlparse, urlunparse > +from xml.dom.minidom import parse, parseString > import httplib > import base64 > -from xml.dom.minidom import parse, parseString > +import errno, os, re > +import socket > +from SocketServer import BaseServer > +from SimpleHTTPServer import SimpleHTTPRequestHandler > +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer > +from OpenSSL import SSL > > -def filter_xml(name, type, ns, sysname): > +def filter_xml(name, type, ns, sysname, interopNS): > return """ > > > @@ -20,8 +27,8 @@ > > > > - > - > + > + > > > > @@ -53,9 +60,9 @@ > > > > - """ % (sysname, name, type, ns) > + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) > > -def handler_xml(name, port, sysname): > +def handler_xml(name, destUrl, sysname, interopNS): > return """ > > > @@ -63,8 +70,8 @@ > > > > - > - > + > + > > > > @@ -81,7 +88,7 @@ > %sHandler > > > - http://localhost:%i > + %s > > > > @@ -89,9 +96,9 @@ > > > > - """ % (sysname, name, port) > + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) > > -def subscription_xml(name, sysname): > +def subscription_xml(name, sysname, interopNS): > return """ > > > @@ -99,8 +106,8 @@ > > > > - > - > + > + > > > > @@ -167,9 +174,9 @@ > > > > - """ % (sysname, name, sysname, name) > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > > -def delete_inst_xml(name, type, sysname, inst_name): > +def delete_inst_xml(name, type, sysname, inst_name, interopNS): > return """ > > > @@ -177,8 +184,8 @@ > > > > - > - > + > + > > > > @@ -200,9 +207,9 @@ > > > ; > - """ % (type, sysname, type, inst_name); > + """ % (interopNS[0], interopNS[1], type, sysname, type, inst_name); > > -def delete_sub_xml(name, sysname): > +def delete_sub_xml(name, sysname, interopNS): > return """ > > > @@ -210,8 +217,8 @@ > > > > - > - > + > + > > > > @@ -273,7 +280,45 @@ > > > ; > - """ % (sysname, name, sysname, name) > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > + > +def trigger_xml(type, interopNS): > + return """ > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + """ % (interopNS[0], interopNS[1], type) > + # FIXME: this should really use indication NS, not interop NS. > + > +def update_url_port(parsedUrl, port): > + # Must manually reconstruct netloc to update the port value. > + if isinstance(parsedUrl.username, basestring): > + if isinstance(parsedUrl.password, basestring): > + netloc = "%s:%s@%s:%s" % (parsedUrl.username, parsedUrl.password, > + parsedUrl.hostname, port) > + else: > + netloc = "%s@%s:%s" % (parsedUrl.username, > + parsedUrl.hostname, port) > + else: > + netloc = "%s:%s" % (parsedUrl.hostname, port) > + > + # Reassemble url with the updated netloc. return a string. > + return urlunparse((parsedUrl.scheme, netloc, > + parsedUrl.path, parsedUrl.params, > + parsedUrl.query, parsedUrl.fragment)) > > class CIMIndication: > def __init__(self, xmldata): > @@ -286,50 +331,135 @@ > def __str__(self): > return self.name > > -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): > +def socket_handler_wrapper(*parms): > + try: > + CIMSocketHandler(*parms) > + except Exception as e: > + print "SSL error: %s" % str(e) > + > +class CIMSocketHandler(SimpleHTTPRequestHandler): > + def setup(self): > + self.connection = self.request > + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) > + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) > + > def do_POST(self): > length = self.headers.getheader('content-length') > data = self.rfile.read(int(length)) > > indication = CIMIndication(data) > - print "Got indication: %s" % indication > + print "Got indication: %s from %s" % (indication, > self.client_address) > if self.server.print_ind: > - print "%s\n\n" % data > + print "%s\n" % data > self.server.indications.append(indication) > + # Silence the unwanted log output from send_response() > + realStderr = sys.stderr > + sys.stderr = open(os.devnull,'a') > + self.send_response(200) > + sys.stderr = realStderr > + > +class SecureHTTPServer(HTTPServer): > + def __init__(self, server_address, HandlerClass): > + BaseServer.__init__(self, server_address, HandlerClass) > + > + def verify_cb(conn, cert, errnum, depth, ok): > + if options.verbose: > + print('Verify peer certificate chain: level %d:' % depth) > + print('subject=%s' % cert.get_subject()) > + print('issuer =%s' % cert.get_issuer()) > + return ok > + > + ctx = SSL.Context(SSL.SSLv23_METHOD) > + #ctx.use_certificate_file(options.certFile) > + ctx.use_certificate_chain_file(options.certFile) > + ctx.use_privatekey_file(options.keyFile) > + > + if options.noverify: > + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) > + else: > + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) > + ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, > + verify_cb) > + ctx.load_verify_locations(options.trustStore) > + > + self.socket = SSL.Connection(ctx, > socket.socket(self.address_family, > + self.socket_type)) > + self.server_bind() > + self.server_activate() > > +# The param defaults allow new options from main() w/o losing compat w/ > cimtest. > class CIMIndicationSubscription: > - def __init__(self, name, typ, ns, print_ind, sysname, port=0): > + def __init__(self, name, typ, ns, print_ind, sysname, port=0, > + interopNS=('root','PG_InterOp'), > destUrl="http://localhost:8000", > + triggermode=False): > self.name = name > self.type = typ > self.ns = ns > self.sysname = sysname > + self.interopNS = interopNS > + self.print_ind = print_ind > + > + # We do not want to open a listener socket in trigger mode. > + if triggermode: > + self.trigger_xml = trigger_xml(typ, interopNS) > + return > + > + parsedUrl = urlparse(destUrl) > + > + # Increment the listener port by the offset value. > + if isinstance(parsedUrl.port, int): > + listenerPort = parsedUrl.port + port > + else: > + listenerPort = 8000 + port > + > + destUrl = update_url_port(parsedUrl, listenerPort) > + > + try: > + if parsedUrl.scheme == "http": > + self.server = HTTPServer((parsedUrl.hostname, > + listenerPort), > + CIMSocketHandler) > + elif parsedUrl.scheme == "https": > + self.server = SecureHTTPServer((parsedUrl.hostname, > + listenerPort), > + socket_handler_wrapper) > + except IOError as e: > + print "Error creating listener socket: %s" % str(e) > + exit(e.errno) > > - self.port = 8000 + port > - self.server = BaseHTTPServer.HTTPServer(('', self.port), > - CIMSocketHandler) > self.server.print_ind = print_ind > self.server.indications = [] > > - self.filter_xml = filter_xml(name, typ, ns, sysname) > - self.handler_xml = handler_xml(name, self.port, sysname) > - self.subscription_xml = subscription_xml(name, sysname) > + self.filter_xml = filter_xml(name, typ, ns, sysname, interopNS) > + self.handler_xml = handler_xml(name, destUrl, sysname, interopNS) > + self.subscription_xml = subscription_xml(name, sysname, interopNS) > > def __do_cimpost(self, conn, body, method, auth_hdr=None): > headers = {"CIMOperation" : "MethodCall", > "CIMMethod" : method, > - "CIMObject" : "root/PG_Interop", > + #"CIMObject" : "root/PG_Interop", > + "CIMObject" : "%s/%s" % self.interopNS, > "Content-Type" : 'application/xml; charset="utf-8"'} > > if auth_hdr: > headers["Authorization"] = "Basic %s" % auth_hdr > > - conn.request("POST", "/cimom", body, headers) > - resp = conn.getresponse() > - if not resp.getheader("content-length"): > - raise Exception("Request Failed: %d %s" % > - (resp.status, resp.reason)) > - > - resp.read() > + try: > + conn.request("POST", "/cimom", body, headers) > + resp = conn.getresponse() > + if not resp.getheader("content-length"): > + raise Exception("Request Failed: %d %s" % > + (resp.status, resp.reason)) > + except IOError as e: > + print "Error connecting to CIMOM: %s" % str(e) > + exit(e.errno) > + > + if self.print_ind: > + print "Reply from CIMOM:" > + #print resp.msg > + print resp.read() > + else: > + resp.read() > > def subscribe(self, url, cred=None): > self.conn = httplib.HTTPConnection(url) > @@ -353,26 +483,39 @@ > else: > auth_hdr = None > > - xml = delete_sub_xml(self.name, self.sysname) > + xml = delete_sub_xml(self.name, self.sysname, self.interopNS) > self.__do_cimpost(self.conn, xml, > "DeleteInstance", auth_hdr) > xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, > - "%sHandler" % self.name) > + "%sHandler" % self.name, self.interopNS) > self.__do_cimpost(self.conn, xml, > "DeleteInstance", auth_hdr) > xml = delete_inst_xml(self.name, "Filter", self.sysname, > - "%sFilter" % self.name) > + "%sFilter" % self.name, self.interopNS) > self.__do_cimpost(self.conn, xml, > "DeleteInstance", auth_hdr) > > -def dump_xml(name, typ, ns, sysname): > - filter_str = filter_xml(name, typ, ns, sysname) > - handler_str = handler_xml(name, 8000, sysname) > - subscript_str = subscription_xml(name, sysname) > - del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % > name) > + def trigger(self, url, cred=None): > + self.conn = httplib.HTTPConnection(url) > + if cred: > + (u, p) = cred > + auth_hdr = base64.b64encode("%s:%s" % (u, p)) > + else: > + auth_hdr = None > + > + self.__do_cimpost(self.conn, self.trigger_xml, > + "SendTestIndication", auth_hdr) > + > +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): > + filter_str = filter_xml(name, typ, ns, sysname, interopNS) > + handler_str = handler_xml(name, destUrl, sysname, interopNS) > + subscript_str = subscription_xml(name, sysname, interopNS) > + del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % > name, > + interopNS) > del_handler_str = delete_inst_xml(name, "HandlerCIMXML", sysname, > - "%sHandler" % name) > - del_subscript_str = delete_sub_xml(name, sysname) > + "%sHandler" % name, interopNS) > + del_subscript_str = delete_sub_xml(name, sysname, interopNS) > + trigger_str = trigger_xml(typ, interopNS) > > print "CreateFilter:\n%s\n" % filter_str > print "DeleteFilter:\n%s\n" % del_filter_str > @@ -380,15 +523,20 @@ > print "DeleteHandler:\n%s\n" % del_handler_str > print "CreateSubscription:\n%s\n" % subscript_str > print "DeleteSubscription:\n%s\n" % del_subscript_str > + print "Indication trigger:\n%s\n" % trigger_str > > def main(): > usage = "usage: %prog [options] provider\nex: %prog > CIM_InstModification" > parser = OptionParser(usage) > > + # FIXME: SecureHTTPServer still relies on this, need a better way. > + global options > + > parser.add_option("-u", "--url", dest="url", default="localhost:5988", > help="URL of CIMOM to connect to (host:port)") > - parser.add_option("-N", "--ns", dest="ns", default="root/virt", > - help="Namespace (default is root/virt)") > + parser.add_option("-N", "--ns", dest="ns", > + help="Namespace in which the register the indication > \ > + (default is the same value as the interop > namespace)") > parser.add_option("-n", "--name", dest="name", default="Test", > help="Name for filter, handler, subscription \ > (default: Test)") > @@ -398,16 +546,41 @@ > parser.add_option("-p", "--print-ind", dest="print_ind", default=False, > action="store_true", > help="Print received indications to stdout.") > + parser.add_option("-v", "--verbose", dest="verbose", default=False, > + action="store_true", > + help="Print addtional debug info.") > parser.add_option("-U", "--user", dest="username", default=None, > - help="HTTP Auth username") > + help="HTTP Auth username (CIMOM)") > parser.add_option("-P", "--pass", dest="password", default=None, > - help="HTTP Auth password") > + help="HTTP Auth password (CIMOM)") > parser.add_option("--port", dest="port", default=0, type=int, > help="Port increment value (server default: 8000)") > + parser.add_option("--dest", dest="destUrl", default="localhost:8000", > + help="URL of destination handler \ > + (default: http://localhost:8000)") > + parser.add_option("--certFile", dest="certFile", default=None, > + help="File containing the local certificate to use") > + parser.add_option("--keyFile", dest="keyFile", default=None, > + help="File containing private key for local cert \ > + (if none provided, assume key is in the certFile)") > + parser.add_option("--trustStore", dest="trustStore", default=None, > + help="File containing trusted certificates for \ > + remote endpoint verification") > + parser.add_option("--noverify", dest="noverify", default=False, > + action="store_true", > + help="Skip verification of remote endpoint > certificate \ > + for incoming https indications") > + parser.add_option("-i", "--interop", dest="interop", > + default="root/interop", > + help="Interop namespace name (default: > root/interop)") > + parser.add_option("-t", "--trigger", dest="trigger", default=False, > + action="store_true", > + help="Trigger mode: send a request to CIMOM to > trigger \ > + an indication via a method call ") > > (options, args) = parser.parse_args() > > - if len(args) == 0: > + if not options.trigger and len(args)==0: > print "Fatal: no indication type provided." > sys.exit(1) > > @@ -421,20 +594,75 @@ > else: > sysname = options.url > > + if "/" in options.interop: > + options.interopNS = tuple(options.interop.split("/")) > + else: > + options.interopNS = ("root", options.interop) > + > + # If no value provided for indication NS, default is same as interopNS. > + if not options.ns: > + options.ns = "%s/%s" % options.interopNS > + > + if options.verbose: > + print "Interop namespace = %s/%s" % options.interopNS > + print "Indication namespace = %s" % options.ns > + > + # If url does not begin with http or https, assume http. > + parsedUrl = urlparse(options.destUrl) > + if not re.search(parsedUrl.scheme, "https"): > + destUrl = "http://" + options.destUrl > + else: > + destUrl = options.destUrl > + > + if parsedUrl.scheme == "https": > + if not options.trustStore and not options.noverify: > + print "Error: must provide --trustStore or --noverify with > https." > + sys.exit(1) > + elif options.trustStore and options.noverify: > + print "Error: options --trustStore and --noverify are > exclusive." > + sys.exit(1) > + if not options.certFile: > + print "Error: no certificate file provided." > + sys.exit(1) > + elif not options.keyFile: > + print "No keyFile provided; assuming private key \ > + contained in certFile." > + options.keyFile = options.certFile > + > if options.dump: > - dump_xml(options.name, args[0], options.ns, sysname) > + if isinstance(parsedUrl.port, int): > + listenerPort = parsedUrl.port + options.port > + else: > + listenerPort = 8000 + options.port > + > + destUrl = update_url_port(parsedUrl, listenerPort) > + dump_xml(options.name, args[0], options.ns, sysname, > options.interopNS, > + destUrl) > + sys.exit(0) > + > + # Trigger mode: currently only supports SFCB Test_Indication provider. > + if options.trigger: > + classname = "Test_Indication" > + sub = CIMIndicationSubscription(options.name, classname, > options.ns, > + options.print_ind, sysname, > options.port, > + options.interopNS, destUrl, True) > + print "Triggering indication for %s" % classname > + sub.trigger(options.url, auth) > sys.exit(0) > > sub = CIMIndicationSubscription(options.name, args[0], options.ns, > - options.print_ind, sysname, > options.port) > + options.print_ind, sysname, > options.port, > + options.interopNS, destUrl) > + > + print "Creating subscription for %s" % args[0] > sub.subscribe(options.url, auth) > print "Watching for %s" % args[0] > > try: > sub.server.serve_forever() > - except KeyboardInterrupt,e: > - sub.unsubscribe(auth) > + except KeyboardInterrupt as e: > print "Cancelling subscription for %s" % args[0] > + sub.unsubscribe(auth) > > if __name__=="__main__": > sys.exit(main()) > > _______________________________________________ > Libvirt-cim mailing list > Libvirt-cim at redhat.com > https://www.redhat.com/mailman/listinfo/libvirt-cim > From deheller at ptd.net Mon Mar 19 23:41:44 2012 From: deheller at ptd.net (Dave Heller) Date: Mon, 19 Mar 2012 19:41:44 -0400 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py In-Reply-To: <1332193175.5173.19.camel@snmishra-desktop.beaverton.ibm.com> Message-ID: <20120319234145.B46531109126@proxyz14.mailnet.ptd.net> Hi Sharad, Actually, I did modify the script in a way that it is still compatible with cimtest (based on tests using ComputerSystemIndication 01_created_indication). But if you think it makes sense to fork it to a standalone script (and rename it), that is fine too. The usage of the script as I intend is interactive from the command line. I can add some documentation with example commands. Is there a standard place this should go, or should I add a README? Thanks, Dave > -----Original Message----- > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > bounces at redhat.com] On Behalf Of Sharad Mishra > Sent: Monday, March 19, 2012 5:40 PM > To: List for discussion and development of libvirt CIM > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > indication_tester.py > > Dave, > > I assume that this patch is not going to be applied to the existing > indication_tester.py in cimtest. In that case, we can add this modified > indication_tester.py as a new file at the same location as current > indication_tester.py. We do need to give this script a new name. This > script is currently not used/invoked by any existing cimtests. Do you > want to write tests to use this script? > > Regards > Sharad Mishra > > On Mon, 2012-03-19 at 13:21 -0400, Dave Heller wrote: > > In the XenKvmLib directory in cimtest, there is a "indication_tester.py" > > module that is both a command-line program and a component of cimtest. > Here > > is a proposed patch that adds new functionality to the cmdline interface > > while preserving compatibility with cimtest. > > > > The most significant new feature is support for SSL indications. There > is > > also a new --trigger mode to generate a test indication using the SFCB > > Test_Indication provider. This allows the script to act as a completely > > stand-alone test tool (for SFCB) without the user having to rely on the > > "xmltest" files and wbemcat. > > > > Below is a complete list of the new features. I am looking for feedback > and > > potentially, the correct process to check into "cimtest". > > > > Best regards, > > Dave Heller > > > > New features: > > > > - Supports https indications. Adds new options --certFile and --keyFile > to > > specify server certificate and private key for the SSL enabled > indication > > listener. > > - Supports verification of indication sender (i.e. CIMOM) SSL > certificate. > > New option --trustStore is used to specify the CA certificate file; > > alternately the --noverify option may be used to disable peer > certificate > > checking. > > - Adds new option --dest to specify handler destination. This same URL > is > > used to register the subscription (at the CIMOM) and start the > indication > > listener (locally). Via --dest one can specify scheme (http or https), > IP or > > hostname, listener port, and optionally userid:password, all in a single > > parameter. > > - New option --interop can be used to override the default interop > namespace > > (i.e root/interop vs. root/PG_InterOp) allowing easy support for both > > openpegasus and sfcb. > > - Modifies the default value of existing --ns option (indication > namespace); > > if not specified the indication namespace will default to the same value > as > > the interop namespace. > > - New option --verbose can be used to display debugging info, such as > > details (subject, issuer) of the indication sender cert (when peer > > verification enabled). > > - Improved error handling (i.e. catch exceptions) for the following > > operations: starting listener locally, receiving incoming indications, > POST > > operations (i.e. subscribe, unsubscribe) to CIMOM. > > - New option --trigger can be used to send a request to CIMOM to trigger > an > > indication via the SFCB Test_Indication provider. > > > > Limitations: > > > > - Not tested on IPv6. > > - The --trigger option currently only supports the sfcb Test_Indication > > provider. (It could be made more general by allowing the XML send to > > support an arbitrary namespace, classname and method call. Open to > > suggestions here.) > > > > > > --- indication_tester.orig.py 2012-03-05 23:12:08.000000000 -0500 > > +++ indication_tester.py 2012-03-19 13:12:55.000000000 -0400 > > @@ -1,18 +1,25 @@ > > #!/usr/bin/python > > -# Copyright IBM Corp. 2007 > > +# Copyright IBM Corp. 2007-2012 > > # > > # indication_tester.py - Tool for testing indication subscription and > > # delivery against a CIMOM > > # Author: Dan Smith > > +# SSL support added by Dave Heller > > > > import sys > > from optparse import OptionParser > > -import BaseHTTPServer > > +from urlparse import urlparse, urlunparse > > +from xml.dom.minidom import parse, parseString > > import httplib > > import base64 > > -from xml.dom.minidom import parse, parseString > > +import errno, os, re > > +import socket > > +from SocketServer import BaseServer > > +from SimpleHTTPServer import SimpleHTTPRequestHandler > > +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer > > +from OpenSSL import SSL > > > > -def filter_xml(name, type, ns, sysname): > > +def filter_xml(name, type, ns, sysname, interopNS): > > return """ > > > > > > @@ -20,8 +27,8 @@ > > > > > > > > - > > - > > + > > + > > > > > > > > @@ -53,9 +60,9 @@ > > > > > > > > - """ % (sysname, name, type, ns) > > + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) > > > > -def handler_xml(name, port, sysname): > > +def handler_xml(name, destUrl, sysname, interopNS): > > return """ > > > > > > @@ -63,8 +70,8 @@ > > > > > > > > - > > - > > + > > + > > > > > > > > @@ -81,7 +88,7 @@ > > %sHandler > > > > > > - http://localhost:%i > > + %s > > > > > > > > @@ -89,9 +96,9 @@ > > > > > > > > - """ % (sysname, name, port) > > + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) > > > > -def subscription_xml(name, sysname): > > +def subscription_xml(name, sysname, interopNS): > > return """ > > > > > > @@ -99,8 +106,8 @@ > > > > > > > > - > > - > > + > > + > > > > > > > > @@ -167,9 +174,9 @@ > > > > > > > > - """ % (sysname, name, sysname, name) > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > > > > -def delete_inst_xml(name, type, sysname, inst_name): > > +def delete_inst_xml(name, type, sysname, inst_name, interopNS): > > return """ > > > > > > @@ -177,8 +184,8 @@ > > > > > > > > - > > - > > + > > + > > > > > > > > @@ -200,9 +207,9 @@ > > > > > > ; > > - """ % (type, sysname, type, inst_name); > > + """ % (interopNS[0], interopNS[1], type, sysname, type, inst_name); > > > > -def delete_sub_xml(name, sysname): > > +def delete_sub_xml(name, sysname, interopNS): > > return """ > > > > > > @@ -210,8 +217,8 @@ > > > > > > > > - > > - > > + > > + > > > > > > > > @@ -273,7 +280,45 @@ > > > > > > ; > > - """ % (sysname, name, sysname, name) > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > > + > > +def trigger_xml(type, interopNS): > > + return """ > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + > > + """ % (interopNS[0], interopNS[1], type) > > + # FIXME: this should really use indication NS, not interop NS. > > + > > +def update_url_port(parsedUrl, port): > > + # Must manually reconstruct netloc to update the port value. > > + if isinstance(parsedUrl.username, basestring): > > + if isinstance(parsedUrl.password, basestring): > > + netloc = "%s:%s@%s:%s" % (parsedUrl.username, > parsedUrl.password, > > + parsedUrl.hostname, port) > > + else: > > + netloc = "%s@%s:%s" % (parsedUrl.username, > > + parsedUrl.hostname, port) > > + else: > > + netloc = "%s:%s" % (parsedUrl.hostname, port) > > + > > + # Reassemble url with the updated netloc. return a string. > > + return urlunparse((parsedUrl.scheme, netloc, > > + parsedUrl.path, parsedUrl.params, > > + parsedUrl.query, parsedUrl.fragment)) > > > > class CIMIndication: > > def __init__(self, xmldata): > > @@ -286,50 +331,135 @@ > > def __str__(self): > > return self.name > > > > -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): > > +def socket_handler_wrapper(*parms): > > + try: > > + CIMSocketHandler(*parms) > > + except Exception as e: > > + print "SSL error: %s" % str(e) > > + > > +class CIMSocketHandler(SimpleHTTPRequestHandler): > > + def setup(self): > > + self.connection = self.request > > + self.rfile = socket._fileobject(self.request, "rb", > self.rbufsize) > > + self.wfile = socket._fileobject(self.request, "wb", > self.wbufsize) > > + > > def do_POST(self): > > length = self.headers.getheader('content-length') > > data = self.rfile.read(int(length)) > > > > indication = CIMIndication(data) > > - print "Got indication: %s" % indication > > + print "Got indication: %s from %s" % (indication, > > self.client_address) > > if self.server.print_ind: > > - print "%s\n\n" % data > > + print "%s\n" % data > > self.server.indications.append(indication) > > + # Silence the unwanted log output from send_response() > > + realStderr = sys.stderr > > + sys.stderr = open(os.devnull,'a') > > + self.send_response(200) > > + sys.stderr = realStderr > > + > > +class SecureHTTPServer(HTTPServer): > > + def __init__(self, server_address, HandlerClass): > > + BaseServer.__init__(self, server_address, HandlerClass) > > + > > + def verify_cb(conn, cert, errnum, depth, ok): > > + if options.verbose: > > + print('Verify peer certificate chain: level %d:' % depth) > > + print('subject=%s' % cert.get_subject()) > > + print('issuer =%s' % cert.get_issuer()) > > + return ok > > + > > + ctx = SSL.Context(SSL.SSLv23_METHOD) > > + #ctx.use_certificate_file(options.certFile) > > + ctx.use_certificate_chain_file(options.certFile) > > + ctx.use_privatekey_file(options.keyFile) > > + > > + if options.noverify: > > + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) > > + else: > > + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) > > + > ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, > > + verify_cb) > > + ctx.load_verify_locations(options.trustStore) > > + > > + self.socket = SSL.Connection(ctx, > > socket.socket(self.address_family, > > + > self.socket_type)) > > + self.server_bind() > > + self.server_activate() > > > > +# The param defaults allow new options from main() w/o losing compat w/ > > cimtest. > > class CIMIndicationSubscription: > > - def __init__(self, name, typ, ns, print_ind, sysname, port=0): > > + def __init__(self, name, typ, ns, print_ind, sysname, port=0, > > + interopNS=('root','PG_InterOp'), > > destUrl="http://localhost:8000", > > + triggermode=False): > > self.name = name > > self.type = typ > > self.ns = ns > > self.sysname = sysname > > + self.interopNS = interopNS > > + self.print_ind = print_ind > > + > > + # We do not want to open a listener socket in trigger mode. > > + if triggermode: > > + self.trigger_xml = trigger_xml(typ, interopNS) > > + return > > + > > + parsedUrl = urlparse(destUrl) > > + > > + # Increment the listener port by the offset value. > > + if isinstance(parsedUrl.port, int): > > + listenerPort = parsedUrl.port + port > > + else: > > + listenerPort = 8000 + port > > + > > + destUrl = update_url_port(parsedUrl, listenerPort) > > + > > + try: > > + if parsedUrl.scheme == "http": > > + self.server = HTTPServer((parsedUrl.hostname, > > + listenerPort), > > + CIMSocketHandler) > > + elif parsedUrl.scheme == "https": > > + self.server = SecureHTTPServer((parsedUrl.hostname, > > + listenerPort), > > + socket_handler_wrapper) > > + except IOError as e: > > + print "Error creating listener socket: %s" % str(e) > > + exit(e.errno) > > > > - self.port = 8000 + port > > - self.server = BaseHTTPServer.HTTPServer(('', self.port), > > - CIMSocketHandler) > > self.server.print_ind = print_ind > > self.server.indications = [] > > > > - self.filter_xml = filter_xml(name, typ, ns, sysname) > > - self.handler_xml = handler_xml(name, self.port, sysname) > > - self.subscription_xml = subscription_xml(name, sysname) > > + self.filter_xml = filter_xml(name, typ, ns, sysname, interopNS) > > + self.handler_xml = handler_xml(name, destUrl, sysname, > interopNS) > > + self.subscription_xml = subscription_xml(name, sysname, > interopNS) > > > > def __do_cimpost(self, conn, body, method, auth_hdr=None): > > headers = {"CIMOperation" : "MethodCall", > > "CIMMethod" : method, > > - "CIMObject" : "root/PG_Interop", > > + #"CIMObject" : "root/PG_Interop", > > + "CIMObject" : "%s/%s" % self.interopNS, > > "Content-Type" : 'application/xml; charset="utf-8"'} > > > > if auth_hdr: > > headers["Authorization"] = "Basic %s" % auth_hdr > > > > - conn.request("POST", "/cimom", body, headers) > > - resp = conn.getresponse() > > - if not resp.getheader("content-length"): > > - raise Exception("Request Failed: %d %s" % > > - (resp.status, resp.reason)) > > - > > - resp.read() > > + try: > > + conn.request("POST", "/cimom", body, headers) > > + resp = conn.getresponse() > > + if not resp.getheader("content-length"): > > + raise Exception("Request Failed: %d %s" % > > + (resp.status, resp.reason)) > > + except IOError as e: > > + print "Error connecting to CIMOM: %s" % str(e) > > + exit(e.errno) > > + > > + if self.print_ind: > > + print "Reply from CIMOM:" > > + #print resp.msg > > + print resp.read() > > + else: > > + resp.read() > > > > def subscribe(self, url, cred=None): > > self.conn = httplib.HTTPConnection(url) > > @@ -353,26 +483,39 @@ > > else: > > auth_hdr = None > > > > - xml = delete_sub_xml(self.name, self.sysname) > > + xml = delete_sub_xml(self.name, self.sysname, self.interopNS) > > self.__do_cimpost(self.conn, xml, > > "DeleteInstance", auth_hdr) > > xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, > > - "%sHandler" % self.name) > > + "%sHandler" % self.name, self.interopNS) > > self.__do_cimpost(self.conn, xml, > > "DeleteInstance", auth_hdr) > > xml = delete_inst_xml(self.name, "Filter", self.sysname, > > - "%sFilter" % self.name) > > + "%sFilter" % self.name, self.interopNS) > > self.__do_cimpost(self.conn, xml, > > "DeleteInstance", auth_hdr) > > > > -def dump_xml(name, typ, ns, sysname): > > - filter_str = filter_xml(name, typ, ns, sysname) > > - handler_str = handler_xml(name, 8000, sysname) > > - subscript_str = subscription_xml(name, sysname) > > - del_filter_str = delete_inst_xml(name, "Filter", sysname, > "%sFilter" % > > name) > > + def trigger(self, url, cred=None): > > + self.conn = httplib.HTTPConnection(url) > > + if cred: > > + (u, p) = cred > > + auth_hdr = base64.b64encode("%s:%s" % (u, p)) > > + else: > > + auth_hdr = None > > + > > + self.__do_cimpost(self.conn, self.trigger_xml, > > + "SendTestIndication", auth_hdr) > > + > > +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): > > + filter_str = filter_xml(name, typ, ns, sysname, interopNS) > > + handler_str = handler_xml(name, destUrl, sysname, interopNS) > > + subscript_str = subscription_xml(name, sysname, interopNS) > > + del_filter_str = delete_inst_xml(name, "Filter", sysname, > "%sFilter" % > > name, > > + interopNS) > > del_handler_str = delete_inst_xml(name, "HandlerCIMXML", sysname, > > - "%sHandler" % name) > > - del_subscript_str = delete_sub_xml(name, sysname) > > + "%sHandler" % name, interopNS) > > + del_subscript_str = delete_sub_xml(name, sysname, interopNS) > > + trigger_str = trigger_xml(typ, interopNS) > > > > print "CreateFilter:\n%s\n" % filter_str > > print "DeleteFilter:\n%s\n" % del_filter_str > > @@ -380,15 +523,20 @@ > > print "DeleteHandler:\n%s\n" % del_handler_str > > print "CreateSubscription:\n%s\n" % subscript_str > > print "DeleteSubscription:\n%s\n" % del_subscript_str > > + print "Indication trigger:\n%s\n" % trigger_str > > > > def main(): > > usage = "usage: %prog [options] provider\nex: %prog > > CIM_InstModification" > > parser = OptionParser(usage) > > > > + # FIXME: SecureHTTPServer still relies on this, need a better way. > > + global options > > + > > parser.add_option("-u", "--url", dest="url", > default="localhost:5988", > > help="URL of CIMOM to connect to (host:port)") > > - parser.add_option("-N", "--ns", dest="ns", default="root/virt", > > - help="Namespace (default is root/virt)") > > + parser.add_option("-N", "--ns", dest="ns", > > + help="Namespace in which the register the > indication > > \ > > + (default is the same value as the interop > > namespace)") > > parser.add_option("-n", "--name", dest="name", default="Test", > > help="Name for filter, handler, subscription \ > > (default: Test)") > > @@ -398,16 +546,41 @@ > > parser.add_option("-p", "--print-ind", dest="print_ind", > default=False, > > action="store_true", > > help="Print received indications to stdout.") > > + parser.add_option("-v", "--verbose", dest="verbose", default=False, > > + action="store_true", > > + help="Print addtional debug info.") > > parser.add_option("-U", "--user", dest="username", default=None, > > - help="HTTP Auth username") > > + help="HTTP Auth username (CIMOM)") > > parser.add_option("-P", "--pass", dest="password", default=None, > > - help="HTTP Auth password") > > + help="HTTP Auth password (CIMOM)") > > parser.add_option("--port", dest="port", default=0, type=int, > > help="Port increment value (server default: > 8000)") > > + parser.add_option("--dest", dest="destUrl", > default="localhost:8000", > > + help="URL of destination handler \ > > + (default: http://localhost:8000)") > > + parser.add_option("--certFile", dest="certFile", default=None, > > + help="File containing the local certificate to > use") > > + parser.add_option("--keyFile", dest="keyFile", default=None, > > + help="File containing private key for local cert > \ > > + (if none provided, assume key is in the > certFile)") > > + parser.add_option("--trustStore", dest="trustStore", default=None, > > + help="File containing trusted certificates for \ > > + remote endpoint verification") > > + parser.add_option("--noverify", dest="noverify", default=False, > > + action="store_true", > > + help="Skip verification of remote endpoint > > certificate \ > > + for incoming https indications") > > + parser.add_option("-i", "--interop", dest="interop", > > + default="root/interop", > > + help="Interop namespace name (default: > > root/interop)") > > + parser.add_option("-t", "--trigger", dest="trigger", default=False, > > + action="store_true", > > + help="Trigger mode: send a request to CIMOM to > > trigger \ > > + an indication via a method call ") > > > > (options, args) = parser.parse_args() > > > > - if len(args) == 0: > > + if not options.trigger and len(args)==0: > > print "Fatal: no indication type provided." > > sys.exit(1) > > > > @@ -421,20 +594,75 @@ > > else: > > sysname = options.url > > > > + if "/" in options.interop: > > + options.interopNS = tuple(options.interop.split("/")) > > + else: > > + options.interopNS = ("root", options.interop) > > + > > + # If no value provided for indication NS, default is same as > interopNS. > > + if not options.ns: > > + options.ns = "%s/%s" % options.interopNS > > + > > + if options.verbose: > > + print "Interop namespace = %s/%s" % options.interopNS > > + print "Indication namespace = %s" % options.ns > > + > > + # If url does not begin with http or https, assume http. > > + parsedUrl = urlparse(options.destUrl) > > + if not re.search(parsedUrl.scheme, "https"): > > + destUrl = "http://" + options.destUrl > > + else: > > + destUrl = options.destUrl > > + > > + if parsedUrl.scheme == "https": > > + if not options.trustStore and not options.noverify: > > + print "Error: must provide --trustStore or --noverify with > > https." > > + sys.exit(1) > > + elif options.trustStore and options.noverify: > > + print "Error: options --trustStore and --noverify are > > exclusive." > > + sys.exit(1) > > + if not options.certFile: > > + print "Error: no certificate file provided." > > + sys.exit(1) > > + elif not options.keyFile: > > + print "No keyFile provided; assuming private key \ > > + contained in certFile." > > + options.keyFile = options.certFile > > + > > if options.dump: > > - dump_xml(options.name, args[0], options.ns, sysname) > > + if isinstance(parsedUrl.port, int): > > + listenerPort = parsedUrl.port + options.port > > + else: > > + listenerPort = 8000 + options.port > > + > > + destUrl = update_url_port(parsedUrl, listenerPort) > > + dump_xml(options.name, args[0], options.ns, sysname, > > options.interopNS, > > + destUrl) > > + sys.exit(0) > > + > > + # Trigger mode: currently only supports SFCB Test_Indication > provider. > > + if options.trigger: > > + classname = "Test_Indication" > > + sub = CIMIndicationSubscription(options.name, classname, > > options.ns, > > + options.print_ind, sysname, > > options.port, > > + options.interopNS, destUrl, True) > > + print "Triggering indication for %s" % classname > > + sub.trigger(options.url, auth) > > sys.exit(0) > > > > sub = CIMIndicationSubscription(options.name, args[0], options.ns, > > - options.print_ind, sysname, > > options.port) > > + options.print_ind, sysname, > > options.port, > > + options.interopNS, destUrl) > > + > > + print "Creating subscription for %s" % args[0] > > sub.subscribe(options.url, auth) > > print "Watching for %s" % args[0] > > > > try: > > sub.server.serve_forever() > > - except KeyboardInterrupt,e: > > - sub.unsubscribe(auth) > > + except KeyboardInterrupt as e: > > print "Cancelling subscription for %s" % args[0] > > + sub.unsubscribe(auth) > > > > if __name__=="__main__": > > sys.exit(main()) > > > > _______________________________________________ > > Libvirt-cim mailing list > > Libvirt-cim at redhat.com > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > > > > _______________________________________________ > Libvirt-cim mailing list > Libvirt-cim at redhat.com > https://www.redhat.com/mailman/listinfo/libvirt-cim From snmishra at linux.vnet.ibm.com Tue Mar 20 17:36:33 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Tue, 20 Mar 2012 10:36:33 -0700 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py In-Reply-To: <20120319234145.B46531109126@proxyz14.mailnet.ptd.net> References: <20120319234145.B46531109126@proxyz14.mailnet.ptd.net> Message-ID: <1332264993.5173.24.camel@snmishra-desktop.beaverton.ibm.com> On Mon, 2012-03-19 at 19:41 -0400, Dave Heller wrote: > Hi Sharad, > > Actually, I did modify the script in a way that it is still compatible with > cimtest (based on tests using ComputerSystemIndication In that case, I am going to apply your patch and test as update to existing indication script. > 01_created_indication). But if you think it makes sense to fork it to a > standalone script (and rename it), that is fine too. > > The usage of the script as I intend is interactive from the command line. I > can add some documentation with example commands. Is there a standard place > this should go, or should I add a README? You can add it to the script header itself. Do you want to send another patch with usage added and "orig" removed from the file name? Regards, Sharad Mishra > > Thanks, > Dave > > > -----Original Message----- > > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > > bounces at redhat.com] On Behalf Of Sharad Mishra > > Sent: Monday, March 19, 2012 5:40 PM > > To: List for discussion and development of libvirt CIM > > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > > indication_tester.py > > > > Dave, > > > > I assume that this patch is not going to be applied to the existing > > indication_tester.py in cimtest. In that case, we can add this modified > > indication_tester.py as a new file at the same location as current > > indication_tester.py. We do need to give this script a new name. This > > script is currently not used/invoked by any existing cimtests. Do you > > want to write tests to use this script? > > > > Regards > > Sharad Mishra > > > > On Mon, 2012-03-19 at 13:21 -0400, Dave Heller wrote: > > > In the XenKvmLib directory in cimtest, there is a "indication_tester.py" > > > module that is both a command-line program and a component of cimtest. > > Here > > > is a proposed patch that adds new functionality to the cmdline interface > > > while preserving compatibility with cimtest. > > > > > > The most significant new feature is support for SSL indications. There > > is > > > also a new --trigger mode to generate a test indication using the SFCB > > > Test_Indication provider. This allows the script to act as a completely > > > stand-alone test tool (for SFCB) without the user having to rely on the > > > "xmltest" files and wbemcat. > > > > > > Below is a complete list of the new features. I am looking for feedback > > and > > > potentially, the correct process to check into "cimtest". > > > > > > Best regards, > > > Dave Heller > > > > > > New features: > > > > > > - Supports https indications. Adds new options --certFile and --keyFile > > to > > > specify server certificate and private key for the SSL enabled > > indication > > > listener. > > > - Supports verification of indication sender (i.e. CIMOM) SSL > > certificate. > > > New option --trustStore is used to specify the CA certificate file; > > > alternately the --noverify option may be used to disable peer > > certificate > > > checking. > > > - Adds new option --dest to specify handler destination. This same URL > > is > > > used to register the subscription (at the CIMOM) and start the > > indication > > > listener (locally). Via --dest one can specify scheme (http or https), > > IP or > > > hostname, listener port, and optionally userid:password, all in a single > > > parameter. > > > - New option --interop can be used to override the default interop > > namespace > > > (i.e root/interop vs. root/PG_InterOp) allowing easy support for both > > > openpegasus and sfcb. > > > - Modifies the default value of existing --ns option (indication > > namespace); > > > if not specified the indication namespace will default to the same value > > as > > > the interop namespace. > > > - New option --verbose can be used to display debugging info, such as > > > details (subject, issuer) of the indication sender cert (when peer > > > verification enabled). > > > - Improved error handling (i.e. catch exceptions) for the following > > > operations: starting listener locally, receiving incoming indications, > > POST > > > operations (i.e. subscribe, unsubscribe) to CIMOM. > > > - New option --trigger can be used to send a request to CIMOM to trigger > > an > > > indication via the SFCB Test_Indication provider. > > > > > > Limitations: > > > > > > - Not tested on IPv6. > > > - The --trigger option currently only supports the sfcb Test_Indication > > > provider. (It could be made more general by allowing the XML send to > > > support an arbitrary namespace, classname and method call. Open to > > > suggestions here.) > > > > > > > > > --- indication_tester.orig.py 2012-03-05 23:12:08.000000000 -0500 > > > +++ indication_tester.py 2012-03-19 13:12:55.000000000 -0400 > > > @@ -1,18 +1,25 @@ > > > #!/usr/bin/python > > > -# Copyright IBM Corp. 2007 > > > +# Copyright IBM Corp. 2007-2012 > > > # > > > # indication_tester.py - Tool for testing indication subscription and > > > # delivery against a CIMOM > > > # Author: Dan Smith > > > +# SSL support added by Dave Heller > > > > > > import sys > > > from optparse import OptionParser > > > -import BaseHTTPServer > > > +from urlparse import urlparse, urlunparse > > > +from xml.dom.minidom import parse, parseString > > > import httplib > > > import base64 > > > -from xml.dom.minidom import parse, parseString > > > +import errno, os, re > > > +import socket > > > +from SocketServer import BaseServer > > > +from SimpleHTTPServer import SimpleHTTPRequestHandler > > > +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer > > > +from OpenSSL import SSL > > > > > > -def filter_xml(name, type, ns, sysname): > > > +def filter_xml(name, type, ns, sysname, interopNS): > > > return """ > > > > > > > > > @@ -20,8 +27,8 @@ > > > > > > > > > > > > - > > > - > > > + > > > + > > > > > > > > > > > > @@ -53,9 +60,9 @@ > > > > > > > > > > > > - """ % (sysname, name, type, ns) > > > + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) > > > > > > -def handler_xml(name, port, sysname): > > > +def handler_xml(name, destUrl, sysname, interopNS): > > > return """ > > > > > > > > > @@ -63,8 +70,8 @@ > > > > > > > > > > > > - > > > - > > > + > > > + > > > > > > > > > > > > @@ -81,7 +88,7 @@ > > > %sHandler > > > > > > > > > - http://localhost:%i > > > + %s > > > > > > > > > > > > @@ -89,9 +96,9 @@ > > > > > > > > > > > > - """ % (sysname, name, port) > > > + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) > > > > > > -def subscription_xml(name, sysname): > > > +def subscription_xml(name, sysname, interopNS): > > > return """ > > > > > > > > > @@ -99,8 +106,8 @@ > > > > > > > > > > > > - > > > - > > > + > > > + > > > > > > > > > > > > @@ -167,9 +174,9 @@ > > > > > > > > > > > > - """ % (sysname, name, sysname, name) > > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > > > > > > -def delete_inst_xml(name, type, sysname, inst_name): > > > +def delete_inst_xml(name, type, sysname, inst_name, interopNS): > > > return """ > > > > > > > > > @@ -177,8 +184,8 @@ > > > > > > > > > > > > - > > > - > > > + > > > + > > > > > > > > > > > > @@ -200,9 +207,9 @@ > > > > > > > > > ; > > > - """ % (type, sysname, type, inst_name); > > > + """ % (interopNS[0], interopNS[1], type, sysname, type, inst_name); > > > > > > -def delete_sub_xml(name, sysname): > > > +def delete_sub_xml(name, sysname, interopNS): > > > return """ > > > > > > > > > @@ -210,8 +217,8 @@ > > > > > > > > > > > > - > > > - > > > + > > > + > > > > > > > > > > > > @@ -273,7 +280,45 @@ > > > > > > > > > ; > > > - """ % (sysname, name, sysname, name) > > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) > > > + > > > +def trigger_xml(type, interopNS): > > > + return """ > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + > > > + """ % (interopNS[0], interopNS[1], type) > > > + # FIXME: this should really use indication NS, not interop NS. > > > + > > > +def update_url_port(parsedUrl, port): > > > + # Must manually reconstruct netloc to update the port value. > > > + if isinstance(parsedUrl.username, basestring): > > > + if isinstance(parsedUrl.password, basestring): > > > + netloc = "%s:%s@%s:%s" % (parsedUrl.username, > > parsedUrl.password, > > > + parsedUrl.hostname, port) > > > + else: > > > + netloc = "%s@%s:%s" % (parsedUrl.username, > > > + parsedUrl.hostname, port) > > > + else: > > > + netloc = "%s:%s" % (parsedUrl.hostname, port) > > > + > > > + # Reassemble url with the updated netloc. return a string. > > > + return urlunparse((parsedUrl.scheme, netloc, > > > + parsedUrl.path, parsedUrl.params, > > > + parsedUrl.query, parsedUrl.fragment)) > > > > > > class CIMIndication: > > > def __init__(self, xmldata): > > > @@ -286,50 +331,135 @@ > > > def __str__(self): > > > return self.name > > > > > > -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): > > > +def socket_handler_wrapper(*parms): > > > + try: > > > + CIMSocketHandler(*parms) > > > + except Exception as e: > > > + print "SSL error: %s" % str(e) > > > + > > > +class CIMSocketHandler(SimpleHTTPRequestHandler): > > > + def setup(self): > > > + self.connection = self.request > > > + self.rfile = socket._fileobject(self.request, "rb", > > self.rbufsize) > > > + self.wfile = socket._fileobject(self.request, "wb", > > self.wbufsize) > > > + > > > def do_POST(self): > > > length = self.headers.getheader('content-length') > > > data = self.rfile.read(int(length)) > > > > > > indication = CIMIndication(data) > > > - print "Got indication: %s" % indication > > > + print "Got indication: %s from %s" % (indication, > > > self.client_address) > > > if self.server.print_ind: > > > - print "%s\n\n" % data > > > + print "%s\n" % data > > > self.server.indications.append(indication) > > > + # Silence the unwanted log output from send_response() > > > + realStderr = sys.stderr > > > + sys.stderr = open(os.devnull,'a') > > > + self.send_response(200) > > > + sys.stderr = realStderr > > > + > > > +class SecureHTTPServer(HTTPServer): > > > + def __init__(self, server_address, HandlerClass): > > > + BaseServer.__init__(self, server_address, HandlerClass) > > > + > > > + def verify_cb(conn, cert, errnum, depth, ok): > > > + if options.verbose: > > > + print('Verify peer certificate chain: level %d:' % depth) > > > + print('subject=%s' % cert.get_subject()) > > > + print('issuer =%s' % cert.get_issuer()) > > > + return ok > > > + > > > + ctx = SSL.Context(SSL.SSLv23_METHOD) > > > + #ctx.use_certificate_file(options.certFile) > > > + ctx.use_certificate_chain_file(options.certFile) > > > + ctx.use_privatekey_file(options.keyFile) > > > + > > > + if options.noverify: > > > + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) > > > + else: > > > + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) > > > + > > ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, > > > + verify_cb) > > > + ctx.load_verify_locations(options.trustStore) > > > + > > > + self.socket = SSL.Connection(ctx, > > > socket.socket(self.address_family, > > > + > > self.socket_type)) > > > + self.server_bind() > > > + self.server_activate() > > > > > > +# The param defaults allow new options from main() w/o losing compat w/ > > > cimtest. > > > class CIMIndicationSubscription: > > > - def __init__(self, name, typ, ns, print_ind, sysname, port=0): > > > + def __init__(self, name, typ, ns, print_ind, sysname, port=0, > > > + interopNS=('root','PG_InterOp'), > > > destUrl="http://localhost:8000", > > > + triggermode=False): > > > self.name = name > > > self.type = typ > > > self.ns = ns > > > self.sysname = sysname > > > + self.interopNS = interopNS > > > + self.print_ind = print_ind > > > + > > > + # We do not want to open a listener socket in trigger mode. > > > + if triggermode: > > > + self.trigger_xml = trigger_xml(typ, interopNS) > > > + return > > > + > > > + parsedUrl = urlparse(destUrl) > > > + > > > + # Increment the listener port by the offset value. > > > + if isinstance(parsedUrl.port, int): > > > + listenerPort = parsedUrl.port + port > > > + else: > > > + listenerPort = 8000 + port > > > + > > > + destUrl = update_url_port(parsedUrl, listenerPort) > > > + > > > + try: > > > + if parsedUrl.scheme == "http": > > > + self.server = HTTPServer((parsedUrl.hostname, > > > + listenerPort), > > > + CIMSocketHandler) > > > + elif parsedUrl.scheme == "https": > > > + self.server = SecureHTTPServer((parsedUrl.hostname, > > > + listenerPort), > > > + socket_handler_wrapper) > > > + except IOError as e: > > > + print "Error creating listener socket: %s" % str(e) > > > + exit(e.errno) > > > > > > - self.port = 8000 + port > > > - self.server = BaseHTTPServer.HTTPServer(('', self.port), > > > - CIMSocketHandler) > > > self.server.print_ind = print_ind > > > self.server.indications = [] > > > > > > - self.filter_xml = filter_xml(name, typ, ns, sysname) > > > - self.handler_xml = handler_xml(name, self.port, sysname) > > > - self.subscription_xml = subscription_xml(name, sysname) > > > + self.filter_xml = filter_xml(name, typ, ns, sysname, interopNS) > > > + self.handler_xml = handler_xml(name, destUrl, sysname, > > interopNS) > > > + self.subscription_xml = subscription_xml(name, sysname, > > interopNS) > > > > > > def __do_cimpost(self, conn, body, method, auth_hdr=None): > > > headers = {"CIMOperation" : "MethodCall", > > > "CIMMethod" : method, > > > - "CIMObject" : "root/PG_Interop", > > > + #"CIMObject" : "root/PG_Interop", > > > + "CIMObject" : "%s/%s" % self.interopNS, > > > "Content-Type" : 'application/xml; charset="utf-8"'} > > > > > > if auth_hdr: > > > headers["Authorization"] = "Basic %s" % auth_hdr > > > > > > - conn.request("POST", "/cimom", body, headers) > > > - resp = conn.getresponse() > > > - if not resp.getheader("content-length"): > > > - raise Exception("Request Failed: %d %s" % > > > - (resp.status, resp.reason)) > > > - > > > - resp.read() > > > + try: > > > + conn.request("POST", "/cimom", body, headers) > > > + resp = conn.getresponse() > > > + if not resp.getheader("content-length"): > > > + raise Exception("Request Failed: %d %s" % > > > + (resp.status, resp.reason)) > > > + except IOError as e: > > > + print "Error connecting to CIMOM: %s" % str(e) > > > + exit(e.errno) > > > + > > > + if self.print_ind: > > > + print "Reply from CIMOM:" > > > + #print resp.msg > > > + print resp.read() > > > + else: > > > + resp.read() > > > > > > def subscribe(self, url, cred=None): > > > self.conn = httplib.HTTPConnection(url) > > > @@ -353,26 +483,39 @@ > > > else: > > > auth_hdr = None > > > > > > - xml = delete_sub_xml(self.name, self.sysname) > > > + xml = delete_sub_xml(self.name, self.sysname, self.interopNS) > > > self.__do_cimpost(self.conn, xml, > > > "DeleteInstance", auth_hdr) > > > xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, > > > - "%sHandler" % self.name) > > > + "%sHandler" % self.name, self.interopNS) > > > self.__do_cimpost(self.conn, xml, > > > "DeleteInstance", auth_hdr) > > > xml = delete_inst_xml(self.name, "Filter", self.sysname, > > > - "%sFilter" % self.name) > > > + "%sFilter" % self.name, self.interopNS) > > > self.__do_cimpost(self.conn, xml, > > > "DeleteInstance", auth_hdr) > > > > > > -def dump_xml(name, typ, ns, sysname): > > > - filter_str = filter_xml(name, typ, ns, sysname) > > > - handler_str = handler_xml(name, 8000, sysname) > > > - subscript_str = subscription_xml(name, sysname) > > > - del_filter_str = delete_inst_xml(name, "Filter", sysname, > > "%sFilter" % > > > name) > > > + def trigger(self, url, cred=None): > > > + self.conn = httplib.HTTPConnection(url) > > > + if cred: > > > + (u, p) = cred > > > + auth_hdr = base64.b64encode("%s:%s" % (u, p)) > > > + else: > > > + auth_hdr = None > > > + > > > + self.__do_cimpost(self.conn, self.trigger_xml, > > > + "SendTestIndication", auth_hdr) > > > + > > > +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): > > > + filter_str = filter_xml(name, typ, ns, sysname, interopNS) > > > + handler_str = handler_xml(name, destUrl, sysname, interopNS) > > > + subscript_str = subscription_xml(name, sysname, interopNS) > > > + del_filter_str = delete_inst_xml(name, "Filter", sysname, > > "%sFilter" % > > > name, > > > + interopNS) > > > del_handler_str = delete_inst_xml(name, "HandlerCIMXML", sysname, > > > - "%sHandler" % name) > > > - del_subscript_str = delete_sub_xml(name, sysname) > > > + "%sHandler" % name, interopNS) > > > + del_subscript_str = delete_sub_xml(name, sysname, interopNS) > > > + trigger_str = trigger_xml(typ, interopNS) > > > > > > print "CreateFilter:\n%s\n" % filter_str > > > print "DeleteFilter:\n%s\n" % del_filter_str > > > @@ -380,15 +523,20 @@ > > > print "DeleteHandler:\n%s\n" % del_handler_str > > > print "CreateSubscription:\n%s\n" % subscript_str > > > print "DeleteSubscription:\n%s\n" % del_subscript_str > > > + print "Indication trigger:\n%s\n" % trigger_str > > > > > > def main(): > > > usage = "usage: %prog [options] provider\nex: %prog > > > CIM_InstModification" > > > parser = OptionParser(usage) > > > > > > + # FIXME: SecureHTTPServer still relies on this, need a better way. > > > + global options > > > + > > > parser.add_option("-u", "--url", dest="url", > > default="localhost:5988", > > > help="URL of CIMOM to connect to (host:port)") > > > - parser.add_option("-N", "--ns", dest="ns", default="root/virt", > > > - help="Namespace (default is root/virt)") > > > + parser.add_option("-N", "--ns", dest="ns", > > > + help="Namespace in which the register the > > indication > > > \ > > > + (default is the same value as the interop > > > namespace)") > > > parser.add_option("-n", "--name", dest="name", default="Test", > > > help="Name for filter, handler, subscription \ > > > (default: Test)") > > > @@ -398,16 +546,41 @@ > > > parser.add_option("-p", "--print-ind", dest="print_ind", > > default=False, > > > action="store_true", > > > help="Print received indications to stdout.") > > > + parser.add_option("-v", "--verbose", dest="verbose", default=False, > > > + action="store_true", > > > + help="Print addtional debug info.") > > > parser.add_option("-U", "--user", dest="username", default=None, > > > - help="HTTP Auth username") > > > + help="HTTP Auth username (CIMOM)") > > > parser.add_option("-P", "--pass", dest="password", default=None, > > > - help="HTTP Auth password") > > > + help="HTTP Auth password (CIMOM)") > > > parser.add_option("--port", dest="port", default=0, type=int, > > > help="Port increment value (server default: > > 8000)") > > > + parser.add_option("--dest", dest="destUrl", > > default="localhost:8000", > > > + help="URL of destination handler \ > > > + (default: http://localhost:8000)") > > > + parser.add_option("--certFile", dest="certFile", default=None, > > > + help="File containing the local certificate to > > use") > > > + parser.add_option("--keyFile", dest="keyFile", default=None, > > > + help="File containing private key for local cert > > \ > > > + (if none provided, assume key is in the > > certFile)") > > > + parser.add_option("--trustStore", dest="trustStore", default=None, > > > + help="File containing trusted certificates for \ > > > + remote endpoint verification") > > > + parser.add_option("--noverify", dest="noverify", default=False, > > > + action="store_true", > > > + help="Skip verification of remote endpoint > > > certificate \ > > > + for incoming https indications") > > > + parser.add_option("-i", "--interop", dest="interop", > > > + default="root/interop", > > > + help="Interop namespace name (default: > > > root/interop)") > > > + parser.add_option("-t", "--trigger", dest="trigger", default=False, > > > + action="store_true", > > > + help="Trigger mode: send a request to CIMOM to > > > trigger \ > > > + an indication via a method call ") > > > > > > (options, args) = parser.parse_args() > > > > > > - if len(args) == 0: > > > + if not options.trigger and len(args)==0: > > > print "Fatal: no indication type provided." > > > sys.exit(1) > > > > > > @@ -421,20 +594,75 @@ > > > else: > > > sysname = options.url > > > > > > + if "/" in options.interop: > > > + options.interopNS = tuple(options.interop.split("/")) > > > + else: > > > + options.interopNS = ("root", options.interop) > > > + > > > + # If no value provided for indication NS, default is same as > > interopNS. > > > + if not options.ns: > > > + options.ns = "%s/%s" % options.interopNS > > > + > > > + if options.verbose: > > > + print "Interop namespace = %s/%s" % options.interopNS > > > + print "Indication namespace = %s" % options.ns > > > + > > > + # If url does not begin with http or https, assume http. > > > + parsedUrl = urlparse(options.destUrl) > > > + if not re.search(parsedUrl.scheme, "https"): > > > + destUrl = "http://" + options.destUrl > > > + else: > > > + destUrl = options.destUrl > > > + > > > + if parsedUrl.scheme == "https": > > > + if not options.trustStore and not options.noverify: > > > + print "Error: must provide --trustStore or --noverify with > > > https." > > > + sys.exit(1) > > > + elif options.trustStore and options.noverify: > > > + print "Error: options --trustStore and --noverify are > > > exclusive." > > > + sys.exit(1) > > > + if not options.certFile: > > > + print "Error: no certificate file provided." > > > + sys.exit(1) > > > + elif not options.keyFile: > > > + print "No keyFile provided; assuming private key \ > > > + contained in certFile." > > > + options.keyFile = options.certFile > > > + > > > if options.dump: > > > - dump_xml(options.name, args[0], options.ns, sysname) > > > + if isinstance(parsedUrl.port, int): > > > + listenerPort = parsedUrl.port + options.port > > > + else: > > > + listenerPort = 8000 + options.port > > > + > > > + destUrl = update_url_port(parsedUrl, listenerPort) > > > + dump_xml(options.name, args[0], options.ns, sysname, > > > options.interopNS, > > > + destUrl) > > > + sys.exit(0) > > > + > > > + # Trigger mode: currently only supports SFCB Test_Indication > > provider. > > > + if options.trigger: > > > + classname = "Test_Indication" > > > + sub = CIMIndicationSubscription(options.name, classname, > > > options.ns, > > > + options.print_ind, sysname, > > > options.port, > > > + options.interopNS, destUrl, True) > > > + print "Triggering indication for %s" % classname > > > + sub.trigger(options.url, auth) > > > sys.exit(0) > > > > > > sub = CIMIndicationSubscription(options.name, args[0], options.ns, > > > - options.print_ind, sysname, > > > options.port) > > > + options.print_ind, sysname, > > > options.port, > > > + options.interopNS, destUrl) > > > + > > > + print "Creating subscription for %s" % args[0] > > > sub.subscribe(options.url, auth) > > > print "Watching for %s" % args[0] > > > > > > try: > > > sub.server.serve_forever() > > > - except KeyboardInterrupt,e: > > > - sub.unsubscribe(auth) > > > + except KeyboardInterrupt as e: > > > print "Cancelling subscription for %s" % args[0] > > > + sub.unsubscribe(auth) > > > > > > if __name__=="__main__": > > > sys.exit(main()) > > > > > > _______________________________________________ > > > Libvirt-cim mailing list > > > Libvirt-cim at redhat.com > > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > > > > > > > > _______________________________________________ > > Libvirt-cim mailing list > > Libvirt-cim at redhat.com > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > _______________________________________________ > Libvirt-cim mailing list > Libvirt-cim at redhat.com > https://www.redhat.com/mailman/listinfo/libvirt-cim > From snmishra at linux.vnet.ibm.com Tue Mar 20 19:30:57 2012 From: snmishra at linux.vnet.ibm.com (Sharad Mishra) Date: Tue, 20 Mar 2012 12:30:57 -0700 Subject: [Libvirt-cim] [PATCH 3/3] ComputerSystem: Reboot state change also a job In-Reply-To: <1331142682.2597.30.camel@snmishra-desktop.beaverton.ibm.com> References: <1331056553-29221-1-git-send-email-eblima@linux.vnet.ibm.com> <1331056553-29221-4-git-send-email-eblima@linux.vnet.ibm.com> <1331142682.2597.30.camel@snmishra-desktop.beaverton.ibm.com> Message-ID: <1332271857.5173.26.camel@snmishra-desktop.beaverton.ibm.com> On Wed, 2012-03-07 at 09:51 -0800, Sharad Mishra wrote: > Visual inspection looks good. > Need to apply and test > > -Sharad > > On Tue, 2012-03-06 at 14:55 -0300, Eduardo Lima (Etrunko) wrote: > > From: "Eduardo Lima (Etrunko)" > > > > As it happens for Shutdown, the RequestStateChange method returns immediately > > with return code 0 (successful) even though the state change is still not > > completed. > > > > Change the current job implementation to support reboot as well. For this we > > need the libvirt domain events APIs and event loop implementation, which are > > only available in libvirt version 0.9.0 or newer. > > > > Signed-off-by: Eduardo Lima (Etrunko) > > --- > > src/Virt_ComputerSystem.c | 90 ++++++++++++++++++++++++++++++++++++++------- > > 1 files changed, 76 insertions(+), 14 deletions(-) > > > > diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c > > index 778809d..04584f3 100644 > > --- a/src/Virt_ComputerSystem.c > > +++ b/src/Virt_ComputerSystem.c > > @@ -32,6 +32,7 @@ > > > > #include > > #include > > +#include > > > > #include > > #include > > @@ -61,6 +62,8 @@ struct _state_change_job { > > uint16_t status; /* job status */ > > }; > > > > +static bool events_registered = false; > > + > > /* Set the "Name" property of an instance from a domain */ > > static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance) > > { > > @@ -1203,14 +1206,12 @@ static CMPIStatus __state_change(const char *name, > > s = state_change_enable(dom, &info); > > else if (state == CIM_STATE_DISABLED) > > s = state_change_disable(dom, &info); > > - else if (state == CIM_STATE_SHUTDOWN) > > - s.rc = CIM_SVPC_RETURN_JOB_STARTED; > > else if (state == CIM_STATE_PAUSED) > > s = state_change_pause(dom, &info); > > - else if (state == CIM_STATE_REBOOT) > > - s = state_change_reboot(dom, &info); > > else if (state == CIM_STATE_RESET) > > s = state_change_reset(dom, &info); > > + else if (state == CIM_STATE_SHUTDOWN || state == CIM_STATE_REBOOT) > > + s.rc = CIM_SVPC_RETURN_JOB_STARTED; > > else > > cu_statusf(_BROKER, &s, > > CMPI_RC_ERR_NOT_SUPPORTED, > > @@ -1333,6 +1334,25 @@ static CMPIStatus create_state_change_job(const CMPIObjectPath *ref, > > return s; > > } > > > > +static void state_change_reboot_cb(virConnectPtr conn, > > + virDomainPtr dom, > > + void *data) > > +{ > > + state_change_job_t *job = (state_change_job_t *) data; > > + job->status = CIM_JOB_STATE_COMPLETED; > > +} > > + > > +static void state_change_shutdown_cb(virConnectPtr conn, > > + virDomainPtr dom, > > + int event, > > + int detail, > > + void *data) > > +{ > > + state_change_job_t *job = (state_change_job_t *) data; > > + if (event == VIR_DOMAIN_EVENT_SHUTDOWN) I don't see "VIR_DOMAIN_EVENT_SHUTDOWN" defined in libvirt.h. -Sharad > > + job->status = CIM_JOB_STATE_COMPLETED; > > +} > > + > > static CMPI_THREAD_RETURN state_change_thread(void *data) > > { > > CMPIStatus s; > > @@ -1341,8 +1361,10 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > > virConnectPtr conn = NULL; > > virDomainPtr dom = NULL; > > virDomainInfo info; > > + int job_cb = -1; > > > > - if (job->dom_state != CIM_STATE_SHUTDOWN) { > > + if (job->dom_state != CIM_STATE_SHUTDOWN && > > + job->dom_state != CIM_STATE_REBOOT) { > > CU_DEBUG("Unrecognized state '%d'", job->dom_state); > > goto end; > > } > > @@ -1382,17 +1404,56 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > > goto out; > > } > > > > - s = state_change_shutdown(dom, &info); > > - if (s.rc != CMPI_RC_OK) { > > - CU_DEBUG("Unable to trigger domain shutdown: '%s'", > > - CMGetCharPtr(s.msg)); > > - goto out; > > + if (events_registered == false) { > > + events_registered = true; > > + virEventRegisterDefaultImpl(); > > + } > > + > > + if (job->dom_state == CIM_STATE_REBOOT) { > > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > > + VIR_DOMAIN_EVENT_ID_REBOOT, > > + VIR_DOMAIN_EVENT_CALLBACK(state_change_reboot_cb), > > + job, NULL); > > + > > + if (job_cb == -1) { > > + CU_DEBUG("Unable to connect domain reboot callback"); > > + goto out; > > + } > > + > > + s = state_change_reboot(dom, &info); > > + > > + if (s.rc != CMPI_RC_OK) { > > + CU_DEBUG("Unable to trigger domain reboot: '%s'", > > + CMGetCharPtr(s.msg)); > > + goto out; > > + } > > + } else if (job->dom_state == CIM_STATE_SHUTDOWN) { > > + job_cb = virConnectDomainEventRegisterAny(conn, NULL, > > + VIR_DOMAIN_EVENT_ID_LIFECYCLE, > > + VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb), > > + job, NULL); > > + > > + if (job_cb == -1) { > > + CU_DEBUG("Unable to connect domain shutdown callback"); > > + goto out; > > + } > > + > > + s = state_change_shutdown(dom, &info); > > + > > + if (s.rc != CMPI_RC_OK) { > > + CU_DEBUG("Unable to trigger domain shutdown: '%s'", > > + CMGetCharPtr(s.msg)); > > + goto out; > > + } > > } > > > > /* Wait for operation (shutdown/reboot) to complete */ > > - while (info.state != VIR_DOMAIN_SHUTOFF) { > > - usleep(100 * 1000); > > - virDomainGetInfo(dom, &info); > > + while (job->status == CIM_JOB_STATE_RUNNING) { > > + if (virEventRunDefaultImpl() < 0) { > > + virErrorPtr err = virGetLastError(); > > + CU_DEBUG("Failed to run event loop: %s\n", > > + err && err->message ? err->message : "Unknown error"); > > + } > > } > > > > CU_DEBUG("Job completed"); > > @@ -1405,6 +1466,8 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > > (CMPIValue *) "Completed", CMPI_chars); > > } > > > > + virConnectDomainEventDeregisterAny(conn, job_cb); > > + > > out: > > virDomainFree(dom); > > virConnectClose(conn); > > @@ -1412,7 +1475,6 @@ static CMPI_THREAD_RETURN state_change_thread(void *data) > > CBDetachThread(_BROKER, job->context); > > free(job->dom_name); > > free(job); > > - > > end: > > return NULL; > > } > > > _______________________________________________ > Libvirt-cim mailing list > Libvirt-cim at redhat.com > https://www.redhat.com/mailman/listinfo/libvirt-cim > From deheller at ptd.net Wed Mar 21 17:00:03 2012 From: deheller at ptd.net (Dave Heller) Date: Wed, 21 Mar 2012 13:00:03 -0400 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py In-Reply-To: <1332264993.5173.24.camel@snmishra-desktop.beaverton.ibm.com> Message-ID: <20120321170004.213FB1D507F5@proxyz12.mailnet.ptd.net> Sorry for the delay. Yes, I will do that and re-post the patch. > -----Original Message----- > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > bounces at redhat.com] On Behalf Of Sharad Mishra > Sent: Tuesday, March 20, 2012 1:37 PM > To: List for discussion and development of libvirt CIM > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > indication_tester.py > > On Mon, 2012-03-19 at 19:41 -0400, Dave Heller wrote: > > Hi Sharad, > > > > Actually, I did modify the script in a way that it is still compatible > with > > cimtest (based on tests using ComputerSystemIndication > > In that case, I am going to apply your patch and test as update to > existing indication script. > > > 01_created_indication). But if you think it makes sense to fork it to a > > standalone script (and rename it), that is fine too. > > > > The usage of the script as I intend is interactive from the command > line. I > > can add some documentation with example commands. Is there a standard > place > > this should go, or should I add a README? > > You can add it to the script header itself. > Do you want to send another patch with usage added and "orig" removed > from the file name? > > Regards, > Sharad Mishra > > > > > Thanks, > > Dave > > > > > -----Original Message----- > > > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > > > bounces at redhat.com] On Behalf Of Sharad Mishra > > > Sent: Monday, March 19, 2012 5:40 PM > > > To: List for discussion and development of libvirt CIM > > > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > > > indication_tester.py > > > > > > Dave, > > > > > > I assume that this patch is not going to be applied to the existing > > > indication_tester.py in cimtest. In that case, we can add this > modified > > > indication_tester.py as a new file at the same location as current > > > indication_tester.py. We do need to give this script a new name. This > > > script is currently not used/invoked by any existing cimtests. Do you > > > want to write tests to use this script? > > > > > > Regards > > > Sharad Mishra > > > > > > On Mon, 2012-03-19 at 13:21 -0400, Dave Heller wrote: > > > > In the XenKvmLib directory in cimtest, there is a > "indication_tester.py" > > > > module that is both a command-line program and a component of > cimtest. > > > Here > > > > is a proposed patch that adds new functionality to the cmdline > interface > > > > while preserving compatibility with cimtest. > > > > > > > > The most significant new feature is support for SSL indications. > There > > > is > > > > also a new --trigger mode to generate a test indication using the > SFCB > > > > Test_Indication provider. This allows the script to act as a > completely > > > > stand-alone test tool (for SFCB) without the user having to rely on > the > > > > "xmltest" files and wbemcat. > > > > > > > > Below is a complete list of the new features. I am looking for > feedback > > > and > > > > potentially, the correct process to check into "cimtest". > > > > > > > > Best regards, > > > > Dave Heller > > > > > > > > New features: > > > > > > > > - Supports https indications. Adds new options --certFile and -- > keyFile > > > to > > > > specify server certificate and private key for the SSL enabled > > > indication > > > > listener. > > > > - Supports verification of indication sender (i.e. CIMOM) SSL > > > certificate. > > > > New option --trustStore is used to specify the CA certificate file; > > > > alternately the --noverify option may be used to disable peer > > > certificate > > > > checking. > > > > - Adds new option --dest to specify handler destination. This same > URL > > > is > > > > used to register the subscription (at the CIMOM) and start the > > > indication > > > > listener (locally). Via --dest one can specify scheme (http or > https), > > > IP or > > > > hostname, listener port, and optionally userid:password, all in a > single > > > > parameter. > > > > - New option --interop can be used to override the default interop > > > namespace > > > > (i.e root/interop vs. root/PG_InterOp) allowing easy support for > both > > > > openpegasus and sfcb. > > > > - Modifies the default value of existing --ns option (indication > > > namespace); > > > > if not specified the indication namespace will default to the same > value > > > as > > > > the interop namespace. > > > > - New option --verbose can be used to display debugging info, such > as > > > > details (subject, issuer) of the indication sender cert (when peer > > > > verification enabled). > > > > - Improved error handling (i.e. catch exceptions) for the following > > > > operations: starting listener locally, receiving incoming > indications, > > > POST > > > > operations (i.e. subscribe, unsubscribe) to CIMOM. > > > > - New option --trigger can be used to send a request to CIMOM to > trigger > > > an > > > > indication via the SFCB Test_Indication provider. > > > > > > > > Limitations: > > > > > > > > - Not tested on IPv6. > > > > - The --trigger option currently only supports the sfcb > Test_Indication > > > > provider. (It could be made more general by allowing the XML send > to > > > > support an arbitrary namespace, classname and method call. Open to > > > > suggestions here.) > > > > > > > > > > > > --- indication_tester.orig.py 2012-03-05 23:12:08.000000000 -0500 > > > > +++ indication_tester.py 2012-03-19 13:12:55.000000000 -0400 > > > > @@ -1,18 +1,25 @@ > > > > #!/usr/bin/python > > > > -# Copyright IBM Corp. 2007 > > > > +# Copyright IBM Corp. 2007-2012 > > > > # > > > > # indication_tester.py - Tool for testing indication subscription > and > > > > # delivery against a CIMOM > > > > # Author: Dan Smith > > > > +# SSL support added by Dave Heller > > > > > > > > import sys > > > > from optparse import OptionParser > > > > -import BaseHTTPServer > > > > +from urlparse import urlparse, urlunparse > > > > +from xml.dom.minidom import parse, parseString > > > > import httplib > > > > import base64 > > > > -from xml.dom.minidom import parse, parseString > > > > +import errno, os, re > > > > +import socket > > > > +from SocketServer import BaseServer > > > > +from SimpleHTTPServer import SimpleHTTPRequestHandler > > > > +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer > > > > +from OpenSSL import SSL > > > > > > > > -def filter_xml(name, type, ns, sysname): > > > > +def filter_xml(name, type, ns, sysname, interopNS): > > > > return """ > > > > > > > > > > > > @@ -20,8 +27,8 @@ > > > > > > > > > > > > > > > > - > > > > - > > > > + > > > > + > > > > > > > > > > > > > > > > @@ -53,9 +60,9 @@ > > > > > > > > > > > > > > > > - """ % (sysname, name, type, ns) > > > > + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) > > > > > > > > -def handler_xml(name, port, sysname): > > > > +def handler_xml(name, destUrl, sysname, interopNS): > > > > return """ > > > > > > > > > > > > @@ -63,8 +70,8 @@ > > > > > > > > > > > > > > > > - > > > > - > > > > + > > > > + > > > > > > > > > > > > > > > > @@ -81,7 +88,7 @@ > > > > %sHandler > > > > > > > > > > > > - http://localhost:%i > > > > + %s > > > > > > > > > > > > > > > > @@ -89,9 +96,9 @@ > > > > > > > > > > > > > > > > - """ % (sysname, name, port) > > > > + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) > > > > > > > > -def subscription_xml(name, sysname): > > > > +def subscription_xml(name, sysname, interopNS): > > > > return """ > > > > > > > > > > > > @@ -99,8 +106,8 @@ > > > > > > > > > > > > > > > > - > > > > - > > > > + > > > > + > > > > > > > > > > > > > > > > @@ -167,9 +174,9 @@ > > > > > > > > > > > > > > > > - """ % (sysname, name, sysname, name) > > > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, > name) > > > > > > > > -def delete_inst_xml(name, type, sysname, inst_name): > > > > +def delete_inst_xml(name, type, sysname, inst_name, interopNS): > > > > return """ > > > > > > > > > > > > @@ -177,8 +184,8 @@ > > > > > > > > > > > > > > > > - > > > > - > > > > + > > > > + > > > > > > > > > > > > > > > > @@ -200,9 +207,9 @@ > > > > > > > > > > > > ; > > > > - """ % (type, sysname, type, inst_name); > > > > + """ % (interopNS[0], interopNS[1], type, sysname, type, > inst_name); > > > > > > > > -def delete_sub_xml(name, sysname): > > > > +def delete_sub_xml(name, sysname, interopNS): > > > > return """ > > > > > > > > > > > > @@ -210,8 +217,8 @@ > > > > > > > > > > > > > > > > - > > > > - > > > > + > > > > + > > > > > > > > > > > > > > > > @@ -273,7 +280,45 @@ > > > > > > > > > > > > ; > > > > - """ % (sysname, name, sysname, name) > > > > + """ % (interopNS[0], interopNS[1], sysname, name, sysname, > name) > > > > + > > > > +def trigger_xml(type, interopNS): > > > > + return """ > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + > > > > + """ % (interopNS[0], interopNS[1], type) > > > > + # FIXME: this should really use indication NS, not interop NS. > > > > + > > > > +def update_url_port(parsedUrl, port): > > > > + # Must manually reconstruct netloc to update the port value. > > > > + if isinstance(parsedUrl.username, basestring): > > > > + if isinstance(parsedUrl.password, basestring): > > > > + netloc = "%s:%s@%s:%s" % (parsedUrl.username, > > > parsedUrl.password, > > > > + parsedUrl.hostname, port) > > > > + else: > > > > + netloc = "%s@%s:%s" % (parsedUrl.username, > > > > + parsedUrl.hostname, port) > > > > + else: > > > > + netloc = "%s:%s" % (parsedUrl.hostname, port) > > > > + > > > > + # Reassemble url with the updated netloc. return a string. > > > > + return urlunparse((parsedUrl.scheme, netloc, > > > > + parsedUrl.path, parsedUrl.params, > > > > + parsedUrl.query, parsedUrl.fragment)) > > > > > > > > class CIMIndication: > > > > def __init__(self, xmldata): > > > > @@ -286,50 +331,135 @@ > > > > def __str__(self): > > > > return self.name > > > > > > > > -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): > > > > +def socket_handler_wrapper(*parms): > > > > + try: > > > > + CIMSocketHandler(*parms) > > > > + except Exception as e: > > > > + print "SSL error: %s" % str(e) > > > > + > > > > +class CIMSocketHandler(SimpleHTTPRequestHandler): > > > > + def setup(self): > > > > + self.connection = self.request > > > > + self.rfile = socket._fileobject(self.request, "rb", > > > self.rbufsize) > > > > + self.wfile = socket._fileobject(self.request, "wb", > > > self.wbufsize) > > > > + > > > > def do_POST(self): > > > > length = self.headers.getheader('content-length') > > > > data = self.rfile.read(int(length)) > > > > > > > > indication = CIMIndication(data) > > > > - print "Got indication: %s" % indication > > > > + print "Got indication: %s from %s" % (indication, > > > > self.client_address) > > > > if self.server.print_ind: > > > > - print "%s\n\n" % data > > > > + print "%s\n" % data > > > > self.server.indications.append(indication) > > > > + # Silence the unwanted log output from send_response() > > > > + realStderr = sys.stderr > > > > + sys.stderr = open(os.devnull,'a') > > > > + self.send_response(200) > > > > + sys.stderr = realStderr > > > > + > > > > +class SecureHTTPServer(HTTPServer): > > > > + def __init__(self, server_address, HandlerClass): > > > > + BaseServer.__init__(self, server_address, HandlerClass) > > > > + > > > > + def verify_cb(conn, cert, errnum, depth, ok): > > > > + if options.verbose: > > > > + print('Verify peer certificate chain: level %d:' % > depth) > > > > + print('subject=%s' % cert.get_subject()) > > > > + print('issuer =%s' % cert.get_issuer()) > > > > + return ok > > > > + > > > > + ctx = SSL.Context(SSL.SSLv23_METHOD) > > > > + #ctx.use_certificate_file(options.certFile) > > > > + ctx.use_certificate_chain_file(options.certFile) > > > > + ctx.use_privatekey_file(options.keyFile) > > > > + > > > > + if options.noverify: > > > > + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) > > > > + else: > > > > + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) > > > > + > > > ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, > > > > + verify_cb) > > > > + ctx.load_verify_locations(options.trustStore) > > > > + > > > > + self.socket = SSL.Connection(ctx, > > > > socket.socket(self.address_family, > > > > + > > > self.socket_type)) > > > > + self.server_bind() > > > > + self.server_activate() > > > > > > > > +# The param defaults allow new options from main() w/o losing > compat w/ > > > > cimtest. > > > > class CIMIndicationSubscription: > > > > - def __init__(self, name, typ, ns, print_ind, sysname, port=0): > > > > + def __init__(self, name, typ, ns, print_ind, sysname, port=0, > > > > + interopNS=('root','PG_InterOp'), > > > > destUrl="http://localhost:8000", > > > > + triggermode=False): > > > > self.name = name > > > > self.type = typ > > > > self.ns = ns > > > > self.sysname = sysname > > > > + self.interopNS = interopNS > > > > + self.print_ind = print_ind > > > > + > > > > + # We do not want to open a listener socket in trigger mode. > > > > + if triggermode: > > > > + self.trigger_xml = trigger_xml(typ, interopNS) > > > > + return > > > > + > > > > + parsedUrl = urlparse(destUrl) > > > > + > > > > + # Increment the listener port by the offset value. > > > > + if isinstance(parsedUrl.port, int): > > > > + listenerPort = parsedUrl.port + port > > > > + else: > > > > + listenerPort = 8000 + port > > > > + > > > > + destUrl = update_url_port(parsedUrl, listenerPort) > > > > + > > > > + try: > > > > + if parsedUrl.scheme == "http": > > > > + self.server = HTTPServer((parsedUrl.hostname, > > > > + listenerPort), > > > > + CIMSocketHandler) > > > > + elif parsedUrl.scheme == "https": > > > > + self.server = SecureHTTPServer((parsedUrl.hostname, > > > > + listenerPort), > > > > + > socket_handler_wrapper) > > > > + except IOError as e: > > > > + print "Error creating listener socket: %s" % str(e) > > > > + exit(e.errno) > > > > > > > > - self.port = 8000 + port > > > > - self.server = BaseHTTPServer.HTTPServer(('', self.port), > > > > - CIMSocketHandler) > > > > self.server.print_ind = print_ind > > > > self.server.indications = [] > > > > > > > > - self.filter_xml = filter_xml(name, typ, ns, sysname) > > > > - self.handler_xml = handler_xml(name, self.port, sysname) > > > > - self.subscription_xml = subscription_xml(name, sysname) > > > > + self.filter_xml = filter_xml(name, typ, ns, sysname, > interopNS) > > > > + self.handler_xml = handler_xml(name, destUrl, sysname, > > > interopNS) > > > > + self.subscription_xml = subscription_xml(name, sysname, > > > interopNS) > > > > > > > > def __do_cimpost(self, conn, body, method, auth_hdr=None): > > > > headers = {"CIMOperation" : "MethodCall", > > > > "CIMMethod" : method, > > > > - "CIMObject" : "root/PG_Interop", > > > > + #"CIMObject" : "root/PG_Interop", > > > > + "CIMObject" : "%s/%s" % self.interopNS, > > > > "Content-Type" : 'application/xml; charset="utf- > 8"'} > > > > > > > > if auth_hdr: > > > > headers["Authorization"] = "Basic %s" % auth_hdr > > > > > > > > - conn.request("POST", "/cimom", body, headers) > > > > - resp = conn.getresponse() > > > > - if not resp.getheader("content-length"): > > > > - raise Exception("Request Failed: %d %s" % > > > > - (resp.status, resp.reason)) > > > > - > > > > - resp.read() > > > > + try: > > > > + conn.request("POST", "/cimom", body, headers) > > > > + resp = conn.getresponse() > > > > + if not resp.getheader("content-length"): > > > > + raise Exception("Request Failed: %d %s" % > > > > + (resp.status, resp.reason)) > > > > + except IOError as e: > > > > + print "Error connecting to CIMOM: %s" % str(e) > > > > + exit(e.errno) > > > > + > > > > + if self.print_ind: > > > > + print "Reply from CIMOM:" > > > > + #print resp.msg > > > > + print resp.read() > > > > + else: > > > > + resp.read() > > > > > > > > def subscribe(self, url, cred=None): > > > > self.conn = httplib.HTTPConnection(url) > > > > @@ -353,26 +483,39 @@ > > > > else: > > > > auth_hdr = None > > > > > > > > - xml = delete_sub_xml(self.name, self.sysname) > > > > + xml = delete_sub_xml(self.name, self.sysname, > self.interopNS) > > > > self.__do_cimpost(self.conn, xml, > > > > "DeleteInstance", auth_hdr) > > > > xml = delete_inst_xml(self.name, "HandlerCIMXML", > self.sysname, > > > > - "%sHandler" % self.name) > > > > + "%sHandler" % self.name, > self.interopNS) > > > > self.__do_cimpost(self.conn, xml, > > > > "DeleteInstance", auth_hdr) > > > > xml = delete_inst_xml(self.name, "Filter", self.sysname, > > > > - "%sFilter" % self.name) > > > > + "%sFilter" % self.name, > self.interopNS) > > > > self.__do_cimpost(self.conn, xml, > > > > "DeleteInstance", auth_hdr) > > > > > > > > -def dump_xml(name, typ, ns, sysname): > > > > - filter_str = filter_xml(name, typ, ns, sysname) > > > > - handler_str = handler_xml(name, 8000, sysname) > > > > - subscript_str = subscription_xml(name, sysname) > > > > - del_filter_str = delete_inst_xml(name, "Filter", sysname, > > > "%sFilter" % > > > > name) > > > > + def trigger(self, url, cred=None): > > > > + self.conn = httplib.HTTPConnection(url) > > > > + if cred: > > > > + (u, p) = cred > > > > + auth_hdr = base64.b64encode("%s:%s" % (u, p)) > > > > + else: > > > > + auth_hdr = None > > > > + > > > > + self.__do_cimpost(self.conn, self.trigger_xml, > > > > + "SendTestIndication", auth_hdr) > > > > + > > > > +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): > > > > + filter_str = filter_xml(name, typ, ns, sysname, interopNS) > > > > + handler_str = handler_xml(name, destUrl, sysname, interopNS) > > > > + subscript_str = subscription_xml(name, sysname, interopNS) > > > > + del_filter_str = delete_inst_xml(name, "Filter", sysname, > > > "%sFilter" % > > > > name, > > > > + interopNS) > > > > del_handler_str = delete_inst_xml(name, "HandlerCIMXML", > sysname, > > > > - "%sHandler" % name) > > > > - del_subscript_str = delete_sub_xml(name, sysname) > > > > + "%sHandler" % name, > interopNS) > > > > + del_subscript_str = delete_sub_xml(name, sysname, interopNS) > > > > + trigger_str = trigger_xml(typ, interopNS) > > > > > > > > print "CreateFilter:\n%s\n" % filter_str > > > > print "DeleteFilter:\n%s\n" % del_filter_str > > > > @@ -380,15 +523,20 @@ > > > > print "DeleteHandler:\n%s\n" % del_handler_str > > > > print "CreateSubscription:\n%s\n" % subscript_str > > > > print "DeleteSubscription:\n%s\n" % del_subscript_str > > > > + print "Indication trigger:\n%s\n" % trigger_str > > > > > > > > def main(): > > > > usage = "usage: %prog [options] provider\nex: %prog > > > > CIM_InstModification" > > > > parser = OptionParser(usage) > > > > > > > > + # FIXME: SecureHTTPServer still relies on this, need a better > way. > > > > + global options > > > > + > > > > parser.add_option("-u", "--url", dest="url", > > > default="localhost:5988", > > > > help="URL of CIMOM to connect to > (host:port)") > > > > - parser.add_option("-N", "--ns", dest="ns", default="root/virt", > > > > - help="Namespace (default is root/virt)") > > > > + parser.add_option("-N", "--ns", dest="ns", > > > > + help="Namespace in which the register the > > > indication > > > > \ > > > > + (default is the same value as the interop > > > > namespace)") > > > > parser.add_option("-n", "--name", dest="name", default="Test", > > > > help="Name for filter, handler, subscription > \ > > > > (default: Test)") > > > > @@ -398,16 +546,41 @@ > > > > parser.add_option("-p", "--print-ind", dest="print_ind", > > > default=False, > > > > action="store_true", > > > > help="Print received indications to stdout.") > > > > + parser.add_option("-v", "--verbose", dest="verbose", > default=False, > > > > + action="store_true", > > > > + help="Print addtional debug info.") > > > > parser.add_option("-U", "--user", dest="username", > default=None, > > > > - help="HTTP Auth username") > > > > + help="HTTP Auth username (CIMOM)") > > > > parser.add_option("-P", "--pass", dest="password", > default=None, > > > > - help="HTTP Auth password") > > > > + help="HTTP Auth password (CIMOM)") > > > > parser.add_option("--port", dest="port", default=0, type=int, > > > > help="Port increment value (server default: > > > 8000)") > > > > + parser.add_option("--dest", dest="destUrl", > > > default="localhost:8000", > > > > + help="URL of destination handler \ > > > > + (default: http://localhost:8000)") > > > > + parser.add_option("--certFile", dest="certFile", default=None, > > > > + help="File containing the local certificate > to > > > use") > > > > + parser.add_option("--keyFile", dest="keyFile", default=None, > > > > + help="File containing private key for local > cert > > > \ > > > > + (if none provided, assume key is in the > > > certFile)") > > > > + parser.add_option("--trustStore", dest="trustStore", > default=None, > > > > + help="File containing trusted certificates > for \ > > > > + remote endpoint verification") > > > > + parser.add_option("--noverify", dest="noverify", default=False, > > > > + action="store_true", > > > > + help="Skip verification of remote endpoint > > > > certificate \ > > > > + for incoming https indications") > > > > + parser.add_option("-i", "--interop", dest="interop", > > > > + default="root/interop", > > > > + help="Interop namespace name (default: > > > > root/interop)") > > > > + parser.add_option("-t", "--trigger", dest="trigger", > default=False, > > > > + action="store_true", > > > > + help="Trigger mode: send a request to CIMOM > to > > > > trigger \ > > > > + an indication via a method call ") > > > > > > > > (options, args) = parser.parse_args() > > > > > > > > - if len(args) == 0: > > > > + if not options.trigger and len(args)==0: > > > > print "Fatal: no indication type provided." > > > > sys.exit(1) > > > > > > > > @@ -421,20 +594,75 @@ > > > > else: > > > > sysname = options.url > > > > > > > > + if "/" in options.interop: > > > > + options.interopNS = tuple(options.interop.split("/")) > > > > + else: > > > > + options.interopNS = ("root", options.interop) > > > > + > > > > + # If no value provided for indication NS, default is same as > > > interopNS. > > > > + if not options.ns: > > > > + options.ns = "%s/%s" % options.interopNS > > > > + > > > > + if options.verbose: > > > > + print "Interop namespace = %s/%s" % options.interopNS > > > > + print "Indication namespace = %s" % options.ns > > > > + > > > > + # If url does not begin with http or https, assume http. > > > > + parsedUrl = urlparse(options.destUrl) > > > > + if not re.search(parsedUrl.scheme, "https"): > > > > + destUrl = "http://" + options.destUrl > > > > + else: > > > > + destUrl = options.destUrl > > > > + > > > > + if parsedUrl.scheme == "https": > > > > + if not options.trustStore and not options.noverify: > > > > + print "Error: must provide --trustStore or --noverify > with > > > > https." > > > > + sys.exit(1) > > > > + elif options.trustStore and options.noverify: > > > > + print "Error: options --trustStore and --noverify are > > > > exclusive." > > > > + sys.exit(1) > > > > + if not options.certFile: > > > > + print "Error: no certificate file provided." > > > > + sys.exit(1) > > > > + elif not options.keyFile: > > > > + print "No keyFile provided; assuming private key \ > > > > + contained in certFile." > > > > + options.keyFile = options.certFile > > > > + > > > > if options.dump: > > > > - dump_xml(options.name, args[0], options.ns, sysname) > > > > + if isinstance(parsedUrl.port, int): > > > > + listenerPort = parsedUrl.port + options.port > > > > + else: > > > > + listenerPort = 8000 + options.port > > > > + > > > > + destUrl = update_url_port(parsedUrl, listenerPort) > > > > + dump_xml(options.name, args[0], options.ns, sysname, > > > > options.interopNS, > > > > + destUrl) > > > > + sys.exit(0) > > > > + > > > > + # Trigger mode: currently only supports SFCB Test_Indication > > > provider. > > > > + if options.trigger: > > > > + classname = "Test_Indication" > > > > + sub = CIMIndicationSubscription(options.name, classname, > > > > options.ns, > > > > + options.print_ind, sysname, > > > > options.port, > > > > + options.interopNS, destUrl, > True) > > > > + print "Triggering indication for %s" % classname > > > > + sub.trigger(options.url, auth) > > > > sys.exit(0) > > > > > > > > sub = CIMIndicationSubscription(options.name, args[0], > options.ns, > > > > - options.print_ind, sysname, > > > > options.port) > > > > + options.print_ind, sysname, > > > > options.port, > > > > + options.interopNS, destUrl) > > > > + > > > > + print "Creating subscription for %s" % args[0] > > > > sub.subscribe(options.url, auth) > > > > print "Watching for %s" % args[0] > > > > > > > > try: > > > > sub.server.serve_forever() > > > > - except KeyboardInterrupt,e: > > > > - sub.unsubscribe(auth) > > > > + except KeyboardInterrupt as e: > > > > print "Cancelling subscription for %s" % args[0] > > > > + sub.unsubscribe(auth) > > > > > > > > if __name__=="__main__": > > > > sys.exit(main()) > > > > > > > > _______________________________________________ > > > > Libvirt-cim mailing list > > > > Libvirt-cim at redhat.com > > > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > > > > > > > > > > > > _______________________________________________ > > > Libvirt-cim mailing list > > > Libvirt-cim at redhat.com > > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > > > _______________________________________________ > > Libvirt-cim mailing list > > Libvirt-cim at redhat.com > > https://www.redhat.com/mailman/listinfo/libvirt-cim > > > > > _______________________________________________ > Libvirt-cim mailing list > Libvirt-cim at redhat.com > https://www.redhat.com/mailman/listinfo/libvirt-cim From veillard at redhat.com Thu Mar 22 14:50:05 2012 From: veillard at redhat.com (Daniel Veillard) Date: Thu, 22 Mar 2012 22:50:05 +0800 Subject: [Libvirt-cim] [PATCH v2] Improve support with sblim-sfcb Message-ID: <20120322145005.GG9627@redhat.com> add schemas (de)registration with sfcb if found in v2: - rebased - fixed patch to apply to libvirt-cim.spec.in - remove spurious changes coming from Fedora Signed-off-by: Daniel Veillard diff --git a/libvirt-cim.spec.in b/libvirt-cim.spec.in index d78eee7..571ee91 100644 --- a/libvirt-cim.spec.in +++ b/libvirt-cim.spec.in @@ -87,6 +87,8 @@ rm -fr $RPM_BUILD_ROOT /etc/init.d/tog-pegasus condrestart +if [ -x /usr/sbin/cimserver ] +then %{_datadir}/%{name}/provider-register.sh -t pegasus \ -n @CIM_VIRT_NS@ \ -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true @@ -102,8 +104,29 @@ rm -fr $RPM_BUILD_ROOT %{_datadir}/%{name}/provider-register.sh -t pegasus \ -n root/cimv2\ -r %{CIMV2_REG} -m %{CIMV2_MOF} -v >/dev/null 2>&1 || true +fi +if [ -x /usr/sbin/sfcbd ] +then +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/interop \ + -r %{INTEROP_REG} -m %{INTEROP_MOF} -v >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/PG_InterOp \ + -r %{PGINTEROP_REG} -m %{PGINTEROP_MOF} -v >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -t sfcb \ + -n root/cimv2\ + -r %{CIMV2_REG} -m %{CIMV2_MOF} -v >/dev/null 2>&1 || true +fi %preun +if [ -x /usr/sbin/cimserver ] +then %{_datadir}/%{name}/provider-register.sh -d -t pegasus \ -n @CIM_VIRT_NS@ \ -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true @@ -116,6 +139,22 @@ rm -fr $RPM_BUILD_ROOT %{_datadir}/%{name}/provider-register.sh -d -t pegasus \ -n root/cimv2 \ -r %{CIMV2_REG} -m %{CIMV2_MOF} >/dev/null 2>&1 || true +fi +if [ -x /usr/sbin/sfcbd ] +then +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/virt \ + -r %{REGISTRATION} -m %{SCHEMA} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/interop \ + -r %{INTEROP_REG} -m %{INTEROP_MOF} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/PG_InterOp \ + -r %{PGINTEROP_REG} -m %{PGINTEROP_MOF} >/dev/null 2>&1 || true +%{_datadir}/%{name}/provider-register.sh -d -t sfcb \ + -n root/cimv2 \ + -r %{CIMV2_REG} -m %{CIMV2_MOF} >/dev/null 2>&1 || true +fi %postun -p /sbin/ldconfig -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ From deheller at ptd.net Thu Mar 22 21:44:40 2012 From: deheller at ptd.net (Dave Heller) Date: Thu, 22 Mar 2012 17:44:40 -0400 Subject: [Libvirt-cim] cimtest - add SSL support to indication_tester.py In-Reply-To: <1332264993.5173.24.camel@snmishra-desktop.beaverton.ibm.com> Message-ID: <20120322214440.59B7117B8008@proxyz14.mailnet.ptd.net> Here is the updated patch, with usage notes added. Let me know if this is the acceptable format. >From 1c8a61d19baf046d32e1216f731958ddb96b89d6 Mon Sep 17 00:00:00 2001 From: Dave Heller Date: Thu, 22 Mar 2012 17:17:04 -0400 Subject: [PATCH] Updates to indication_tester.py --- .../libvirt-cim/lib/XenKvmLib/indication_tester.py | 901 +++++++++++++------- 1 files changed, 611 insertions(+), 290 deletions(-) diff --git a/suites/libvirt-cim/lib/XenKvmLib/indication_tester.py b/suites/libvirt-cim/lib/XenKvmLib/indication_tester.py index f6cbcf0..5d708d9 100644 --- a/suites/libvirt-cim/lib/XenKvmLib/indication_tester.py +++ b/suites/libvirt-cim/lib/XenKvmLib/indication_tester.py @@ -1,279 +1,411 @@ #!/usr/bin/python -# Copyright IBM Corp. 2007 +# Copyright IBM Corp. 2007-2012 # # indication_tester.py - Tool for testing indication subscription and # delivery against a CIMOM # Author: Dan Smith +# SSL support added by Dave Heller +# +# This file is both a module of the cimtest "libvirt-cim" suite and a standalone +# program for indication testing. The following notes describe the usage of the +# standalone program. +# +# USAGE: +# +# Use the -h or --help option to see the complete list of command line options. +# +# Although the script supports http or https indications to the handler +# Destination, it currently does not support https connections to the CIMOM. +# All communication to the CIMOM is over http, so the CIMOM must have a http +# port open for the script to work. +# +# Note there is some inconsistency between the --url option (CIMOM URL) and the +# --dest option (handler Destination URL). The --dest option is a true URL in +# which you may specify scheme (http or https), user and password, in addition +# to server and port. The --url option accepts only server and port. Use the +# --user and --pass options to specify CIMOM credentials. +# +# Use the --interop option to override the default interop namespace +# (root/interop). You may specify the full namespace name (with '/') or an +# abbreviated name (no '/'). In the latter case, the script will prepend +# "root/" to the name. (Example: --interop PG_InterOp) +# +# Use the --ns option to override the indication namespace (which by default is +# the same as the interop namespace). Note this option currently does not +# support abbreviations; you must specify the full namespace name. +# +# Use the --print-ind option to see responses from the CIMOM. Use the --verbose +# option to see additional debug info such as details of the CIMOM SSL +# certificates when https indications are received. +# +# Use --certFile and --keyFile to specify the indications receiver's (i.e. +# the script's) SSL certificate and key in PEM format. If --keyFile is not +# provided, the --certFile must contain the key. +# +# Use --trustStore to specify CA certificate(s) to be used to verify the CIMOM's +# certificate, or use --noverify to skip this verification. +# +# Use --trigger to cause the CIMOM to send a test indication. Currently this +# feature only supports the SFCB Test_Indication provider. (Build SFCB with +# "configure --enable-tests" before running "make".) Otherwise, the indication +# must be triggered through some independent method. +# +# EXAMPLES: +# +# Start a listener for indication class "Test_Indication" using all default +# values. The CIMOM and the listener are both running on localhost; the interop +# and indication namespaces are both: root/interop: +# +# $ indication_tester.py Test_Indication +# +# Start a listener for class "CIM_Alerts" in root/mynamespace using the +# OpenPegasus CIMOM: +# +# $ indication_tester.py --interop PG_InterOp --ns root/mynamespace CIM_Alerts +# +# Start a listener on the local host but register the subscription to a CIMOM on +# a remote host. Note the handler Destination must point to the local host in a +# manner that is resolvable to the CIMOM: +# +# $ indication_tester.py --url remotesys.mydomain.com:5988 --dest \ +# http://localsys.mydomain.com:8000 Test_Indication +# +# Start a listener to receive an indication via https, do not verify the CIMOM +# certificate: +# +# $ indication_tester.py --noverify --certFile mycert.pem --keyFile mykey.pem \ +# Test_Indication +# +# Same as above but additionally verify the CIMOM cert against known CA certs: +# +# $ indication_tester.py --trustStore CAcerts.pem --certFile mycert.pem +# --keyFile mykey.pem Test_Indication +# +# Trigger an indication using the SFCB Test_Indication provider. Note this is a +# *separate invocation* of the script, independent of the listener (presumably +# already started per the examples above). In trigger mode, the script sends +# the appropriate method call to the CIMOM and exits. If successful, the +# indication will be received at the listener: +# +# $ indication_tester.py --trigger +# +# Same as above but the CIMOM is running on a remote system: +# +# $ indication_tester.py --url remotesys:5988 --trigger import sys from optparse import OptionParser -import BaseHTTPServer +from urlparse import urlparse, urlunparse +from xml.dom.minidom import parse, parseString import httplib import base64 -from xml.dom.minidom import parse, parseString +import errno, os, re +import socket +from SocketServer import BaseServer +from SimpleHTTPServer import SimpleHTTPRequestHandler +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from OpenSSL import SSL -def filter_xml(name, type, ns, sysname): +def filter_xml(name, type, ns, sysname, interopNS): return """ - - - - - - - - - - + + + + + + + + + + - - CIM_ComputerSystem - - - %s - - - CIM_IndicationFilter - - - %sFilter - - - SELECT * FROM %s - - - - WQL - - - %s - - - - - - + + CIM_ComputerSystem + + + %s + + + CIM_IndicationFilter + + + %sFilter + + + SELECT * FROM %s + + + + WQL + + + %s + + + + + + - """ % (sysname, name, type, ns) + """ % (interopNS[0], interopNS[1], sysname, name, type, ns) -def handler_xml(name, port, sysname): +def handler_xml(name, destUrl, sysname, interopNS): return """ - - - - - - - - - - - - - CIM_ComputerSystem - - - %s - - - CIM_IndicationHandlerCIMXML - - - %sHandler - - - http://localhost:%i - - - - - - + + + + + + + + + + + + + CIM_ComputerSystem + + + %s + + + CIM_IndicationHandlerCIMXML + + + %sHandler + + + %s + + + + + + - """ % (sysname, name, port) + """ % (interopNS[0], interopNS[1], sysname, name, destUrl) -def subscription_xml(name, sysname): +def subscription_xml(name, sysname, interopNS): return """ - - - - - - - - - - - - - - - - - CIM_ComputerSystem - - - - - %s - - - - - CIM_IndicationFilter - - - - - %sFilter - - - - - - - - - - - CIM_ComputerSystem - - - - + + + + + + + + + + + + + + + + + CIM_ComputerSystem + + + + + %s + + + + + CIM_IndicationFilter + + + + + %sFilter + + + + + + + + + + + CIM_ComputerSystem + + + + %s - - - - - CIM_IndicationHandlerCIMXML - - - - - %sHandler - - - - - - - 2 - - - - - - + + + + + CIM_IndicationHandlerCIMXML + + + + + %sHandler + + + + + + + 2 + + + + + + - """ % (sysname, name, sysname, name) + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) -def delete_inst_xml(name, type, sysname, inst_name): +def delete_inst_xml(name, type, sysname, inst_name, interopNS): return """ - - - - - - - - - - - - - CIM_ComputerSystem - - - %s - - - CIM_Indication%s - - - %s - - - - - - + + + + + + + + + + + + + CIM_ComputerSystem + + + %s + + + CIM_Indication%s + + + %s + + + + + + ; - """ % (type, sysname, type, inst_name); + """ % (interopNS[0], interopNS[1], type, sysname, type, inst_name); -def delete_sub_xml(name, sysname): +def delete_sub_xml(name, sysname, interopNS): return """ - - - - - - - - - - - - - - - - - CIM_ComputerSystem - - - - + + + + + + + + + + + + + + + + + CIM_ComputerSystem + + + + %s - - - - - CIM_IndicationFilter - - - - - %sFilter - - - - - - - - - - - CIM_ComputerSystem - - - - + + + + + CIM_IndicationFilter + + + + + %sFilter + + + + + + + + + + + CIM_ComputerSystem + + + + %s - - - - - CIM_IndicationHandlerCIMXML - - - - - %sHandler - - - - - - - - - - + + + + + CIM_IndicationHandlerCIMXML + + + + + %sHandler + + + + + + + + + + ; - """ % (sysname, name, sysname, name) + """ % (interopNS[0], interopNS[1], sysname, name, sysname, name) + +def trigger_xml(type, interopNS): + return """ + + + + + + + + + + + + + + + + + """ % (interopNS[0], interopNS[1], type) + # FIXME: this should really use indication NS, not interop NS. + +def update_url_port(parsedUrl, port): + # Must manually reconstruct netloc to update the port value. + if isinstance(parsedUrl.username, basestring): + if isinstance(parsedUrl.password, basestring): + netloc = "%s:%s@%s:%s" % (parsedUrl.username, parsedUrl.password, + parsedUrl.hostname, port) + else: + netloc = "%s@%s:%s" % (parsedUrl.username, + parsedUrl.hostname, port) + else: + netloc = "%s:%s" % (parsedUrl.hostname, port) + + # Reassemble url with the updated netloc. return a string. + return urlunparse((parsedUrl.scheme, netloc, + parsedUrl.path, parsedUrl.params, + parsedUrl.query, parsedUrl.fragment)) class CIMIndication: def __init__(self, xmldata): @@ -286,50 +418,135 @@ class CIMIndication: def __str__(self): return self.name -class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): +def socket_handler_wrapper(*parms): + try: + CIMSocketHandler(*parms) + except Exception as e: + print "SSL error: %s" % str(e) + +class CIMSocketHandler(SimpleHTTPRequestHandler): + def setup(self): + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + def do_POST(self): length = self.headers.getheader('content-length') data = self.rfile.read(int(length)) indication = CIMIndication(data) - print "Got indication: %s" % indication + print "Got indication: %s from %s" % (indication, self.client_address) if self.server.print_ind: - print "%s\n\n" % data + print "%s\n" % data self.server.indications.append(indication) + # Silence the unwanted log output from send_response() + realStderr = sys.stderr + sys.stderr = open(os.devnull,'a') + self.send_response(200) + sys.stderr = realStderr + +class SecureHTTPServer(HTTPServer): + def __init__(self, server_address, HandlerClass): + BaseServer.__init__(self, server_address, HandlerClass) + + def verify_cb(conn, cert, errnum, depth, ok): + if options.verbose: + print('Verify peer certificate chain: level %d:' % depth) + print('subject=%s' % cert.get_subject()) + print('issuer =%s' % cert.get_issuer()) + return ok + + ctx = SSL.Context(SSL.SSLv23_METHOD) + #ctx.use_certificate_file(options.certFile) + ctx.use_certificate_chain_file(options.certFile) + ctx.use_privatekey_file(options.keyFile) + if options.noverify: + ctx.set_verify(SSL.VERIFY_NONE, verify_cb) + else: + #ctx.set_verify(SSL.VERIFY_PEER, verify_cb) + ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, + verify_cb) + ctx.load_verify_locations(options.trustStore) + + self.socket = SSL.Connection(ctx, socket.socket(self.address_family, + self.socket_type)) + self.server_bind() + self.server_activate() + +# The param defaults allow new options from main() w/o losing compat w/ cimtest. class CIMIndicationSubscription: - def __init__(self, name, typ, ns, print_ind, sysname, port=0): + def __init__(self, name, typ, ns, print_ind, sysname, port=0, + interopNS=('root','PG_InterOp'), destUrl="http://localhost:8000", + triggermode=False): self.name = name self.type = typ self.ns = ns self.sysname = sysname + self.interopNS = interopNS + self.print_ind = print_ind + + # We do not want to open a listener socket in trigger mode. + if triggermode: + self.trigger_xml = trigger_xml(typ, interopNS) + return + + parsedUrl = urlparse(destUrl) + + # Increment the listener port by the offset value. + if isinstance(parsedUrl.port, int): + listenerPort = parsedUrl.port + port + else: + listenerPort = 8000 + port + + destUrl = update_url_port(parsedUrl, listenerPort) + + try: + if parsedUrl.scheme == "http": + self.server = HTTPServer((parsedUrl.hostname, + listenerPort), + CIMSocketHandler) + elif parsedUrl.scheme == "https": + self.server = SecureHTTPServer((parsedUrl.hostname, + listenerPort), + socket_handler_wrapper) + except IOError as e: + print "Error creating listener socket: %s" % str(e) + exit(e.errno) - self.port = 8000 + port - self.server = BaseHTTPServer.HTTPServer(('', self.port), - CIMSocketHandler) self.server.print_ind = print_ind self.server.indications = [] - self.filter_xml = filter_xml(name, typ, ns, sysname) - self.handler_xml = handler_xml(name, self.port, sysname) - self.subscription_xml = subscription_xml(name, sysname) + self.filter_xml = filter_xml(name, typ, ns, sysname, interopNS) + self.handler_xml = handler_xml(name, destUrl, sysname, interopNS) + self.subscription_xml = subscription_xml(name, sysname, interopNS) def __do_cimpost(self, conn, body, method, auth_hdr=None): headers = {"CIMOperation" : "MethodCall", "CIMMethod" : method, - "CIMObject" : "root/PG_Interop", + #"CIMObject" : "root/PG_Interop", + "CIMObject" : "%s/%s" % self.interopNS, "Content-Type" : 'application/xml; charset="utf-8"'} - + if auth_hdr: headers["Authorization"] = "Basic %s" % auth_hdr - conn.request("POST", "/cimom", body, headers) - resp = conn.getresponse() - if not resp.getheader("content-length"): - raise Exception("Request Failed: %d %s" % - (resp.status, resp.reason)) + try: + conn.request("POST", "/cimom", body, headers) + resp = conn.getresponse() + if not resp.getheader("content-length"): + raise Exception("Request Failed: %d %s" % + (resp.status, resp.reason)) + except IOError as e: + print "Error connecting to CIMOM: %s" % str(e) + exit(e.errno) - resp.read() + if self.print_ind: + print "=== Reply from CIMOM ===" + #print resp.msg + print resp.read() + else: + resp.read() def subscribe(self, url, cred=None): self.conn = httplib.HTTPConnection(url) @@ -346,33 +563,52 @@ class CIMIndicationSubscription: self.__do_cimpost(self.conn, self.subscription_xml, "CreateInstance", auth_hdr) - def unsubscribe(self, cred=None): + # Note, param order is different here to maintain compat with cimtest. + # Better way would be to update cimtest 'indications.py' module. + def unsubscribe(self, cred=None, url=None): + # Without this, can get BadStatusLine exception in SFCB in some cases. + if url: + self.conn = httplib.HTTPConnection(url) + if cred: (u, p) = cred auth_hdr = base64.b64encode("%s:%s" % (u, p)) else: auth_hdr = None - xml = delete_sub_xml(self.name, self.sysname) + xml = delete_sub_xml(self.name, self.sysname, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, - "%sHandler" % self.name) + "%sHandler" % self.name, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) xml = delete_inst_xml(self.name, "Filter", self.sysname, - "%sFilter" % self.name) + "%sFilter" % self.name, self.interopNS) self.__do_cimpost(self.conn, xml, "DeleteInstance", auth_hdr) -def dump_xml(name, typ, ns, sysname): - filter_str = filter_xml(name, typ, ns, sysname) - handler_str = handler_xml(name, 8000, sysname) - subscript_str = subscription_xml(name, sysname) - del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % name) + def trigger(self, url, cred=None): + self.conn = httplib.HTTPConnection(url) + if cred: + (u, p) = cred + auth_hdr = base64.b64encode("%s:%s" % (u, p)) + else: + auth_hdr = None + + self.__do_cimpost(self.conn, self.trigger_xml, + "SendTestIndication", auth_hdr) + +def dump_xml(name, typ, ns, sysname, interopNS, destUrl): + filter_str = filter_xml(name, typ, ns, sysname, interopNS) + handler_str = handler_xml(name, destUrl, sysname, interopNS) + subscript_str = subscription_xml(name, sysname, interopNS) + del_filter_str = delete_inst_xml(name, "Filter", sysname, "%sFilter" % name, + interopNS) del_handler_str = delete_inst_xml(name, "HandlerCIMXML", sysname, - "%sHandler" % name) - del_subscript_str = delete_sub_xml(name, sysname) + "%sHandler" % name, interopNS) + del_subscript_str = delete_sub_xml(name, sysname, interopNS) + trigger_str = trigger_xml(typ, interopNS) print "CreateFilter:\n%s\n" % filter_str print "DeleteFilter:\n%s\n" % del_filter_str @@ -380,15 +616,20 @@ def dump_xml(name, typ, ns, sysname): print "DeleteHandler:\n%s\n" % del_handler_str print "CreateSubscription:\n%s\n" % subscript_str print "DeleteSubscription:\n%s\n" % del_subscript_str - + print "Indication trigger:\n%s\n" % trigger_str + def main(): usage = "usage: %prog [options] provider\nex: %prog CIM_InstModification" parser = OptionParser(usage) - + + # FIXME: SecureHTTPServer still relies on this, need a better way. + global options + parser.add_option("-u", "--url", dest="url", default="localhost:5988", help="URL of CIMOM to connect to (host:port)") - parser.add_option("-N", "--ns", dest="ns", default="root/virt", - help="Namespace (default is root/virt)") + parser.add_option("-N", "--ns", dest="ns", + help="Namespace in which the register the indication \ + (default is the same value as the interop namespace)") parser.add_option("-n", "--name", dest="name", default="Test", help="Name for filter, handler, subscription \ (default: Test)") @@ -398,43 +639,123 @@ def main(): parser.add_option("-p", "--print-ind", dest="print_ind", default=False, action="store_true", help="Print received indications to stdout.") + parser.add_option("-v", "--verbose", dest="verbose", default=False, + action="store_true", + help="Print additional debug info.") parser.add_option("-U", "--user", dest="username", default=None, - help="HTTP Auth username") + help="HTTP Auth username (CIMOM)") parser.add_option("-P", "--pass", dest="password", default=None, - help="HTTP Auth password") + help="HTTP Auth password (CIMOM)") parser.add_option("--port", dest="port", default=0, type=int, help="Port increment value (server default: 8000)") + parser.add_option("--dest", dest="destUrl", default="localhost:8000", + help="URL of destination handler \ + (default: http://localhost:8000)") + parser.add_option("--certFile", dest="certFile", default=None, + help="File containing the local certificate to use") + parser.add_option("--keyFile", dest="keyFile", default=None, + help="File containing private key for local cert \ + (if none provided, assume key is in the certFile)") + parser.add_option("--trustStore", dest="trustStore", default=None, + help="File containing trusted certificates for \ + remote endpoint verification") + parser.add_option("--noverify", dest="noverify", default=False, + action="store_true", + help="Skip verification of remote endpoint certificate \ + for incoming https indications") + parser.add_option("-i", "--interop", dest="interop", + default="root/interop", + help="Interop namespace name (default: root/interop)") + parser.add_option("-t", "--trigger", dest="trigger", default=False, + action="store_true", + help="Trigger mode: send a request to CIMOM to trigger \ + an indication via a method call ") (options, args) = parser.parse_args() - if len(args) == 0: + if not options.trigger and len(args)==0: print "Fatal: no indication type provided." sys.exit(1) - + if options.username: auth = (options.username, options.password) else: auth = None - + if ":" in options.url: (sysname, port) = options.url.split(":") else: sysname = options.url - + + if "/" in options.interop: + options.interopNS = tuple(options.interop.split("/")) + else: + options.interopNS = ("root", options.interop) + + # If no value provided for indication NS, default is same as interopNS. + if not options.ns: + options.ns = "%s/%s" % options.interopNS + + if options.verbose: + print "Interop namespace = %s/%s" % options.interopNS + print "Indication namespace = %s" % options.ns + + # If url does not begin with http or https, assume http. + parsedUrl = urlparse(options.destUrl) + if not re.search(parsedUrl.scheme, "https"): + destUrl = "http://" + options.destUrl + else: + destUrl = options.destUrl + + if parsedUrl.scheme == "https": + if not options.trustStore and not options.noverify: + print "Error: must provide --trustStore or --noverify with https." + sys.exit(1) + elif options.trustStore and options.noverify: + print "Error: options --trustStore and --noverify are exclusive." + sys.exit(1) + if not options.certFile: + print "Error: no certificate file provided." + sys.exit(1) + elif not options.keyFile: + print "No keyFile provided; assuming private key \ + contained in certFile." + options.keyFile = options.certFile + if options.dump: - dump_xml(options.name, args[0], options.ns, sysname) + if isinstance(parsedUrl.port, int): + listenerPort = parsedUrl.port + options.port + else: + listenerPort = 8000 + options.port + + destUrl = update_url_port(parsedUrl, listenerPort) + dump_xml(options.name, args[0], options.ns, sysname, options.interopNS, + destUrl) + sys.exit(0) + + # Trigger mode: currently only supports SFCB Test_Indication provider. + if options.trigger: + classname = "Test_Indication" + sub = CIMIndicationSubscription(options.name, classname, options.ns, + options.print_ind, sysname, options.port, + options.interopNS, destUrl, True) + print "Triggering indication for %s" % classname + sub.trigger(options.url, auth) sys.exit(0) sub = CIMIndicationSubscription(options.name, args[0], options.ns, - options.print_ind, sysname, options.port) + options.print_ind, sysname, options.port, + options.interopNS, destUrl) + + print "Creating subscription for %s" % args[0] sub.subscribe(options.url, auth) print "Watching for %s" % args[0] try: sub.server.serve_forever() - except KeyboardInterrupt,e: - sub.unsubscribe(auth) + except KeyboardInterrupt as e: print "Cancelling subscription for %s" % args[0] + sub.unsubscribe(auth, options.url) if __name__=="__main__": sys.exit(main()) -- 1.7.1 > -----Original Message----- > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > bounces at redhat.com] On Behalf Of Sharad Mishra > Sent: Tuesday, March 20, 2012 1:37 PM > To: List for discussion and development of libvirt CIM > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > indication_tester.py > > On Mon, 2012-03-19 at 19:41 -0400, Dave Heller wrote: > > Hi Sharad, > > > > Actually, I did modify the script in a way that it is still compatible > with > > cimtest (based on tests using ComputerSystemIndication > > In that case, I am going to apply your patch and test as update to > existing indication script. > > > 01_created_indication). But if you think it makes sense to fork it to a > > standalone script (and rename it), that is fine too. > > > > The usage of the script as I intend is interactive from the command > line. I > > can add some documentation with example commands. Is there a standard > place > > this should go, or should I add a README? > > You can add it to the script header itself. > Do you want to send another patch with usage added and "orig" removed > from the file name? > > Regards, > Sharad Mishra > > > > > Thanks, > > Dave > > > > > -----Original Message----- > > > From: libvirt-cim-bounces at redhat.com [mailto:libvirt-cim- > > > bounces at redhat.com] On Behalf Of Sharad Mishra > > > Sent: Monday, March 19, 2012 5:40 PM > > > To: List for discussion and development of libvirt CIM > > > Subject: Re: [Libvirt-cim] cimtest - add SSL support to > > > indication_tester.py > > > > > > Dave, > > > > > > I assume that this patch is not going to be applied to the existing > > > indication_tester.py in cimtest. In that case, we can add this > modified > > > indication_tester.py as a new file at the same location as current > > > indication_tester.py. We do need to give this script a new name. This > > > script is currently not used/invoked by any existing cimtests. Do you > > > want to write tests to use this script? > > > > > > Regards > > > Sharad Mishra > > > > > > On Mon, 2012-03-19 at 13:21 -0400, Dave Heller wrote: > > > > In the XenKvmLib directory in cimtest, there is a > "indication_tester.py" > > > > module that is both a command-line program and a component of > cimtest. > > > Here > > > > is a proposed patch that adds new functionality to the cmdline > interface > > > > while preserving compatibility with cimtest. > > > > > > > > The most significant new feature is support for SSL indications. > There > > > is > > > > also a new --trigger mode to generate a test indication using the > SFCB > > > > Test_Indication provider. This allows the script to act as a > completely > > > > stand-alone test tool (for SFCB) without the user having to rely on > the > > > > "xmltest" files and wbemcat. > > > > > > > > Below is a complete list of the new features. I am looking for > feedback > > > and > > > > potentially, the correct process to check into "cimtest". > > > > > > > > Best regards, > > > > Dave Heller > > > > > > > > New features: > > > > > > > > - Supports https indications. Adds new options --certFile and -- > keyFile > > > to > > > > specify server certificate and private key for the SSL enabled > > > indication > > > > listener. > > > > - Supports verification of indication sender (i.e. CIMOM) SSL > > > certificate. > > > > New option --trustStore is used to specify the CA certificate file; > > > > alternately the --noverify option may be used to disable peer > > > certificate > > > > checking. > > > > - Adds new option --dest to specify handler destination. This same > URL > > > is > > > > used to register the subscription (at the CIMOM) and start the > > > indication > > > > listener (locally). Via --dest one can specify scheme (http or > https), > > > IP or > > > > hostname, listener port, and optionally userid:password, all in a > single > > > > parameter. > > > > - New option --interop can be used to override the default interop > > > namespace > > > > (i.e root/interop vs. root/PG_InterOp) allowing easy support for > both > > > > openpegasus and sfcb. > > > > - Modifies the default value of existing --ns option (indication > > > namespace); > > > > if not specified the indication namespace will default to the same > value > > > as > > > > the interop namespace. > > > > - New option --verbose can be used to display debugging info, such > as > > > > details (subject, issuer) of the indication sender cert (when peer > > > > verification enabled). > > > > - Improved error handling (i.e. catch exceptions) for the following > > > > operations: starting listener locally, receiving incoming > indications, > > > POST > > > > operations (i.e. subscribe, unsubscribe) to CIMOM. > > > > - New option --trigger can be used to send a request to CIMOM to > trigger > > > an > > > > indication via the SFCB Test_Indication provider. > > > > > > > > Limitations: > > > > > > > > - Not tested on IPv6. > > > > - The --trigger option currently only supports the sfcb > Test_Indication > > > > provider. (It could be made more general by allowing the XML send > to > > > > support an arbitrary namespace, classname and method call. Open to > > > > suggestions here.) > > > > > > > >