
# HG changeset patch # User Deepti B.Kalakeri <deeptik@linux.vnet.ibm.com> # Date 1247143660 25200 # Node ID 73aedcd4026381023be4d25d741ce74113b8f8e6 # Parent 7b18b827fca6916fd87ff417f79a96c8a19b4913 [TEST] Added the indications.py to support Indications. Included the following important functions in indications.py - sub_ind() --> Subscribe for the indications to be watched. - handle_request() --> Filters the required indications. - poll_for_ind() --> Wait for the required indications to be triggered. Tested with KVM and current sources on F10. Signed-off-by: Deepti B. Kalakeri <deeptik@linux.vnet.ibm.com> diff -r 7b18b827fca6 -r 73aedcd40263 suites/libvirt-cim/lib/XenKvmLib/indications.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/suites/libvirt-cim/lib/XenKvmLib/indications.py Thu Jul 09 05:47:40 2009 -0700 @@ -0,0 +1,461 @@ +#!/usr/bin/python +# +# Copyright 2009 IBM Corp. +# +# Authors: +# Deepti B. Kalakeri <deeptik@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# + +import httplib +import base64 +import BaseHTTPServer +from xml.dom.minidom import parseString +from signal import SIGKILL +from CimTest.Globals import logger +from XenKvmLib.vxml import set_default +from XenKvmLib.classes import get_typed_class +from CimTest.ReturnCodes import PASS, FAIL +from os import waitpid, kill, WNOHANG + +def filter_xml(name, type, ns, sysname): + return """ + <?xml version="1.0" encoding="utf-8"?> + <CIM CIMVERSION="2.0" DTDVERSION="2.0"> + <MESSAGE ID="4711" PROTOCOLVERSION="1.0"> + <SIMPLEREQ> + <IMETHODCALL NAME="CreateInstance"> + <LOCALNAMESPACEPATH> + <NAMESPACE NAME="root"/> + <NAMESPACE NAME="PG_InterOp"/> + </LOCALNAMESPACEPATH> + <IPARAMVALUE NAME="NewInstance"> + <INSTANCE CLASSNAME="CIM_IndicationFilter"> + <PROPERTY NAME="SystemCreationClassName" TYPE="string"> + <VALUE>CIM_ComputerSystem</VALUE> + </PROPERTY> + <PROPERTY NAME="SystemName" TYPE="string"> + <VALUE>%s</VALUE> + </PROPERTY> + <PROPERTY NAME="CreationClassName" TYPE="string"> + <VALUE>CIM_IndicationFilter</VALUE> + </PROPERTY> + <PROPERTY NAME="Name" TYPE="string"> + <VALUE>%sFilter</VALUE> + </PROPERTY> + <PROPERTY NAME="Query" TYPE="string"> + <VALUE> SELECT * FROM %s + </VALUE> + </PROPERTY> + <PROPERTY NAME="QueryLanguage" TYPE="string"> + <VALUE>WQL</VALUE> + </PROPERTY> + <PROPERTY NAME="SourceNamespace" TYPE="string"> + <VALUE>%s</VALUE> + </PROPERTY> + </INSTANCE> + </IPARAMVALUE> + </IMETHODCALL> + </SIMPLEREQ> + </MESSAGE> + </CIM> + """ % (sysname, name, type, ns) + +def handler_xml(name, port, sysname): + return """ + <?xml version="1.0" encoding="utf-8"?> + <CIM CIMVERSION="2.0" DTDVERSION="2.0"> + <MESSAGE ID="4711" PROTOCOLVERSION="1.0"> + <SIMPLEREQ> + <IMETHODCALL NAME="CreateInstance"> + <LOCALNAMESPACEPATH> + <NAMESPACE NAME="root"/> + <NAMESPACE NAME="PG_InterOp"/> + </LOCALNAMESPACEPATH> + <IPARAMVALUE NAME="NewInstance"> + <INSTANCE CLASSNAME="CIM_IndicationHandlerCIMXML"> + <PROPERTY NAME="SystemCreationClassName" TYPE="string"> + <VALUE>CIM_ComputerSystem</VALUE> + </PROPERTY> + <PROPERTY NAME="SystemName" TYPE="string"> + <VALUE>%s</VALUE> + </PROPERTY> + <PROPERTY NAME="CreationClassName" TYPE="string"> + <VALUE>CIM_IndicationHandlerCIMXML</VALUE> + </PROPERTY> + <PROPERTY NAME="Name" TYPE="string"> + <VALUE>%sHandler</VALUE> + </PROPERTY> + <PROPERTY NAME="Destination" TYPE="string"> + <VALUE>http://localhost:%i</VALUE> + </PROPERTY> + </INSTANCE> + </IPARAMVALUE> + </IMETHODCALL> + </SIMPLEREQ> + </MESSAGE> + </CIM> + """ % (sysname, name, port) + +def subscription_xml(name, sysname): + return """ + <?xml version="1.0" encoding="utf-8"?> + <CIM CIMVERSION="2.0" DTDVERSION="2.0"> + <MESSAGE ID="4711" PROTOCOLVERSION="1.0"> + <SIMPLEREQ> + <IMETHODCALL NAME="CreateInstance"> + <LOCALNAMESPACEPATH> + <NAMESPACE NAME="root"/> + <NAMESPACE NAME="PG_InterOp"/> + </LOCALNAMESPACEPATH> + <IPARAMVALUE NAME="NewInstance"> + <INSTANCE CLASSNAME="CIM_IndicationSubscription"> + <PROPERTY.REFERENCE NAME="Filter" + REFERENCECLASS="CIM_IndicationFilter"> + <VALUE.REFERENCE> + <INSTANCENAME CLASSNAME="CIM_IndicationFilter"> + <KEYBINDING NAME="SystemCreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_ComputerSystem + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="SystemName"> + <KEYVALUE VALUETYPE="string"> + %s + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="CreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_IndicationFilter + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="Name"> + <KEYVALUE VALUETYPE="string"> + %sFilter + </KEYVALUE> + </KEYBINDING> + </INSTANCENAME> + </VALUE.REFERENCE> + </PROPERTY.REFERENCE> + <PROPERTY.REFERENCE NAME="Handler" + REFERENCECLASS="CIM_IndicationHandler"> + <VALUE.REFERENCE> + <INSTANCENAME CLASSNAME="CIM_IndicationHandlerCIMXML"> + <KEYBINDING NAME="SystemCreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_ComputerSystem + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="SystemName"> + <KEYVALUE VALUETYPE="string"> + %s + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="CreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_IndicationHandlerCIMXML + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="Name"> + <KEYVALUE VALUETYPE="string"> + %sHandler + </KEYVALUE> + </KEYBINDING> + </INSTANCENAME> + </VALUE.REFERENCE> + </PROPERTY.REFERENCE> + <PROPERTY NAME="SubscriptionState" TYPE="uint16"> + <VALUE> 2 </VALUE> + </PROPERTY> + </INSTANCE> + </IPARAMVALUE> + </IMETHODCALL> + </SIMPLEREQ> + </MESSAGE> + </CIM> + """ % (sysname, name, sysname, name) + +def delete_inst_xml(name, type, sysname, inst_name): + return """ + <?xml version="1.0" encoding="utf-8"?> + <CIM CIMVERSION="2.0" DTDVERSION="2.0"> + <MESSAGE ID="4711" PROTOCOLVERSION="1.0"> + <SIMPLEREQ> + <IMETHODCALL NAME="DeleteInstance"> + <LOCALNAMESPACEPATH> + <NAMESPACE NAME="root"/> + <NAMESPACE NAME="PG_InterOp"/> + </LOCALNAMESPACEPATH> + <IPARAMVALUE NAME="InstanceName"> + <INSTANCENAME CLASSNAME="CIM_Indication%s"> + <KEYBINDING NAME="SystemCreationClassName"> + <KEYVALUE>CIM_ComputerSystem</KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="SystemName"> + <KEYVALUE>%s</KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="CreationClassName"> + <KEYVALUE>CIM_Indication%s</KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="Name"> + <KEYVALUE>%s</KEYVALUE> + </KEYBINDING> + </INSTANCENAME> + </IPARAMVALUE> + </IMETHODCALL> + </SIMPLEREQ> + </MESSAGE> + </CIM>; + """ % (type, sysname, type, inst_name); + +def delete_sub_xml(name, sysname): + return """ + <?xml version="1.0" encoding="utf-8"?> + <CIM CIMVERSION="2.0" DTDVERSION="2.0"> + <MESSAGE ID="4711" PROTOCOLVERSION="1.0"> + <SIMPLEREQ> + <IMETHODCALL NAME="DeleteInstance"> + <LOCALNAMESPACEPATH> + <NAMESPACE NAME="root"/> + <NAMESPACE NAME="PG_InterOp"/> + </LOCALNAMESPACEPATH> + <IPARAMVALUE NAME="InstanceName"> + <INSTANCENAME CLASSNAME="CIM_IndicationSubscription"> + <KEYBINDING NAME="Filter"> + <VALUE.REFERENCE> + <INSTANCENAME CLASSNAME="CIM_IndicationFilter"> + <KEYBINDING NAME="SystemCreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_ComputerSystem + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="SystemName"> + <KEYVALUE VALUETYPE="string"> + %s + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="CreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_IndicationFilter + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="Name"> + <KEYVALUE VALUETYPE="string"> + %sFilter + </KEYVALUE> + </KEYBINDING> + </INSTANCENAME> + </VALUE.REFERENCE> + </KEYBINDING> + <KEYBINDING NAME="Handler"> + <VALUE.REFERENCE> + <INSTANCENAME CLASSNAME="CIM_IndicationHandlerCIMXML"> + <KEYBINDING NAME="SystemCreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_ComputerSystem + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="SystemName"> + <KEYVALUE VALUETYPE="string"> + %s + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="CreationClassName"> + <KEYVALUE VALUETYPE="string"> + CIM_IndicationHandlerCIMXML + </KEYVALUE> + </KEYBINDING> + <KEYBINDING NAME="Name"> + <KEYVALUE VALUETYPE="string"> + %sHandler + </KEYVALUE> + </KEYBINDING> + </INSTANCENAME> + </VALUE.REFERENCE> + </KEYBINDING> + </INSTANCENAME> + </IPARAMVALUE> + </IMETHODCALL> + </SIMPLEREQ> + </MESSAGE> + </CIM>; + """ % (sysname, name, sysname, name) + +class CIMIndication: + def __init__(self, xmldata): + dom = parseString(xmldata) + + instances = dom.getElementsByTagName("INSTANCE") + attrs = instances[0].attributes.items() + self.name = attrs[0][1] + + def __str__(self): + return self.name + +class CIMSocketHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_POST(self): + length = self.headers.getheader('content-length') + data = self.rfile.read(int(length)) + + indication = CIMIndication(data) + print "Got indication: %s" % indication + if self.server.print_ind: + print "%s\n\n" % data + self.server.indications.append(indication) + +class CIMIndicationSubscription: + def __init__(self, name, typ, ns, print_ind, sysname, port=0): + self.name = name + self.type = typ + self.ns = ns + self.sysname = sysname + + 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) + + def __do_cimpost(self, conn, body, method, auth_hdr=None): + headers = {"CIMOperation" : "MethodCall", + "CIMMethod" : method, + "CIMObject" : "root/PG_Interop", + "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() + + def subscribe(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.filter_xml, + "CreateInstance", auth_hdr) + self.__do_cimpost(self.conn, self.handler_xml, + "CreateInstance", auth_hdr) + self.__do_cimpost(self.conn, self.subscription_xml, + "CreateInstance", auth_hdr) + + def unsubscribe(self, cred=None): + 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) + self.__do_cimpost(self.conn, xml, + "DeleteInstance", auth_hdr) + xml = delete_inst_xml(self.name, "HandlerCIMXML", self.sysname, + "%sHandler" % self.name) + self.__do_cimpost(self.conn, xml, + "DeleteInstance", auth_hdr) + xml = delete_inst_xml(self.name, "Filter", self.sysname, + "%sFilter" % self.name) + self.__do_cimpost(self.conn, xml, + "DeleteInstance", auth_hdr) + + +def sub_ind(ip, virt, ind_names): + dict = set_default(ip) + sub_list = {} + port = 5 + + for ind, iname in ind_names.iteritems(): + ind_name = get_typed_class(virt, iname) + + sub_name = "Test%s" % ind_name + port += 1 + + sub = CIMIndicationSubscription(sub_name, ind_name, + dict['default_ns'], + dict['default_print_ind'], + dict['default_sysname'], + port) + sub.subscribe(dict['default_url'], dict['default_auth']) + logger.info("Watching for %s", iname) + ind_names[ind] = ind_name + sub_list[ind] = sub + + return sub_list, ind_names, dict + +def handle_request(sub, ind_name, dict, exp_ind_ct): + #sfcb delivers indications to all registrations, even if the indication + #isn't what the registration was subscribed to. So, for modified and + #deleted indications, we must loop through until the indication we are + #looking for is triggered. + for i in range(0, exp_ind_ct): + sub.server.handle_request() + if len(sub.server.indications) < 1: + logger.error("No valid indications received") + return FAIL + + if str(sub.server.indications[0]) == ind_name: + sub.unsubscribe(dict['default_auth']) + logger.info("Cancelling subscription for %s", ind_name) + return PASS + else: + sub.server.indications.remove(sub.server.indications[0]) + + logger.error("Did not recieve indication %s", ind_name) + return FAIL + +def poll_for_ind(pid, ind_name, timeout=20): + status = FAIL + for i in range(0, timeout): + pw = waitpid(pid, WNOHANG) + + # If pid exits, waitpid returns [pid, return_code] + # If pid is still running, waitpid returns [0, 0] + # Only return a success if waitpid returns the expected pid + # and the return code is 0. + if pw[0] == pid and pw[1] == 0: + logger.info("Great, got '%s' indication successfully", ind_name) + status = PASS + break + elif pw[1] == 0 and i < timeout: + if i % 10 == 0: + logger.info("In child, waiting for '%s' indication", ind_name) + sleep(1) + else: + # Time is up and waitpid never returned the expected pid + if pw[0] != pid: + logger.error("Waited too long for '%s' indication", ind_name) + kill(pid, SIGKILL) + else: + logger.error("Received Indication error: '%d'", pw[1]) + + status = FAIL + break + + return status