ALAHYANE Rachid wrote:
Here is my apache logs : ------------------------------------------------------------------------------------------------
==> /var/log/httpd/error_log <==
[Wed Apr 21 20:02:51 2010] [warn] mod_python (pid=1529, interpreter='rpcserver.domain.org <http://rpcserver.domain.org>'): 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". [Wed Apr 21 20:02:51 2010] [notice] mod_python (pid=1529, interpreter='rpcserver.domain.org <http://rpcserver.domain.org>'): Importing module '/usr/lib/python2.6/site-packages/webservices/account.py' /usr/lib/python2.6/site-packages/mod_python/importer.py:32: DeprecationWarning: the md5 module is deprecated; use hashlib instead
  import md5
ipa: ERROR: Could not create log_dir '/root/.ipa/log'
ipa: ERROR: could not load plugin module '/usr/lib/python2.6/site-packages/ipalib/plugins/migration.py'
Traceback (most recent call last):
File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 533, in import_plugins
    __import__(fullname)
File "/usr/lib/python2.6/site-packages/ipalib/plugins/migration.py", line 33, in <module>
    from ipaserver.plugins.ldap2 import ldap2
File "/usr/lib/python2.6/site-packages/ipaserver/__init__.py", line 33, in <module>
    api.bootstrap(context='server', debug=True, log=None)
File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 380, in bootstrap
    self.__doing('bootstrap')
File "/usr/lib/python2.6/site-packages/ipalib/plugable.py", line 365, in __doing
    '%s.%s() already called' % (self.__class__.__name__, name)
StandardError: API.bootstrap() already called


Very strange. You explicitly set the context to 'webservices' and this backtrace shows it as 'server' which is why migration.py is trying to load ldap2 (and blowing up).

Jason, any ideas?

rob



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

------------------------------------------------------------------------------------------------


And here my xmlrpchandler
------------------------------------------------------------------------------------------------
import sys
import os
from mod_python import apache
import xmlrpclib
import types

import imp
import re


# Functions we want callable via XML-RPC
__all__ = ['listMethods', 'methodSignature', 'methodHelp', 'multicall']

# For method signatures
INT = 'int'
STRING = 'string'
BOOLEAN = 'boolean'
DOUBLE = 'double'
DATETIME = 'dateTime.iso8601'
BASE64 = 'base64'
ARRAY = 'array'
STRUCT = 'struct'

# Saw this done in mod_python's apache.py. I just fixed it up a little...
_suffixes = map(lambda x: x[0].replace('.', '\\.'), imp.get_suffixes())
_exp = '(' + '|'.join(_suffixes) + ')$'
_suffix_re = re.compile(_exp)

_environ = {}




def listMethods(env=None):
    """Enumerates all available XML-RPC methods."""
    __xmlrpc_signature = '[[ARRAY]]'

    method_list = []

    # scan the directory which this module resides in
    path = os.path.dirname(sys.modules[__name__].__file__)
    try:
        module_list = []
files = os.listdir(path)
        for f in files:
            # does it have a module suffix?
            if not _suffix_re.search(f):
                continue
            # strip module suffix
            module_name = _suffix_re.sub('', f)
            # ensure it's not private or this module
            if module_name[0] == '_' or module_name == __name__:
                continue
            if module_name not in module_list:
                module_list.append(module_name)

        for module_name in module_list:
            try:
                module = apache.import_module(module_name, path=[path])
            except:
                pass
            else:
                # scan module for non-private functions
                func_list = getattr(module, '__all__', dir(module))
                for func_name in func_list:
                    if func_name[0] != '_' and \
                           callable(getattr(module, func_name, None)):
method_list.append('%s.%s' % (module_name, func_name))
    except:
        pass
# add system methods
    method_list.extend(map(lambda x: 'system.%s' % x, __all__))

    method_list.sort()
    return method_list

def methodSignature(method, env=None):
    """Returns an XML-RPC method's signature."""
    __xmlrpc_signature = '[[ARRAY, STRING]]'
func = _map_methodName(method)
    if not func:
        return xmlrpclib.Fault(1, '%s: not implemented' % method)

    return _get_signature(func)

def methodHelp(method, env=None):
    """Returns an XML-RPC method's help string."""
    __xmlrpc_signature = '[[STRING, STRING]]'
func = _map_methodName(method)
    if not func:
        return xmlrpclib.Fault(1, '%s: not implemented' % method)

    if func.__doc__:
        help = _strip_docstring(func.__doc__)
    else:
        help = ''
return help

def multicall(call_params, env=None):
    """Executes multiple method calls with a single request."""
    __xmlrpc_signature = '[[ARRAY, ARRAY]]'

    result_list = []

    for param in call_params:
        if type(param) != dict:
            result_list.append(_fault_struct(1, 'struct expected'))
            continue

        if not param.has_key('methodName') or \
               not param.has_key('params'):
