~openerp-community/openerp-tools/extra

« back to all changes in this revision

Viewing changes to erppeek/erppeek.py

  • Committer: Florent Xicluna
  • Date: 2012-03-13 22:21:23 UTC
  • Revision ID: florent.xicluna@gmail.com-20120313222123-k65rjq8povl1urvh
update erppeek to cover all 5 XML-RPC services, and implement module upgrade helpers

Show diffs side-by-side

added added

removed removed

Lines of Context:
60
60
DEFAULT_PASSWORD = 'admin'
61
61
 
62
62
USAGE = """\
63
 
Usage:
64
 
    do(obj, method, *params)        # Generic 'service.execute'
65
 
 
 
63
Usage (main commands):
66
64
    search(obj, domain)
67
65
    search(obj, domain, offset=0, limit=None, order=None)
68
66
                                    # Return a list of IDs
77
75
    keys(obj)                       # List field names of the model
78
76
    fields(obj, names=None)         # Return details for the fields
79
77
    field(obj, name)                # Return details for the field
 
78
 
 
79
    do(obj, method, *params)        # Generic 'service.execute'
 
80
    wizard(name)                    # Return the 'id' of a new wizard
 
81
    wizard(name_or_id, datas=None, action='init')
 
82
                                    # Generic 'wizard.execute'
 
83
 
 
84
    client                          # Client object, connected
 
85
    client.modules(name)            # List modules matching pattern
 
86
    client.upgrade(module1, module2, ...)
 
87
                                    # Upgrade the modules
80
88
"""
81
89
 
 
90
STABLE_STATES = ('uninstallable', 'uninstalled', 'installed')
 
91
DOMAIN_OPERATORS = frozenset('!|&')
82
92
# Supported operators are
83
93
#   =, !=, >, >=, <, <=, like, ilike, in,
84
94
#   not like, not ilike, not in, child_of
92
102
 
93
103
ini_path = os.path.splitext(__file__)[0] + '.ini'
94
104
 
95
 
 
96
 
def read_config(section):
 
105
# Published object methods
 
106
_methods = {
 
107
    'db': ['create', 'drop', 'dump', 'restore', 'rename', 'list', 'list_lang',
 
108
           'change_admin_password', 'server_version', 'migrate_databases'],
 
109
    'common': ['about', 'login', 'timezone_get', 'get_server_environment',
 
110
               'login_message', 'check_connectivity'],
 
111
    'object': ['execute', 'exec_workflow'],
 
112
    'wizard': ['execute', 'create'],
 
113
    'report': ['report', 'report_get'],
 
114
}
 
115
_methods_6_1 = {
 
116
    'db': ['create_database', 'db_exist'],
 
117
    'common': ['get_stats', 'list_http_services', 'version',
 
118
               'authenticate', 'get_os_time', 'get_sqlcount'],
 
119
    'object': ['execute_kw'],
 
120
    'wizard': [],
 
121
    'report': ['render_report'],
 
122
}
 
123
# Hidden methods:
 
124
#  - (not in 6.1) 'common': ['logout', 'ir_get', 'ir_set', 'ir_del']
 
125
#  - (not in 6.1) 'object': ['obj_list']
 
126
#  - 'db': ['get_progress']
 
127
#  - 'common': ['get_available_updates', 'get_migration_scripts', 'set_loglevel']
 
128
 
 
129
 
 
130
def read_config(section=None):
97
131
    p = ConfigParser.SafeConfigParser()
98
132
    with open(ini_path) as f:
99
133
        p.readfp(f)
 
134
    if section is None:
 
135
        return p.sections()
100
136
    server = 'http://%s:%s' % (p.get(section, 'host'), p.get(section, 'port'))
101
137
    db = p.get(section, 'database')
102
138
    user = p.get(section, 'username')
149
185
    domain = params[0]
150
186
    for idx, term in enumerate(domain):
151
187
        if isinstance(term, basestring):
 
