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

Revision 24440, 5.0 KB checked in by broder, 14 years ago (diff)
Add python-moira.
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 (connect, 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 disconnect():
37    """Disconnect from the active Moira server"""
38    _moira.disconnect()
39    _clear_caches()
40
41
42def _load_help(handle):
43    """Fetch info about the arguments and return values for a query.
44
45    This uses the "_help" Moira query to retrieve names for the
46    arguments and return values to and from a particular Moira
47    query. These values are cached and used for translating arguments
48    and return values into and out of dictionaries and into and out of
49    tuples.
50    """
51    help_string = ', '.join(query('_help', handle)[0]).strip()
52
53    handle_str, arg_str, return_str = help_re.match(help_string).groups('')
54
55    handles = handle_str.split(', ')
56    args = arg_str.split(', ')
57    returns = return_str.split(', ')
58
59    for h in handles:
60        _arg_cache[h] = args
61        _return_cache[h] = returns
62
63
64def _list_query(handle, *args):
65    """
66    Execute a Moira query and return the result as a list of tuples.
67   
68    This bypasses the tuple -> dict conversion done in moira.query()
69    """
70    results = []
71    _moira._query(handle, results.append, *args)
72    return results
73
74
75def _parse_args(handle, args, kwargs):
76    """
77    Convert a set of arguments into the canonical Moira list form.
78
79    Both query and access accept either positional arguments or
80    keyword arguments, cross-referenced against the argument names
81    given by the "_help" query.
82
83    This function takes the args and kwargs as they're provided to
84    either of those functions and returns a list of purely positional
85    arguments that can be passed to the low-level Moira query
86    function.
87    """
88    if (handle not in _return_cache or
89        not _return_cache[handle]):
90        _load_help(handle)
91
92    if kwargs:
93        return tuple(kwargs.get(i, '*')
94                     for i in _arg_cache[handle])
95    else:
96        return args
97
98
99def query(handle, *args, **kwargs):
100    """
101    Execute a Moira query and return the result as a list of dicts.
102   
103    Arguments can be specified either as positional or keyword
104    arguments. If specified by keyword, they are cross-referenced with
105    the argument name given by the query "_help handle".
106   
107    All of the real work of Moira is done in queries. There are over
108    100 queries, each of which requires different arguments. The
109    arguments to the queries should be passed as separate arguments to
110    the function.
111    """
112    if handle.startswith('_'):
113        return _list_query(handle, *args)
114    else:
115        fmt = kwargs.pop('fmt', dict)
116
117        args = _parse_args(handle, args, kwargs)
118
119        plain_results = _list_query(handle, *args)
120        results = []
121
122        for r in plain_results:
123            results.append(fmt(zip(_return_cache[handle], r)))
124
125        return results
126
127
128def access(handle, *args, **kwargs):
129    """
130    Determine if the user has the necessary access to perform a query.
131
132    As with moira.query, arguments can be specified either as
133    positional or keyword arguments. If specified as keywords, they
134    are cross-referenced with the argument names given by the "_help"
135    query.
136
137    This function returns True if the user, as currently
138    authenticated, would be allowed to perform the query with the
139    given arguments, and False otherwise.
140    """
141    args = _parse_args(handle, args, kwargs)
142
143    try:
144        _moira._access(handle, *args)
145        return True
146    except MoiraException, e:
147        if e.code != errors()['MR_PERM']:
148            raise
149        return False
150
151
152def errors():
153    """
154    Return a dict of Moira error codes.
155
156    This function parses error codes out of the Moira header files and
157    returns a dictionary of those error codes.
158
159    The value that's returned should be treated as immutable. It's a
160    bug that it isn't.
161    """
162    if not _et_cache:
163        for prefix in ('/usr/include',
164                       '/sw/include'):
165            header = os.path.join(prefix, 'moira/mr_et.h')
166            if os.path.exists(header):
167                for line in open(header):
168                    m = et_re.search(line)
169                    if m:
170                        errname, errcode = m.groups()
171                        _et_cache[errname] = int(errcode)
172
173    return _et_cache
174
175
176__all__ = ['connect', 'disconnect', 'auth', 'host', 'motd', 'noop', 'query',
177           'access', 'errors', '_list_query', 'MoiraException']
Note: See TracBrowser for help on using the repository browser.