From 58746226d4b36bc40de91d4d1dd283e9faaff639 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Fri, 12 Feb 2010 16:34:21 -0500 Subject: Use the Output tuple to determine the order of output The attributes displayed is now dependant upon their definition in a Param. This enhances that, giving some level of control over how the result is displayed to the user. This also fixes displaying group membership, including failures of adding/removing entries. All tests pass now though there is still one problem. We need to return the dn as well. Once that is fixed we just need to comment out all the dn entries in the tests and they should once again pass. --- ipalib/__init__.py | 20 ++++++------- ipalib/cli.py | 13 +++++---- ipalib/frontend.py | 29 +++++++++++++------ ipalib/output.py | 50 +++++++++++++++++++++++++++++---- ipalib/plugins/baseldap.py | 12 ++++---- ipalib/plugins/cert.py | 68 +++++++++++++++++++++++++++++++++++++++++++-- ipalib/plugins/group.py | 32 +++++++++++++++++---- ipalib/plugins/host.py | 28 +++++++++++++++---- ipalib/plugins/hostgroup.py | 18 ++++++++++-- ipalib/plugins/netgroup.py | 25 +++++++++++------ ipalib/plugins/rolegroup.py | 22 +++++++++++---- ipalib/plugins/service.py | 4 +++ ipalib/plugins/taskgroup.py | 18 ++++++++++-- ipalib/plugins/user.py | 14 ++++++---- 14 files changed, 277 insertions(+), 76 deletions(-) (limited to 'ipalib') diff --git a/ipalib/__init__.py b/ipalib/__init__.py index 83956e16d..beaf0ab51 100644 --- a/ipalib/__init__.py +++ b/ipalib/__init__.py @@ -584,9 +584,9 @@ For example, say we setup a command like this: ... ... def execute(self, key, **options): ... items = dict( -... fruit='apple', -... pet='dog', -... city='Berlin', +... fruit=u'apple', +... pet=u'dog', +... city=u'Berlin', ... ) ... if key in items: ... return dict(result=items[key]) @@ -627,9 +627,9 @@ through the ``ipa`` script basically will do the following: ----------- show-items: ----------- - city = 'Berlin' - fruit = 'apple' - pet = 'dog' + city = u'Berlin' + fruit = u'apple' + pet = u'dog' ------- 3 items ------- @@ -641,9 +641,9 @@ Similarly, calling it with ``reverse=True`` would result in the following: ----------- show-items: ----------- - pet = 'dog' - fruit = 'apple' - city = 'Berlin' + pet = u'dog' + fruit = u'apple' + city = u'Berlin' -------------------------- 3 items (in reverse order) -------------------------- @@ -652,7 +652,7 @@ Lastly, providing a ``key`` would result in the following: >>> result = api.Command.show_items(u'city') >>> api.Command.show_items.output_for_cli(textui, result, 'city', reverse=False) -city = 'Berlin' +city = u'Berlin' See the `ipalib.cli.textui` plugin for a description of its methods. diff --git a/ipalib/cli.py b/ipalib/cli.py index b3980945b..d8c4b8058 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -144,7 +144,6 @@ class textui(backend.Backend): Convert a binary value to base64. We know a value is binary if it is a python str type, otherwise it is a plain string. """ - assert isinstance(value, basestring) if type(value) is str: return base64.b64encode(value) else: @@ -231,15 +230,15 @@ class textui(backend.Backend): >>> items = [ ... ('in_server', True), - ... ('mode', 'production'), + ... ('mode', u'production'), ... ] >>> ui = textui() >>> ui.print_keyval(items) in_server = True - mode = 'production' + mode = u'production' >>> ui.print_keyval(items, indent=0) in_server = True - mode = 'production' + mode = u'production' Also see `textui.print_indented`. """ @@ -354,7 +353,11 @@ class textui(backend.Backend): if isinstance(value, (list, tuple)): value = map(lambda v: self.encode_binary(v), value) value = ', '.join(value) - self.print_indented(format % (label, value), indent) + if isinstance(value, dict): + self.print_indented(format % (label, ''), indent) + self.print_entry(value, params, indent=indent+1) + else: + self.print_indented(format % (label, value), indent) def print_dashed(self, string, above=True, below=True, indent=0, dash='-'): diff --git a/ipalib/frontend.py b/ipalib/frontend.py index ae7ec9454..0a1566e49 100644 --- a/ipalib/frontend.py +++ b/ipalib/frontend.py @@ -824,16 +824,27 @@ class Command(HasParam): result = output.get('result') summary = output.get('summary') - if (summary and isinstance(result, (list, tuple, dict)) and result): - textui.print_name(self.name) - - if isinstance(result, (tuple, list)): - textui.print_entries(result, self.output_params) - elif isinstance(result, dict): - textui.print_entry(result, self.output_params) + for o in self.output: + if 'no_display' in self.output[o].flags: + continue + result = output[o] + + if isinstance(result, (tuple, list)): + textui.print_entries(result, self.output_params) + elif isinstance(result, dict): + textui.print_entry(result, self.output_params) + elif isinstance(result, unicode): + if o == 'summary': + textui.print_summary(result) + else: + textui.print_indented(result) + elif isinstance(result, bool): + # the Delete commands return a boolean indicating + # success or failure. Ignore these. + pass + elif isinstance(result, int): + textui.print_count(result, '%s %%d' % self.output[o].doc) - if isinstance(summary, unicode): - textui.print_summary(summary) class LocalOrRemote(Command): diff --git a/ipalib/output.py b/ipalib/output.py index 425ff977c..757e7155e 100644 --- a/ipalib/output.py +++ b/ipalib/output.py @@ -29,18 +29,55 @@ from plugable import ReadOnly, lock class Output(ReadOnly): """ Simple description of a member in the return value ``dict``. + + This class controls both the type of object being returned by + a command as well as how the output will be displayed. + + For example, this class defines two return results: an entry + and a value. + + >>> from ipalib import crud, output + >>> class user(crud.Update): + ... + ... has_output = ( + ... output.Entry('result'), + ... output.value, + ... ) + + The order of the values in has_output controls the order of output. + If you have values that you don't want to be printed then add + ``'no_display'`` to flags. + + The difference between ``'no_dipslay`` and ``'no_output'`` is + that ``'no_output`` will prevent a Param value from being returned + at all. ``'no_display'`` will cause the API to return a value, it + simply won't be displayed to the user. This is so some things may + be returned that while not interesting to us, but may be to others. + + >>> from ipalib import crud, output + >>> myvalue = output.Output('myvalue', unicode, + ... 'Do not print this value', flags=['no_display'], + ... ) + >>> class user(crud.Update): + ... + ... has_output = ( + ... output.Entry('result'), + ... myvalue, + ... ) """ type = None validate = None doc = None + flags = [] - def __init__(self, name, type=None, doc=None): + def __init__(self, name, type=None, doc=None, flags=[]): self.name = name if type is not None: self.type = type if doc is not None: self.doc = doc + self.flags = flags lock(self) def __repr__(self): @@ -77,28 +114,29 @@ summary = Output('summary', (unicode, NoneType), ) value = Output('value', unicode, - "The primary_key value of the entry, e.g. 'jdoe' for a user" + "The primary_key value of the entry, e.g. 'jdoe' for a user", + flags=['no_display'], ) -standard = (result, summary) +standard = (summary, result) standard_entry = ( + summary, Entry('result'), value, - summary, ) standard_list_of_entries = ( + summary, ListOfEntries('result'), Output('count', int, 'Number of entries returned'), Output('truncated', bool, 'True if not all results were returned'), - summary, ) standard_delete = ( + summary, Output('result', bool, 'True means the operation was successful'), value, - summary, ) standard_value = standard_delete diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index eeea7a6c4..964f8e5d9 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -102,7 +102,7 @@ class LDAPObject(Object): for ldap_obj_name in self.attribute_members[attr]: ldap_obj = self.api.Object[ldap_obj_name] if member.find(ldap_obj.container_dn) > 0: - new_attr = '%s %s' % (attr, ldap_obj.object_name) + new_attr = '%s_%s' % (attr, ldap_obj.object_name) entry_attrs.setdefault(new_attr, []).append( ldap_obj.get_primary_key_from_dn(member) ) @@ -521,14 +521,14 @@ class LDAPAddMember(LDAPModMember): has_output = ( output.Entry('result'), - output.Output('completed', - type=int, - doc='Number of members added', - ), output.Output('failed', type=dict, doc='Members that could not be added', ), + output.Output('completed', + type=int, + doc='Number of members added', + ), ) @@ -557,7 +557,7 @@ class LDAPAddMember(LDAPModMember): else: completed += 1 - (dn, entry_attrs) = ldap.get_entry(dn, member_dns.keys()) + (dn, entry_attrs) = ldap.get_entry(dn, member_dns.keys()+self.obj.default_attributes) (completed, dn) = self.post_callback( ldap, completed, failed, dn, entry_attrs, *keys, **options diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py index 55b3b70bb..3931d214a 100644 --- a/ipalib/plugins/cert.py +++ b/ipalib/plugins/cert.py @@ -35,12 +35,12 @@ from ipalib.plugins.virtual import * from ipalib.plugins.service import split_principal import base64 from ipalib.request import context -from ipapython import dnsclient from pyasn1.error import PyAsn1Error import logging import traceback from ipalib.request import ugettext as _ from ipalib.request import context +from ipalib.output import Output def get_serial(certificate): """ @@ -184,6 +184,25 @@ class cert_request(VirtualCommand): default=False, autofill=True ), + Str('certificate?', + label='Certificate', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('subject?', + label='Subject', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('serial_number?', + label='Serial number', + flags=['no_create', 'no_update', 'no_search'], + ), + ) + + has_output = ( + Output('result', + type=dict, + doc='Dictionary mapping variable name to value', + ), ) def execute(self, csr, **kw): @@ -268,7 +287,11 @@ class cert_request(VirtualCommand): serial = get_serial(base64.b64encode(service['usercertificate'][0])) # revoke the certificate and remove it from the service # entry before proceeding - api.Command['cert_revoke'](unicode(serial), revocation_reason=4) + try: + api.Command['cert_revoke'](unicode(serial), revocation_reason=4) + except errors.NotImplementedError: + # some CA's might not implement revoke + pass api.Command['service_mod'](principal, usercertificate=None) # Request the certificate @@ -299,7 +322,18 @@ class cert_status(VirtualCommand): Check status of a certificate signing request. """ - takes_args = ('request_id') + takes_args = ( + Str('request_id', + label='Request id', + flags=['no_create', 'no_update', 'no_search'], + ), + ) + takes_options = ( + Str('cert_request_status?', + label='Request status', + flags=['no_create', 'no_update', 'no_search'], + ), + ) operation = "certificate status" @@ -318,7 +352,19 @@ class cert_get(VirtualCommand): """ takes_args = (Str('serial_number', + label='Serial number', doc='serial number in decimal or if prefixed with 0x in hexadecimal')) + takes_options = ( + Str('certificate?', + label='Certificate', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('subject?', + label='Subject', + flags=['no_create', 'no_update', 'no_search'], + ), + ) + operation="retrieve certificate" def execute(self, serial_number): @@ -337,6 +383,12 @@ class cert_revoke(VirtualCommand): takes_args = (Str('serial_number', doc='serial number in decimal or if prefixed with 0x in hexadecimal')) + takes_options = ( + Flag('revoked?', + label='Revoked', + flags=['no_create', 'no_update', 'no_search'], + ), + ) operation = "revoke certificate" # FIXME: The default is 0. Is this really an Int param? @@ -366,6 +418,16 @@ class cert_remove_hold(VirtualCommand): takes_args = (Str('serial_number', doc='serial number in decimal or if prefixed with 0x in hexadecimal')) + takes_options = ( + Flag('unrevoked?', + label='Unrevoked', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('error_string?', + label='Error', + flags=['no_create', 'no_update', 'no_search'], + ), + ) operation = "certificate remove hold" def execute(self, serial_number, **kw): diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py index 0cc42a7a6..a04330cc8 100644 --- a/ipalib/plugins/group.py +++ b/ipalib/plugins/group.py @@ -43,12 +43,12 @@ class group(LDAPObject): attribute_names = { 'cn': 'name', 'gidnumber': 'group id', - 'member user': 'member users', - 'member group': 'member groups', - 'memberof group': 'member of groups', - 'memberof netgroup': 'member of netgroups', - 'memberof rolegroup': 'member of rolegroup', - 'memberof taskgroup': 'member of taskgroup', + 'member_user': 'member users', + 'member_group': 'member groups', + 'memberof_group': 'member of groups', + 'memberof_netgroup': 'member of netgroups', + 'memberof_rolegroup': 'member of rolegroup', + 'memberof_taskgroup': 'member of taskgroup', } attribute_members = { 'member': ['user', 'group'], @@ -74,6 +74,26 @@ class group(LDAPObject): label='GID', doc='GID (use this option to set it manually)', ), + Str('member_group?', + label='Member Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member_user?', + label='Member Users', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member?', + label='Failed Members', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('user?', + label='Users', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('group?', + label='Groups', + flags=['no_create', 'no_update', 'no_search'], + ), ) api.register(group) diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index 7e9dd90ba..c459cfe09 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -54,8 +54,8 @@ class host(LDAPObject): object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser', 'ipaservice'] # object_class_config = 'ipahostobjectclasses' default_attributes = [ - 'fqdn', 'description', 'l', 'nshostlocation', - 'nshardwareplatform', 'nsosversion', 'usercertificate', + 'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname', + 'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof', ] uuid_attribute = 'ipauniqueid' attribute_names = { @@ -70,9 +70,9 @@ class host(LDAPObject): 'enrolledby user': 'enrolled by', 'krbprincipalname': 'kerberos principal', 'ipauniqueid': 'unique identifier', - 'memberof hostgroup': 'member of hostgroups', - 'memberof netgroup': 'member of netgroups', - 'memberof rolegroup': 'member of rolegroups', + 'memberof_hostgroup': 'member of hostgroups', + 'memberof_netgroup': 'member of netgroups', + 'memberof_rolegroup': 'member of rolegroups', } attribute_members = { 'enrolledby': ['user'], @@ -93,7 +93,7 @@ class host(LDAPObject): label='Description', doc='Description of the host', ), - Str('locality?', + Str('l?', cli_name='locality', label='Locality', doc='Locality of the host (Baltimore, MD)', @@ -122,6 +122,22 @@ class host(LDAPObject): cli_name='certificate', doc='base-64 encoded server certificate', ), + Str('krbprincipalname?', + label='Principal Name', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberof_hostgroup?', + label='Member of Host Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberof_netgroup?', + label='Member Net Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberof_rolegroup?', + label='Member of Role Groups', + flags=['no_create', 'no_update', 'no_search'], + ), ) def get_dn(self, *keys, **options): diff --git a/ipalib/plugins/hostgroup.py b/ipalib/plugins/hostgroup.py index 7accca621..5a723b8c8 100644 --- a/ipalib/plugins/hostgroup.py +++ b/ipalib/plugins/hostgroup.py @@ -37,9 +37,9 @@ class hostgroup(LDAPObject): uuid_attribute = 'ipauniqueid' attribute_names = { 'cn': 'names', - 'member host': 'member hosts', - 'member hostgroup': 'member hostgroups', - 'memberof hostgroup': 'member of hostgroup', + 'member_host': 'member hosts', + 'member_hostgroup': 'member hostgroups', + 'memberof_hostgroup': 'member of hostgroup', } attribute_members = { 'member': ['host', 'hostgroup'], @@ -61,6 +61,18 @@ class hostgroup(LDAPObject): label='Description', doc='A description of this group', ), + Str('member_host?', + label='Member Hosts', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member_hostgroup?', + label='Member Host Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberof_hostgroup?', + label='Member of Hostgroups', + flags=['no_create', 'no_update', 'no_search'], + ), ) api.register(hostgroup) diff --git a/ipalib/plugins/netgroup.py b/ipalib/plugins/netgroup.py index 094a6d870..767bc1b35 100644 --- a/ipalib/plugins/netgroup.py +++ b/ipalib/plugins/netgroup.py @@ -34,22 +34,21 @@ class netgroup(LDAPObject): object_name = 'netgroup' object_name_plural = 'netgroups' object_class = ['ipaobject', 'ipaassociation', 'ipanisnetgroup'] - default_attributes = ['cn', 'description', 'member', 'memberof'] + default_attributes = ['cn', 'description', 'member', 'memberof', 'externalhost'] uuid_attribute = 'ipauniqueid' attribute_names = { 'cn': 'name', - 'member user': 'member users', - 'member group': 'member groups', - 'member host': 'member hosts', - 'member hostgroup': 'member hostgroups', - 'member netgroup': 'member netgroups', - 'memberof netgroup': 'member of netgroups', - 'externalhost': 'external hosts', + 'member_user': 'member users', + 'member_group': 'member groups', + 'member_host': 'member hosts', + 'member_hostgroup': 'member hostgroups', + 'member_netgroup': 'member netgroups', + 'memberof_netgroup': 'member of netgroups', + 'externalhost': 'externalhost', } attribute_members = { 'member': ['user', 'group', 'host', 'hostgroup', 'netgroup'], 'memberof': ['netgroup'], - 'externalhost': [], } label = _('Net Groups') @@ -72,6 +71,14 @@ class netgroup(LDAPObject): label='NIS domain name', doc='NIS domain name', ), + Str('member_host?', + label='Member Host', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('externalhost?', + label='External Host', + flags=['no_create', 'no_update', 'no_search'], + ), ) def get_dn(self, *keys, **kwargs): diff --git a/ipalib/plugins/rolegroup.py b/ipalib/plugins/rolegroup.py index ea89aa519..bf21e23c0 100644 --- a/ipalib/plugins/rolegroup.py +++ b/ipalib/plugins/rolegroup.py @@ -36,11 +36,11 @@ class rolegroup(LDAPObject): default_attributes = ['cn', 'description', 'member', 'memberof'] attribute_names = { 'cn': 'name', - 'member user': 'member users', - 'member group': 'member groups', - 'member host': 'member hosts', - 'member hostgroup': 'member hostgroups', - 'memberof taskgroup': 'member of taskgroup', + 'member_user': 'member users', + 'member_group': 'member groups', + 'member_host': 'member hosts', + 'member_hostgroup': 'member hostgroups', + 'memberof_taskgroup': 'member of taskgroup', } attribute_members = { 'member': ['user', 'group', 'host', 'hostgroup'], @@ -62,6 +62,18 @@ class rolegroup(LDAPObject): label='Description', doc='A description of this rolegroup', ), + Str('member_group?', + label='Member Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member_user?', + label='Member Users', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberof_taskgroup?', + label='Member of Task Groups', + flags=['no_create', 'no_update', 'no_search'], + ), ) api.register(rolegroup) diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py index a5de17b3a..d7c795ba3 100644 --- a/ipalib/plugins/service.py +++ b/ipalib/plugins/service.py @@ -123,12 +123,14 @@ class service(LDAPObject): takes_params = ( Str('krbprincipalname', validate_principal, + label='Principal', cli_name='principal', doc='service principal', primary_key=True, normalizer=lambda value: normalize_principal(value), ), Bytes('usercertificate?', validate_certificate, + label='Certificate', cli_name='certificate', doc='base-64 encoded server certificate', ), @@ -141,6 +143,7 @@ class service_add(LDAPCreate): """ Add new service. """ + msg_summary = _('Added service "%(value)s"') member_attributes = ['managedby'] takes_options = ( Flag('force', @@ -187,6 +190,7 @@ class service_del(LDAPDelete): """ Delete an existing service. """ + msg_summary = _('Deleted service "%(value)s"') member_attributes = ['managedby'] def pre_callback(self, ldap, dn, *keys, **options): if self.api.env.enable_ra: diff --git a/ipalib/plugins/taskgroup.py b/ipalib/plugins/taskgroup.py index a39f5c004..afdbf6561 100644 --- a/ipalib/plugins/taskgroup.py +++ b/ipalib/plugins/taskgroup.py @@ -37,9 +37,9 @@ class taskgroup(LDAPObject): default_attributes = ['cn', 'description', 'member', 'memberof'] attribute_names = { 'cn': 'name', - 'member user': 'member users', - 'member group': 'member groups', - 'member rolegroup': 'member rolegroups', + 'member_user': 'member users', + 'member_group': 'member groups', + 'member_rolegroup': 'member rolegroups', # FIXME: 'memberof ???': 'member of ???' } attribute_members = { @@ -62,6 +62,18 @@ class taskgroup(LDAPObject): label='Description', doc='taskgroup description', ), + Str('member_group?', + label='Member Groups', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member_user?', + label='Member Users', + flags=['no_create', 'no_update', 'no_search'], + ), + Str('member_rolegroup?', + label='Member Role Groups', + flags=['no_create', 'no_update', 'no_search'], + ), ) api.register(taskgroup) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index c06a9280f..00d64c7e4 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -38,7 +38,7 @@ class user(LDAPObject): object_class_config = 'ipauserobjectclasses' default_attributes = [ 'uid', 'givenname', 'sn', 'homedirectory', 'loginshell', 'ou', - 'telephonenumber', 'title', + 'telephonenumber', 'title', 'memberof', ] uuid_attribute = 'ipauniqueid' attribute_names = { @@ -53,10 +53,10 @@ class user(LDAPObject): 'krbpasswordexpiration': 'password expiration', 'uidnumber': 'uid number', 'gidnumber': 'gid number', - 'memberof group': 'member of groups', - 'memberof netgroup': 'member of netgroups', - 'memberof rolegroup': 'member of rolegroups', - 'memberof taskgroup': 'member of taskgroups', + 'memberof_group': 'member of groups', + 'memberof_netgroup': 'member of netgroups', + 'memberof_rolegroup': 'member of rolegroups', + 'memberof_taskgroup': 'member of taskgroups', 'ipauniqueid': 'unique identifier' } attribute_order = [ @@ -128,6 +128,10 @@ class user(LDAPObject): cli_name='street', label='Street address', ), + Str('memberof_group?', + label='Groups', + flags=['no_create', 'no_update', 'no_search'], + ), ) api.register(user) -- cgit