result_list.append(_fault_struct(1, 'methodName/params members ' \
                                             'missing'))
            continue
method, params = param['methodName'], param['params']

        if method == 'system.multicall':
            result_list.append(_fault_struct(1, 'system.multicall: ' \
                                             'recursion forbidden'))
            continue
try:
            result = _dispatch(method, params)
            if isinstance(result, xmlrpclib.Fault):
                result = _fault_struct(result.faultCode, result.faultString)
            else:
                result = (result,)
        except:
            result_list.append(_fault_struct(2, '%s: %s: %s' %
                                             (method, sys.exc_type,
                                              sys.exc_value)))
        else:
            result_list.append(result)
return result_list

def _expand_tabs(s, width=8):
    """Expands tabs to spaces, assuming tabs are of the specified width."""
o = ''
    col = 0
    for c in s:
        if c == '\t':
            next = width - (col % width)
            o += '        '[:next]
            col += next
        else:
            o += c
            col += 1
    return o

def _strip_docstring(s):
    """Takes a docstring and removes any extraneous indentation."""
# Break into lines and expand tabs.
    s = s.split('\n')
    s = map(_expand_tabs, s)

    # Convert lines with only spaces to empty strings.
    for i in range(len(s)):
        if s[i] and not s[i].strip():
            s[i] = ''

    # Single line or empty docstring.
    if len(s) == 1:
        return s[0]

    # Take care of the first line.
    o = ''
    o += s[0] + '\n'

# Go through each line. The first non-blank line determines the indent for
    # the entire docstring. Unindent each line.
    indent = 0
    for line in s[1:]:
        if line:
            if not indent:
                indent = len(line) - len(line.lstrip())
            if line.startswith(' '*indent):
                line = line[indent:]
            else:
                # Indent was short. Strip as much as we can anyway.
                line = line.lstrip()
            o += line + '\n'
        else:
            o += '\n'

    # If docstring ends with two linefeeds, remove one of them.
    if o[-2:] == '\n\n':
        o = o[:-1]
return o

def _get_func_const(func, name, default=None):
    """Get the value of a constant variable defined in a function."""

    func_code = getattr(func, 'func_code', None)
    if func_code:
        if name in func_code.co_names:
            i = list(func_code.co_names).index(name) + 1
            return func_code.co_consts[i]
    return default

def _get_signature(func):
    """Return the parameter signature of a function.

    Will first check func.__xmlrpc_signature, expecting it to be a list
    of signatures. Otherwise, it will check a constant variable called
    __xmlrpc_signature defined within the function. The variable MUST
    be a string and must evaluate to a list of signatures.

    Returns an empty list if none neither are a valid signature list.
    """
if hasattr(func, '__xmlrpc_signature'):
        sig = func.__xmlrpc_signature
    else:
        sig_str = _get_func_const(func, '__xmlrpc_signature')
        sig = []
        if sig_str:
            try:
                sig = eval(sig_str)
                # need to validate the signature someday...
            except:
                pass
return sig

_type_map = {
    types.IntType: INT,
    types.LongType: INT,
    types.StringType: STRING,
    types.FloatType: DOUBLE,
    types.TupleType: ARRAY,
    types.ListType: ARRAY,
    types.DictType: STRUCT
    }

def _xmlrpc_type(v):
    """Returns an XML-RPC type for a given value."""

    t = type(v)
    if _type_map.has_key(t):
        return _type_map[t]
    if t is types.InstanceType:
        if isinstance(v, xmlrpclib.DateTime):
            return DATETIME
        elif isinstance(v, xmlrpclib.Binary):
            return BASE64
        elif isinstance(v, xmlrpclib.Boolean):
            return BOOLEAN
    # Huh?!
    return STRING

def _match_signature(params, sig_list):
    """Matches an argument list with a signature list.

    If signature list is empty, any sort of argument list is accepted.
    """
param_types = map(_xmlrpc_type, params)
    empty_sig = 1
    for sig in sig_list:
        empty_sig = 0

        # skip return type
        sig = sig[1:]
if len(param_types) == len(sig) and param_types == sig:
            return 1
return empty_sig

def _fault_struct(faultCode, faultString):
    """Returns a Fault as a dictionary."""

    return { 'faultCode': faultCode, 'faultString': faultString }

def _map_methodName(method):
    """Maps a methodName in the form of module.function to a function."""

    # parse methodName as module.function
    method = method.split('.')
    if len(method) != 2:
        return None

    module_name, func_name = method

    if module_name == 'system':
        # reserved functions are implemented in this module
        module = sys.modules[__name__]
    else:
        # attempt to load module from the same directory as this module
        path = os.path.dirname(sys.modules[__name__].__file__)

        module = None
        # ensure it is not private
        if module_name[0] != '_' and module_name != __name__:
            try:
                module = apache.import_module(module_name, path=[path])
            except:
                pass

        if module is None:
            return None

    # now see if module has callable function named func_name
    if hasattr(module, '__all__') and func_name not in module.__all__:
        return None