188
            if term in DOMAIN_OPERATORS:
 
189
                continue
152
190
            m = _term_re.match(term.strip())
153
191
            if not m:
154
 
                continue
 
192
                raise ValueError("Cannot parse term %r" % term)
155
193
            (field, operator, value) = m.groups()
156
194
            try:
157
195
                value = literal_eval(value)
158
196
            except Exception:
 
197
                # Interpret the value as a string
159
198
                pass
160
199
            domain[idx] = (field, operator, value)
161
200
    if kwargs and len(params) == 1:
173
212
 
174
213
    @faultmanagement
175
214
    def __init__(self, server, db, user, password):
176
 
        sock = xmlrpclib.ServerProxy(server + '/xmlrpc/common')
177
 
        uid = sock.login(db, user, password)
 
215
        def get_proxy(name, prefix=server + '/xmlrpc/'):
 
216
            return xmlrpclib.ServerProxy(prefix + name, allow_none=True)
 
217
        self.common = common = get_proxy('common')
 
218
        uid = common.login(db, user, password)
178
219
        if uid is False:
179
220
            raise RuntimeError('Invalid username or password')
180
 
        sock = xmlrpclib.ServerProxy(server + '/xmlrpc/object', allow_none=True)
 
221
        self.db = sockdb = get_proxy('db')
 
222
        sock = get_proxy('object')
 
223
        wizard = get_proxy('wizard')
 
224
        report = get_proxy('report')
181
225
        self._server = server
 
226
        self.server_version = ver = sockdb.server_version()
 
227
        self.major_version = major_version = '.'.join(ver.split('.', 2)[:2])
 
228
        # Authenticated endpoints
182
229
        self._execute = functools.partial(sock.execute, db, uid, password)
 
230
        self.exec_workflow = functools.partial(sock.exec_workflow, db, uid, password)
 
231
        self.wizard_execute = functools.partial(wizard.execute, db, uid, password)
 
232
        self.wizard_create = functools.partial(wizard.create, db, uid, password)
 
233
        self.report = functools.partial(report.report, db, uid, password)
 
234
        self.report_get = functools.partial(report.report_get, db, uid, password)
 
235
        m_db = _methods['db'][:]
 
236
        m_common = _methods['common'][:]
 
237
        # Set the special value returned by dir(...)
 
238
        sockdb.__dir__ = lambda m=m_db: m
 
239
        common.__dir__ = lambda m=m_common: m
 
240
        if major_version[:2] != '5.':
 
241
            # Only for OpenERP >= 6
 
242
            self.execute_kw = functools.partial(sock.execute, db, uid, password)
 
243
            self.render_report = functools.partial(report.render_report, db, uid, password)
 
244
            m_db += _methods_6_1['db']
 
245
            m_common += _methods_6_1['common']
183
246
 
184
247
    @classmethod
185
248
    def from_config(cls, environment):
216
279
            print 'Ignoring: %s = %r' % item
217
280
        return self._execute(obj, method, *params)
218
281
 
 
282
    @faultmanagement
 
283
    def wizard(self, name, datas=None, action='init', context=None):
 
284
        if isinstance(name, (int, long)):
 
285
            wiz_id = name
 
286
        else:
 
287
            wiz_id = self.wizard_create(name)
 
288
        if datas is None:
 
289
            if action == 'init' and name != wiz_id:
 
290
                return wiz_id
 
291
            datas = {}
 
292
        return self.wizard_execute(wiz_id, datas, action, context)
 
293
 
 
294
    def _upgrade(self, modules, button):
 
295
        # Click upgrade/install/uninstall button
 
296
        ids = self.search('ir.module.module', [('name', 'in', modules)])
 
297
        if ids is None:
 
298
            return
 
299
        self.execute('ir.module.module', button, ids)
 
300
        mods = self.read('ir.module.module',
 
301
                         [('state', 'not in', STABLE_STATES)], 'name state')
 
302
        if not mods:
 
