source: trunk/debathena/debathena/python-moira/moira.py @ 24605

Revision 24605, 5.4 KB checked in by broder, 14 years ago (diff)
Snapshot new version of PyMoira that includes mrclient bindings.
Line 
1"""
2Python bindings for the Moira library
3
4Moira is the Athena Service Management system.  It serves as the
5central repository for information about users, groups hosts, print
6queues, and several other aspects of the Athena environment.
7"""
8
9import os
10import re
11
12import _moira
13from _moira import (auth, host, motd, noop, proxy, MoiraException)
14
15
16help_re = re.compile('([a-z0-9_, ]*) \(([a-z0-9_, ]*)\)(?: => ([a-z0-9_, ]*))?',
17                     re.I)
18et_re = re.compile(r'^\s*#\s*define\s+([A-Za-z0-9_]+)\s+.*?([0-9]+)')
19
20
21_arg_cache = {}
22_return_cache = {}
23_et_cache = {}
24
25
26def _clear_caches():
27    """Clear query caches.
28
29    Clears all caches that may only be accurate for a particular Moira
30    server or query version.
31    """
32    _arg_cache.clear()
33    _return_cache.clear()
34
35
36def connect(server=''):
37    _moira.connect(server)
38    version(-1)
39connect.__doc__ = _moira.connect.__doc__
40
41
42def disconnect():
43    """Disconnect from the active Moira server"""
44    _moira.disconnect()
45    _clear_caches()
46
47
48def _load_help(handle):
49    """Fetch info about the arguments and return values for a query.
50
51    This uses the "_help" Moira query to retrieve names for the
52    arguments and return values to and from a particular Moira
53    query. These values are cached and used for translating arguments
54    and return values into and out of dictionaries and into and out of
55    tuples.
56    """
57    help_string = ', '.join(query('_help', handle)[0]).strip()
58
59    handle_str, arg_str, return_str = help_re.match(help_string).groups('')
60
61    handles = handle_str.split(', ')
62    args = arg_str.split(', ')
63    returns = return_str.split(', ')
64
65    for h in handles:
66        _arg_cache[h] = args
67        _return_cache[h] = returns
68
69
70def _list_query(handle, *args):
71    """
72    Execute a Moira query and return the result as a list of tuples.
73   
74    This bypasses the tuple -> dict conversion done in moira.query()
75    """
76    results = []
77    _moira._query(handle, results.append, *args)
78    return results
79
80
81def _parse_args(handle, args, kwargs):
82    """
83    Convert a set of arguments into the canonical Moira list form.
84
85    Both query and access accept either positional arguments or
86    keyword arguments, cross-referenced against the argument names
87    given by the "_help" query.
88
89    This function takes the args and kwargs as they're provided to
90    either of those functions and returns a list of purely positional
91    arguments that can be passed to the low-level Moira query
92    function.
93    """
94    if (handle not in _return_cache or
95        not _return_cache[handle]):
96        _load_help(handle)
97
98    if kwargs:
99        return tuple(kwargs.get(i, '*')
100                     for i in _arg_cache[handle])
101    else:
102        return args
103
104
105def query(handle, *args, **kwargs):
106    """
107    Execute a Moira query and return the result as a list of dicts.
108   
109    Arguments can be specified either as positional or keyword
110    arguments. If specified by keyword, they are cross-referenced with
111    the argument name given by the query "_help handle".
112   
113    All of the real work of Moira is done in queries. There are over
114    100 queries, each of which requires different arguments. The
115    arguments to the queries should be passed as separate arguments to
116    the function.
117    """
118    if handle.startswith('_'):
119        return _list_query(handle, *args)
120    else:
121        fmt = kwargs.pop('fmt', dict)
122
123        args = _parse_args(handle, args, kwargs)
124
125        plain_results = _list_query(handle, *args)
126        results = []
127
128        for r in plain_results:
129            results.append(fmt(zip(_return_cache[handle], r)))
130
131        return results
132
133
134def access(handle, *args, **kwargs):
135    """
136    Determine if the user has the necessary access to perform a query.
137
138    As with moira.query, arguments can be specified either as
139    positional or keyword arguments. If specified as keywords, they
140    are cross-referenced with the argument names given by the "_help"
141    query.
142
143    This function returns True if the user, as currently
144    authenticated, would be allowed to perform the query with the
145    given arguments, and False otherwise.
146    """
147    args = _parse_args(handle, args, kwargs)
148
149    try:
150        _moira._access(handle, *args)
151        return True
152    except MoiraException, e:
153        if e.code != errors()['MR_PERM']:
154            raise
155        return False
156
157
158def version(ver):
159    # Changing the Moira version can change a query's arguments and
160    # return values
161    _clear_caches()
162    return _moira.version(ver)
163version.__doc__ = _moira.version.__doc__
164
165
166def errors():
167    """
168    Return a dict of Moira error codes.
169
170    This function parses error codes out of the Moira header files and
171    returns a dictionary of those error codes.
172
173    The value that's returned should be treated as immutable. It's a
174    bug that it isn't.
175    """
176    if not _et_cache:
177        for prefix in ('/usr/include',
178                       '/sw/include'):
179            header = os.path.join(prefix, 'moira/mr_et.h')
180            if os.path.exists(header):
181                for line in open(header):
182                    m = et_re.search(line)
183                    if m:
184                        errname, errcode = m.groups()
185                        _et_cache[errname] = int(errcode)
186
187    return _et_cache
188
189
190__all__ = ['connect', 'disconnect', 'auth', 'host', 'motd', 'noop', 'query',
191           'proxy', 'version', 'access', 'errors', '_list_query',
192           'MoiraException']
Note: See TracBrowser for help on using the repository browser.