if func_name[0] != '_' and hasattr(module, func_name):
        func = getattr(module, func_name)

        if callable(func):
            return func

    return None

def _dispatch(method, params):
    """Calls an XML-RPC method."""
    global _environ

    func = _map_methodName(method)
    if func is None:
        return xmlrpclib.Fault(1, '%s: not implemented' % method)

    # check arguments
    sig_list = _get_signature(func)
    if not _match_signature(params, sig_list):
        return xmlrpclib.Fault(1, '%s: bad arguments' % method )
# call the function
    result = apply(func, params, {'env':_environ})
# if result is None, set it to False
    if result is None:
        result = xmlrpclib.False
return result

# These HTTP headers are required according to the XML-RPC spec
_required_headers = ['Host', 'User-Agent', 'Content-Type', 'Content-Length']

def handler(req):
    """mod_python handler."""
    global _environ
    _environ = dict(apache.build_cgi_env(req))
    # only accept POST requests
    if req.method != 'POST':
        # We set this even though it doesn't seem to do anything...
        req.err_headers_out['Allow'] = 'POST'
        return apache.HTTP_METHOD_NOT_ALLOWED

    # check that all required headers are present
    for h in _required_headers:
        if not req.headers_in.has_key(h):
            return apache.HTTP_BAD_REQUEST

    if not req.headers_in['Content-Type'].startswith('text/xml'):
        return apache.HTTP_UNSUPPORTED_MEDIA_TYPE

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

        # read and parse request
        data = req.read(length)
        params, method = xmlrpclib.loads(data)

        try:
            # dispatch the method
            response = _dispatch(method, params)

            # convert response to singleton, if necessary
            if not isinstance(response, xmlrpclib.Fault):
                response = (response,)
        except:
            # caught an exception, return it as our fault response
            response = xmlrpclib.Fault(2, '%s: %s: %s' %
(method, sys.exc_type, sys.exc_value))

        # convert response to xml
        response = xmlrpclib.dumps(response, methodresponse=1)
    except:
        # eh?!
        return apache.HTTP_BAD_REQUEST
    else:
        # send response
        req.content_type = 'text/xml'
        req.headers_out['Content-Length'] = str(len(response))
        req.send_http_header()
        req.write(response)
        return apache.OK
------------------------------------------------------------------------------------------------


And here my code of client ------------------------------------------------------------------------------------------------ #!/usr/bin/python
import xmlrpclib as rpc
remote = rpc.ServerProxy('http://rpcserver.domain.org/xmlrpc <http://client2.gamma.agorabox.org/xmlrpc>')
print "+++++++ remote.account.getUserInfos(u'admin')"
print remote.account.getUserInfos(u'admin')
------------------------------------------------------------------------------------------------



---
Meilleures salutations / Best Regards

Rachid ALAHYANE



2010/4/21 Rob Crittenden <rcrit...@redhat.com <mailto:rcrit...@redhat.com>>

    ALAHYANE Rachid wrote:

        Ok so, my end goal is to use the ipa methods with xml-rpc as
        following,

         *  ipaServer : my ipa server, used to authenticate users and
        serves response for xml-rpc calls from rpcServer
         *  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.
         * myClient : this host is the one which will make the rpc calls
        to rpcServer.

        NB : 'account.py' is called by xmlrpchandler (it is my python
        handler) when getUserInfos is called by myclient .


    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).

    What is strange is that this code works fine standalone. Can you
    show us all the code in your xmlrpchandler?

    BTW, your English is fine :-)

    rob




        Example :
        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.


        this is my configurations :

        == On rpcServer ==
        --------- httpd conf ------------
        <Files "xmlrpc">
## python conf # ....
         SetHandler python-program
         PythonHandler xmlrpchandler
         PythonDebug on
        </Files>
        ------------------------------------

        the handler xmlrpchandler calls the following method when the
        client requests for the remote method getUserInfos().

        --------- account.py ------------
        def getUserInfos(user_name, env=None):

           from ipalib import api

           api.bootstrap_with_global_options(context='webservices')
           api.finalize()
           # mode Server is False. I am not on the server ipa
           api.Backend.xmlclient.connect()
           return api.Command.user_show(user_name)
         ------------------------------------


        == On myClient ==
        When I call  this method from my client, I get this exception :
        ------------------------------------
        <Fault 2: "remote.account.getUserInfos: <type
        'exceptions.StandardError'>: API.bootstrap() already called">
        ------------------------------------


        I hope that is clearer now, despite my bad English ;)


        ---
        Meilleures salutations / Best Regards

        Rachid ALAHYANE






_______________________________________________
Freeipa-users mailing list
Freeipa-users@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-users

Reply via email to