summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/__init__.py51
-rw-r--r--ipalib/cli.py4
-rw-r--r--ipalib/frontend.py18
-rw-r--r--ipalib/plugins/aci.py2
-rw-r--r--ipalib/plugins/automount.py2
-rw-r--r--ipalib/plugins/batch.py1
-rw-r--r--ipalib/plugins/cert.py12
-rw-r--r--ipalib/plugins/dns.py4
-rw-r--r--ipalib/plugins/hbacrule.py4
-rw-r--r--ipalib/plugins/internal.py2
-rw-r--r--ipalib/plugins/passwd.py2
-rw-r--r--ipalib/plugins/ping.py2
-rw-r--r--ipalib/plugins/selinuxusermap.py4
-rw-r--r--ipalib/plugins/sudorule.py4
14 files changed, 61 insertions, 51 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index 8bf37f04..aab74008 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -134,7 +134,7 @@ implement a ``run()`` method, like this:
>>> class my_command(Command):
... """My example plugin with run()."""
...
-... def run(self):
+... def run(self, **options):
... return dict(result='My run() method was called!')
...
>>> api = create_api()
@@ -174,17 +174,22 @@ For example, say you have a command plugin like this:
>>> class my_command(Command):
... """Forwarding vs. execution."""
...
-... def forward(self):
+... def forward(self, **options):
... return dict(
... result='forward(): in_server=%r' % self.env.in_server
... )
...
-... def execute(self):
+... def execute(self, **options):
... return dict(
... result='execute(): in_server=%r' % self.env.in_server
... )
...
+The ``options`` will contain a dict of command options. One option is added
+automatically: ``version``. It contains the API version of the client.
+In order to maintain forward compatibility, you should always specify the
+API version current at the time you're writing your client.
+
If ``my_command`` is loaded in a *client* context, ``forward()`` will be
called:
@@ -192,7 +197,7 @@ called:
>>> api.env.in_server = False # run() will dispatch to forward()
>>> api.register(my_command)
>>> api.finalize()
->>> api.Command.my_command() # Call your command plugin
+>>> api.Command.my_command(version=u'2.47') # Call your command plugin
{'result': 'forward(): in_server=False'}
On the other hand, if ``my_command`` is loaded in a *server* context,
@@ -202,7 +207,7 @@ On the other hand, if ``my_command`` is loaded in a *server* context,
>>> api.env.in_server = True # run() will dispatch to execute()
>>> api.register(my_command)
>>> api.finalize()
->>> api.Command.my_command() # Call your command plugin
+>>> api.Command.my_command(version=u'2.47') # Call your command plugin
{'result': 'execute(): in_server=True'}
Normally there should be no reason to override `frontend.Command.forward()`,
@@ -314,7 +319,7 @@ Second, we have our frontend plugin, the command:
>>> class my_command(Command):
... """My example command plugin."""
...
-... def execute(self):
+... def execute(self, **options):
... """Implemented against Backend.my_backend"""
... return dict(result=self.Backend.my_backend.do_stuff())
...
@@ -324,7 +329,7 @@ Lastly, we call ``api.finalize()`` and see what happens when we call
``my_command()``:
>>> api.finalize()
->>> api.Command.my_command()
+>>> api.Command.my_command(version=u'2.47')
{'result': 'my_backend.do_stuff() indeed did do stuff!'}
When not in a server context, ``my_command.execute()`` never gets called, so
@@ -337,11 +342,11 @@ example:
>>> class my_command(Command):
... """My example command plugin."""
...
-... def execute(self):
+... def execute(self, **options):
... """Same as above."""
... return dict(result=self.Backend.my_backend.do_stuff())
...
-... def forward(self):
+... def forward(self, **options):
... return dict(result='Just my_command.forward() getting called here.')
...
>>> api.register(my_command)
@@ -371,7 +376,7 @@ several other commands in a single operation. For example:
>>> class meta_command(Command):
... """My meta-command plugin."""
...
-... def execute(self):
+... def execute(self, **options):
... """Calls command_1(), command_2()"""
... msg = '%s; %s.' % (
... self.Command.command_1()['result'],
@@ -379,18 +384,18 @@ several other commands in a single operation. For example:
... )
... return dict(result=msg)
>>> class command_1(Command):
-... def execute(self):
+... def execute(self, **options):
... return dict(result='command_1.execute() called')
...
>>> class command_2(Command):
-... def execute(self):
+... def execute(self, **options):
... return dict(result='command_2.execute() called')
...
>>> api.register(meta_command)
>>> api.register(command_1)
>>> api.register(command_2)
>>> api.finalize()
->>> api.Command.meta_command()
+>>> api.Command.meta_command(version=u'2.47')
{'result': 'command_1.execute() called; command_2.execute() called.'}
Because this is quite useful, we are going to revise our golden rule somewhat:
@@ -425,9 +430,9 @@ For example:
>>> api.env.in_server = True
>>> api.register(nudge)
>>> api.finalize()
->>> api.Command.nudge(u'Jason')
+>>> api.Command.nudge(u'Jason', version=u'2.47')
{'result': u'Jason, go write more documentation!'}
->>> api.Command.nudge(u'Jason', stuff=u'unit tests')
+>>> api.Command.nudge(u'Jason', stuff=u'unit tests', version=u'2.47')
{'result': u'Jason, go write more unit tests!'}
The ``args`` and ``options`` attributes are `plugable.NameSpace` instances
@@ -438,25 +443,27 @@ containing a command's arguments and options, respectively, as you can see:
>>> api.Command.nudge.args.programmer
Str('programmer')
>>> list(api.Command.nudge.options) # Iterates through option names
-['stuff']
+['stuff', 'version']
>>> api.Command.nudge.options.stuff
Str('stuff', default=u'documentation')
>>> api.Command.nudge.options.stuff.default
u'documentation'
+The 'version' option is added to commands automatically.
+
The arguments and options must not contain colliding names. They are both
merged together into the ``params`` attribute, another `plugable.NameSpace`
instance, as you can see:
>>> api.Command.nudge.params
-NameSpace(<2 members>, sort=False)
+NameSpace(<3 members>, sort=False)
>>> list(api.Command.nudge.params) # Iterates through the param names
-['programmer', 'stuff']
+['programmer', 'stuff', 'version']
When calling a command, its positional arguments can also be provided as
keyword arguments, and in any order. For example:
->>> api.Command.nudge(stuff=u'lines of code', programmer=u'Jason')
+>>> api.Command.nudge(stuff=u'lines of code', programmer=u'Jason', version=u'2.47')
{'result': u'Jason, go write more lines of code!'}
When a command plugin is called, the values supplied for its parameters are
@@ -669,7 +676,7 @@ For example:
...
... takes_args = 'color'
...
-... def execute(self, color):
+... def execute(self, color, **options):
... """Uses self.log.error()"""
... if color not in ('red', 'blue', 'green'):
... self.log.error("I don't have %s paint!", color) # Log error
@@ -746,14 +753,14 @@ For example:
>>> class motd(Command):
... """Print message of the day."""
...
-... def execute(self):
+... def execute(self, **options):
... return dict(result=self.env.message)
...
>>> api = create_api()
>>> api.bootstrap(in_server=True, message='Hello, world!')
>>> api.register(motd)
>>> api.finalize()
->>> api.Command.motd()
+>>> api.Command.motd(version=u'2.47')
{'result': u'Hello, world!'}
Also see the `plugable.API.bootstrap_with_global_options()` method.
diff --git a/ipalib/cli.py b/ipalib/cli.py
index 3d59e4a0..f1d2f874 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -755,7 +755,7 @@ class help(frontend.Local):
super(help, self)._on_finalize()
- def run(self, key, outfile=None):
+ def run(self, key, outfile=None, **options):
if outfile is None:
outfile = sys.stdout
writer = self._writer(outfile)
@@ -872,7 +872,7 @@ class console(frontend.Command):
has_output = tuple()
- def run(self):
+ def run(self, **options):
code.interact(
'(Custom IPA interactive Python console)',
local=dict(api=self.api)
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 52ddf28c..c27ff138 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -526,7 +526,6 @@ class Command(HasParam):
yield (name, options.pop(name))
# If any options remain, they are either internal or unknown
unused_keys = set(options).difference(self.internal_options)
- unused_keys.discard('version')
if unused_keys:
raise OptionError(_('Unknown option: %(option)s'),
option=unused_keys.pop())
@@ -743,7 +742,8 @@ class Command(HasParam):
if self.api.env.in_server:
if 'version' in options:
self.verify_client_version(options['version'])
- del options['version']
+ else:
+ options['version'] = API_VERSION
return self.execute(*args, **options)
return self.forward(*args, **options)
@@ -897,12 +897,12 @@ 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
+ break
+ yield Str('version?',
+ doc=_('Client version. Used to determine if server will accept request.'),
+ exclude='webui',
+ flags=['no_option', 'no_output'],
+ )
def validate_output(self, output):
"""
@@ -1282,7 +1282,7 @@ class Method(Attribute, Command):
>>> from ipalib import create_api
>>> api = create_api()
>>> class user_add(Method):
- ... def run(self):
+ ... def run(self, **options):
... return dict(result='Added the user!')
...
>>> class user(Object):
diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py
index 7c4e8a54..a97bb48b 100644
--- a/ipalib/plugins/aci.py
+++ b/ipalib/plugins/aci.py
@@ -566,7 +566,7 @@ class aci_del(crud.Delete):
takes_options = (_prefix_option,)
- def execute(self, aciname, aciprefix):
+ def execute(self, aciname, aciprefix, **options):
"""
Execute the aci-delete operation.
diff --git a/ipalib/plugins/automount.py b/ipalib/plugins/automount.py
index 19b60905..fcda0a10 100644
--- a/ipalib/plugins/automount.py
+++ b/ipalib/plugins/automount.py
@@ -889,7 +889,7 @@ class automountkey_del(LDAPDelete):
),
)
def get_options(self):
- for option in self.takes_options:
+ for option in super(automountkey_del, self).get_options():
if option.name == 'continue':
# TODO: hide for now - remove in future major release
yield option.clone(exclude='webui',
diff --git a/ipalib/plugins/batch.py b/ipalib/plugins/batch.py
index db9c08f1..5fd5943f 100644
--- a/ipalib/plugins/batch.py
+++ b/ipalib/plugins/batch.py
@@ -95,6 +95,7 @@ class batch(Command):
a, kw = arg['params']
newkw = dict((str(k), v) for k, v in kw.iteritems())
params = api.Command[name].args_options_2_params(*a, **newkw)
+ newkw.setdefault('version', options['version'])
result = api.Command[name](*a, **newkw)
self.info(
diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py
index 51493c34..6b84c723 100644
--- a/ipalib/plugins/cert.py
+++ b/ipalib/plugins/cert.py
@@ -306,8 +306,7 @@ class cert_request(VirtualCommand):
ldap = self.api.Backend.ldap2
principal = kw.get('principal')
add = kw.get('add')
- del kw['principal']
- del kw['add']
+ request_type = kw.get('request_type')
service = None
"""
@@ -414,7 +413,8 @@ class cert_request(VirtualCommand):
api.Command['host_mod'](hostname, usercertificate=None)
# Request the certificate
- result = self.Backend.ra.request_certificate(csr, **kw)
+ result = self.Backend.ra.request_certificate(
+ csr, request_type=request_type)
cert = x509.load_certificate(result['certificate'])
result['issuer'] = unicode(cert.issuer)
result['valid_not_before'] = unicode(cert.valid_not_before_str)
@@ -596,10 +596,12 @@ class cert_revoke(VirtualCommand):
result = api.Command['cert_show'](unicode(serial_number))['result']
except errors.NotImplementedError:
pass
- if kw['revocation_reason'] == 7:
+ revocation_reason = kw['revocation_reason']
+ if revocation_reason == 7:
raise errors.CertificateOperationError(error=_('7 is not a valid revocation reason'))
return dict(
- result=self.Backend.ra.revoke_certificate(serial_number, **kw)
+ result=self.Backend.ra.revoke_certificate(
+ serial_number, revocation_reason=revocation_reason)
)
api.register(cert_revoke)
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index c329c100..61c2de32 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -2730,13 +2730,13 @@ class dnsrecord_del(LDAPUpdate):
error=_('Zone record \'%s\' cannot be deleted') \
% _dns_zone_record
)
- return self.obj.methods.delentry(*keys)
+ return self.obj.methods.delentry(*keys, version=options['version'])
result = super(dnsrecord_del, self).execute(*keys, **options)
if getattr(context, 'del_all', False) and not \
self.obj.is_pkey_zone_record(*keys):
- return self.obj.methods.delentry(*keys)
+ return self.obj.methods.delentry(*keys, version=options['version'])
return result
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
diff --git a/ipalib/plugins/hbacrule.py b/ipalib/plugins/hbacrule.py
index 0b1e8b83..8bc4c6df 100644
--- a/ipalib/plugins/hbacrule.py
+++ b/ipalib/plugins/hbacrule.py
@@ -303,7 +303,7 @@ class hbacrule_enable(LDAPQuery):
msg_summary = _('Enabled HBAC rule "%(value)s"')
has_output = output.standard_value
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)
@@ -330,7 +330,7 @@ class hbacrule_disable(LDAPQuery):
msg_summary = _('Disabled HBAC rule "%(value)s"')
has_output = output.standard_value
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)
diff --git a/ipalib/plugins/internal.py b/ipalib/plugins/internal.py
index cfb5d60f..e9fc9de4 100644
--- a/ipalib/plugins/internal.py
+++ b/ipalib/plugins/internal.py
@@ -682,7 +682,7 @@ class i18n_messages(Command):
has_output = (
Output('messages', dict, doc=_('Dict of I18N messages')),
)
- def execute(self):
+ def execute(self, **options):
return dict([("messages",json_serialize(self.messages))])
def output_for_cli(self, textui, result, *args, **options):
diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py
index 68aa3ebb..280517cd 100644
--- a/ipalib/plugins/passwd.py
+++ b/ipalib/plugins/passwd.py
@@ -88,7 +88,7 @@ class passwd(Command):
has_output = output.standard_value
msg_summary = _('Changed password for "%(value)s"')
- def execute(self, principal, password, current_password):
+ def execute(self, principal, password, current_password, **options):
"""
Execute the passwd operation.
diff --git a/ipalib/plugins/ping.py b/ipalib/plugins/ping.py
index 0da07e0b..e9dc28fe 100644
--- a/ipalib/plugins/ping.py
+++ b/ipalib/plugins/ping.py
@@ -58,7 +58,7 @@ class ping(Command):
output.summary,
)
- def execute(self):
+ def execute(self, **options):
"""
A possible enhancement would be to take an argument and echo it
back but a fixed value works for now.
diff --git a/ipalib/plugins/selinuxusermap.py b/ipalib/plugins/selinuxusermap.py
index 32c55850..60eb053a 100644
--- a/ipalib/plugins/selinuxusermap.py
+++ b/ipalib/plugins/selinuxusermap.py
@@ -394,7 +394,7 @@ class selinuxusermap_enable(LDAPQuery):
msg_summary = _('Enabled SELinux User Map "%(value)s"')
has_output = output.standard_value
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)
@@ -421,7 +421,7 @@ class selinuxusermap_disable(LDAPQuery):
msg_summary = _('Disabled SELinux User Map "%(value)s"')
has_output = output.standard_value
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)
diff --git a/ipalib/plugins/sudorule.py b/ipalib/plugins/sudorule.py
index 11108099..878033f0 100644
--- a/ipalib/plugins/sudorule.py
+++ b/ipalib/plugins/sudorule.py
@@ -321,7 +321,7 @@ api.register(sudorule_show)
class sudorule_enable(LDAPQuery):
__doc__ = _('Enable a Sudo Rule.')
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)
@@ -345,7 +345,7 @@ api.register(sudorule_enable)
class sudorule_disable(LDAPQuery):
__doc__ = _('Disable a Sudo Rule.')
- def execute(self, cn):
+ def execute(self, cn, **options):
ldap = self.obj.backend
dn = self.obj.get_dn(cn)