303
            return
 
304
        print '%s module(s) selected' % len(ids)
 
305
        print '%s module(s) to update:' % len(mods)
 
306
        for mod in mods:
 
307
            print '  %(state)s\t%(name)s' % mod
 
308
 
 
309
        if self.major_version[:2] == '5.':
 
310
            # Wizard "Apply Scheduled Upgrades"
 
311
            rv = self.wizard('module.upgrade', action='start')
 
312
            if 'config' not in [state[0] for state in rv.get('state', ())]:
 
313
                # Something bad happened
 
314
                return rv
 
315
        else:
 
316
            self.execute('base.module.upgrade', 'upgrade_module', [])
 
317
 
 
318
    def upgrade(self, *modules):
 
319
        # Button "Schedule Upgrade"
 
320
        return self._upgrade(modules, button='button_upgrade')
 
321
 
 
322
    def install(self, *modules):
 
323
        # Button "Schedule for Installation"
 
324
        return self._upgrade(modules, button='button_install')
 
325
 
 
326
    def uninstall(self, *modules):
 
327
        # Button "Uninstall (beta)"
 
328
        return self._upgrade(modules, button='button_uninstall')
 
329
 
219
330
    def search(self, obj, *params, **kwargs):
220
331
        return self.execute(obj, 'search', *params, **kwargs)
221
332
 
230
341
        if models:
231
342
            return sorted([m['model'] for m in models])
232
343
 
 
344
    def modules(self, name, installed=None):
 
345
        domain = [('name', 'like', name)]
 
346
        if installed is not None:
 
347
            op = installed and '=' or '!='
 
348
            domain.append(('state', op, 'installed'))
 
349
        mods = self.read('ir.module.module', domain, 'name state')
 
350
        if mods:
 
351
            res = {}
 
352
            for mod in mods:
 
353
                if mod['state'] in res:
 
354
                    res[mod['state']].append(mod['name'])
 
355
                else:
 
356
                    res[mod['state']] = [mod['name']]
 
357
            return res
 
358
 
233
359
    def keys(self, obj):
234
 
        return sorted(self.execute(obj, 'fields_get_keys'))
 
360
        obj_keys = self.execute(obj, 'fields_get_keys')
 
361
        return obj_keys and sorted(obj_keys)
235
362
 
236
363
    def fields(self, obj, names=None):
237
364
        return self.execute(obj, 'fields_get', names)
253
380
    parser = optparse.OptionParser(
254
381
            usage='%prog [options] [id [id ...]]',
255
382
            description='Inspect data on OpenERP objects')
256
 
    parser.add_option('-m', '--model',
257
 
            help='the type of object to find')
 
383
    parser.add_option('-l', '--list', action='store_true', dest='list_env',
 
384
            help='list sections of %r' % ini_path)
258
385
    parser.add_option('--env',
259
386
            help='read connection settings from the given '
260
387
                 'section of %r' % ini_path)
266
393
    parser.add_option('-p', '--password', default=DEFAULT_PASSWORD,
267
394
            help='password (yes this will be in your shell history and '
268
395
                 'ps from other users)')
 
396
    parser.add_option('-m', '--model',
 
397
            help='the type of object to find')
269
398
    parser.add_option('-s', '--search', action='append', dest='search',
270
399
            help='search condition (multiple allowed); alternatively, pass '
271
400
                 'multiple IDs as positional parameters after the options')
276
405
 
277
406
    (args, ids) = parser.parse_args()
278
407
 
 
408
    if args.list_env:
 
409
        print 'Available settings: ', ' '.join(read_config())
 
410
        return None, None
 
411
 
279
412
    if args.env:
280
413
        client = Client.from_config(args.env)
281
414
        # Tweak prompt
325
458
        # interactive usage
326
459
        print USAGE
327
460
        do = client.execute
 
461
        wizard = client.wizard
328
462
        read = client.read
329
463
        search = client.search
330
464
        count = client.count