X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=PLC%2FMethod.py;h=942549d64cff58368c24a105fca389cb0920d524;hb=9d42db6d25662e18156bda85c5416f38cfa5276d;hp=ec0a244b6144ce50a8fce9ae605f6493f6e073ef;hpb=35a0ef8704967dee011105f1aebfeeb6b6766641;p=plcapi.git diff --git a/PLC/Method.py b/PLC/Method.py index ec0a244..942549d 100644 --- a/PLC/Method.py +++ b/PLC/Method.py @@ -4,7 +4,8 @@ # Mark Huang # Copyright (C) 2006 The Trustees of Princeton University # -# $Id: Method.py,v 1.13 2006/10/25 19:33:52 mlhuang Exp $ +# $Id$ +# $URL$ # import xmlrpclib @@ -14,15 +15,18 @@ import os import time import pprint +from types import StringTypes + from PLC.Faults import * -from PLC.Parameter import Parameter, Mixed +from PLC.Parameter import Parameter, Mixed, python_type, xmlrpc_type from PLC.Auth import Auth from PLC.Debug import profile, log from PLC.Events import Event, Events from PLC.Nodes import Node, Nodes from PLC.Persons import Person, Persons -class Method: +# we inherit object because we use new-style classes for legacy methods +class Method (object): """ Base class for all PLCAPI functions. At a minimum, all PLCAPI functions must define: @@ -78,46 +82,67 @@ class Method: try: start = time.time() - (min_args, max_args, defaults) = self.args() + + # legacy code cannot be type-checked, due to the way Method.args() works + if not hasattr(self,"skip_typecheck"): + (min_args, max_args, defaults) = self.args() - # Check that the right number of arguments were passed in - if len(args) < len(min_args) or len(args) > len(max_args): - raise PLCInvalidArgumentCount(len(args), len(min_args), len(max_args)) + # Check that the right number of arguments were passed in + if len(args) < len(min_args) or len(args) > len(max_args): + raise PLCInvalidArgumentCount(len(args), len(min_args), len(max_args)) - for name, value, expected in zip(max_args, args, self.accepts): - self.type_check(name, value, expected, args) + for name, value, expected in zip(max_args, args, self.accepts): + self.type_check(name, value, expected, args) result = self.call(*args, **kwds) runtime = time.time() - start - - if self.api.config.PLC_API_DEBUG: - self.log(0, runtime, *args) + + if self.api.config.PLC_API_DEBUG or hasattr(self, 'message'): + self.log(None, runtime, *args) return result except PLCFault, fault: - # Prepend method name to expected faults - fault.faultString = self.name + ": " + fault.faultString + + caller = "" + if isinstance(self.caller, Person): + caller = 'person_id %s' % self.caller['person_id'] + elif isinstance(self.caller, Node): + caller = 'node_id %s' % self.caller['node_id'] + + # Prepend caller and method name to expected faults + fault.faultString = caller + ": " + self.name + ": " + fault.faultString runtime = time.time() - start - self.log(fault.faultCode, runtime, *args) - raise fault + + if self.api.config.PLC_API_DEBUG: + self.log(fault, runtime, *args) + + raise fault - def log(self, fault_code, runtime, *args): + def log(self, fault, runtime, *args): """ Log the transaction """ # Do not log system or Get calls - if self.name.startswith('system') or self.name.startswith('Get'): - return False + #if self.name.startswith('system') or self.name.startswith('Get'): + # return False # Create a new event event = Event(self.api) - event['fault_code'] = fault_code + event['fault_code'] = 0 + if fault: + event['fault_code'] = fault.faultCode event['runtime'] = runtime # Redact passwords and sessions if args and isinstance(args[0], dict): + # what type of auth this is + if args[0].has_key('AuthMethod'): + auth_methods = ['session', 'password', 'capability', 'gpg', 'hmac','anonymous'] + auth_method = args[0]['AuthMethod'] + if auth_method in auth_methods: + event['auth_type'] = auth_method for password in 'AuthString', 'session': if args[0].has_key(password): auth = args[0].copy() @@ -127,6 +152,7 @@ class Method: # Log call representation # XXX Truncate to avoid DoS event['call'] = self.name + pprint.saferepr(args) + event['call_name'] = self.name # Both users and nodes can call some methods if isinstance(self.caller, Person): @@ -136,13 +162,20 @@ class Method: event.sync(commit = False) - # XXX object_ids is currently defined as a class variable - if hasattr(self, 'object_ids'): - for object_id in self.object_ids: - event.add_object(object_id, commit = False) + if hasattr(self, 'event_objects') and isinstance(self.event_objects, dict): + for key in self.event_objects.keys(): + for object_id in self.event_objects[key]: + event.add_object(key, object_id, commit = False) + + # Set the message for this event + if fault: + event['message'] = fault.faultString + elif hasattr(self, 'message'): + event['message'] = self.message + # Commit - event.sync(commit = True) + event.sync() def help(self, indent = " "): """ @@ -199,7 +232,7 @@ class Method: elif isinstance(param, Mixed): for subparam in param: text += param_text(name, subparam, indent + step, step) - elif isinstance(param, (list, tuple)): + elif isinstance(param, (list, tuple, set)): for subparam in param: text += param_text("", subparam, indent + step, step) @@ -225,7 +258,7 @@ class Method: That represents the minimum and maximum sets of arguments that this function accepts and the defaults for the optional arguments. """ - + # Inspect call. Remove self from the argument list. max_args = self.call.func_code.co_varnames[1:self.call.func_code.co_argcount] defaults = self.call.func_defaults @@ -261,11 +294,7 @@ class Method: return except PLCInvalidArgument, fault: pass - xmlrpc_types = [xmlrpc_type(item) for item in expected] - raise PLCInvalidArgument("expected %s, got %s" % \ - (" or ".join(xmlrpc_types), - xmlrpc_type(type(value))), - name) + raise fault # If an authentication structure is expected, save it and # authenticate after basic type checking is done. @@ -278,13 +307,19 @@ class Method: if isinstance(expected, Parameter): min = expected.min max = expected.max + nullok = expected.nullok expected = expected.type else: min = None max = None + nullok = False expected_type = python_type(expected) + # If value can be NULL + if value is None and nullok: + return + # Strings are a special case. Accept either unicode or str # types if a string is expected. if expected_type in StringTypes and isinstance(value, StringTypes): @@ -309,6 +344,11 @@ class Method: if max is not None and \ len(value.encode(self.api.encoding)) > max: raise PLCInvalidArgument, "%s must be at most %d bytes long" % (name, max) + elif expected_type in (list, tuple, set): + if min is not None and len(value) < min: + raise PLCInvalidArgument, "%s must contain at least %d items" % (name, min) + if max is not None and len(value) > max: + raise PLCInvalidArgument, "%s must contain at most %d items" % (name, max) else: if min is not None and value < min: raise PLCInvalidArgument, "%s must be > %s" % (name, str(min)) @@ -316,7 +356,7 @@ class Method: raise PLCInvalidArgument, "%s must be < %s" % (name, str(max)) # If a list with particular types of items is expected - if isinstance(expected, (list, tuple)): + if isinstance(expected, (list, tuple, set)): for i in range(len(value)): if i >= len(expected): j = len(expected) - 1 @@ -338,46 +378,3 @@ class Method: if auth is not None: auth.check(self, *args) - -def python_type(arg): - """ - Returns the Python type of the specified argument, which may be a - Python type, a typed value, or a Parameter. - """ - - if isinstance(arg, Parameter): - arg = arg.type - - if isinstance(arg, type): - return arg - else: - return type(arg) - -def xmlrpc_type(arg): - """ - Returns the XML-RPC type of the specified argument, which may be a - Python type, a typed value, or a Parameter. - """ - - arg_type = python_type(arg) - - if arg_type == NoneType: - return "nil" - elif arg_type == IntType or arg_type == LongType: - return "int" - elif arg_type == bool: - return "boolean" - elif arg_type == FloatType: - return "double" - elif arg_type in StringTypes: - return "string" - elif arg_type == ListType or arg_type == TupleType: - return "array" - elif arg_type == DictType: - return "struct" - elif arg_type == Mixed: - # Not really an XML-RPC type but return "mixed" for - # documentation purposes. - return "mixed" - else: - raise PLCAPIError, "XML-RPC cannot marshal %s objects" % arg_type