Here is my apache logs : <div>------------------------------------------------------------------------------------------------</div>
<div><div>==> /var/log/httpd/error_log <==</div><div>[Wed Apr 21 20:02:51 2010] [warn] mod_python (pid=1529, interpreter='<a href="http://rpcserver.domain.org">rpcserver.domain.org</a>'): Module directory listed in "sys.path". This may cause problems. Please check code. File being imported is "/usr/lib/python2.6/site-packages/webservices/account.py".</div>


<div>[Wed Apr 21 20:02:51 2010] [notice] mod_python (pid=1529, interpreter='<a href="http://rpcserver.domain.org">rpcserver.domain.org</a>'): Importing module '/usr/lib/python2.6/site-packages/webservices/account.py'</div>


<div>/usr/lib/python2.6/site-packages/mod_python/importer.py:32: DeprecationWarning: the md5 module is deprecated; use hashlib instead</div><div>  import md5</div><div>ipa: ERROR: Could not create log_dir '/root/.ipa/log'</div>


<div>ipa: ERROR: could not load plugin module '/usr/lib/python2.6/site-packages/ipalib/plugins/migration.py'</div><div>Traceback (most recent call last):</div><div>  File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 533, in import_plugins</div>


<div>    __import__(fullname)</div><div>  File "/usr/lib/python2.6/site-packages/ipalib/plugins/migration.py", line 33, in <module></div><div>    from ipaserver.plugins.ldap2 import ldap2</div><div>  File "/usr/lib/python2.6/site-packages/ipaserver/__init__.py", line 33, in <module></div>


<div>    api.bootstrap(context='server', debug=True, log=None)</div><div>  File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 380, in bootstrap</div><div>    self.__doing('bootstrap')</div>


<div>  File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 365, in __doing</div><div>    '%s.%s() already called' % (self.__class__.__name__, name)</div><div>StandardError: API.bootstrap() already called</div>


<div><br></div><div><br></div><div>==> /var/log/httpd/access_log <==</div><div>172.30.0.135 - - [21/Apr/2010:20:02:51 +0200] "POST /xmlrpc HTTP/1.0" 200 348 "-" "<a href="http://xmlrpclib.py/1.0.1" target="_blank">xmlrpclib.py/1.0.1</a> (by <a href="http://www.pythonware.com" target="_blank">www.pythonware.com</a>)"</div>


<div><br></div></div><div>------------------------------------------------------------------------------------------------</div><div><div><br></div></div><div><br></div><div>And here my xmlrpchandler</div><div><div><div>

------------------------------------------------------------------------------------------------</div>
</div></div><div><div>import sys</div><div>import os</div><div>from mod_python import apache</div><div>import xmlrpclib</div><div>import types</div><div><br></div><div>import imp</div><div>import re</div><div><br></div><div>


<br></div><div># Functions we want callable via XML-RPC</div><div>__all__ = ['listMethods', 'methodSignature', 'methodHelp', 'multicall']</div><div><br></div><div># For method signatures</div>


<div>INT = 'int'</div><div>STRING = 'string'</div><div>BOOLEAN = 'boolean'</div><div>DOUBLE = 'double'</div><div>DATETIME = 'dateTime.iso8601'</div><div>BASE64 = 'base64'</div>


<div>ARRAY = 'array'</div><div>STRUCT = 'struct'</div><div><br></div><div># Saw this done in mod_python's apache.py. I just fixed it up a little...</div><div>_suffixes = map(lambda x: x[0].replace('.', '\\.'), imp.get_suffixes())</div>


<div>_exp = '(' + '|'.join(_suffixes) + ')$'</div><div>_suffix_re = re.compile(_exp)</div><div><br></div><div>_environ = {}</div><div><br></div><div><br></div><div><br></div><div><br></div><div>def listMethods(env=None):</div>


<div>    """Enumerates all available XML-RPC methods."""</div><div>    __xmlrpc_signature = '[[ARRAY]]'</div><div><br></div><div>    method_list = []</div><div><br></div><div>    # scan the directory which this module resides in</div>


<div>    path = os.path.dirname(sys.modules[__name__].__file__)</div><div>    try:</div><div>        module_list = []</div><div>        </div><div>        files = os.listdir(path)</div><div>        for f in files:</div><div>


            # does it have a module suffix?</div><div>            if not _suffix_re.search(f):</div><div>                continue</div><div>            # strip module suffix</div><div>            module_name = _suffix_re.sub('', f)</div>


