diff options
-rwxr-xr-x | gen-doc.bash | 29 | ||||
-rw-r--r-- | ipa_webui/__init__.py | 24 | ||||
-rw-r--r-- | ipa_webui/controller.py | 71 | ||||
-rw-r--r-- | ipa_webui/templates/__init__.py | 21 | ||||
-rw-r--r-- | ipa_webui/templates/form.kid | 16 | ||||
-rw-r--r-- | ipa_webui/templates/main.kid | 14 | ||||
-rw-r--r-- | ipa_webui/tests/__init__.py | 21 | ||||
-rw-r--r-- | ipa_webui/tests/test_controllers.py | 70 | ||||
-rw-r--r-- | ipalib/cli.py | 58 | ||||
-rw-r--r-- | ipalib/config.py | 37 | ||||
-rw-r--r-- | ipalib/plugins/f_user.py | 4 | ||||
-rw-r--r-- | ipalib/tests/test_config.py | 101 | ||||
-rwxr-xr-x | webui-cherry.py | 47 |
13 files changed, 481 insertions, 32 deletions
diff --git a/gen-doc.bash b/gen-doc.bash index ba77547c..f2176de4 100755 --- a/gen-doc.bash +++ b/gen-doc.bash @@ -2,11 +2,10 @@ # Hackish script to generate documentation using epydoc -mod="ipalib" -d="./$mod-doc" -f="$d.tar.bz2" +sources="ipalib ipa_server ipa_webui" +out="./freeipa2-dev-doc" -init="./$mod/__init__.py" +init="./ipalib/__init__.py" echo "Looking for $init" if [[ ! -f $init ]] then @@ -16,19 +15,15 @@ fi echo "You appear to be in the project directory" # Documentation -if [[ -d $d ]] +if [[ -d $out ]] then - echo "Removing old $d directory" - rm -r $d + echo "Removing old $out directory" + rm -r $out fi -echo "Creating documentation in $d" -epydoc -v --output=$d --docformat=restructuredtext --html --no-frames $mod +echo "Creating documentation in $out" -# Tarball -if [[ -f $f ]] -then - echo "Removing old $f file" - rm $f -fi -echo "Creating tarball $f" -tar --create --bzip2 --file=$f $d +epydoc -v --parse-only --html --no-frames --include-log \ + --name="FreeIPA2 developer documentation" \ + --docformat=restructuredtext \ + --output=$out \ + $sources diff --git a/ipa_webui/__init__.py b/ipa_webui/__init__.py new file mode 100644 index 00000000..d0b43301 --- /dev/null +++ b/ipa_webui/__init__.py @@ -0,0 +1,24 @@ +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +IPA web-based user interface. +""" + +import kid +kid.enable_import() diff --git a/ipa_webui/controller.py b/ipa_webui/controller.py new file mode 100644 index 00000000..a2a270cb --- /dev/null +++ b/ipa_webui/controller.py @@ -0,0 +1,71 @@ +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Controller classes. +""" + +import simplejson +from ipalib.plugable import ReadOnly, lock + + +class Controller(ReadOnly): + exposed = True + + def __init__(self, template=None): + self.template = template + lock(self) + + def output_xhtml(self, **kw): + return self.template.serialize( + output='xhtml-strict', + format='pretty', + **kw + ) + + def output_json(self, **kw): + return simplejson.dumps(kw, sort_keys=True, indent=4) + + def __call__(self, **kw): + json = bool(kw.pop('_format', None) == 'json') + result = self.run(**kw) + assert type(result) is dict + if json or self.template is None: + return self.output_json(**result) + return self.output_xhtml(**result) + + def run(self, **kw): + return {} + + +class Command(Controller): + def __init__(self, command, template=None): + self.command = command + super(Command, self).__init__(template) + + def run(self, **kw): + return dict(command=self.command) + + +class Index(Controller): + def __init__(self, api, template=None): + self.api = api + super(Index, self).__init__(template) + + def run(self): + return dict(api=self.api) diff --git a/ipa_webui/templates/__init__.py b/ipa_webui/templates/__init__.py new file mode 100644 index 00000000..710dbb2e --- /dev/null +++ b/ipa_webui/templates/__init__.py @@ -0,0 +1,21 @@ +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Kid templates. +""" diff --git a/ipa_webui/templates/form.kid b/ipa_webui/templates/form.kid new file mode 100644 index 00000000..64015700 --- /dev/null +++ b/ipa_webui/templates/form.kid @@ -0,0 +1,16 @@ +<?xml version='1.0' encoding='utf-8'?> +<html xmlns:py="http://purl.org/kid/ns#"> + +<head> + <title>Hello</title> +</head> + +<body> + <table> + <tr py:for="param in command.params()"> + <td py:content="param.name"/> + </tr> + </table> +</body> + +</html> diff --git a/ipa_webui/templates/main.kid b/ipa_webui/templates/main.kid new file mode 100644 index 00000000..692f2b57 --- /dev/null +++ b/ipa_webui/templates/main.kid @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='utf-8'?> +<html xmlns:py="http://purl.org/kid/ns#"> + +<head> + <title>FreeIPA</title> +</head> + +<body> + <p py:for="name in api.Command"> + <a href="${name}" py:content="name"/> + </p> +</body> + +</html> diff --git a/ipa_webui/tests/__init__.py b/ipa_webui/tests/__init__.py new file mode 100644 index 00000000..c0647132 --- /dev/null +++ b/ipa_webui/tests/__init__.py @@ -0,0 +1,21 @@ +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Test the `ipa_webui` package. +""" diff --git a/ipa_webui/tests/test_controllers.py b/ipa_webui/tests/test_controllers.py new file mode 100644 index 00000000..f5944dd9 --- /dev/null +++ b/ipa_webui/tests/test_controllers.py @@ -0,0 +1,70 @@ +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Test the `ipa_webui.controller` module. +""" + +from ipa_webui import controller + + + +class test_Controller(object): + """ + Test the `controller.Controller` class. + """ + + def test_init(self): + """ + Test the `ipa_webui.controller.Controller.__init__()` method. + """ + o = controller.Controller() + assert o.template is None + template = 'The template.' + o = controller.Controller(template) + assert o.template is template + + def test_output_xhtml(self): + """ + Test the `ipa_webui.controller.Controller.output_xhtml` method. + """ + class Template(object): + def __init__(self): + self.calls = 0 + self.kw = {} + + def serialize(self, **kw): + self.calls += 1 + self.kw = kw + return dict(kw) + + d = dict(output='xhtml-strict', format='pretty') + t = Template() + o = controller.Controller(t) + assert o.output_xhtml() == d + assert t.calls == 1 + + def test_output_json(self): + """ + Test the `ipa_webui.controller.Controller.output_json` method. + """ + o = controller.Controller() + assert o.output_json() == '{}' + e = '{\n "age": 27, \n "first": "John", \n "last": "Doe"\n}' + j = o.output_json(last='Doe', first='John', age=27) + assert j == e diff --git a/ipalib/cli.py b/ipalib/cli.py index d66e1e2e..aae4e31c 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -205,6 +205,9 @@ class CLI(object): def __init__(self, api): self.__api = api + self.__all_interactive = False + self.__not_interactive = False + self.__config = None def __get_api(self): return self.__api @@ -219,6 +222,7 @@ class CLI(object): print '\nSpecial CLI commands:' for cmd in self.api.Application(): self.print_cmd(cmd) + print '\nUse the --help option to see all the global options' print '' def print_cmd(self, cmd): @@ -252,19 +256,21 @@ class CLI(object): def run(self): self.finalize() - if len(sys.argv) < 2: + (args, env_dict) = self.parse_globals() + env_dict.update(config.read_config(self.__config)) + self.api.env.update(config.generate_env(env_dict)) + if len(args) < 1: self.print_commands() - print 'Usage: ipa COMMAND' + print 'Usage: ipa [global-options] COMMAND' sys.exit(2) - self.api.env.update(config.generate_env()) - key = sys.argv[1] + key = args[0] if key not in self: self.print_commands() print 'ipa: ERROR: unknown command %r' % key sys.exit(2) self.run_cmd( self[key], - list(s.decode('utf-8') for s in sys.argv[2:]) + list(s.decode('utf-8') for s in args[1:]) ) def run_cmd(self, cmd, argv): @@ -275,7 +281,10 @@ class CLI(object): for param in cmd.params(): if param.name not in kw: if not param.required: - continue + if not self.__all_interactive: + continue + elif self.__not_interactive: + exit_error('Not enough arguments given') default = param.get_default(**kw) if default is None: prompt = '%s: ' % param.name @@ -318,11 +327,46 @@ class CLI(object): ) return parser + def parse_globals(self, argv=sys.argv[1:]): + env_dict = {} + parser = optparse.OptionParser() + parser.disable_interspersed_args() + parser.add_option('-a', dest='interactive', action='store_true', + help='Prompt for all missing options interactively') + parser.add_option('-n', dest='interactive', action='store_false', + help='Don\'t prompt for any options interactively') + parser.add_option('-c', dest='config_file', + help='Specify different configuration file') + parser.add_option('-e', dest='environment', + help='Specify or override environment variables') + parser.add_option('-v', dest='verbose', action='store_true', + help='Verbose output') + (options, args) = parser.parse_args(argv) + + if options.interactive == True: + self.__all_interactive = True + elif options.interactive == False: + self.__not_interactive = True + if options.config_file: + self.__config = options.config_file + if options.environment: + for a in options.environment.split(','): + a = a.split('=', 1) + if len(a) < 2: + parser.error('badly specified environment string,'\ + 'use var1=val1[,var2=val2]..') + env_dict[a[0].strip()] = a[1].strip() + if options.verbose != None: + env_dict.update(verbose=True) + + return (args, env_dict) + + def get_usage(self, cmd): return ' '.join(self.get_usage_iter(cmd)) def get_usage_iter(self, cmd): - yield 'Usage: %%prog %s' % to_cli(cmd.name) + yield 'Usage: %%prog [global-options] %s' % to_cli(cmd.name) for arg in cmd.args(): name = to_cli(arg.name).upper() if arg.multivalue: diff --git a/ipalib/config.py b/ipalib/config.py index f327cab7..a0a33b40 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -17,7 +17,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +from ConfigParser import SafeConfigParser, ParsingError import types +import os DEFAULT_CONF='/etc/ipa/ipa.conf' @@ -26,7 +28,8 @@ def generate_env(d={}): server_context = False, query_dns = True, verbose = False, - servers = LazyIter(get_servers), + interactive = True, + server = LazyIter(get_servers), realm = LazyProp(get_realm), domain = LazyProp(get_domain), ) @@ -68,11 +71,33 @@ class LazyIter(LazyProp): yield item -def read_config(file=DEFAULT_CONF): - assert isinstance(file, basestring) - # open the file and read configuration, return a dict - # for now, these are here just for testing purposes - return dict(servers="server.ipatest.com", realm="IPATEST.COM") +def read_config(config_file=None): + assert config_file == None or isinstance(config_file, (basestring, file)) + + parser = SafeConfigParser() + if config_file == None: + files = [DEFAULT_CONF, os.path.expanduser('~/.ipa.conf')] + else: + files = [config_file] + + for f in files: + try: + if isinstance(f, file): + parser.readfp(f) + else: + parser.read(f) + except ParsingError: + print "Can't read %s" % f + + ret = {} + if parser.has_section('defaults'): + for name, value in parser.items('defaults'): + value = tuple(elem.strip() for elem in value.split(',')) + if len(value) == 1: + value = value[0] + ret[name] = value + + return ret # these functions are here just to "emulate" dns resolving for now diff --git a/ipalib/plugins/f_user.py b/ipalib/plugins/f_user.py index e560d03c..b006c24b 100644 --- a/ipalib/plugins/f_user.py +++ b/ipalib/plugins/f_user.py @@ -37,10 +37,10 @@ class envtest(frontend.Command): print "Environment variables:" for var in api.env: val = api.env[var] - if var is 'servers': + if var is 'server': print "" print " Servers:" - for item in api.env.servers: + for item in api.env.server: print " %s" % item print "" else: diff --git a/ipalib/tests/test_config.py b/ipalib/tests/test_config.py new file mode 100644 index 00000000..de7d4c22 --- /dev/null +++ b/ipalib/tests/test_config.py @@ -0,0 +1,101 @@ +# Authors: +# Martin Nagy <mnagy@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.config` module. +""" + +import types + +from tstutil import raises +from ipalib import config + + +def test_generate_env(): + """ + Test the `config.generate_env` function + """ + + # Make sure we don't overwrite any properties + env = dict( + query_dns = False, + server = ('first', 'second'), + realm = 'myrealm', + ) + d = config.generate_env(env) + assert d['query_dns'] == False + + # Make sure the servers is overwrote properly (that it is still LazyProp) + iter = d['server'].get_value() + assert iter.next() == 'first' + assert iter.next() == 'second' + + +def test_LazyProp(): + """ + Test the `config.LazyProp` class + """ + + def dummy(): + return 1 + + # Basic sanity testing with no initial value + prop = config.LazyProp(dummy) + assert prop.get_value() == 1 + prop.set_value(2) + assert prop.get_value() == 2 + + # Basic sanity testing with initial value + prop = config.LazyProp(dummy, 3) + assert prop.get_value() == 3 + prop.set_value(4) + assert prop.get_value() == 4 + + +def test_LazyIter(): + """ + Test the `config.LazyIter` class + """ + + def dummy(): + yield 1 + yield 2 + + # Basic sanity testing with no initial value + prop = config.LazyIter(dummy) + iter = prop.get_value() + assert iter.next() == 1 + assert iter.next() == 2 + raises(StopIteration, iter.next) + + # Basic sanity testing with initial value + prop = config.LazyIter(dummy, 0) + iter = prop.get_value() + assert iter.next() == 0 + assert iter.next() == 1 + assert iter.next() == 2 + raises(StopIteration, iter.next) + + +def test_read_config(): + """ + Test the `config.read_config` class + """ + + raises(AssertionError, config.read_config, 1) diff --git a/webui-cherry.py b/webui-cherry.py new file mode 100755 index 00000000..985a838b --- /dev/null +++ b/webui-cherry.py @@ -0,0 +1,47 @@ +#!/usr/bin/python + +# Authors: Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +A web-UI test server using cherrypy. +""" + +from cherrypy import expose, config, quickstart +from ipa_webui.templates import form, main +from ipa_webui import controller +from ipalib import api +from ipalib import load_plugins + + +api.finalize() + + +class root(object): + index = controller.Index(api, main) + + def __init__(self): + for cmd in api.Command(): + ctr = controller.Command(cmd, form) + setattr(self, cmd.name, ctr) + + + + + +quickstart(root()) |