summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2008-06-23 12:30:56 -0400
committerMichael DeHaan <mdehaan@redhat.com>2008-06-23 12:30:56 -0400
commit5aeaa5a411c2e5b8f297ba1eed02aa2663ff7225 (patch)
tree3d93438c48edb9b3fdb27dafb01cd38b3f754321
parent76996c8ac3016389fdafb47e7791f2c636e2a4ed (diff)
parent30a2391e1078bca14c83ad3b433a46f6929b29f4 (diff)
downloadthird_party-func-5aeaa5a411c2e5b8f297ba1eed02aa2663ff7225.tar.gz
third_party-func-5aeaa5a411c2e5b8f297ba1eed02aa2663ff7225.tar.xz
third_party-func-5aeaa5a411c2e5b8f297ba1eed02aa2663ff7225.zip
Merge branch 'makkalot_extreme'
-rw-r--r--func/minion/func_arg.py225
-rw-r--r--func/minion/modules/func_module.py57
-rw-r--r--func/minion/modules/iptables/port.py85
-rw-r--r--func/minion/modules/service.py29
-rw-r--r--funcweb/TODO.txt20
-rw-r--r--funcweb/funcweb/config/app.cfg3
-rw-r--r--funcweb/funcweb/controllers.py144
-rw-r--r--funcweb/funcweb/static/css/style.css10
-rw-r--r--funcweb/funcweb/static/images/loading.gifbin0 -> 46766 bytes
-rw-r--r--funcweb/funcweb/static/javascript/ajax.js83
-rw-r--r--funcweb/funcweb/templates/master.html16
-rw-r--r--funcweb/funcweb/templates/method.html9
-rw-r--r--funcweb/funcweb/templates/method_args.html16
-rw-r--r--funcweb/funcweb/templates/minion.html5
-rw-r--r--funcweb/funcweb/templates/minions.html7
-rw-r--r--funcweb/funcweb/templates/module.html2
-rw-r--r--funcweb/funcweb/templates/run.html5
-rw-r--r--funcweb/funcweb/tests/test_widget_automation.py130
-rw-r--r--funcweb/funcweb/tests/test_widget_validation.py339
-rw-r--r--funcweb/funcweb/widget_automation.py263
-rw-r--r--funcweb/funcweb/widget_validation.py333
-rw-r--r--test/unittest/test_client.py11
-rw-r--r--test/unittest/test_func_arg.py151
23 files changed, 1910 insertions, 33 deletions
diff --git a/func/minion/func_arg.py b/func/minion/func_arg.py
new file mode 100644
index 0000000..5025e17
--- /dev/null
+++ b/func/minion/func_arg.py
@@ -0,0 +1,225 @@
+##
+## Copyright 2007, Red Hat, Inc
+## see AUTHORS
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##
+
+class ArgCompatibility(object):
+ """
+ That class is going to test if the module that was created by module
+ writer if he/she obeys to the rules we put here
+ """
+
+ #these are the common options can be used with all types
+ __common_options = ('optional','default','description','order')
+ __method_options = ('description','args') #making method declarations more generic like method_name:{'args':{...},'description':"bla bla"}
+
+ #basic types has types also
+ __basic_types={
+ 'range':[1,],
+ 'min':0,
+ 'max':0,
+ 'optional':False,
+ 'description':'',
+ 'options':[1,],
+ 'min_length':0,
+ 'max_length':0,
+ 'validator':'',
+ 'type':'',
+ 'default':None, #its type is unknown,
+ 'order':0
+ }
+
+ def __init__(self,get_args_result):
+ """
+ The constructor initilized by the get_method_args()(the dict to test)
+ @param : get_args_result : The dict with all method related info
+ """
+ self.__args_to_check = get_args_result
+
+ #what options does each of the basic_types have :
+ self.__valid_args={
+ 'int':('range','min','max',),
+ 'string':('options','min_length','max_length','validator',),
+ 'boolean':(),
+ 'float':('range','min','max'),
+ 'hash':('validator',),
+ 'list':('validator',),
+ }
+
+
+ def _is_type_options_compatible(self,argument_dict):
+ """
+ Checks the method's argument_dict's options and looks inside
+ self.__valid_args to see if the used option is there
+
+ @param : argument_dict : current argument to check
+ @return : True of raises IncompatibleTypesException
+
+ """
+ #did module writer add a key 'type'
+ if not argument_dict.has_key('type') or not self.__valid_args.has_key(argument_dict['type']):
+ raise IncompatibleTypesException("%s is not in valid options,possible ones are :%s"%(argument_dict['type'],str(self.__valid_args)))
+
+ #we need some specialization about if user has defined options
+ #there is no need for using validator,min_lenght,max_length
+ if argument_dict.has_key('options'):
+ for arg_option in argument_dict.keys():
+ if arg_option!='options' and arg_option in self.__valid_args['string']:
+ raise IncompatibleTypesException('The options keyword should be used alone in a string cant be used with min_length,max_length,validator together')
+
+ #if range keyword is used into a int argument the others shouldnt be there
+ if argument_dict.has_key('range'):
+ if len(argument_dict['range'])!=2:
+ raise IncompatibleTypesException('The range option should have 2 members [min,max]')
+ if not argument_dict['range'][0] < argument_dict['range'][1]:
+ raise IncompatibleTypesException('In the range option first member should be smaller than second [min,max]')
+ #check if another option was used there ...
+ for arg_option in argument_dict.keys():
+ if arg_option!='range' and arg_option in self.__valid_args['int']:
+ raise IncompatibleTypesException('The options range should be used alone into a int argument')
+
+
+ # we will use it everytime so not make lookups
+ the_type = argument_dict['type']
+ from itertools import chain #may i use chain ?
+
+ for key,value in argument_dict.iteritems():
+
+ if key == "type":
+ continue
+ if key not in chain(self.__valid_args[the_type],self.__common_options):
+ raise IncompatibleTypesException("There is no option like %s in %s"%(key,the_type))
+
+ return True
+
+
+ def _is_basic_types_compatible(self,type_dict):
+ """
+ Validates that if the types that were submitted with
+ get_method_args were compatible with our format above
+ in __basic_types
+
+ @param : type_dict : The type to examine
+ @return : True or raise IncompatibleTypesException Exception
+ """
+ #print "The structure we got is %s:"%(type_dict)
+ for key,value in type_dict.iteritems():
+
+ #do we have that type
+ if not self.__basic_types.has_key(key):
+ raise IncompatibleTypesException("%s not in the basic_types"%key)
+
+ #if type matches and dont match default
+ #print "The key: %s its value %s and type %s"%(key,value,type(value))
+ if key!='default' and type(value)!=type(self.__basic_types[key]):
+ raise IncompatibleTypesException("The %s keyword should be in that type %s"%(key,type(self.__basic_types[key])))
+
+ return True
+
+ def is_all_arguments_registered(self,cls,method_name,arguments):
+ """
+ Method inspects the method arguments and checks if the user
+ has registered all the arguments succesfully and also adds a
+ 'order' keyword to method arguments to
+ """
+ import inspect
+ from itertools import chain
+ #get the arguments from real object we have [args],*arg,**kwarg,[defaults]
+ tmp_arguments=inspect.getargspec(getattr(cls,method_name))
+ check_args=[arg for arg in chain(tmp_arguments[0],tmp_arguments[1:3]) if arg and arg!='self']
+ #print "The arguments taken from the inspect are :",check_args
+ #the size may change of the hash so should a copy of it
+ copy_arguments = arguments.copy()
+ for compare_arg in copy_arguments.iterkeys():
+ if not compare_arg in check_args:
+ raise ArgumentRegistrationError("The argument %s is not in the %s"%(compare_arg,method_name))
+ else:
+ #should set its ordering thing
+ arguments[compare_arg]['order']=check_args.index(compare_arg)
+
+ return True
+
+ def validate_all(self):
+ """
+ Validates the output for minion module's
+ get_method_args method
+
+ The structure that is going to be validated is in that format :
+
+ {
+ method_name1 : {'args':{...},
+ 'description':"wowo"},
+ method_name12 : {...}
+ }
+
+ @return : True or raise IncompatibleTypesException Exception
+ """
+
+ for method in self.__args_to_check.iterkeys():
+ #here we got args or description part
+ #check if user did submit something not in the __method_options
+ for method_option in self.__args_to_check[method].iterkeys():
+ if method_option not in self.__method_options:
+ raise IncompatibleTypesException("There is no option for method_name like %s,possible ones are : %s"%(method_option,str(self.__method_options)))
+ #check what is inside the args
+ if method_option == "args":
+ for argument in self.__args_to_check[method][method_option].itervalues():
+ #print argument
+ #check if user registered all the args and add to them ordering option!
+ self._is_basic_types_compatible(argument)
+ self._is_type_options_compatible(argument)
+
+ return True
+
+
+###The Exception classes here they will be raised during the validation part
+###If a module passes all the tests it is ready tobe registered
+
+
+class IncompatibleTypesException(Exception):
+ """
+ Raised when we assign some values that breaksour rules
+ @see ArgCompatibility class for allowed situations
+ """
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+ def __str__(self):
+ return "%s" %(self.value,)
+
+class NonExistingMethodRegistered(IncompatibleTypesException):
+ """
+ That Exception is raised when a non existent module is
+ tried to be registerd we shouldnt allow that
+ """
+ pass
+
+class UnregisteredMethodArgument(IncompatibleTypesException):
+ """
+ That exception is to try to remove the errors that user may
+ do during method registration process. If a argument is missed
+ to be registerted in the method that exception is Raised!
+ """
+ pass
+
+
+class NonExistingMethodRegistered(IncompatibleTypesException):
+ """
+ Raised when module writer registers a method that doesnt
+ exist in his/her module class (probably by mistake)
+ """
+ pass
+
+class ArgumentRegistrationError(IncompatibleTypesException):
+ """
+ When user forgets to register soem of the arguments in the list
+ or adds some argument that is not there
+ """
+ pass
diff --git a/func/minion/modules/func_module.py b/func/minion/modules/func_module.py
index 7d476dc..53fd66b 100644
--- a/func/minion/modules/func_module.py
+++ b/func/minion/modules/func_module.py
@@ -15,7 +15,7 @@ import inspect
from func import logger
from func.config import read_config
from func.commonconfig import FuncdConfig
-
+from func.minion.func_arg import * #the arg getter stuff
class FuncModule(object):
@@ -34,7 +34,8 @@ class FuncModule(object):
"module_version" : self.__module_version,
"module_api_version" : self.__module_api_version,
"module_description" : self.__module_description,
- "list_methods" : self.__list_methods
+ "list_methods" : self.__list_methods,
+ "get_method_args" : self.__get_method_args,
}
def __init_log(self):
@@ -58,8 +59,7 @@ class FuncModule(object):
"""
handlers = {}
for attr in dir(self):
- if inspect.ismethod(getattr(self, attr)) and attr[0] != '_' and \
- attr != 'register_rpc':
+ if self.__is_public_valid_method(attr):
handlers[attr] = getattr(self, attr)
return handlers
@@ -74,3 +74,52 @@ class FuncModule(object):
def __module_description(self):
return self.description
+
+ def __is_public_valid_method(self,attr):
+ if inspect.ismethod(getattr(self, attr)) and attr[0] != '_' and\
+ attr != 'register_rpc' and attr!='register_method_args':
+ return True
+ return False
+
+ def __get_method_args(self):
+ """
+ Gets arguments with their formats according to ArgCompatibility
+ class' rules.
+
+ @return : dict with args or Raise Exception if something wrong
+ happens
+ """
+ tmp_arg_dict = self.register_method_args()
+
+ #if it is not implemeted then return empty stuff
+ if not tmp_arg_dict:
+ return {}
+
+ #see if user tried to register an not implemented method :)
+ for method in tmp_arg_dict.iterkeys():
+ if not hasattr(self,method):
+ raise NonExistingMethodRegistered("%s is not in %s "%(method,self.__class__.__name__))
+
+ #create argument validation instance
+ self.arg_comp = ArgCompatibility(tmp_arg_dict)
+ #see if all registered arguments are there
+ for method in tmp_arg_dict.iterkeys():
+ self.arg_comp.is_all_arguments_registered(self,method,tmp_arg_dict[method]['args'])
+ #see if the options that were used are OK..
+ self.arg_comp.validate_all()
+
+ return tmp_arg_dict
+
+ def register_method_args(self):
+ """
+ That is the method where users should override in their
+ modules according to be able to send their method arguments
+ to the Overlord. If they dont have it nothing breaks
+ just that one in the base class is called
+
+ @return : empty {}
+ """
+
+ # to know they didnt implement it
+ return {}
+
diff --git a/func/minion/modules/iptables/port.py b/func/minion/modules/iptables/port.py
index 370123b..3d3902b 100644
--- a/func/minion/modules/iptables/port.py
+++ b/func/minion/modules/iptables/port.py
@@ -128,6 +128,91 @@ class Port(func_module.FuncModule):
clear_all("-D OUTPUT -p %s --%sport %s -d %s -j REJECT" % (prot, dir, port, ip) )
return call_if_policy("OUTPUT", "DROP", "-I OUTPUT -p %s --%sport %s -d %s -j ACCEPT" % (prot, dir, port, ip) )
+ def register_method_args(self):
+ """
+ Export the methods and their definitons
+ """
+ #they are all same so just declare here
+ port={
+ 'type':'string',
+ 'optional':False,
+
+ }
+ ip={
+ 'type':'string',
+ 'optional':False,
+ 'default':'0.0.0.0'
+ }
+ prot={
+ 'type':'string',
+ 'options':['tcp','udp','icmp','sctp'],
+ 'default':'tcp',
+ 'optional':False
+ }
+ dir={
+ 'type':'string',
+ 'default':'dst',
+ 'options':['src','dst'],
+ 'optional':False
+ }
+
+ return {
+ 'drop_from':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+ }
+ },
+ 'reject_from':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+
+ }
+ },
+ 'accept_from':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+
+ }
+ },
+ 'drop_to':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+
+ }
+ },
+ 'reject_to':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+
+ }
+ },
+ 'accept_to':{'args':
+ {
+ 'ip':ip,
+ 'prot':prot,
+ 'dir':dir,
+ 'port':port
+
+ }
+ },
+
+ }
+
def parse_dir(dir):
if (dir == "dst"):
return "d"
diff --git a/func/minion/modules/service.py b/func/minion/modules/service.py
index 062aea5..25acea3 100644
--- a/func/minion/modules/service.py
+++ b/func/minion/modules/service.py
@@ -86,3 +86,32 @@ class Service(func_module.FuncModule):
tokens = line.split()
results.append((tokens[0], tokens[-1].replace("...","")))
return results
+
+ def register_method_args(self):
+ """
+ Implementing the argument getter
+ """
+
+ #service_name options they are same so use only one
+ service_name = {
+ 'type':'string',
+ 'optional':False,
+ 'description':'The name of the running services',
+ 'validator':'^[a-z\-\_0-9]+$'}
+
+ return {
+ 'get_running':{'args':{}},
+ 'get_enabled':{'args':{}},
+ 'inventory':{'args':{}},
+ 'status':{'args':{
+ 'service_name':service_name,
+ },
+ 'description':'Getting the status of the service_name'
+ },
+ 'reload':{'args':{'service_name':service_name}},
+ 'restart':{'args':{'service_name':service_name}},
+ 'stop':{'args':{'service_name':service_name}},
+ 'start':{'args':{'service_name':service_name}},
+
+
+ }
diff --git a/funcweb/TODO.txt b/funcweb/TODO.txt
new file mode 100644
index 0000000..8b76171
--- /dev/null
+++ b/funcweb/TODO.txt
@@ -0,0 +1,20 @@
+Things to be done for funcweb (makkalot)
+
+ ? : not sure
+ - : not completed
+ + : completed
+ / : being implemented
+
+
+Widget Automation for Minion methods :
+ (+) Creating InputWidgets for method arguments
+ (+) Creating WidgetList with created InputWidgets
+ (+) Creating Validators for method arguments
+ (+)Combining Validators and WidgetList into a RemoteForm
+ (/) Writing more more tests for the API
+ (-) Fix tha css and look and feel of the web UI it sucks!
+ (/) Package the funcweb (rpm thing)
+ (-) Test more modules export all if have time !
+
+New Ideas :
+ -????
diff --git a/funcweb/funcweb/config/app.cfg b/funcweb/funcweb/config/app.cfg
index e691bde..336dc84 100644
--- a/funcweb/funcweb/config/app.cfg
+++ b/funcweb/funcweb/config/app.cfg
@@ -7,7 +7,8 @@
# The commented out values below are the defaults
# VIEW
-
+#tg.include_widgets = ['turbogears.mochikit']
+tg.mochikit_all = True
# which view (template engine) to use if one is not specified in the
# template name
tg.defaultview = "genshi"
diff --git a/funcweb/funcweb/controllers.py b/funcweb/funcweb/controllers.py
index b45d81b..f2c030f 100644
--- a/funcweb/funcweb/controllers.py
+++ b/funcweb/funcweb/controllers.py
@@ -1,10 +1,35 @@
import logging
log = logging.getLogger(__name__)
-from turbogears import controllers, expose, flash, identity, redirect
+from turbogears import controllers, expose, flash, identity, redirect, error_handler,validate
from func.overlord.client import Overlord, Minions
+from funcweb.widget_automation import WidgetListFactory,RemoteFormAutomation,RemoteFormFactory
+from funcweb.widget_validation import WidgetSchemaFactory
+
+# it is assigned into method_display on every request
+global_form = None
+
+def validate_decorator_updater(validator_value=None):
+ """
+ When we pass the global_form directly to
+ turbogears.validate it is not updated on
+ every request we should pass a callable
+ to trigger it ;)
+
+ @param :validator_value : Does nothing in the code
+ of validate it is passed but is irrelevant in our case
+ because we compute the global_form in the method_display
+
+ @return : the current form and schema to validate the
+ current input in cherrypy's request
+ """
+ global global_form
+ return global_form
class Root(controllers.RootController):
+
+
+ #will be reused for widget validation
@expose(template="funcweb.templates.minions")
@identity.require(identity.not_anonymous())
@@ -16,7 +41,7 @@ class Root(controllers.RootController):
@expose(template="funcweb.templates.minion")
@identity.require(identity.not_anonymous())
- def minion(self, name, module=None, method=None):
+ def minion(self, name="*", module=None, method=None):
""" Display module or method details for a specific minion.
If only the minion name is given, it will display a list of modules
@@ -26,7 +51,18 @@ class Root(controllers.RootController):
"""
fc = Overlord(name)
if not module: # list all modules
+ #just list those who have get_method_args
modules = fc.system.list_modules()
+ display_modules = []
+ #FIXME slow really i know !
+ for module in modules.itervalues():
+ for mod in module:
+ #if it is not empty
+ if getattr(fc,mod).get_method_args()[name]:
+ display_modules.append(mod)
+ modules = {}
+ modules[name]=display_modules
+
return dict(modules=modules)
else: # a module is specified
if method: # minion.module.method specified; bring up execution form
@@ -38,13 +74,41 @@ class Root(controllers.RootController):
tg_template="funcweb.templates.module")
- @expose(template="funcweb.templates.run")
+ @expose(template="funcweb.templates.method_args")
@identity.require(identity.not_anonymous())
- def run(self, minion="*", module=None, method=None, arguments=''):
+ def method_display(self,minion=None,module=None,method=None):
+
+ global global_form
fc = Overlord(minion)
- results = getattr(getattr(fc, module), method)(*arguments.split())
- cmd = "%s.%s.%s(%s)" % (minion, module, method, arguments)
- return dict(cmd=cmd, results=results)
+ method_args = getattr(fc,module).get_method_args()
+
+ if not method_args.values():
+ print "Not registered method here"
+ return dict(minion_form = None,minion=minion,module=module,method=method)
+
+ minion_arguments = method_args[minion][method]['args']
+ if minion_arguments:
+ wlist_object = WidgetListFactory(minion_arguments,minion=minion,module=module,method=method)
+ wlist_object = wlist_object.get_widgetlist_object()
+ #create the validation parts for the remote form
+ wf = WidgetSchemaFactory(minion_arguments)
+ schema_man=wf.get_ready_schema()
+
+ #create the final form
+ minion_form = RemoteFormAutomation(wlist_object,schema_man)
+ global_form = minion_form.for_widget
+ #print global_form
+ #i use that when something goes wrong to check the problem better to stay here ;)
+ #self.minion_form =RemoteFormFactory(wlist_object,schema_man).get_remote_form()
+
+ del wlist_object
+ del minion_arguments
+
+ return dict(minion_form =minion_form,minion=minion,module=module,method=method)
+ else:
+ return dict(minion_form = None,minion=minion,module=module,method=method)
+
+
@expose(template="funcweb.templates.login")
def login(self, forward_url=None, previous_url=None, *args, **kw):
@@ -71,6 +135,72 @@ class Root(controllers.RootController):
return dict(message=msg, previous_url=previous_url, logging_in=True,
original_parameters=request.params,
forward_url=forward_url)
+
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def handle_minion_error(self,tg_errors=None):
+ """
+ The method checks the result from turbogears.validate
+ decorator so if it has the tg_errors we know that the
+ form validation is failed. That prevents the extra traffic
+ to be sent to the minions!
+ """
+ if tg_errors:
+ #print tg_errors
+ return str(tg_errors)
+
+
+ @expose(allow_json=True)
+ @error_handler(handle_minion_error)
+ @validate(form=validate_decorator_updater)
+ @identity.require(identity.not_anonymous())
+ def post_form(self,**kw):
+ """
+ Data processing part
+ """
+ if kw.has_key('minion') and kw.has_key('module') and kw.has_key('method'):
+ #assign them because we need the rest so dont control everytime
+ #and dont make lookup everytime ...
+ minion = kw['minion']
+ del kw['minion']
+ module = kw['module']
+ del kw['module']
+ method = kw['method']
+ del kw['method']
+
+ #everytime we do that should be a clever way for that ???
+ fc = Overlord(minion)
+ #get again the method args to get their order :
+ arguments=getattr(fc,module).get_method_args()
+ #so we know the order just allocate and put them there
+ cmd_args=['']*(len(kw.keys()))
+
+ for arg in kw.keys():
+ #wow what a lookup :)
+ index_of_arg = arguments[minion][method]['args'][arg]['order']
+ cmd_args[index_of_arg]=kw[arg]
+
+ #now execute the stuff
+ result = getattr(getattr(fc,module),method)(*cmd_args)
+ return str(result)
+
+ else:
+ return "Missing arguments sorry can not proceess the form"
+
+ @expose(template="funcweb.templates.method_args")
+ @identity.require(identity.not_anonymous())
+ def execute_link(self,minion=None,module=None,method=None):
+ """
+ Method is fot those minion methods that dont accept any
+ arguments so they provide only some information,executed
+ by pressing only the link !
+ """
+ fc = Overlord(minion)
+ result = getattr(getattr(fc,module),method)()
+ return str(result)
+
+
@expose()
def logout(self):
diff --git a/funcweb/funcweb/static/css/style.css b/funcweb/funcweb/static/css/style.css
index b320cb6..703e22e 100644
--- a/funcweb/funcweb/static/css/style.css
+++ b/funcweb/funcweb/static/css/style.css
@@ -506,7 +506,7 @@ background-position:100% -24px;
/*********** My Fedora Widget stuff *************/
.col {
- margin: 5px
+ margin: 10px
padding: 0 0 2ex;
}
@@ -642,7 +642,7 @@ background-position:100% -24px;
.col {
float: left;
- margin: 5px;
+ margin: 30px;
padding: 0 0 2ex;
padding-bottom: 32767px;
margin-bottom: -32767px;
@@ -677,6 +677,10 @@ background-position:100% -24px;
}
+#col5 {
+ width: 200px;
+ margin-left: 500px;
+}
.widget-list {
/*display: table-cell;*/
}
@@ -699,6 +703,8 @@ background-position:100% -24px;
background-color: white;
}
+
+
.widget h2 {
border-width: 1px 1px 1px 1px;
font-weight: bold;
diff --git a/funcweb/funcweb/static/images/loading.gif b/funcweb/funcweb/static/images/loading.gif
new file mode 100644
index 0000000..83729ec
--- /dev/null
+++ b/funcweb/funcweb/static/images/loading.gif
Binary files differ
diff --git a/funcweb/funcweb/static/javascript/ajax.js b/funcweb/funcweb/static/javascript/ajax.js
new file mode 100644
index 0000000..7df4749
--- /dev/null
+++ b/funcweb/funcweb/static/javascript/ajax.js
@@ -0,0 +1,83 @@
+function remoteFormRequest(form, target, options) {
+ var query = Array();
+ var contents = formContents(form);
+ for (var j=0; j<contents[0].length; j++)
+ query[contents[0][j]] = contents[1][j];
+ query["tg_random"] = new Date().getTime();
+ //makePOSTRequest(form.action, target, queryString(query));
+ remoteRequest(form, form.action, target, query, options);
+ return true;
+}
+
+function remoteRequest(source, target_url, target_dom, data, options) {
+ //before
+ if (options['before']) {
+ eval(options['before']);
+ }
+ if ((!options['confirm']) || confirm(options['confirm'])) {
+ makePOSTRequest(source, target_url, getElement(target_dom), queryString(data), options);
+ //after
+ if (options['after']) {
+ eval(options['after']);
+ }
+ }
+ return true;
+}
+
+function makePOSTRequest(source, url, target, parameters, options) {
+ var http_request = false;
+ if (window.XMLHttpRequest) { // Mozilla, Safari,...
+ http_request = new XMLHttpRequest();
+ if (http_request.overrideMimeType) {
+ http_request.overrideMimeType('text/xml');
+ }
+ } else if (window.ActiveXObject) { // IE
+ try {
+ http_request = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ http_request = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e) {}
+ }
+ }
+ if (!http_request) {
+ alert('Cannot create XMLHTTP instance');
+ return false;
+ }
+
+ var insertContents = function () {
+ if (http_request.readyState == 4) {
+ // loaded
+ if (options['loaded']) {
+ eval(options['loaded']);
+ }
+ if (http_request.status == 200) {
+ if(target) {
+ target.innerHTML = http_request.responseText;
+ }
+ //success
+ if (options['on_success']) {
+ eval(options['on_success']);
+ }
+ } else {
+ //failure
+ if (options['on_failure']) {
+ eval(options['on_failure']);
+ } else {
+ alert('There was a problem with the request. Status('+http_request.status+')');
+ }
+ }
+ //complete
+ if (options['on_complete']) {
+ eval(options['on_complete']);
+ }
+ }
+ }
+
+ http_request.onreadystatechange = insertContents;
+ http_request.open('POST', url, true);
+ http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ http_request.setRequestHeader("Content-length", parameters.length);
+ http_request.setRequestHeader("Connection", "close");
+ http_request.send(parameters);
+}
diff --git a/funcweb/funcweb/templates/master.html b/funcweb/funcweb/templates/master.html
index ab14ca0..073adc1 100644
--- a/funcweb/funcweb/templates/master.html
+++ b/funcweb/funcweb/templates/master.html
@@ -9,9 +9,9 @@
<title py:content="'Your Title Goes Here'"></title>
<script type="text/javascript" src="${tg.url('/static/javascript/jquery.js')}" />
-
- <link py:for="js in tg_js_head" py:strip="">${XML(js)}</link>
- <link py:for="css in tg_css" py:strip="">${XML(css)}</link>
+ <link py:for="js in tg_js_head" py:strip="">${ET(js.display())}</link>
+ <link py:for="css in tg_css" py:strip="">${ET(css.display())}</link>
+ <script type="text/javascript" src="${tg.url('/static/javascript/ajax.js')}" />
<link href="${tg.url('/static/images/favicon.ico')}"
type="image/vnd.microsoft.icon" rel="shortcut icon" />
@@ -20,10 +20,16 @@
<style type="text/css" media="screen">
@import url("/static/css/style.css");
- </style>
+ </style>
+
+ <script type="text/javascript">
+ jQuery._$ = MochiKit.DOM.getElement;
+ var myj = jQuery.noConflict();
+ </script>
+
</head>
<body py:match="body" py:attrs="select('@*')">
- <div py:for="js in tg_js_bodytop" py:replace="XML(js.display())" />
+ <div py:for="js in tg_js_bodytop" py:replace="ET(js.display())" />
<div class="wrapper">
<div class="head">
<h1><a href="http://fedorahosted.org/func">Func</a></h1>
diff --git a/funcweb/funcweb/templates/method.html b/funcweb/funcweb/templates/method.html
index 1f67ce9..af20d86 100644
--- a/funcweb/funcweb/templates/method.html
+++ b/funcweb/funcweb/templates/method.html
@@ -2,18 +2,17 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
+
+
<body>
<div class="method">
${minion}.${module}.${method}
-
- <form action="" method="GET" onsubmit="$('#results').hide().load('/run/${minion}/${module}/${method}/').show('slow'); return false;">
+
+ <form action="" method="GET" onsubmit="myj('#results').hide().load('/run/${minion}/${module}/${method}/').show('slow'); return false;">
<input type="text" name="arguments" id="methodargs" class="methodargs"/>
</form>
<div class="results" id="results" />
- <script type="text/javascript">
- $(document).ready($("#methodargs").focus());
- </script>
</div>
</body>
</html>
diff --git a/funcweb/funcweb/templates/method_args.html b/funcweb/funcweb/templates/method_args.html
new file mode 100644
index 0000000..2e7d4bd
--- /dev/null
+++ b/funcweb/funcweb/templates/method_args.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<body>
+ <div py:if="minion_form" id="men">
+ ${ET(minion_form.display(displays_on='genshi'))}
+ </div>
+ <div py:if="not minion_form" id="men">
+ <a href="#" onclick="myj('#results').hide().load('/execute_link/${minion}/${module}/${method}/').show('slow');">Run Method</a>
+ <div class="results" id="results" />
+ </div>
+</body>
+</html>
+
diff --git a/funcweb/funcweb/templates/minion.html b/funcweb/funcweb/templates/minion.html
index f171b00..a526764 100644
--- a/funcweb/funcweb/templates/minion.html
+++ b/funcweb/funcweb/templates/minion.html
@@ -2,12 +2,13 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
+
<body>
- <div id="modules" class="modules">
+ <div id="modules" class="modules">
<ul py:for="minion, mods in modules.items()">
<h2>${minion[:13]}</h2>
<li py:for="module in mods">
- <a href="#" onclick="$('#col4').hide();$('#col3').hide().load('/minion/${minion}/${module}').show('slow');">${module}</a>
+ <a href="#" onclick="myj('#col5').hide();myj('#col3').hide().load('/minion/${minion}/${module}').show('slow');">${module}</a>
</li>
</ul>
</div>
diff --git a/funcweb/funcweb/templates/minions.html b/funcweb/funcweb/templates/minions.html
index 94ccbc1..6a39f54 100644
--- a/funcweb/funcweb/templates/minions.html
+++ b/funcweb/funcweb/templates/minions.html
@@ -3,20 +3,21 @@
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html"/>
-<head/>
+ <head/>
<body>
- <div class="col-group">
+ <div class="col-group">
<div class="col" id="col1">
<ul>
<li><h2>minions</h2></li>
<li py:for="minion in minions">
- <a onclick="$('#col3').hide();$('#col4').hide();$('#col2').hide().load('/minion/${minion}').show('slow');" href="#">${minion}</a>
+ <a onclick="jQuery('#col3').hide();myj('#col4').hide();myj('#col5').hide();getElement('col2').innerHTML=toHTML(IMG({src:'../static/images/loading.gif',width:'80',height:'80'}));myj('#col2').hide().load('/minion/${minion}').show('slow');" href="#">${minion}</a>
</li>
</ul>
</div>
<div class="col" id="col2" />
<div class="col" id="col3" />
<div class="col" id="col4" />
+ <div class="col" id="col5" />
</div>
</body>
</html>
diff --git a/funcweb/funcweb/templates/module.html b/funcweb/funcweb/templates/module.html
index 2d433f6..a4794d0 100644
--- a/funcweb/funcweb/templates/module.html
+++ b/funcweb/funcweb/templates/module.html
@@ -7,7 +7,7 @@
<ul py:for="minion, methods in modules.items()">
<h2>${module}</h2>
<li py:for="method in methods">
- <a href="#" onclick="$('#col4').hide().load('/minion/${minion}/${module}/${method}').show('slow')">${method}</a>
+ <a href="#" onclick="myj('#col2').hide();myj('#col5').hide().show('slow');myj('#col4').hide().load('/method_display/${minion}/${module}/${method}').show('slow')">${method}</a>
</li>
</ul>
</div>
diff --git a/funcweb/funcweb/templates/run.html b/funcweb/funcweb/templates/run.html
index cb17cdd..7ae047b 100644
--- a/funcweb/funcweb/templates/run.html
+++ b/funcweb/funcweb/templates/run.html
@@ -2,7 +2,8 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
-<body>
- ${results.values()[0]}
+ <body>
+
+ ${results.values()[0]}
</body>
</html>
diff --git a/funcweb/funcweb/tests/test_widget_automation.py b/funcweb/funcweb/tests/test_widget_automation.py
new file mode 100644
index 0000000..d02de93
--- /dev/null
+++ b/funcweb/funcweb/tests/test_widget_automation.py
@@ -0,0 +1,130 @@
+import unittest
+import turbogears
+from turbogears import testutil
+
+from funcweb.widget_automation import WidgetListFactory,RemoteFormAutomation,RemoteFormFactory
+from funcweb.widget_validation import WidgetSchemaFactory
+
+class TestWidgetListFactory(unittest.TestCase):
+
+ def setUp(self):
+ self.widget_factory = WidgetListFactory(self.get_test_default_args(),minion="myminion",module="mymodule",method="my_method")
+
+
+ def tearDown(self):
+ pass
+
+ def test_default_args(self):
+ compare_with = self.get_test_default_args()
+ widget_list=self.widget_factory.get_widgetlist()
+
+ #print "The widget list is like :",widget_list
+
+ for argument_name,argument_options in compare_with.iteritems():
+ assert widget_list.has_key(argument_name) == True
+ #print "The argument name is :",argument_name
+ #because some of them dont have it like boolean
+ if argument_options.has_key('default'):
+ assert argument_options['default'] == getattr(widget_list[argument_name],'default')
+
+ if argument_options.has_key("description"):
+ assert argument_options['description']==getattr(widget_list[argument_name],'help_text')
+
+ if argument_options.has_key("options"):
+ assert argument_options['options'] == getattr(widget_list[argument_name],"options")
+
+ #that should be enough
+ def test_get_widgetlist_object(self):
+ compare_with = self.get_test_default_args()
+ widget_list_object = self.widget_factory.get_widgetlist_object()
+
+ #print widget_list_object
+
+ all_fields = [getattr(field,"name") for field in widget_list_object]
+ #print all_fields
+ for argument_name in compare_with.keys():
+ print argument_name
+ assert argument_name in all_fields
+ #print getattr(widget_list_object,argument_name)
+
+
+ def test_remote_form(self):
+ schema_factory = WidgetSchemaFactory(self.get_test_default_args())
+ schema_validator=schema_factory.get_ready_schema()
+ widget_list_object = self.widget_factory.get_widgetlist_object()
+ remote_form = RemoteFormAutomation(widget_list_object,schema_validator)
+ #print remote_form
+
+ def test_remote_form_factory(self):
+ from turbogears.view import load_engines
+ load_engines()
+
+ # WidgetsList object
+ widget_list_object = self.widget_factory.get_widgetlist_object()
+ #print widget_list_object
+ remote_form = RemoteFormFactory(widget_list_object).get_remote_form()
+
+ #it is a key,value dict
+ widget_list=self.widget_factory.get_widgetlist()
+ #print widget_list
+ all_fields = [getattr(field,"name") for field in remote_form.fields]
+ #print all_fields
+ #will check if the remote form object hass all the names in it
+ for argument_name in widget_list.items():
+ argument_name in all_fields
+
+
+ #print remote_form.render()
+
+ def get_test_default_args(self):
+ return {
+ 'string_default':{
+ 'type':'string',
+ 'default':'default string',
+ 'optional':False,
+ 'description':'default description'
+ },
+ 'int_default':{
+ 'type':'int',
+ 'default':'default int',
+ 'optional':False,
+ 'description':'default description'
+ },
+ #no sense to have default
+ 'boolean_default':{
+ 'type':'boolean',
+ 'optional':False,
+ 'description':'default description'
+ },
+ 'float_default':{
+ 'type':'float',
+ 'default':'default float',
+ 'optional':False,
+ 'description':'default description'
+
+ },
+ 'hash_default':{
+ 'type':'hash',
+ 'default':'default hash',
+ 'optional':False,
+ 'description':'default description'
+
+ },
+ 'list_default':{
+ 'type':'list',
+ 'default':'default list',
+ 'optional':False,
+ 'description':'default description'
+
+ },
+ #will be converted to dropdown
+ 'special_string':{
+ 'type':'string',
+ 'default':'myfirst',
+ 'options':['myfirst','mysecond','mythird'],
+ 'optional':False,
+ 'description':'default dropdown list'
+ }
+
+ }
+
diff --git a/funcweb/funcweb/tests/test_widget_validation.py b/funcweb/funcweb/tests/test_widget_validation.py
new file mode 100644
index 0000000..c800d41
--- /dev/null
+++ b/funcweb/funcweb/tests/test_widget_validation.py
@@ -0,0 +1,339 @@
+import unittest
+import turbogears
+from turbogears import testutil
+from funcweb.widget_validation import WidgetSchemaFactory,MinionIntValidator,MinionFloatValidator,MinionListValidator,MinionHashValidator
+from turbogears import validators
+
+class TestWidgetValidator(unittest.TestCase):
+
+ def test_string_validator(self):
+ wf = WidgetSchemaFactory(self.get_string_params())
+ schema_man=wf.get_ready_schema()
+
+ conversion_schema = {
+ 'max_length':'max',
+ 'min_length':'min',
+ 'validator':'regex'
+ }
+
+ #do better test here
+ for argument_name,arg_options in self.get_string_params().iteritems():
+ #print argument_name
+ assert hasattr(schema_man,argument_name)==True
+ #not very efficient but it si just a test :)
+ if argument_name != 'string_mix':
+ for arg,value in arg_options.iteritems():
+ #print getattr(schema_man,argument_name)
+ if conversion_schema.has_key(arg):
+ if hasattr(getattr(schema_man,argument_name),conversion_schema[arg]):
+ #print arg,value
+ #couldnt find a way to test it !??
+ if arg != 'validator':
+ assert getattr(getattr(schema_man,argument_name),conversion_schema[arg])==value
+ #print getattr(getattr(schema_man,argument_name),conversion_schema[arg])
+ else:
+ #just print it to see what is inside because the test will be very hardcoded otherwise
+ #print getattr(schema_man,argument_name)
+ continue
+ print "Happy tests !"
+
+ def test_int_validator(self):
+ wf = WidgetSchemaFactory(self.get_int_params())
+ schema_man=wf.get_ready_schema()
+
+ for argument_name,arg_options in self.get_int_params().iteritems():
+ #print argument_name
+ assert hasattr(schema_man,argument_name)==True
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+
+ #if the argument includes some range
+ if arg_options.has_key('range'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'max') == arg_options['range'][1]
+ assert getattr(getattr(schema_man,argument_name),'min') == arg_options['range'][0]
+ if arg_options.has_key('min'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'min') == arg_options['min']
+
+ if arg_options.has_key('max'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'max') == arg_options['max']
+
+ if arg_options.has_key('optional'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional']
+
+
+ print "Happy test!"
+
+ def test_float_validator(self):
+ wf = WidgetSchemaFactory(self.get_float_params())
+ schema_man=wf.get_ready_schema()
+
+ for argument_name,arg_options in self.get_float_params().iteritems():
+ #print argument_name
+ assert hasattr(schema_man,argument_name)==True
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+
+ if arg_options.has_key('min'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'min') == arg_options['min']
+
+ if arg_options.has_key('max'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'max') == arg_options['max']
+
+ if arg_options.has_key('optional'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional']
+
+
+ print "Happy test!"
+
+ def test_bool_validator(self):
+ testing_data = self.get_bool_params()
+ wf = WidgetSchemaFactory(testing_data)
+ schema_man=wf.get_ready_schema()
+
+ for argument_name,arg_options in testing_data.iteritems():
+ #print argument_name
+ #should all the argument names really
+ assert hasattr(schema_man,argument_name)==True
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+
+ if arg_options.has_key('optional'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional']
+
+ print "Happy test!"
+
+
+
+ def test_list_validator(self,the_type='list'):
+ if the_type == 'list':
+ testing_data = self.get_list_params()
+ else:
+ testing_data = self.get_hash_params()
+
+ wf = WidgetSchemaFactory(testing_data)
+ schema_man=wf.get_ready_schema()
+
+ for argument_name,arg_options in testing_data.iteritems():
+ #print argument_name
+ #should all the argument names really
+ assert hasattr(schema_man,argument_name)==True
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+
+ if arg_options.has_key('validator'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert getattr(getattr(schema_man,argument_name),'regex_string') == arg_options['validator']
+
+ if arg_options.has_key('optional'):
+ #print " ",argument_name," : ",getattr(schema_man,argument_name)
+ assert not getattr(getattr(schema_man,argument_name),'not_empty') == arg_options['optional']
+
+
+ print "Happy test!"
+
+
+ def test_hash_validator(self):
+ self.test_list_validator(the_type = 'hash')
+
+ def test_minion_int_validator(self):
+ mv=MinionIntValidator(max = 44,min=2)
+ self.assertRaises(validators.Invalid,mv.to_python,100)
+ self.assertRaises(validators.Invalid,mv.to_python,1)
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21) == 21
+
+ #dont use the min
+ mv=MinionIntValidator(max = 44)
+ self.assertRaises(validators.Invalid,mv.to_python,100)
+ assert mv.to_python(1)==1
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21) == 21
+
+ mv=MinionIntValidator(min=12)
+ self.assertRaises(validators.Invalid,mv.to_python,10)
+ assert mv.to_python(14)==14
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21) == 21
+
+ mv=MinionIntValidator()
+ assert mv.to_python(14)==14
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+
+ def test_minion_float_validator(self):
+ mv=MinionFloatValidator(max = 44.0,min=2.0)
+ self.assertRaises(validators.Invalid,mv.to_python,100.0)
+ self.assertRaises(validators.Invalid,mv.to_python,1.0)
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21.0) == 21.0
+
+ #dont use the min
+ mv=MinionFloatValidator(max = 44.0)
+ self.assertRaises(validators.Invalid,mv.to_python,100.0)
+ assert mv.to_python(1.0)==1.0
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21.0) == 21.0
+
+ mv=MinionFloatValidator(min=12.0)
+ self.assertRaises(validators.Invalid,mv.to_python,10.0)
+ assert mv.to_python(14.0)==14.0
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+ assert mv.to_python(21.0) == 21.0
+
+ mv=MinionFloatValidator()
+ assert mv.to_python(14.0)==14.0
+ self.assertRaises(validators.Invalid,mv.to_python,'some_string')
+
+ def test_minion_list_validator(self):
+
+ #test default
+ mv = MinionListValidator()
+ assert mv.to_python(['fedora','debian','others']) == ['fedora','debian','others']
+ del mv
+
+ #test with regex
+ mv = MinionListValidator(regex_string='^[A-Z]+$',not_empty=True)
+ assert mv.to_python(['FEDORA','MACOSX']) == ['FEDORA','MACOSX']
+ self.assertRaises(validators.Invalid,mv.to_python,[])
+ self.assertRaises(validators.Invalid,mv.to_python,['hey_error'])
+ self.assertRaises(validators.Invalid,mv.to_python,{})
+ del mv
+
+
+ print "Happy testing !"
+
+
+ def test_minion_hash_validator(self):
+
+ #test default
+ mv = MinionHashValidator()
+ assert mv.to_python({'fedora':1,'debian':2,'others':3}) == {'fedora':1,'debian':2,'others':3}
+ del mv
+
+ #test with regex
+ mv = MinionHashValidator(regex_string='^[A-Z]+$',not_empty=True)
+ assert mv.to_python({'FEDORA':'FEDORA','MACOSX':'MACOSX'}) == {'FEDORA':'FEDORA','MACOSX':'MACOSX'}
+ self.assertRaises(validators.Invalid,mv.to_python,{})
+ self.assertRaises(validators.Invalid,mv.to_python,{'hey_error':12})
+ self.assertRaises(validators.Invalid,mv.to_python,"hwy")
+ del mv
+
+ print "Happy testing !"
+
+
+ def get_string_params(self):
+ return {
+ 'string_default':{
+ 'type':'string',
+ 'default':'default string',
+ 'description':'default description'
+ },
+ 'string_regex':{
+ 'type':'string',
+ 'default':'some',
+ 'validator':'^[a-z]*$'
+ }
+ ,
+ 'min_max_string':{
+ 'type':'string',
+ 'default':'myfirst',
+ 'optional':False,
+ 'description':'default dropdown list',
+ 'max_length':12,
+ 'min_length':5
+ },
+ 'string_mix':{
+ 'type':'string',
+ 'optional':False,
+ 'max_length':123,
+ 'min_length':12,
+ 'validator':'^[A-Z]+$'
+ }
+ }
+
+ def get_int_params(self):
+ return {
+ 'int_default':{
+ 'type':'int',
+ 'default':2,
+ 'description':'default integer'
+ },
+ 'min_max':{
+ 'type':'int',
+ 'default':12,
+ 'optional':False,
+ 'description':'default dropdown list',
+ 'max':12,
+ 'min':5
+ },
+ 'range_int':{
+ 'type':'int',
+ 'optional':False,
+ 'range':[1,55]
+ }
+ }
+
+ def get_float_params(self):
+ return {
+ 'float_default':{
+ 'type':'float',
+ 'default':2.0,
+ 'description':'default float'
+ },
+ 'min_max':{
+ 'type':'float',
+ 'default':11.0,
+ 'optional':False,
+ 'description':'default dropdown list',
+ 'max':12.0,
+ 'min':5.0
+ },
+ }
+
+ def get_list_params(self):
+ return {
+ 'list_default':{
+ 'type':'list',
+ 'default':'cooler',
+ 'description':'default list'
+ },
+ 'list_regex':{
+ 'type':'list',
+ 'default':'hey',
+ 'optional':False,
+ 'description':'default regex list',
+ 'validator':'^[A-Z]$'
+ },
+ }
+
+ def get_hash_params(self):
+ return {
+ 'hash_default':{
+ 'type':'hash',
+ 'default':{'hey':12},
+ 'description':'default hash'
+ },
+ 'hash_regex':{
+ 'type':'hash',
+ 'default':{'hey':12},
+ 'optional':False,
+ 'description':'default regex hash',
+ 'validator':'^[A-Z]$'
+ },
+ }
+
+ def get_bool_params(self):
+ return {
+ 'bool_default':{
+ 'type':'boolean',
+ 'default':{'hey':12},
+ 'description':'default hash',
+ 'optional':False,
+ },
+ }
+
+
+
diff --git a/funcweb/funcweb/widget_automation.py b/funcweb/funcweb/widget_automation.py
new file mode 100644
index 0000000..5d299bd
--- /dev/null
+++ b/funcweb/funcweb/widget_automation.py
@@ -0,0 +1,263 @@
+#the purpose of that module is to make widget automation
+#for registered minion modules so we dont hace to write
+#that same boring stuff for every added module !
+
+from turbogears.widgets.base import Widget,WidgetsList
+from turbogears import widgets
+
+class WidgetListFactory(object):
+ """
+ The class is responsible for taking the method arguments
+ and converting them to the appropriate widget equivalents
+
+ Examples :
+ """
+ #which type matches for which InputWidget
+ __convert_table={
+ 'int':{
+ 'default_value':"TextField",
+ },
+ 'string':{
+ 'default_value':"TextField",
+ 'options':"SingleSelectField"},
+ 'boolean':{
+ 'default_value':"CheckBox"},
+ 'float':{
+ 'default_value':"TextField",
+ },
+ 'hash':{
+ 'default_value':"TextArea"},
+ 'list':{
+ 'default_value':"TextArea"}
+ }
+ #will contain the input widget created in that class
+
+ def __init__(self,argument_dict,minion=None,module=None,method=None):
+ """
+ Initiated with argument_dict of a method to return back
+ a WidgetsList object to be placed into a form object
+
+ @param:argument_dict : The structure we got here is like
+ {'arg1':{'type':'int'..},'arg2':{'min':111} ...}
+ """
+
+ self.__argument_dict = argument_dict
+ self.__widget_list={}
+
+ #these fields will be passed ass hidden so we need them
+ self.minion = minion
+ self.module = module
+ self.method = method
+
+ def __add_general_widget(self):
+
+ #key is the argument_name and the argument are options
+ for key,argument in self.__argument_dict.iteritems():
+ #get the type of the argument
+ current_type = argument['type']
+
+ act_special = False #if it has some special parameters
+ #it should be passed to its specialized method,if it has
+ #for example options in its key it should be shown as a
+ #SingleSelectField not a TextFiled
+
+ for type_match in self.__convert_table[current_type].keys():
+ if type_match!='default_value' and argument.has_key(type_match):
+ act_special = True
+
+ # print key,argument
+
+ if act_special:
+ #calling for example __add_specialized_string(..)
+ getattr(self,"_%s__add_specialized_%s"%(self.__class__.__name__,current_type))(argument,key)
+ else:
+ temp_object = getattr(widgets,self.__convert_table[current_type]['default_value'])()
+ #add common options to it
+ self.__add_commons_to_object(temp_object,argument,key)
+ #add a new entry to final list
+ self.__widget_list[key]=temp_object
+ #print "That have the object :",getattr(self.__widget_list[key],"default")
+ del temp_object
+
+ #print "That have the object :",getattr(self.__widget_list["list_default"],"default")
+
+ #adding the hidden fields (that part wass adde later can be made more generic)
+ if self.minion:
+ self.__widget_list['minion']= getattr(widgets,'HiddenField')(name="minion",value=self.minion,default=self.minion)
+ if self.module:
+ self.__widget_list['module']= getattr(widgets,'HiddenField')(name="module",value=self.module,default=self.module)
+ if self.method:
+ self.__widget_list['method']= getattr(widgets,'HiddenField')(name="method",value=self.method,default=self.method)
+
+
+
+ def __add_specialized_string(self,argument,argument_name):
+ """
+ Specialized option adder, called when the type:string is used
+ with option 'options' so we should create a new SingleSelectField
+ that one canno be created like others because user should supply options
+ in the constructor of the SingleSelectField so it becomes a special one
+
+ @param : argument : the argument options,
+ @param : argument_name : the name of the argument also the name of the widget
+ @return : Nothing
+ """
+
+ #allittle bit difficult to follow but that structure does
+ #temp_object = SingleSelectField() for example
+
+ temp_object = getattr(widgets,self.__convert_table[argument['type']]['options'])(options = argument['options'])
+ self.__add_commons_to_object(temp_object,argument,argument_name)
+ #add a new entry to final list
+ self.__widget_list[argument_name]=temp_object
+ del temp_object
+
+ def __add_commons_to_object(self,object,argument,argument_name):
+ """
+ As it was thought all input widgets have the same
+ common parameters they take so that method will add
+ them to instantiated object for ex (TextField) if they
+ occur into the argument ...
+
+ @param object : instantiated inputwidget object
+ @param method argument to lookup {type:'int','max':12 ...}
+ @return :None
+ """
+ #firstly set the name of the argument
+ setattr(object,"name",argument_name)
+
+ #print "The argument name is :",argument_name
+ #print "The argument options are :",argument
+
+ if argument.has_key('default'):
+ setattr(object,"default",argument["default"])
+ if argument.has_key('description'):
+ setattr(object,'help_text',argument['description'])
+
+ def get_widgetlist(self):
+ """
+ Return back a dictionay with argument_name : input_widget
+ pairs. That method may not be called directly,get_widgetlist_object
+ is better for using in web interface
+ """
+ #compute the list
+ self.__add_general_widget()
+ return self.__widget_list
+
+ def get_widgetlist_object(self):
+ """
+ Method return back the final widgetlist object
+ which is turbogears.widgets.WidgetsList
+ """
+
+ #print self.__widget_list
+ if len(self.__widget_list.keys())==0:
+ self.__add_general_widget() #not very efficient
+
+ widget_list_object = widgets.WidgetsList()
+ for name,input_widget in self.__widget_list.iteritems():
+ #it is a list indeed
+ widget_list_object.append(input_widget)
+
+ #get the object back
+ #print widget_list_object
+ return widget_list_object
+
+####################################################################################################################
+from turbogears.widgets.base import CoreWD
+from turbogears.widgets import RemoteForm
+from turbogears import validators, expose
+
+class RemoteFormAutomation(CoreWD):
+ """
+ Base class for ajaxian Form creation
+ """
+
+ name = "Ajaxian Minion Submit Form"
+
+ template = """
+ <div>
+ ${for_widget.display(action='post_form')}
+ <div id="loading"></div>
+ <div id="post_data"></div>
+ </div>
+ """
+
+ full_class_name = "funcweb.controllers.Root"
+
+ def __init__(self,generated_fields,validator_schema,*args,**kwarg):
+ """
+ The constructor part same as normal one except
+ it takes a WidgetsList object into generated_fields
+ which is generated by WidgetListFactory
+ """
+ #call the master :)
+ super(RemoteFormAutomation,self).__init__(*args,**kwarg)
+ self.for_widget = RemoteForm(
+ fields = generated_fields,
+ validator = validator_schema,
+ name = "minion_form",
+ update = "col5",
+ before='getElement(\'loading\').innerHTML=toHTML(IMG({src:\'../static/images/loading.gif\',width:\'80\',height:\'80\'}));',
+ on_complete='getElement(\'loading\' ).innerHTML=\'Done!\';',
+ )
+
+####################################################################################################
+class RemoteFormFactory(object):
+ """
+ Gets the WidgetListFactory object
+ and return back a RemoteForm the same as above
+ just for testing
+ """
+ #some values that may want to change later
+ name = "minion_form",
+ update = "col5",
+ before='getElement(\'loading\').innerHTML=toHTML(IMG({src:\'../static/images/loading.gif\',width:\'80\',height:\'80\'}));',
+ on_complete='getElement(\'loading\' ).innerHTML=\'Done!\';',
+ submit_text = "Send Minion Form"
+ action = "/post_form"
+
+ def __init__(self,wlist_object,validator_schema):
+ self.wlist_object = wlist_object
+ self.validator_schema = validator_schema
+
+ def get_remote_form(self):
+
+ #print self.wlist_object
+
+ return RemoteForm(
+ name = self.name,
+ fields = self.wlist_object,
+ before = self.before,
+ on_complete = self.on_complete,
+ update = self.update,
+ submit_text = self.submit_text,
+ action = self.action,
+ validator = self.validator_schema
+ )
+
+class RemoteLinkFactory(CoreWD):
+ """
+ A rpc executer for the methods without arguments
+ """
+
+ name = "Minion Remote Link Executer"
+
+ template ="""
+ <div>
+ ${for_widget.display()}
+ <div id="items">Minion Result</div>
+ </div>
+ """
+
+ def __init__(self,*args,**kwargs):
+ """The init part here"""
+ super(SomeRemoteLink,self).__init__(*args,**kwargs)
+ self.for_widget = LinkRemoteFunction(
+ name = "catexecuter",
+ action = "/catlister",
+ data = dict(give_all="all"),
+ update = "items"
+ )
+
+
diff --git a/funcweb/funcweb/widget_validation.py b/funcweb/funcweb/widget_validation.py
new file mode 100644
index 0000000..0347fa6
--- /dev/null
+++ b/funcweb/funcweb/widget_validation.py
@@ -0,0 +1,333 @@
+from turbogears import validators #the shiny validator part
+
+class WidgetSchemaFactory(object):
+ """
+ The purpose of the class is to produce a
+ validators.Schema object according to method
+ arguments that are retrieved from minions
+ """
+
+ def __init__(self,method_argument_dict):
+ """
+ @param method_argument_dict : The dict that is
+ from minion in format of {'arg':{'type':'string','options':[...]}}
+ the format is defined in func/minion/func_arg.py
+ """
+
+ self.method_argument_dict = method_argument_dict
+ self.validator_list = {} #the validator that will create final schema
+
+ def _add_validators(self):
+ """
+ Method is an entry point of factory iters over the all arguments
+ and according to their types it sends the process to more specialized
+ validator adders
+ """
+
+ for argument_name,argument_values in self.method_argument_dict.iteritems():
+ #some lazy stuff :)
+ #for ex : _add_int_validator(some_arg)
+ getattr(self,"_add_%s_validator"%(argument_values['type']))(argument_name)
+
+ def _add_boolean_validator(self,argument_name):
+ bool_data_set = {}
+
+ #the optional keyword
+ if self.method_argument_dict[argument_name].has_key('optional'):
+ if self.method_argument_dict[argument_name]['optional']:
+ bool_data_set['not_empty']=False
+ else:
+ bool_data_set['not_empty']=True
+
+
+ if bool_data_set:
+ self.validator_list[argument_name]=validators.Bool(**bool_data_set)
+ else:
+ self.validator_list[argument_name]=validators.Bool()
+
+
+
+
+ def _add_int_validator(self,argument_name):
+ """
+ Gets the options of the int type and adds a
+ new validator to validator_list
+ """
+ #the initializer for the int_validator
+ int_data_set = {}
+
+ #the optional keyword
+ if self.method_argument_dict[argument_name].has_key('optional'):
+ if self.method_argument_dict[argument_name]['optional']:
+ int_data_set['not_empty']=False
+ else:
+ int_data_set['not_empty']=True
+
+ if self.method_argument_dict[argument_name].has_key('range'):
+ #because the range is [min,max] list the 0 is min 1 is the max
+ int_data_set['min']=self.method_argument_dict[argument_name]['range'][0]
+ int_data_set['max']=self.method_argument_dict[argument_name]['range'][1]
+ if self.method_argument_dict[argument_name].has_key('min'):
+ int_data_set['min']=self.method_argument_dict[argument_name]['min']
+ if self.method_argument_dict[argument_name].has_key('max'):
+ int_data_set['max']=self.method_argument_dict[argument_name]['max']
+
+ #add the validator to the list
+ if int_data_set:
+ self.validator_list[argument_name]=MinionIntValidator(**int_data_set)
+ else:
+ self.validator_list[argument_name]=MinionIntValidator()
+
+
+
+
+ def _add_string_validator(self,argument_name):
+ """
+ Gets the options of the string type and adds a
+ new validator to validator_list
+ """
+
+ string_data_set={}
+ str_validator_list =[]
+
+ if self.method_argument_dict[argument_name].has_key('optional'):
+ if self.method_argument_dict[argument_name]['optional']:
+ string_data_set['not_empty']=False
+ else:
+ string_data_set['not_empty']=True
+
+ if self.method_argument_dict[argument_name].has_key('min_length'):
+ string_data_set['min']=self.method_argument_dict[argument_name]['min_length']
+ if self.method_argument_dict[argument_name].has_key('max_length'):
+ string_data_set['max']=self.method_argument_dict[argument_name]['max_length']
+ if self.method_argument_dict[argument_name].has_key('validator'):
+ str_validator_list.append(getattr(validators,'Regex')(self.method_argument_dict[argument_name]['validator']))
+
+ #if we have set a string_data_set
+ if string_data_set:
+ str_validator_list.append(getattr(validators,'String')(**string_data_set))
+
+ #if true it should be a validator.All thing
+ if len(str_validator_list)>1:
+ self.validator_list[argument_name]=getattr(validators,'All')(*str_validator_list)
+ elif str_validator_list:
+ self.validator_list[argument_name]=str_validator_list[0]
+ else: #if there is no option
+ self.validator_list[argument_name]=getattr(validators,'String')()
+
+
+
+
+ def _add_float_validator(self,argument_name):
+ """
+ Gets the options of the float type and adds a
+ new validator to validator_list
+ """
+
+ #the initializer for the float_validator
+ float_data_set = {}
+
+ #is it optional
+ if self.method_argument_dict[argument_name].has_key('optional'):
+ if self.method_argument_dict[argument_name]['optional']:
+ float_data_set['not_empty']=False
+ else:
+ float_data_set['not_empty']=True
+
+
+ if self.method_argument_dict[argument_name].has_key('min'):
+ float_data_set['min']=self.method_argument_dict[argument_name]['min']
+ if self.method_argument_dict[argument_name].has_key('max'):
+ float_data_set['max']=self.method_argument_dict[argument_name]['max']
+
+ #add the validator to the list
+ if float_data_set:
+ self.validator_list[argument_name]=MinionFloatValidator(**float_data_set)
+ else:
+ self.validator_list[argument_name]=MinionFloatValidator()
+
+
+
+ def _add_list_validator(self,argument_name,the_type='list'):
+ """
+ Gets the options of the list type and adds a
+ new validator to validator_list
+ """
+ list_data_set = {}
+
+ #is it optional
+ if self.method_argument_dict[argument_name].has_key('optional'):
+ if self.method_argument_dict[argument_name]['optional']:
+ list_data_set['not_empty']=False
+ else:
+ list_data_set['not_empty']=True
+
+ if self.method_argument_dict[argument_name].has_key('validator'):
+ list_data_set['regex_string'] = self.method_argument_dict[argument_name]['validator']
+
+ if list_data_set:
+ if the_type == 'list':
+ self.validator_list[argument_name]=MinionListValidator(**list_data_set)
+ else:
+ self.validator_list[argument_name]=MinionHashValidator(**list_data_set)
+
+ else:
+ if the_type == 'list':
+ self.validator_list[argument_name]=MinionListValidator()
+ else:
+ self.validator_list[argument_name]=MinionHashValidator()
+
+
+
+ def _add_hash_validator(self,argument_name):
+ """
+ Gets the options of the hash type and adds a
+ new validator to validator_list
+ """
+ self._add_list_validator(argument_name,the_type = 'hash')
+
+
+ def get_ready_schema(self):
+ """
+ Get the final validator schema
+ """
+ final_schema = validators.Schema()
+ if not self.validator_list:
+ self._add_validators()
+
+ for vd_name,vd in self.validator_list.iteritems():
+ #setattr(final_schema,vd_name,vd)
+ getattr(final_schema,'fields')[vd_name]= vd
+
+ return final_schema
+
+########################################################################
+class MinionIntValidator(validators.FancyValidator):
+
+ """
+ Confirms that the input/output is of the proper type of int.
+
+ """
+ #automatically will be assigned
+ min = None
+ max = None
+
+ def _to_python(self,value,state):
+ """
+ Will check just the type here and return
+ value to be validated in validate_python
+ """
+ try:
+ value = int(value)
+ except (ValueError, TypeError):
+ raise validators.Invalid('The field should be integer',value,state)
+
+ return int(value)
+
+
+ def validate_python(self,value,state):
+ """
+ The actual validator
+ """
+ #firstly run the supers one
+ if self.min and self.min:
+ if value < self.min:
+ raise validators.Invalid('The number you entered should be bigger that %d'%(self.min),value,state)
+
+ if self.max and self.max:
+ if value > self.max:
+ raise validators.Invalid('The number you entered exceeds the %d'%(self.max),value,state)
+
+
+##################################################################
+class MinionFloatValidator(MinionIntValidator):
+
+ def _to_python(self,value,state):
+ """
+ Will check just the type here and return
+ value to be validated in validate_python
+ """
+ try:
+ value = float(value)
+ except (ValueError, TypeError):
+ raise validators.Invalid('The field should be a float',value,state)
+
+ return float(value)
+
+#################################################################
+class MinionListValidator(validators.FancyValidator):
+
+ regex_string = None
+
+ def _to_python(self,value,state):
+ """
+ Will check just the type here and return
+ value to be validated in validate_python
+ """
+ #will add more beautiful validation here after
+ #integrate the complex widgets for lists and dicts
+ if self.not_empty:
+ if len(value)==0:
+ raise validators.Invalid('Empty list passed when not_empty is set',value,state)
+
+
+ tmp = []
+ if type(tmp) != type(value):
+ value = list(value)
+ value = [list_value.strip() for list_value in value]
+
+ return value
+
+ def validate_python(self,value,state):
+ import re
+ if self.regex_string:
+ try:
+ compiled_regex = re.compile(self.regex_string)
+ except Exception,e:
+ raise validators.Invalid('The passed regex_string is not a valid expression'%self.regex_string,value,state)
+
+ for list_value in value:
+ if not re.match(compiled_regex,str(list_value)):
+ raise validators.Invalid('The %s doesnt match to the regex expression that was supplied'%list_value,value,state)
+
+ #there is no else for now :)
+
+class MinionHashValidator(validators.FancyValidator):
+
+ regex_string = None
+
+ def _to_python(self,value,state):
+ """
+ Will check just the type here and return
+ value to be validated in validate_python
+ """
+ #will add more beautiful validation here after
+ #integrate the complex widgets for lists and dicts
+ if self.not_empty:
+ if len(value)==0:
+ raise validators.Invalid('Empty hash passed when not_empty is set',value,state)
+
+ #check the type firstly
+ tmp = {}
+ if type(tmp) != type(value):
+ raise validators.Invalid('The value passed to MinionHashValidator should be a dict object',value,state)
+
+ #print value
+ return value
+
+ def validate_python(self,value,state):
+ #print value
+ import re
+ if self.regex_string:
+ try:
+ compiled_regex = re.compile(self.regex_string)
+ except Exception,e:
+ raise validators.Invalid('The passed regex_string is not a valid expression'%self.regex_string,value,state)
+ for dict_value in value.itervalues():
+ if not re.match(compiled_regex,str(dict_value)):
+ raise validators.Invalid('The %s doesnt match to the regex expression that was supplied'%dict_value,value,state)
+
+
+
+if __name__ == "__main__":
+ pass
diff --git a/test/unittest/test_client.py b/test/unittest/test_client.py
index 578dd90..1a3f7cd 100644
--- a/test/unittest/test_client.py
+++ b/test/unittest/test_client.py
@@ -13,7 +13,7 @@ import socket
class BaseTest:
# assume we are talking to localhost
-# th = socket.gethostname()
+ # th = socket.gethostname()
th = socket.getfqdn()
nforks=1
async=False
@@ -46,6 +46,12 @@ class BaseTest:
result = mod.list_methods()
self.assert_on_fault(result)
+ def test_module_get_method_args(self):
+ mod = getattr(self.overlord,self.module)
+ arg_result=mod.get_method_args()
+ print arg_result
+ self.assert_on_fault(arg_result)
+
def test_module_inventory(self):
mod = getattr(self.overlord, self.module)
result = mod.list_methods()
@@ -70,6 +76,7 @@ class BaseTest:
test_module_description.intro = True
test_module_list_methods.intro = True
test_module_inventory.intro = True
+ test_module_get_method_args.intro = True
class TestTest(BaseTest):
module = "test"
@@ -381,6 +388,8 @@ class TestSystem(BaseTest):
def test_module_description(self):
pass
+ def test_module_get_method_args(self):
+ pass
#import time
diff --git a/test/unittest/test_func_arg.py b/test/unittest/test_func_arg.py
new file mode 100644
index 0000000..f22861a
--- /dev/null
+++ b/test/unittest/test_func_arg.py
@@ -0,0 +1,151 @@
+##
+## Copyright 2007, Red Hat, Inc
+## see AUTHORS
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##
+
+#tester module for ArgCompatibility
+from func.minion.func_arg import ArgCompatibility
+
+class TestArgCompatibility:
+
+ def setUp(self):
+ #create the simple object
+ self.ac = ArgCompatibility(self.dummy_arg_getter())
+
+ def test_arg_compatibility(self):
+ """
+ Testing the method argument compatiblity
+ """
+ result = self.ac.validate_all()
+ assert result == True
+
+ self.ac = ArgCompatibility(self.dummy_no_getter())
+ result = self.ac.validate_all()
+ assert result == True
+
+ self.ac = ArgCompatibility(self.dummy_empty_args())
+ result = self.ac.validate_all()
+ assert result == True
+
+ def test_is_all_arguments_registered(self):
+ #create the dummy class
+ tc = FooClass()
+ arguments = tc.register_method()
+ assert self.ac.is_all_arguments_registered(tc,'foomethod',arguments['foomethod']['args'])==True
+ print arguments
+ assert self.ac.validate_all()==True
+
+ def dummy_no_getter(self):
+ return {}
+
+ def dummy_empty_args(self):
+ return{
+ 'myfunc':{
+ 'args':{},
+ 'description':'Cool methods here'
+ }
+ }
+
+ def dummy_arg_getter(self):
+ """
+ A simple method to test the stuff we have written for
+ arg compatiblity. I just return a dict with proper stuff
+ Should more an more tests here to see if didnt miss something
+ """
+ return {
+ 'hifunc':{
+
+ 'args':{
+ 'app':{
+ 'type':'int',
+ 'range':[0,100],
+ 'optional':False,
+ 'default' : 12
+ },
+
+ 'platform':{
+ 'type':'string',
+ 'options':["fedora","redhat","ubuntu"],
+ 'description':"Hey im a fedora fan",
+ 'default':'fedora8',
+ },
+
+ 'platform2':{
+ 'type':'string',
+ 'min_length':4,
+ 'max_length':33,
+ 'description':"Hey im a fedora fan",
+ 'default':'fedora8',
+ },
+
+
+ 'is_independent':{
+ 'type':'boolean',
+ 'default' :False,
+ 'description':'Are you independent ?',
+ 'optional':False
+ },
+
+ 'some_string':{
+ 'type':'string',
+ 'validator': "^[a-zA-Z]$",
+ 'description':'String to be validated',
+ 'default':'makkalot',
+ 'optional':False}, # validator is a re string for those whoo need better validation,so when we have options there is no need to use validator and reverse is True
+ #to define also a float we dont need it actually but maybe useful for the UI stuff.
+ 'some_float':{
+ 'type':'float',
+ 'description':'The float point value',
+ 'default':33.44,
+ 'optional':False
+ },
+
+ 'some_iterable':{
+ 'type':'list',
+ 'description':'The value and description for *arg',
+ 'optional':True, #that is where it makes sense
+ 'validator':'^[0-9]+$',#maybe useful to say it is to be a number for example
+ },
+
+ 'some_hash':{
+ 'type':'hash',
+ 'description':"Dummy desc here",
+ 'optional':True, #of course it is,
+ 'validator':"^[a-z]*$",#only for values not keys
+
+ }
+ },
+ 'description':"The dummy method description",
+ }
+ }
+
+
+class FooClass(object):
+ """
+ Sample class for testing the is_all_arguments_registered
+ method functionality ...
+ """
+
+ def foomethod(self,arg1,arg5,arg4,*arg,**kw):
+ pass
+
+ def register_method(self):
+ return{
+ 'foomethod':{
+ 'args':{
+ 'arg1':{},
+ 'arg4':{},
+ 'arg5':{},
+ 'arg':{},
+ 'kw':{},
+ }
+ }
+ }
+