diff options
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/cli.py | 13 | ||||
-rw-r--r-- | ipalib/constants.py | 3 | ||||
-rw-r--r-- | ipalib/crud.py | 4 | ||||
-rw-r--r-- | ipalib/frontend.py | 38 | ||||
-rw-r--r-- | ipalib/plugins/batch.py | 14 | ||||
-rw-r--r-- | ipalib/plugins/ping.py | 3 |
6 files changed, 69 insertions, 6 deletions
diff --git a/ipalib/cli.py b/ipalib/cli.py index c5fea8f28..c634d4909 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -32,7 +32,14 @@ import fcntl import termios import struct import base64 -import default_encoding_utf8 +try: + import default_encoding_utf8 +except ImportError: + # This is a chicken-and-egg problem. The api can't be imported unless + # this is already installed and since it is installed with IPA therein + # lies the problem. Skip it for now so ipalib can be imported in-tree + # even in cases that IPA isn't installed on the dev machine. + pass import frontend import backend @@ -42,6 +49,7 @@ from errors import PublicError, CommandError, HelpError, InternalError, NoSuchNa from constants import CLI_TAB from parameters import Password, Bytes, File from text import _ +from ipapython.version import API_VERSION def to_cli(name): @@ -884,6 +892,7 @@ class cli(backend.Executioner): if not isinstance(cmd, frontend.Local): self.create_context() kw = self.parse(cmd, argv) + kw['version'] = API_VERSION if self.env.interactive: self.prompt_interactively(cmd, kw) self.load_files(cmd, kw) @@ -931,6 +940,8 @@ class cli(backend.Executioner): dest=option.name, help=unicode(option.doc), ) + if 'no_option' in option.flags: + continue if option.password and self.env.interactive: kw['action'] = 'store_true' elif option.type is bool and option.autofill: diff --git a/ipalib/constants.py b/ipalib/constants.py index b6aa85466..9a2f19826 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -140,6 +140,9 @@ DEFAULT_CONFIG = ( ('enable_ra', False), ('ra_plugin', 'selfsign'), + # Used when verifying that the API hasn't changed. Not for production. + ('validate_api', False), + # ******************************************************** # The remaining keys are never set from the values here! # ******************************************************** diff --git a/ipalib/crud.py b/ipalib/crud.py index 6df3c733d..76d57daef 100644 --- a/ipalib/crud.py +++ b/ipalib/crud.py @@ -76,7 +76,7 @@ us: >>> list(api.Command.user_add.args) ['login'] >>> list(api.Command.user_add.options) -['first', 'last', 'all', 'raw'] +['first', 'last', 'all', 'raw', 'version'] Notice that ``'ipauniqueid'`` isn't included in the options for our ``user_add`` plugin. This is because of the ``'no_create'`` flag we used when defining the @@ -94,7 +94,7 @@ class created them for us: >>> list(api.Command.user_show.args) ['login'] >>> list(api.Command.user_show.options) -['all', 'raw'] +['all', 'raw', 'version'] As you can see, `Retrieve` plugins take a single argument (the primary key) and no options. If needed, you can still specify options for your `Retrieve` plugin diff --git a/ipalib/frontend.py b/ipalib/frontend.py index 7177bd185..eeed3980c 100644 --- a/ipalib/frontend.py +++ b/ipalib/frontend.py @@ -30,9 +30,11 @@ from util import make_repr from output import Output, Entry, ListOfEntries from text import _, ngettext -from errors import ZeroArgumentError, MaxArgumentError, OverlapError, RequiresRoot +from errors import ZeroArgumentError, MaxArgumentError, OverlapError, RequiresRoot, VersionError, RequirementError from errors import InvocationError from constants import TYPE_ERROR +from ipapython.version import API_VERSION +from distutils import version RULE_FLAG = 'validation_rule' @@ -412,6 +414,8 @@ class Command(HasParam): self.info( '%s(%s)', self.name, ', '.join(self._repr_iter(**params)) ) + if not self.api.env.in_server and 'version' not in params: + params['version'] = API_VERSION self.validate(**params) (args, options) = self.params_2_args_options(**params) ret = self.run(*args, **options) @@ -680,6 +684,30 @@ class Command(HasParam): value = kw.get(param.name, None) param.validate(value, self.env.context) + def verify_client_version(self, client_version): + """ + Compare the version the client provided to the version of the + server. + + If the client major version does not match then return an error. + If the client minor version is less than or equal to the server + then let the request proceed. + """ + ver = version.LooseVersion(client_version) + if len(ver.version) < 2: + raise VersionError(cver=ver.version, sver=server_ver.version, server= self.env.xmlrpc_uri) + client_major = ver.version[0] + client_minor = ver.version[1] + + server_ver = version.LooseVersion(API_VERSION) + server_major = server_ver.version[0] + server_minor = server_ver.version[1] + + if server_major != client_major: + raise VersionError(cver=client_version, sver=API_VERSION, server=self.env.xmlrpc_uri) + if client_minor > server_minor: + raise VersionError(cver=client_version, sver=API_VERSION, server=self.env.xmlrpc_uri) + def run(self, *args, **options): """ Dispatch to `Command.execute` or `Command.forward`. @@ -693,6 +721,9 @@ class Command(HasParam): performs is executed remotely. """ if self.api.env.in_server: + if 'version' in options: + self.verify_client_version(options['version']) + del options['version'] return self.execute(*args, **options) return self.forward(*args, **options) @@ -826,6 +857,11 @@ class Command(HasParam): exclude='webui', flags=['no_output'], ) + yield Str('version?', + doc=_('Client version. Used to determine if server will accept request.'), + exclude='webui', + flags=['no_option', 'no_output'], + ) return def validate_output(self, output): diff --git a/ipalib/plugins/batch.py b/ipalib/plugins/batch.py index 07c50600a..f6f662f28 100644 --- a/ipalib/plugins/batch.py +++ b/ipalib/plugins/batch.py @@ -33,7 +33,7 @@ where the contenst of the file batch_request.json follow the below example {"method":"user_show","params":[["admin"],{"all":true}]} ],{}],"id":1} -THe format of the response is nested the same way. At the top you will see +The format of the response is nested the same way. At the top you will see "error": null, "id": 1, "result": { @@ -51,6 +51,7 @@ from ipalib import Str, List from ipalib.output import Output from ipalib import output from ipalib.text import _ +from ipapython.version import API_VERSION class batch(Command): INTERNAL = True @@ -61,6 +62,17 @@ class batch(Command): ), ) + take_options = ( + Str('version', + cli_name='version', + doc=_('Client version. Used to determine if server will accept request.'), + exclude='webui', + flags=['no_option', 'no_output'], + default=API_VERSION, + autofill=True, + ) + ) + has_output = ( Output('count', int, doc=_('')), Output('results', list, doc=_('')) diff --git a/ipalib/plugins/ping.py b/ipalib/plugins/ping.py index 634c4f875..c2f9b6b28 100644 --- a/ipalib/plugins/ping.py +++ b/ipalib/plugins/ping.py @@ -23,6 +23,7 @@ Ping the remote IPA server from ipalib import api from ipalib import Command from ipalib import output +from ipapython.version import VERSION, API_VERSION class ping(Command): """ @@ -37,6 +38,6 @@ class ping(Command): A possible enhancement would be to take an argument and echo it back but a fixed value works for now. """ - return dict(summary=u'pong') + return dict(summary=u'IPA server version %s. API version %s' % (VERSION, API_VERSION)) api.register(ping) |