summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/cli.py13
-rw-r--r--ipalib/constants.py3
-rw-r--r--ipalib/crud.py4
-rw-r--r--ipalib/frontend.py38
-rw-r--r--ipalib/plugins/batch.py14
-rw-r--r--ipalib/plugins/ping.py3
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)