<div>            # ensure it's not private or this module</div><div>            if module_name[0] == '_' or module_name == __name__:</div><div>                continue</div><div>            if module_name not in module_list:</div>


<div>                module_list.append(module_name)</div><div><br></div><div>        for module_name in module_list:</div><div>            try:</div><div>                module = apache.import_module(module_name, path=[path])</div>


<div>            except:</div><div>                pass</div><div>            else:</div><div>                # scan module for non-private functions</div><div>                func_list = getattr(module, '__all__', dir(module))</div>


<div>                for func_name in func_list:</div><div>                    if func_name[0] != '_' and \</div><div>                           callable(getattr(module, func_name, None)):</div><div>                        method_list.append('%s.%s' % (module_name, func_name))</div>


<div>    except:</div><div>        pass</div><div>    </div><div>    # add system methods</div><div>    method_list.extend(map(lambda x: 'system.%s' % x, __all__))</div><div><br></div><div>    method_list.sort()</div>


<div>    return method_list</div><div><br></div><div>def methodSignature(method, env=None):</div><div>    """Returns an XML-RPC method's signature."""</div><div>    __xmlrpc_signature = '[[ARRAY, STRING]]'</div>


<div>    </div><div>    func = _map_methodName(method)</div><div>    if not func:</div><div>        return xmlrpclib.Fault(1, '%s: not implemented' % method)</div><div><br></div><div>    return _get_signature(func)</div>


<div><br></div><div>def methodHelp(method, env=None):</div><div>    """Returns an XML-RPC method's help string."""</div><div>    __xmlrpc_signature = '[[STRING, STRING]]'</div><div>


    </div><div>    func = _map_methodName(method)</div><div>    if not func:</div><div>        return xmlrpclib.Fault(1, '%s: not implemented' % method)</div><div><br></div><div>    if func.__doc__:</div><div>        help = _strip_docstring(func.__doc__)</div>


<div>    else:</div><div>        help = ''</div><div>        </div><div>    return help</div><div><br></div><div>def multicall(call_params, env=None):</div><div>    """Executes multiple method calls with a single request."""</div>


<div>    __xmlrpc_signature = '[[ARRAY, ARRAY]]'</div><div><br></div><div>    result_list = []</div><div><br></div><div>    for param in call_params:</div><div>        if type(param) != dict:</div><div>            result_list.append(_fault_struct(1, 'struct expected'))</div>


<div>            continue</div><div><br></div><div>        if not param.has_key('methodName') or \</div><div>               not param.has_key('params'):</div><div>            result_list.append(_fault_struct(1, 'methodName/params members ' \</div>


<div>                                             'missing'))</div><div>            continue</div><div>        </div><div>        method, params = param['methodName'], param['params']</div><div><br>


</div><div>        if method == 'system.multicall':</div><div>            result_list.append(_fault_struct(1, 'system.multicall: ' \</div><div>                                             'recursion forbidden'))</div>


<div>            continue</div><div>        </div><div>        try:</div><div>            result = _dispatch(method, params)</div><div>            if isinstance(result, xmlrpclib.Fault):</div><div>                result = _fault_struct(result.faultCode, result.faultString)</div>


<div>            else:</div><div>                result = (result,)</div><div>        except:</div><div>            result_list.append(_fault_struct(2, '%s: %s: %s' %</div><div>                                             (method, sys.exc_type,</div>


<div>                                              sys.exc_value)))</div><div>        else:</div><div>            result_list.append(result)</div><div>        </div><div>    return result_list</div><div><br></div><div>def _expand_tabs(s, width=8):</div>


<div>    """Expands tabs to spaces, assuming tabs are of the specified width."""</div><div>    </div><div>    o = ''</div><div>    col = 0</div><div>    for c in s:</div><div>        if c == '\t':</div>


<div>            next = width - (col % width)</div><div>            o += '        '[:next]</div><div>            col += next</div><div>        else:</div><div>            o += c</div><div>            col += 1</div>


<div>    return o</div><div><br></div><div>def _strip_docstring(s):</div><div>    """Takes a docstring and removes any extraneous indentation."""</div><div>    </div><div>    # Break into lines and expand tabs.</div>


<div>    s = s.split('\n')</div><div>    s = map(_expand_tabs, s)</div><div><br></div><div>    # Convert lines with only spaces to empty strings.</div><div>    for i in range(len(s)):</div><div>        if s[i] and not s[i].strip():</div>


