schema: add object class schema

Support object classes defined by object plugins in API schema.

Added new commands `class-show` and `class-find` to retrieve information
about object classes. `param-show` and `param-find` now support both
commands and classes.

https://fedorahosted.org/freeipa/ticket/4739

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Jan Cholasta 2016-06-16 13:21:17 +02:00
parent 3ec7a52aea
commit ec1b3e71b2
4 changed files with 267 additions and 97 deletions

24
API.txt
View File

@ -843,6 +843,26 @@ option: Str('version?')
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: class_find
args: 1,4,4
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('version?')
output: Output('count', type=[<type 'int'>])
output: ListOfEntries('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('truncated', type=[<type 'bool'>])
command: class_show
args: 1,3,3
arg: Str('name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('version?')
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: command_defaults
args: 1,3,1
arg: Str('name')
@ -3324,7 +3344,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: param_find
args: 2,4,4
arg: Str('commandname', cli_name='command')
arg: Str('metaobjectname', cli_name='metaobject')
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('pkey_only?', autofill=True, default=False)
@ -3336,7 +3356,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('truncated', type=[<type 'bool'>])
command: param_show
args: 2,3,3
arg: Str('commandname', cli_name='command')
arg: Str('metaobjectname', cli_name='metaobject')
arg: Str('name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)

View File

@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=188
# Last change: mbabinsk - extend server-del to perform full master removal
IPA_API_VERSION_MINOR=189
# Last change: schema: add object class schema

View File

@ -10,8 +10,8 @@ import types
import six
from ipaclient.plugins.rpcclient import rpcclient
from ipalib import Command
from ipalib import parameters, plugable
from ipalib.frontend import Command, Object
from ipalib.output import Output
from ipalib.parameters import Bool, DefaultFrom, Flag, Password, Str
from ipalib.text import ConcatenatedLazyText
@ -226,8 +226,23 @@ def _create_command(schema):
return command
class _LazySchemaCommand(object):
def __init__(self, schema):
def _create_class(schema):
cls = {}
cls['name'] = str(schema['name'])
if 'doc' in schema:
cls['doc'] = ConcatenatedLazyText(schema['doc'])
if 'topic_topic' in schema:
cls['topic'] = str(schema['topic_topic'])
else:
cls['topic'] = None
cls['takes_params'] = tuple(_create_param(s) for s in schema['params'])
return cls
class _LazySchemaPlugin(object):
def __init__(self, base, schema):
self.__base = base
self.__schema = schema
self.__class = None
self.__module__ = None
@ -236,21 +251,32 @@ class _LazySchemaCommand(object):
def name(self):
return str(self.__schema['name'])
bases = (_SchemaCommand,)
@property
def bases(self):
if self.__base is Command:
return (_SchemaCommand,)
else:
return (self.__base,)
def __call__(self, api):
if self.__class is None:
command = _create_command(self.__schema)
name = command.pop('name')
command = type(name, (_SchemaCommand,), command)
command.__module__ = self.__module__
self.__class = command
if self.__base is Command:
metaobject = _create_command(self.__schema)
else:
metaobject = _create_class(self.__schema)
metaobject = type(self.name, self.bases, metaobject)
metaobject.__module__ = self.__module__
self.__class = metaobject
return self.__class(api)
def _create_commands(schema):
return [_LazySchemaCommand(s) for s in schema]
return [_LazySchemaPlugin(Command, s) for s in schema]
def _create_classes(schema):
return [_LazySchemaPlugin(Object, s) for s in schema]
def _create_topic(schema):
@ -289,6 +315,7 @@ def get_package(api):
client.disconnect()
commands = _create_commands(schema['commands'])
classes = _create_classes(schema['classes'])
topics = _create_topics(schema['topics'])
package = types.ModuleType(package_name)
@ -308,6 +335,11 @@ def get_package(api):
command = module.register()(command)
setattr(module, command.name, command)
for cls in classes:
cls.__module__ = module_name
cls = module.register()(cls)
setattr(module, cls.name, command)
for topic in topics:
name = topic.pop('name')
module_name = '.'.join((package_name, name))

View File

@ -8,6 +8,7 @@ import sys
import six
from .baseldap import LDAPObject
from ipalib import errors
from ipalib.crud import PKQuery, Retrieve, Search
from ipalib.frontend import Command, Local, Method, Object
@ -130,8 +131,41 @@ class MetaSearch(BaseMetaSearch):
@register()
class command(MetaObject):
takes_params = BaseMetaObject.takes_params + (
class metaobject(MetaObject):
takes_params = MetaObject.takes_params + (
Str(
'params_param*',
label=_("Parameters"),
flags={'no_search'},
),
)
def _iter_params(self, metaobj):
raise NotImplementedError()
def _get_obj(self, metaobj, all=False, **kwargs):
obj = dict()
obj['name'] = unicode(metaobj.name)
if all:
params = [unicode(p.name) for p in self._iter_params(metaobj)]
if params:
obj['params_param'] = params
return obj
class metaobject_show(MetaRetrieve):
pass
class metaobject_find(MetaSearch):
pass
@register()
class command(metaobject):
takes_params = metaobject.takes_params + (
Str(
'args_param*',
label=_("Arguments"),
@ -154,42 +188,49 @@ class command(MetaObject):
),
)
def _get_obj(self, command, **kwargs):
obj = dict()
obj['name'] = unicode(command.name)
def _iter_params(self, cmd):
for arg in cmd.args():
yield arg
for option in cmd.options():
if option.name == 'version':
continue
yield option
if command.doc:
obj['doc'] = unicode(command.doc)
def _get_obj(self, cmd, **kwargs):
obj = super(command, self)._get_obj(cmd, **kwargs)
if command.topic:
if cmd.doc:
obj['doc'] = unicode(cmd.doc)
if cmd.topic:
try:
topic = self.api.Object.topic.retrieve(unicode(command.topic))
topic = self.api.Object.topic.retrieve(unicode(cmd.topic))
except errors.NotFound:
pass
else:
obj['topic_topic'] = topic['name']
if command.NO_CLI:
if cmd.NO_CLI:
obj['no_cli'] = True
if len(command.args):
obj['args_param'] = tuple(unicode(n) for n in command.args)
if len(cmd.args):
obj['args_param'] = tuple(unicode(n) for n in cmd.args)
if len(command.options):
if len(cmd.options):
obj['options_param'] = tuple(
unicode(n) for n in command.options if n != 'version')
unicode(n) for n in cmd.options if n != 'version')
if len(command.output_params):
if len(cmd.output_params):
obj['output_params_param'] = tuple(
unicode(n) for n in command.output_params)
unicode(n) for n in cmd.output_params)
return obj
def _retrieve(self, name, **kwargs):
try:
command = self.api.Command[name]
if not isinstance(command, Local):
return command
cmd = self.api.Command[name]
if not isinstance(cmd, Local):
return cmd
except KeyError:
pass
@ -200,18 +241,18 @@ class command(MetaObject):
)
def _search(self, **kwargs):
for command in self.api.Command():
if not isinstance(command, Local):
yield command
for cmd in self.api.Command():
if not isinstance(cmd, Local):
yield cmd
@register()
class command_show(MetaRetrieve):
class command_show(metaobject_show):
__doc__ = _("Display information about a command.")
@register()
class command_find(MetaSearch):
class command_find(metaobject_find):
__doc__ = _("Search for commands.")
@ -235,6 +276,52 @@ class command_defaults(PKQuery):
return dict(result=result)
@register()
class class_(metaobject):
name = 'class'
def _iter_params(self, metaobj):
for param in metaobj.params():
yield param
if isinstance(metaobj, LDAPObject) and 'show' in metaobj.methods:
members = (
'{}_{}'.format(attr_name, obj_name)
for attr_name, obj_names in metaobj.attribute_members.items()
for obj_name in obj_names)
passwords = (name for _, name in metaobj.password_attributes)
names = set(itertools.chain(members, passwords))
for param in metaobj.methods.show.output_params():
if param.name in names and param.name not in metaobj.params:
yield param
def _retrieve(self, name, **kwargs):
try:
return self.api.Object[name]
except KeyError:
pass
raise errors.NotFound(
reason=_("%(pkey)s: %(oname)s not found") % {
'pkey': name, 'oname': self.name,
}
)
def _search(self, **kwargs):
return self.api.Object()
@register()
class class_show(metaobject_show):
__doc__ = _("Display information about a class.")
@register()
class class_find(metaobject_find):
__doc__ = _("Search for classes.")
@register()
class topic_(MetaObject):
name = 'topic'
@ -328,13 +415,17 @@ class BaseParam(BaseMetaObject):
),
)
def _split_search_args(self, commandname, criteria=None):
return [commandname], criteria
@property
def parent(self):
raise AttributeError('parent')
def _split_search_args(self, parent_name, criteria=None):
return [parent_name], criteria
class BaseParamMethod(Method):
def get_args(self):
parent = self.api.Object.command
parent = self.obj.parent
parent_key = parent.primary_key
yield parent_key.clone_rename(
parent.name + parent_key.name,
@ -449,6 +540,11 @@ class param(BaseParam):
label=_("No option"),
flags={'no_search'},
),
Bool(
'no_output?',
label=_("No output"),
flags={'no_search'},
),
Bool(
'suppress_empty?',
label=_("Suppress empty"),
@ -461,7 +557,13 @@ class param(BaseParam):
),
)
def _get_obj(self, param, **kwargs):
@property
def parent(self):
return self.api.Object.metaobject
def _get_obj(self, metaobj_param, **kwargs):
metaobj, param = metaobj_param
obj = dict()
obj['name'] = unicode(param.name)
@ -480,56 +582,62 @@ class param(BaseParam):
obj['sensitive'] = True
for key, value in param._Param__clonekw.items():
if key in ('alwaysask',
'autofill',
'confirm',
'sortorder'):
obj[key] = value
elif key in ('cli_metavar',
'cli_name',
'doc',
'hint',
'label',
'option_group'):
if key in ('doc',
'label'):
obj[key] = unicode(value)
elif key == 'default':
if param.multivalue:
obj[key] = [unicode(v) for v in value]
else:
obj[key] = [unicode(value)]
elif key == 'default_from':
obj['default_from_param'] = list(unicode(k)
for k in value.keys)
elif key in ('deprecated_cli_aliases',
'exclude',
elif key in ('exclude',
'include'):
obj[key] = list(unicode(v) for v in value)
elif key in ('exponential',
'normalizer',
'only_absolute',
'precision'):
obj['no_convert'] = True
if isinstance(metaobj, Command):
if key in ('alwaysask',
'autofill',
'confirm',
'sortorder'):
obj[key] = value
elif key in ('cli_metavar',
'cli_name',
'hint',
'option_group'):
obj[key] = unicode(value)
elif key == 'default':
if param.multivalue:
obj[key] = [unicode(v) for v in value]
else:
obj[key] = [unicode(value)]
elif key == 'default_from':
obj['default_from_param'] = list(unicode(k)
for k in value.keys)
elif key == 'deprecated_cli_aliases':
obj[key] = list(unicode(v) for v in value)
elif key in ('exponential',
'normalizer',
'only_absolute',
'precision'):
obj['no_convert'] = True
for flag in (param.flags or []):
if flag in ('dnsrecord_extra',
'dnsrecord_part',
'no_option',
if flag in ('no_output',
'suppress_empty'):
obj[flag] = True
if isinstance(metaobj, Command):
if flag in ('dnsrecord_extra',
'dnsrecord_part',
'no_option'):
obj[flag] = True
return obj
def _retrieve(self, commandname, name, **kwargs):
command = self.api.Command[commandname]
def _retrieve(self, metaobjectname, name, **kwargs):
try:
metaobj = self.api.Command[metaobjectname]
plugin = self.api.Object['command']
except KeyError:
metaobj = self.api.Object[metaobjectname]
plugin = self.api.Object['class']
if name != 'version':
try:
return command.params[name]
except KeyError:
try:
return command.output_params[name]
except KeyError:
pass
for param in plugin._iter_params(metaobj):
if param.name == name:
return metaobj, param
raise errors.NotFound(
reason=_("%(pkey)s: %(oname)s not found") % {
@ -537,15 +645,15 @@ class param(BaseParam):
}
)
def _search(self, commandname, **kwargs):
command = self.api.Command[commandname]
def _search(self, metaobjectname, **kwargs):
try:
metaobj = self.api.Command[metaobjectname]
plugin = self.api.Object['command']
except KeyError:
metaobj = self.api.Object[metaobjectname]
plugin = self.api.Object['class']
result = itertools.chain(
(p for p in command.params() if p.name != 'version'),
(p for p in command.output_params()
if p.name not in command.params))
return result
return ((metaobj, param) for param in plugin._iter_params(metaobj))
@register()
@ -568,8 +676,12 @@ class output(BaseParam):
),
)
def _get_obj(self, command_output, **kwargs):
command, output = command_output
@property
def parent(self):
return self.api.Object.command
def _get_obj(self, cmd_output, **kwargs):
cmd, output = cmd_output
required = True
multivalue = False
@ -577,8 +689,8 @@ class output(BaseParam):
type_type = dict
multivalue = isinstance(output, ListOfEntries)
elif isinstance(output, (PrimaryKey, ListOfPrimaryKeys)):
if getattr(command, 'obj', None) and command.obj.primary_key:
type_type = command.obj.primary_key.type
if getattr(cmd, 'obj', None) and cmd.obj.primary_key:
type_type = cmd.obj.primary_key.type
else:
type_type = type(None)
multivalue = isinstance(output, ListOfPrimaryKeys)
@ -618,9 +730,9 @@ class output(BaseParam):
return obj
def _retrieve(self, commandname, name, **kwargs):
command = self.api.Command[commandname]
cmd = self.api.Command[commandname]
try:
return (command, command.output[name])
return (cmd, cmd.output[name])
except KeyError:
raise errors.NotFound(
reason=_("%(pkey)s: %(oname)s not found") % {
@ -629,8 +741,8 @@ class output(BaseParam):
)
def _search(self, commandname, **kwargs):
command = self.api.Command[commandname]
return ((command, output) for output in command.output())
cmd = self.api.Command[commandname]
return ((cmd, output) for output in cmd.output())
@register()
@ -656,11 +768,17 @@ class schema(Command):
command['output'] = list(
self.api.Object.output.search(name, **kwargs))
classes = list(self.api.Object['class'].search(**kwargs))
for cls in classes:
cls['params'] = list(
self.api.Object.param.search(cls['name'], **kwargs))
topics = list(self.api.Object.topic.search(**kwargs))
schema = dict()
schema['version'] = API_VERSION
schema['commands'] = commands
schema['classes'] = classes
schema['topics'] = topics
return dict(result=schema)