<div>            s[i] = ''</div><div><br></div><div>    # Single line or empty docstring.</div><div>    if len(s) == 1:</div><div>        return s[0]</div><div><br></div><div>    # Take care of the first line.</div>


<div>    o = ''</div><div>    o += s[0] + '\n'</div><div><br></div><div>    # Go through each line. The first non-blank line determines the indent for</div><div>    # the entire docstring. Unindent each line.</div>


<div>    indent = 0</div><div>    for line in s[1:]:</div><div>        if line:</div><div>            if not indent:</div><div>                indent = len(line) - len(line.lstrip())</div><div>            if line.startswith(' '*indent):</div>


<div>                line = line[indent:]</div><div>            else:</div><div>                # Indent was short. Strip as much as we can anyway.</div><div>                line = line.lstrip()</div><div>            o += line + '\n'</div>


<div>        else:</div><div>            o += '\n'</div><div><br></div><div>    # If docstring ends with two linefeeds, remove one of them.</div><div>    if o[-2:] == '\n\n':</div><div>        o = o[:-1]</div>


<div>    </div><div>    return o</div><div><br></div><div>def _get_func_const(func, name, default=None):</div><div>    """Get the value of a constant variable defined in a function."""</div>

<div>
<br></div><div>    func_code = getattr(func, 'func_code', None)</div><div>    if func_code:</div><div>        if name in func_code.co_names:</div><div>            i = list(func_code.co_names).index(name) + 1</div>


<div>            return func_code.co_consts[i]</div><div>    return default</div><div><br></div><div>def _get_signature(func):</div><div>    """Return the parameter signature of a function.</div><div><br></div>


<div>    Will first check func.__xmlrpc_signature, expecting it to be a list</div><div>    of signatures. Otherwise, it will check a constant variable called</div><div>    __xmlrpc_signature defined within the function. The variable MUST</div>


<div>    be a string and must evaluate to a list of signatures.</div><div><br></div><div>    Returns an empty list if none neither are a valid signature list.</div><div>    """</div><div>    </div><div>    if hasattr(func, '__xmlrpc_signature'):</div>


<div>        sig = func.__xmlrpc_signature</div><div>    else:</div><div>        sig_str = _get_func_const(func, '__xmlrpc_signature')</div><div>        sig = []</div><div>        if sig_str:</div><div>            try:</div>


<div>                sig = eval(sig_str)</div><div>                # need to validate the signature someday...</div><div>            except:</div><div>                pass</div><div>        </div><div>    return sig</div>


<div><br></div><div>_type_map = {</div><div>    types.IntType: INT,</div><div>    types.LongType: INT,</div><div>    types.StringType: STRING,</div><div>    types.FloatType: DOUBLE,</div><div>    types.TupleType: ARRAY,</div>


<div>    types.ListType: ARRAY,</div><div>    types.DictType: STRUCT</div><div>    }</div><div><br></div><div>def _xmlrpc_type(v):</div><div>    """Returns an XML-RPC type for a given value."""</div>


<div><br></div><div>    t = type(v)</div><div>    if _type_map.has_key(t):</div><div>        return _type_map[t]</div><div>    if t is types.InstanceType:</div><div>        if isinstance(v, xmlrpclib.DateTime):</div><div>


            return DATETIME</div><div>        elif isinstance(v, xmlrpclib.Binary):</div><div>            return BASE64</div><div>        elif isinstance(v, xmlrpclib.Boolean):</div><div>            return BOOLEAN</div><div>


    # Huh?!</div><div>    return STRING</div><div><br></div><div>def _match_signature(params, sig_list):</div><div>    """Matches an argument list with a signature list.</div><div><br></div><div>    If signature list is empty, any sort of argument list is accepted.</div>


<div>    """</div><div>    </div><div>    param_types = map(_xmlrpc_type, params)</div><div>    empty_sig = 1</div><div>    for sig in sig_list:</div><div>        empty_sig = 0</div><div><br></div><div>        # skip return type</div>


<div>        sig = sig[1:]</div><div>        </div><div>        if len(param_types) == len(sig) and param_types == sig:</div><div>            return 1</div><div>        </div><div>    return empty_sig</div><div><br></div>


<div>def _fault_struct(faultCode, faultString):</div><div>    """Returns a Fault as a dictionary."""</div><div><br></div><div>    return { 'faultCode': faultCode, 'faultString': faultString }</div>


<div><br></div><div>def _map_methodName(method):</div><div>    """Maps a methodName in the form of module.function to a function."""</div><div><br></div><div>    # parse methodName as module.function</div>


<div>    method = method.split('.')</div><div>    if len(method) != 2:</div><div>        return None</div><div><br></div><div>    module_name, func_name = method</div><div><br></div><div>    if module_name == 'system':</div>


<div>        # reserved functions are implemented in this module</div><div>        module = sys.modules[__name__]</div><div>    else:</div><div>        # attempt to load module from the same directory as this module</div>


<div>        path = os.path.dirname(sys.modules[__name__].__file__)</div><div><br></div><div>        module = None</div><div>        # ensure it is not private</div><div>        if module_name[0] != '_' and module_name != __name__:</div>


<div>            try:</div><div>                module = apache.import_module(module_name, path=[path])</div><div>            except:</div><div>                pass</div><div><br></div><div>        if module is None:</div>


<div>            return None</div><div><br></div><div>    # now see if module has callable function named func_name</div><div>    if hasattr(module, '__all__') and func_name not in module.__all__:</div><div>        return None</div>


<div>    </div><div>    if func_name[0] != '_' and hasattr(module, func_name):</div><div>        func = getattr(module, func_name)</div><div><br></div><div>        if callable(func):</div><div>            return func</div>


<div><br></div><div>    return None</div><div><br></div><div>def _dispatch(method, params):</div><div>    """Calls an XML-RPC method."""</div><div>    global _environ</div><div><br></div><div>


    func = _map_methodName(method)</div><div>    if func is None:</div><div>        return xmlrpclib.Fault(1, '%s: not implemented' % method)</div><div><br></div><div>    # check arguments</div><div>    sig_list = _get_signature(func)</div>


<div>    if not _match_signature(params, sig_list):</div><div>        return xmlrpclib.Fault(1, '%s: bad arguments' % method )</div><div>    </div><div>    # call the function</div><div>    result = apply(func, params, {'env':_environ})</div>


<div>    </div><div>    # if result is None, set it to False</div><div>    if result is None:</div><div>        result = xmlrpclib.False</div><div>                </div><div>    return result</div><div><br></div><div># These HTTP headers are required according to the XML-RPC spec</div>


<div>_required_headers = ['Host', 'User-Agent', 'Content-Type', 'Content-Length']</div><div><br></div><div>def handler(req):</div><div>    """mod_python handler."""</div>


<div>    global _environ</div><div>    _environ = dict(apache.build_cgi_env(req))</div><div>    # only accept POST requests</div><div>    if req.method != 'POST':</div><div>        # We set this even though it doesn't seem to do anything...</div>


<div>        req.err_headers_out['Allow'] = 'POST'</div><div>        return apache.HTTP_METHOD_NOT_ALLOWED</div><div><br></div><div>    # check that all required headers are present</div><div>    for h in _required_headers:</div>


<div>        if not req.headers_in.has_key(h):</div><div>            return apache.HTTP_BAD_REQUEST</div><div><br></div><div>    if not req.headers_in['Content-Type'].startswith('text/xml'):</div><div>        return apache.HTTP_UNSUPPORTED_MEDIA_TYPE</div>


<div><br></div><div>    # The structure of the following was inspired by Brian Quinlan's</div><div>    # SimpleXMLRPCServer.py (which was inspired by Fredrik Lundh's code).</div><div>    try:</div><div>        length = int(req.headers_in['Content-Length'])</div>


<div><br></div><div>        # read and parse request</div><div>        data = req.read(length)</div><div>        params, method = xmlrpclib.loads(data)</div><div><br></div><div>        try:</div><div>            # dispatch the method</div>


<div>            response = _dispatch(method, params)</div><div><br></div><div>            # convert response to singleton, if necessary</div><div>            if not isinstance(response, xmlrpclib.Fault):</div><div>                response = (response,)</div>


<div>        except:</div><div>            # caught an exception, return it as our fault response</div><div>            response = xmlrpclib.Fault(2, '%s: %s: %s' %</div><div>                                       (method, sys.exc_type, sys.exc_value))</div>


<div><br></div><div>        # convert response to xml</div><div>        response = xmlrpclib.dumps(response, methodresponse=1)</div><div>    except:</div><div>        # eh?!</div><div>        return apache.HTTP_BAD_REQUEST</div>


<div>    else:</div><div>        # send response</div><div>        req.content_type = 'text/xml'</div><div>        req.headers_out['Content-Length'] = str(len(response))</div><div>        req.send_http_header()</div>


<div>        req.write(response)</div><div>        return apache.OK</div></div><div><div>------------------------------------------------------------------------------------------------</div></div><div><br></div><div><br>


</div><div>And here my code of client </div><div><div>------------------------------------------------------------------------------------------------</div></div><div>#!/usr/bin/python                                                                                                                                                                <div>


<br></div><div>import xmlrpclib as rpc</div><div>remote = rpc.ServerProxy('<a href="http://client2.gamma.agorabox.org/xmlrpc" target="_blank">http://<span class="Apple-style-span" style="color: rgb(0, 0, 0); -webkit-text-decorations-in-effect: none; ">rpcserver.domain.org</span>/xmlrpc</a>')</div>

<div>print "+++++++ remote.account.getUserInfos(u'admin')"</div>
<div>print remote.account.getUserInfos(u'admin')</div><div><div><div>------------------------------------------------------------------------------------------------</div></div></div><div><br></div><div><br></div>


<div><br></div><div>---</div>Meilleures salutations / Best Regards<br><br>Rachid ALAHYANE<br><br>
<br><br><div class="gmail_quote">2010/4/21 Rob Crittenden <span dir="ltr"><<a href="mailto:rcritten@redhat.com" target="_blank">rcritten@redhat.com</a>></span><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">



<div>ALAHYANE Rachid wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Ok so, my end goal is to use the ipa methods with xml-rpc as following,<br>
<br>
 *  ipaServer : my ipa server, used to authenticate users and serves response for xml-rpc calls from rpcServer<br>
 *  rpcServer : this host is my xml-rpc server, I installed freeipa libraires on it, and an apache server with mod_python and mod_auth_kerb. This hosts will be used as a client ipa, It is for these reasons that i used `account.py` from within apache.<br>




 * myClient : this host is the one which will make the rpc calls to rpcServer.<br>
<br>
NB : 'account.py' is called by xmlrpchandler (it is my python handler) when getUserInfos is called by myclient .<br>
</blockquote>
<br></div>
Can you set LogLevel debug on the rpcServer web server and see if you get a backtrace (or look in /var/log/httpd/error_log, you may already have one).<br>
<br>
What is strange is that this code works fine standalone. Can you show us all the code in your xmlrpchandler?<br>
<br>
BTW, your English is fine :-)<br><font color="#888888">
<br>
rob</font><div><div></div><div><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
Example :<br>
myClient calls the remote.account.getUserInfos(u'admin'), rpcServer (in mode client) intercepts this call and forwards it to the ipaServer. This last one sends the response to rpcServer (via xml-rpc) and then rpcServer responds to myClient.<br>




<br>
<br>
this is my configurations :<br>
<br>
== On rpcServer ==<br>
--------- httpd conf ------------<br>
<Files "xmlrpc"><br>
  ## python conf                                                                                                                                                                   # ....<br>
  SetHandler python-program<br>
  PythonHandler xmlrpchandler<br>
  PythonDebug on<br>
</Files><br>
------------------------------------<br>
<br>
the handler xmlrpchandler calls the following method when the client requests for the remote method getUserInfos().<br>
<br>
--------- account.py ------------<br>
def getUserInfos(user_name, env=None):<br>
<br>
    from ipalib import api<br>
<br>
    api.bootstrap_with_global_options(context='webservices')<br>
    api.finalize()<br>
    # mode Server is False. I am not on the server ipa<br>
    api.Backend.xmlclient.connect()<br>
    return api.Command.user_show(user_name)  ------------------------------------<br>
<br>
<br>
== On myClient ==<br>
When I call  this method from my client, I get this exception : <br>
------------------------------------<br>
<Fault 2: "remote.account.getUserInfos: <type 'exceptions.StandardError'>: API.bootstrap() already called"><br>
------------------------------------<br>
<br>
<br>
I hope that is clearer now, despite my bad English ;)<br>
<br>
<br>
---<br>
Meilleures salutations / Best Regards<br>
<br>
Rachid ALAHYANE<br>
<br>
<br>
<br>
</blockquote>
<br>
</div></div></blockquote></div><br>
</div>