mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Internationalization for public errors
Currently, we throw many public exceptions without proper i18n. Wrap natural-language error messages in _() so they can be translated. In the service plugin, raise NotFound errors using handle_not_found helper so the error message contains the offending service. Use ScriptError instead of NotFoundError in bindinstance install. https://fedorahosted.org/freeipa/ticket/1953
This commit is contained in:
committed by
Martin Kosek
parent
4f03aed5e6
commit
a95eaeac8e
@@ -621,7 +621,7 @@ class textui(backend.Backend):
|
||||
|
||||
counter = len(entries)
|
||||
if counter == 0:
|
||||
raise NotFound(reason="No matching entries found")
|
||||
raise NotFound(reason=_("No matching entries found"))
|
||||
|
||||
i = 1
|
||||
for e in entries:
|
||||
|
||||
@@ -400,7 +400,7 @@ class NetworkError(PublicError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise NetworkError(uri='ldap://localhost:389', error=u'Connection refused')
|
||||
>>> raise NetworkError(uri='ldap://localhost:389', error=_(u'Connection refused'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
NetworkError: cannot connect to 'ldap://localhost:389': Connection refused
|
||||
@@ -434,7 +434,7 @@ class XMLRPCMarshallError(PublicError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise XMLRPCMarshallError(error='int exceeds XML-RPC limits')
|
||||
>>> raise XMLRPCMarshallError(error=_('int exceeds XML-RPC limits'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
XMLRPCMarshallError: error marshalling data for XML-RPC transport: int exceeds XML-RPC limits
|
||||
@@ -476,7 +476,7 @@ class KerberosError(AuthenticationError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise KerberosError(major='Unspecified GSS failure. Minor code may provide more information', minor='No credentials cache found')
|
||||
>>> raise KerberosError(major=_('Unspecified GSS failure. Minor code may provide more information'), minor=_('No credentials cache found'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
KerberosError: Kerberos error: Unspecified GSS failure. Minor code may provide more information/No credentials cache found
|
||||
@@ -754,7 +754,7 @@ class ConversionError(InvocationError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ConversionError(name='age', error=u'must be an integer')
|
||||
>>> raise ConversionError(name='age', error=_(u'must be an integer'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ConversionError: invalid 'age': must be an integer
|
||||
@@ -770,7 +770,7 @@ class ValidationError(InvocationError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ValidationError(name='sn', error=u'can be at most 128 characters')
|
||||
>>> raise ValidationError(name='sn', error=_(u'can be at most 128 characters'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: invalid 'sn': can be at most 128 characters
|
||||
@@ -897,7 +897,7 @@ class MalformedServicePrincipal(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise MalformedServicePrincipal(reason='missing service')
|
||||
>>> raise MalformedServicePrincipal(reason=_('missing service'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
MalformedServicePrincipal: Service principal is not of the form: service/fully-qualified host name: missing service
|
||||
@@ -1073,7 +1073,7 @@ class Base64DecodeError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise Base64DecodeError(reason='Incorrect padding')
|
||||
>>> raise Base64DecodeError(reason=_('Incorrect padding'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
Base64DecodeError: Base64 decoding failed: Incorrect padding
|
||||
@@ -1089,10 +1089,10 @@ class RemoteRetrieveError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise RemoteRetrieveError(reason="Error: Failed to get certificate chain.")
|
||||
>>> raise RemoteRetrieveError(reason=_("Failed to get certificate chain."))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
RemoteRetrieveError: Error: Failed to get certificate chain.
|
||||
RemoteRetrieveError: Failed to get certificate chain.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1184,7 +1184,7 @@ class FileError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise FileError(reason="cannot write file \'test\'")
|
||||
>>> raise FileError(reason=_("cannot write file \'test\'"))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
FileError: cannot write file 'test'
|
||||
@@ -1232,7 +1232,7 @@ class ReverseMemberError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ReverseMemberError(verb='added', exc="Group 'foo' not found.")
|
||||
>>> raise ReverseMemberError(verb=_('added'), exc=_("Group 'foo' not found."))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ReverseMemberError: A problem was encountered when verifying that all members were added: Group 'foo' not found.
|
||||
@@ -1397,7 +1397,7 @@ class DatabaseError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise DatabaseError(desc="Can't contact LDAP server", info='Info goes here')
|
||||
>>> raise DatabaseError(desc=_("Can't contact LDAP server"), info=_('Info goes here'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
DatabaseError: Can't contact LDAP server: Info goes here
|
||||
@@ -1428,7 +1428,7 @@ class ObjectclassViolation(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ObjectclassViolation(info='attribute "krbPrincipalName" not allowed')
|
||||
>>> raise ObjectclassViolation(info=_('attribute "krbPrincipalName" not allowed'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ObjectclassViolation: attribute "krbPrincipalName" not allowed
|
||||
@@ -1491,7 +1491,7 @@ class BadSearchFilter(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise BadSearchFilter(info='invalid syntax')
|
||||
>>> raise BadSearchFilter(info=_('invalid syntax'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
BadSearchFilter: Bad search filter invalid syntax
|
||||
@@ -1515,7 +1515,7 @@ class CertificateOperationError(CertificateError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise CertificateOperationError(error=u'bad serial number')
|
||||
>>> raise CertificateOperationError(error=_(u'bad serial number'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
CertificateOperationError: Certificate operation cannot be completed: bad serial number
|
||||
@@ -1531,7 +1531,7 @@ class CertificateFormatError(CertificateError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise CertificateFormatError(error=u'improperly formated DER-encoded certificate')
|
||||
>>> raise CertificateFormatError(error=_(u'improperly formated DER-encoded certificate'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
CertificateFormatError: Certificate format error: improperly formated DER-encoded certificate
|
||||
@@ -1548,7 +1548,7 @@ class MutuallyExclusiveError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise MutuallyExclusiveError(reason=u'hosts may not be added when hostcategory=all')
|
||||
>>> raise MutuallyExclusiveError(reason=_(u'hosts may not be added when hostcategory=all'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
MutuallyExclusiveError: hosts may not be added when hostcategory=all
|
||||
@@ -1565,7 +1565,7 @@ class NonFatalError(ExecutionError):
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise NonFatalError(reason=u'The host was added but the DNS update failed')
|
||||
>>> raise NonFatalError(reason=_(u'The host was added but the DNS update failed'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
NonFatalError: The host was added but the DNS update failed
|
||||
@@ -1646,7 +1646,7 @@ class ProtectedEntryError(ExecutionError):
|
||||
**4309** Raised when an entry being deleted is protected
|
||||
|
||||
For example:
|
||||
>>> raise ProtectedEntryError(label=u'group', key=u'admins', reason=u'privileged group')
|
||||
>>> raise ProtectedEntryError(label=u'group', key=u'admins', reason=_(u'privileged group'))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ProtectedEntryError: group admins cannot be deleted: privileged group
|
||||
|
||||
@@ -1840,7 +1840,7 @@ class AccessTime(Str):
|
||||
raise ValidationError(name=self.get_param_name(), error=e.args[0])
|
||||
except IndexError:
|
||||
raise ValidationError(
|
||||
name=self.get_param_name(), error='incomplete time value'
|
||||
name=self.get_param_name(), error=ugettext('incomplete time value')
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
@@ -440,7 +440,9 @@ class automountlocation_import(LDAPQuery):
|
||||
result['duplicatekeys'].append(am[0])
|
||||
pass
|
||||
else:
|
||||
raise errors.DuplicateEntry(message=unicode('key %(key)s already exists' % {'key':am[0]}))
|
||||
raise errors.DuplicateEntry(
|
||||
message=_('key %(key)s already exists') % dict(
|
||||
key=am[0]))
|
||||
# Add the new map
|
||||
if not am[1].startswith('-'):
|
||||
try:
|
||||
@@ -454,7 +456,9 @@ class automountlocation_import(LDAPQuery):
|
||||
result['duplicatemaps'].append(am[0])
|
||||
pass
|
||||
else:
|
||||
raise errors.DuplicateEntry(message=unicode('map %(map)s already exists' % {'map':am[1]}))
|
||||
raise errors.DuplicateEntry(
|
||||
message=_('map %(map)s already exists') % dict(
|
||||
map=am[1]))
|
||||
except errors.DuplicateEntry:
|
||||
# This means the same map is used on several mount points.
|
||||
pass
|
||||
|
||||
@@ -194,7 +194,8 @@ def validate_del_attribute(ugettext, attr):
|
||||
def validate_attribute(ugettext, name, attr):
|
||||
m = re.match("\s*(.*?)\s*=\s*(.*?)\s*$", attr)
|
||||
if not m or len(m.groups()) != 2:
|
||||
raise errors.ValidationError(name=name, error='Invalid format. Should be name=value')
|
||||
raise errors.ValidationError(
|
||||
name=name, error=_('Invalid format. Should be name=value'))
|
||||
|
||||
def get_effective_rights(ldap, dn, attrs=None):
|
||||
assert isinstance(dn, DN)
|
||||
@@ -690,16 +691,22 @@ def _check_limit_object_class(attributes, attrs, allow_only):
|
||||
for (oid, attr) in attributes[0].iteritems():
|
||||
if attr.names[0].lower() in limitattrs:
|
||||
if not allow_only:
|
||||
raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=attr.names[0].lower()))
|
||||
raise errors.ObjectclassViolation(
|
||||
info=_('attribute "%(attribute)s" not allowed') % dict(
|
||||
attribute=attr.names[0].lower()))
|
||||
limitattrs.remove(attr.names[0].lower())
|
||||
# And now the MAY
|
||||
for (oid, attr) in attributes[1].iteritems():
|
||||
if attr.names[0].lower() in limitattrs:
|
||||
if not allow_only:
|
||||
raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=attr.names[0].lower()))
|
||||
raise errors.ObjectclassViolation(
|
||||
info=_('attribute "%(attribute)s" not allowed') % dict(
|
||||
attribute=attr.names[0].lower()))
|
||||
limitattrs.remove(attr.names[0].lower())
|
||||
if len(limitattrs) > 0 and allow_only:
|
||||
raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=limitattrs[0]))
|
||||
raise errors.ObjectclassViolation(
|
||||
info=_('attribute "%(attribute)s" not allowed') % dict(
|
||||
attribute=limitattrs[0]))
|
||||
|
||||
|
||||
class CallbackInterface(Method):
|
||||
@@ -882,8 +889,7 @@ last, after all sets and adds."""),
|
||||
try:
|
||||
entry_attrs[attr].remove(delval)
|
||||
except ValueError:
|
||||
raise errors.AttrValueNotFound(attr=attr,
|
||||
value=delval)
|
||||
raise errors.AttrValueNotFound(attr=attr, value=delval)
|
||||
|
||||
if needldapattrs:
|
||||
try:
|
||||
|
||||
@@ -298,7 +298,10 @@ class cert_request(VirtualCommand):
|
||||
subject_host = get_csr_hostname(csr)
|
||||
(servicename, hostname, realm) = split_principal(principal)
|
||||
if subject_host.lower() != hostname.lower():
|
||||
raise errors.ACIError(info="hostname in subject of request '%s' does not match principal hostname '%s'" % (subject_host, hostname))
|
||||
raise errors.ACIError(
|
||||
info=_("hostname in subject of request '%(subject_host)s' "
|
||||
"does not match principal hostname '%(hostname)s'") % dict(
|
||||
subject_host=subject_host, hostname=hostname))
|
||||
|
||||
dn = None
|
||||
service = None
|
||||
@@ -314,16 +317,19 @@ class cert_request(VirtualCommand):
|
||||
dn = service['dn']
|
||||
except errors.NotFound, e:
|
||||
if not add:
|
||||
raise errors.NotFound(reason="The service principal for this request doesn't exist.")
|
||||
raise errors.NotFound(reason=_("The service principal for "
|
||||
"this request doesn't exist."))
|
||||
try:
|
||||
service = api.Command['service_add'](principal, **{'force': True})['result']
|
||||
dn = service['dn']
|
||||
except errors.ACIError:
|
||||
raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
|
||||
raise errors.ACIError(info=_('You need to be a member of '
|
||||
'the serviceadmin role to add services'))
|
||||
|
||||
# We got this far so the service entry exists, can we write it?
|
||||
if not ldap.can_write(dn, "usercertificate"):
|
||||
raise errors.ACIError(info="Insufficient 'write' privilege to the 'userCertificate' attribute of entry '%s'." % dn)
|
||||
raise errors.ACIError(info=_("Insufficient 'write' privilege "
|
||||
"to the 'userCertificate' attribute of entry '%s'.") % dn)
|
||||
|
||||
# Validate the subject alt name, if any
|
||||
request = pkcs10.load_certificate_request(csr)
|
||||
@@ -337,11 +343,14 @@ class cert_request(VirtualCommand):
|
||||
# We don't want to issue any certificates referencing
|
||||
# machines we don't know about. Nothing is stored in this
|
||||
# host record related to this certificate.
|
||||
raise errors.NotFound(reason='no host record for subject alt name %s in certificate request' % name)
|
||||
raise errors.NotFound(reason=_('no host record for '
|
||||
'subject alt name %s in certificate request') % name)
|
||||
authprincipal = getattr(context, 'principal')
|
||||
if authprincipal.startswith("host/"):
|
||||
if not hostdn in service.get('managedby', []):
|
||||
raise errors.ACIError(info="Insufficient privilege to create a certificate with subject alt name '%s'." % name)
|
||||
raise errors.ACIError(info=_(
|
||||
"Insufficient privilege to create a certificate "
|
||||
"with subject alt name '%s'.") % name)
|
||||
|
||||
if 'usercertificate' in service:
|
||||
serial = x509.get_serial_number(service['usercertificate'][0], datatype=x509.DER)
|
||||
|
||||
@@ -2617,8 +2617,7 @@ class dnsrecord_del(LDAPUpdate):
|
||||
attr_name = unicode(param.label or param.name)
|
||||
except:
|
||||
attr_name = attr
|
||||
raise errors.AttrValueNotFound(attr=attr_name,
|
||||
value=val)
|
||||
raise errors.AttrValueNotFound(attr=attr_name, value=val)
|
||||
entry_attrs[attr] = list(set(old_entry[attr]))
|
||||
|
||||
del_all = False
|
||||
|
||||
@@ -147,7 +147,8 @@ def get_uuid(ldap):
|
||||
entry_attrs, 'ipaentitlementid', api.env.container_entitlements,
|
||||
)
|
||||
if not ldap.can_read(dn, 'userpkcs12'):
|
||||
raise errors.ACIError(info='not allowed to perform this command')
|
||||
raise errors.ACIError(
|
||||
info=_('not allowed to perform this command'))
|
||||
|
||||
if not 'userpkcs12' in result:
|
||||
return (None, uuid, None, None)
|
||||
@@ -338,7 +339,9 @@ class entitle_consume(LDAPUpdate):
|
||||
available = result['quantity'] - result['consumed']
|
||||
|
||||
if quantity > available:
|
||||
raise errors.ValidationError(name='quantity', error='There are only %d entitlements left' % available)
|
||||
raise errors.ValidationError(
|
||||
name='quantity',
|
||||
error=_('There are only %d entitlements left') % available)
|
||||
|
||||
try:
|
||||
cp = UEPConnection(handler='/candlepin', cert_file=certfile, key_file=keyfile)
|
||||
@@ -508,12 +511,13 @@ class entitle_register(LDAPCreate):
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
dn = DN(self.obj.container_dn, self.api.env.basedn)
|
||||
if not ldap.can_add(dn):
|
||||
raise errors.ACIError(info='No permission to register')
|
||||
raise errors.ACIError(info=_('No permission to register'))
|
||||
os.environ['LANG'] = 'en_US'
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
if 'ipaentitlementid' in options:
|
||||
raise errors.ValidationError(name='ipaentitlementid', error='Registering to specific UUID is not supported yet.')
|
||||
raise errors.ValidationError(name='ipaentitlementid',
|
||||
error=_('Registering to specific UUID is not supported yet.'))
|
||||
|
||||
try:
|
||||
registrations = api.Command['entitle_find']()
|
||||
|
||||
@@ -446,7 +446,8 @@ class hbacrule_add_user(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'usercategory' in entry_attrs and \
|
||||
entry_attrs['usercategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="users cannot be added when user category='all'")
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_("users cannot be added when user category='all'"))
|
||||
return dn
|
||||
|
||||
api.register(hbacrule_add_user)
|
||||
@@ -475,7 +476,8 @@ class hbacrule_add_host(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'hostcategory' in entry_attrs and \
|
||||
entry_attrs['hostcategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="hosts cannot be added when host category='all'")
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_("hosts cannot be added when host category='all'"))
|
||||
return dn
|
||||
|
||||
api.register(hbacrule_add_host)
|
||||
@@ -504,7 +506,8 @@ class hbacrule_add_sourcehost(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'sourcehostcategory' in entry_attrs and \
|
||||
entry_attrs['sourcehostcategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="source hosts cannot be added when sourcehost category='all'")
|
||||
raise errors.MutuallyExclusiveError(reason=_(
|
||||
"source hosts cannot be added when sourcehost category='all'"))
|
||||
return add_external_pre_callback('host', ldap, dn, keys, options)
|
||||
|
||||
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
|
||||
@@ -541,7 +544,8 @@ class hbacrule_add_service(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'servicecategory' in entry_attrs and \
|
||||
entry_attrs['servicecategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="services cannot be added when service category='all'")
|
||||
raise errors.MutuallyExclusiveError(reason=_(
|
||||
"services cannot be added when service category='all'"))
|
||||
return dn
|
||||
|
||||
api.register(hbacrule_add_service)
|
||||
|
||||
@@ -621,7 +621,7 @@ class host_mod(LDAPUpdate):
|
||||
|
||||
# Once a principal name is set it cannot be changed
|
||||
if 'cn' in entry_attrs:
|
||||
raise errors.ACIError(info='cn is immutable')
|
||||
raise errors.ACIError(info=_('cn is immutable'))
|
||||
if 'locality' in entry_attrs:
|
||||
entry_attrs['l'] = entry_attrs['locality']
|
||||
del entry_attrs['locality']
|
||||
|
||||
@@ -210,9 +210,9 @@ class netgroup_mod(LDAPUpdate):
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
if is_all(options, 'usercategory') and 'memberuser' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason="user category cannot be set to 'all' while there are allowed users")
|
||||
raise errors.MutuallyExclusiveError(reason=_("user category cannot be set to 'all' while there are allowed users"))
|
||||
if is_all(options, 'hostcategory') and 'memberhost' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason="host category cannot be set to 'all' while there are allowed hosts")
|
||||
raise errors.MutuallyExclusiveError(reason=_("host category cannot be set to 'all' while there are allowed hosts"))
|
||||
return dn
|
||||
|
||||
api.register(netgroup_mod)
|
||||
|
||||
@@ -112,7 +112,7 @@ class passwd(Command):
|
||||
current_password == MAGIC_VALUE:
|
||||
# No cheating
|
||||
self.log.warn('User attempted to change password using magic value')
|
||||
raise errors.ACIError(info='Invalid credentials')
|
||||
raise errors.ACIError(info=_('Invalid credentials'))
|
||||
|
||||
if current_password == MAGIC_VALUE:
|
||||
ldap.modify_password(dn, password)
|
||||
|
||||
@@ -303,7 +303,8 @@ class permission_del(LDAPDelete):
|
||||
def pre_callback(self, ldap, dn, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
if not options.get('force') and not self.obj.check_system(ldap, dn, *keys):
|
||||
raise errors.ACIError(info='A SYSTEM permission may not be removed')
|
||||
raise errors.ACIError(
|
||||
info=_('A SYSTEM permission may not be removed'))
|
||||
# remove permission even when the underlying ACI is missing
|
||||
try:
|
||||
self.api.Command.aci_del(keys[-1], aciprefix=ACI_PREFIX)
|
||||
@@ -323,7 +324,8 @@ class permission_mod(LDAPUpdate):
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
if not self.obj.check_system(ldap, dn, *keys):
|
||||
raise errors.ACIError(info='A SYSTEM permission may not be modified')
|
||||
raise errors.ACIError(
|
||||
info=_('A SYSTEM permission may not be modified'))
|
||||
|
||||
# check if permission is in LDAP
|
||||
try:
|
||||
@@ -350,7 +352,7 @@ class permission_mod(LDAPUpdate):
|
||||
pass # permission may be renamed, continue
|
||||
else:
|
||||
raise errors.ValidationError(
|
||||
name='rename',error=_('New name can not be empty'))
|
||||
name='rename', error=_('New name can not be empty'))
|
||||
|
||||
opts = self.obj.filter_aci_attributes(options)
|
||||
setattr(context, 'aciupdate', False)
|
||||
|
||||
@@ -61,7 +61,7 @@ def valid_arg(ugettext, action):
|
||||
if a != 'enable' and a != 'disable':
|
||||
raise errors.ValidationError(
|
||||
name='action',
|
||||
error='Unknown command %s' % action
|
||||
error=_('Unknown command %s') % action
|
||||
)
|
||||
|
||||
class pkinit_anonymous(Command):
|
||||
|
||||
@@ -283,9 +283,11 @@ class selinuxusermap_mod(LDAPUpdate):
|
||||
raise errors.MutuallyExclusiveError(reason=notboth_err)
|
||||
|
||||
if is_all(options, 'usercategory') and 'memberuser' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason="user category cannot be set to 'all' while there are allowed users")
|
||||
raise errors.MutuallyExclusiveError(reason=_("user category "
|
||||
"cannot be set to 'all' while there are allowed users"))
|
||||
if is_all(options, 'hostcategory') and 'memberhost' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason="host category cannot be set to 'all' while there are allowed hosts")
|
||||
raise errors.MutuallyExclusiveError(reason=_("host category "
|
||||
"cannot be set to 'all' while there are allowed hosts"))
|
||||
|
||||
if 'ipaselinuxuser' in entry_attrs:
|
||||
validate_selinuxuser_inlist(ldap, entry_attrs['ipaselinuxuser'])
|
||||
@@ -414,7 +416,8 @@ class selinuxusermap_add_user(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'usercategory' in entry_attrs and \
|
||||
entry_attrs['usercategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="users cannot be added when user category='all'")
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_("users cannot be added when user category='all'"))
|
||||
if 'seealso' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason=notboth_err)
|
||||
return dn
|
||||
@@ -445,7 +448,8 @@ class selinuxusermap_add_host(LDAPAddMember):
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'hostcategory' in entry_attrs and \
|
||||
entry_attrs['hostcategory'][0].lower() == 'all':
|
||||
raise errors.MutuallyExclusiveError(reason="hosts cannot be added when host category='all'")
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_("hosts cannot be added when host category='all'"))
|
||||
if 'seealso' in entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason=notboth_err)
|
||||
return dn
|
||||
|
||||
@@ -126,18 +126,15 @@ def split_principal(principal):
|
||||
# may not include the realm.
|
||||
sp = principal.split('/')
|
||||
if len(sp) != 2:
|
||||
raise errors.MalformedServicePrincipal(reason='missing service')
|
||||
raise errors.MalformedServicePrincipal(reason=_('missing service'))
|
||||
|
||||
service = sp[0]
|
||||
if len(service) == 0:
|
||||
raise errors.MalformedServicePrincipal(
|
||||
reason='blank service'
|
||||
)
|
||||
raise errors.MalformedServicePrincipal(reason=_('blank service'))
|
||||
sr = sp[1].split('@')
|
||||
if len(sr) > 2:
|
||||
raise errors.MalformedServicePrincipal(
|
||||
reason='unable to determine realm'
|
||||
)
|
||||
reason=_('unable to determine realm'))
|
||||
|
||||
hostname = sr[0].lower()
|
||||
if len(sr) == 2:
|
||||
@@ -286,7 +283,9 @@ class service_add(LDAPCreate):
|
||||
try:
|
||||
hostresult = api.Command['host_show'](hostname)['result']
|
||||
except errors.NotFound:
|
||||
raise errors.NotFound(reason="The host '%s' does not exist to add a service to." % hostname)
|
||||
raise errors.NotFound(
|
||||
reason=_("The host '%s' does not exist to add a service to.") %
|
||||
hostname)
|
||||
|
||||
cert = options.get('usercertificate')
|
||||
if cert:
|
||||
@@ -330,7 +329,10 @@ class service_del(LDAPDelete):
|
||||
(service, hostname, realm) = split_principal(keys[-1])
|
||||
check_required_principal(ldap, hostname, service)
|
||||
if self.api.env.enable_ra:
|
||||
try:
|
||||
(dn, entry_attrs) = ldap.get_entry(dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
cert = entry_attrs.get('usercertificate')
|
||||
if cert:
|
||||
cert = cert[0]
|
||||
@@ -376,7 +378,11 @@ class service_mod(LDAPUpdate):
|
||||
if cert:
|
||||
dercert = x509.normalize_certificate(cert)
|
||||
x509.verify_cert_subject(ldap, hostname, dercert)
|
||||
(dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
|
||||
try:
|
||||
(dn, entry_attrs_old) = ldap.get_entry(
|
||||
dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
if 'usercertificate' in entry_attrs_old:
|
||||
# FIXME: what to do here? do we revoke the old cert?
|
||||
fmt = 'entry already has a certificate, serial number: %s' % (
|
||||
|
||||
@@ -286,9 +286,9 @@ class sudorule_mod(LDAPUpdate):
|
||||
self.obj.handle_not_found(*keys)
|
||||
|
||||
if is_all(options, 'usercategory') and 'memberuser' in _entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason=_("user category cannot be set to 'all' while there are users"))
|
||||
raise errors.MutuallyExclusiveError(reason=_("user category cannot be set to 'all' while there are allowed users"))
|
||||
if is_all(options, 'hostcategory') and 'memberhost' in _entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason=_("host category cannot be set to 'all' while there are hosts"))
|
||||
raise errors.MutuallyExclusiveError(reason=_("host category cannot be set to 'all' while there are allowed hosts"))
|
||||
if is_all(options, 'cmdcategory') and ('memberallowcmd' or
|
||||
'memberdenywcmd') in _entry_attrs:
|
||||
raise errors.MutuallyExclusiveError(reason=_("command category cannot be set to 'all' while there are allow or deny commands"))
|
||||
|
||||
@@ -114,8 +114,9 @@ def validate_nsaccountlock(entry_attrs):
|
||||
if not isinstance(nsaccountlock, (bool, Bool)):
|
||||
if not isinstance(nsaccountlock, basestring):
|
||||
raise errors.OnlyOneValueAllowed(attr='nsaccountlock')
|
||||
if nsaccountlock.lower() not in ('true','false'):
|
||||
raise errors.ValidationError(name='nsaccountlock', error='must be TRUE or FALSE')
|
||||
if nsaccountlock.lower() not in ('true', 'false'):
|
||||
raise errors.ValidationError(name='nsaccountlock',
|
||||
error=_('must be TRUE or FALSE'))
|
||||
|
||||
def convert_nsaccountlock(entry_attrs):
|
||||
if not 'nsaccountlock' in entry_attrs:
|
||||
@@ -134,9 +135,7 @@ def split_principal(principal):
|
||||
parts = principal.split('@')
|
||||
user = parts[0].lower()
|
||||
if len(parts) > 2:
|
||||
raise errors.MalformedUserPrincipal(
|
||||
principal=principal
|
||||
)
|
||||
raise errors.MalformedUserPrincipal(principal=principal)
|
||||
|
||||
if len(parts) == 2:
|
||||
realm = parts[1].upper()
|
||||
|
||||
@@ -24,6 +24,7 @@ from ipalib import api
|
||||
from ipalib import Command
|
||||
from ipalib import errors
|
||||
from ipapython.dn import DN
|
||||
from ipalib.text import _
|
||||
|
||||
class VirtualCommand(Command):
|
||||
"""
|
||||
@@ -48,7 +49,7 @@ class VirtualCommand(Command):
|
||||
This should be executed before any actual work is done.
|
||||
"""
|
||||
if self.operation is None and operation is None:
|
||||
raise errors.ACIError(info='operation not defined')
|
||||
raise errors.ACIError(info=_('operation not defined'))
|
||||
|
||||
if operation is None:
|
||||
operation = self.operation
|
||||
@@ -60,8 +61,9 @@ class VirtualCommand(Command):
|
||||
|
||||
try:
|
||||
if not ldap.can_write(operationdn, "objectclass"):
|
||||
raise errors.ACIError(info='not allowed to perform this command')
|
||||
raise errors.ACIError(
|
||||
info=_('not allowed to perform this command'))
|
||||
except errors.NotFound:
|
||||
raise errors.ACIError(info='No such virtual command')
|
||||
raise errors.ACIError(info=_('No such virtual command'))
|
||||
|
||||
return True
|
||||
|
||||
@@ -49,6 +49,7 @@ from ipalib.request import context, Connection
|
||||
from ipalib.util import get_current_principal
|
||||
from ipapython import ipautil
|
||||
from ipapython import kernel_keyring
|
||||
from ipalib.text import _
|
||||
|
||||
import httplib
|
||||
import socket
|
||||
@@ -484,7 +485,8 @@ class xmlclient(Connectible):
|
||||
serverproxy = None
|
||||
|
||||
if serverproxy is None:
|
||||
raise NetworkError(uri='any of the configured servers', error=', '.join(servers))
|
||||
raise NetworkError(uri=_('any of the configured servers'),
|
||||
error=', '.join(servers))
|
||||
return serverproxy
|
||||
|
||||
def destroy_connection(self):
|
||||
|
||||
@@ -143,7 +143,7 @@ def check_writable_file(filename):
|
||||
open the file to test writability.
|
||||
"""
|
||||
if filename is None:
|
||||
raise errors.FileError(reason='Filename is empty')
|
||||
raise errors.FileError(reason=_('Filename is empty'))
|
||||
try:
|
||||
if os.path.exists(filename):
|
||||
if not os.access(filename, os.W_OK):
|
||||
|
||||
@@ -195,7 +195,8 @@ def normalize_certificate(rawcert):
|
||||
serial = unicode(get_serial_number(dercert, DER))
|
||||
except NSPRError, nsprerr:
|
||||
if nsprerr.errno == -8183: # SEC_ERROR_BAD_DER
|
||||
raise errors.CertificateFormatError(error='improperly formatted DER-encoded certificate')
|
||||
raise errors.CertificateFormatError(
|
||||
error=_('improperly formatted DER-encoded certificate'))
|
||||
else:
|
||||
raise errors.CertificateFormatError(error=str(nsprerr))
|
||||
|
||||
|
||||
@@ -17,15 +17,17 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from ipalib import api, errors
|
||||
import httplib
|
||||
import xml.dom.minidom
|
||||
from ipapython import nsslib, ipautil
|
||||
import nss.nss as nss
|
||||
from nss.error import NSPRError
|
||||
from ipalib.errors import NetworkError, CertificateOperationError
|
||||
from urllib import urlencode
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipapython import nsslib, ipautil
|
||||
from ipalib.errors import NetworkError, CertificateOperationError
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipalib.text import _
|
||||
|
||||
def get_ca_certchain(ca_host=None):
|
||||
"""
|
||||
@@ -52,12 +54,14 @@ def get_ca_certchain(ca_host=None):
|
||||
reason = item_node[0].childNodes[0].data
|
||||
raise errors.RemoteRetrieveError(reason=reason)
|
||||
except Exception, e:
|
||||
raise errors.RemoteRetrieveError(reason="Retrieving CA cert chain failed: %s" % str(e))
|
||||
raise errors.RemoteRetrieveError(
|
||||
reason=_("Retrieving CA cert chain failed: %s") % e)
|
||||
finally:
|
||||
if doc:
|
||||
doc.unlink()
|
||||
else:
|
||||
raise errors.RemoteRetrieveError(reason="request failed with HTTP status %d" % res.status)
|
||||
raise errors.RemoteRetrieveError(
|
||||
reason=_("request failed with HTTP status %d") % res.status)
|
||||
|
||||
return chain
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ from ipalib.parameters import IA5Str
|
||||
from ipalib.util import (validate_zonemgr, normalize_zonemgr,
|
||||
get_dns_forward_zone_update_policy, get_dns_reverse_zone_update_policy)
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipalib.text import _
|
||||
|
||||
import ipalib
|
||||
from ipalib import api, util, errors
|
||||
@@ -277,7 +278,8 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
|
||||
# automatically retrieve list of DNS masters
|
||||
dns_masters = api.Object.dnsrecord.get_dns_masters()
|
||||
if not dns_masters:
|
||||
raise errors.NotFound("No IPA server with DNS support found!")
|
||||
raise installutils.ScriptError(
|
||||
"No IPA server with DNS support found!")
|
||||
ns_main = dns_masters.pop(0)
|
||||
ns_replicas = dns_masters
|
||||
addresses = resolve_host(ns_main)
|
||||
@@ -321,7 +323,8 @@ def add_reverse_zone(zone, ns_hostname=None, ns_ip_address=None,
|
||||
# automatically retrieve list of DNS masters
|
||||
dns_masters = api.Object.dnsrecord.get_dns_masters()
|
||||
if not dns_masters:
|
||||
raise errors.NotFound("No IPA server with DNS support found!")
|
||||
raise installutils.ScriptError(
|
||||
"No IPA server with DNS support found!")
|
||||
ns_main = dns_masters.pop(0)
|
||||
ns_replicas = dns_masters
|
||||
addresses = resolve_host(ns_main)
|
||||
|
||||
@@ -41,6 +41,7 @@ from ipapython import services as ipaservices
|
||||
from ipalib import x509
|
||||
from ipapython.dn import DN
|
||||
from ipalib.errors import CertificateOperationError
|
||||
from ipalib.text import _
|
||||
|
||||
from nss.error import NSPRError
|
||||
import nss.nss as nss
|
||||
@@ -663,7 +664,8 @@ class CertDB(object):
|
||||
dogtag.https_request(self.host_name, api.env.ca_ee_install_port, "/ca/ee/ca/profileSubmitSSLClient", self.secdir, password, "ipaCert", **params)
|
||||
|
||||
if http_status != 200:
|
||||
raise CertificateOperationError(error='Unable to communicate with CMS (%s)' % \
|
||||
raise CertificateOperationError(
|
||||
error=_('Unable to communicate with CMS (%s)') %
|
||||
http_reason_phrase)
|
||||
|
||||
# The result is an XML blob. Pull the certificate out of that
|
||||
|
||||
@@ -106,7 +106,7 @@ class join(Command):
|
||||
# It exists, can we write the password attributes?
|
||||
allowed = ldap.can_write(dn, 'krblastpwdchange')
|
||||
if not allowed:
|
||||
raise errors.ACIError(info="Insufficient 'write' privilege to the 'krbLastPwdChange' attribute of entry '%s'." % dn)
|
||||
raise errors.ACIError(info=_("Insufficient 'write' privilege to the 'krbLastPwdChange' attribute of entry '%s'.") % dn)
|
||||
|
||||
kw = {'fqdn': hostname, 'all': True}
|
||||
attrs_list = api.Command['host_show'](**kw)['result']
|
||||
|
||||
@@ -208,7 +208,8 @@ class ra(rabase.rabase):
|
||||
serial = x509.get_serial_number(cert)
|
||||
except NSPRError, e:
|
||||
self.log.error('Unable to decode certificate in entry: %s' % str(e))
|
||||
raise errors.CertificateOperationError(error='Unable to decode certificate in entry: %s' % str(e))
|
||||
raise errors.CertificateOperationError(
|
||||
error=_('Unable to decode certificate in entry: %s') % str(e))
|
||||
|
||||
# To make it look like dogtag return just the base64 data.
|
||||
cert = cert.replace('\n','')
|
||||
|
||||
@@ -26,6 +26,15 @@ Also see the `ipalib.rpc` module.
|
||||
from cgi import parse_qs
|
||||
from xml.sax.saxutils import escape
|
||||
from xmlrpclib import Fault
|
||||
from wsgiref.util import shift_path_info
|
||||
import base64
|
||||
import os
|
||||
import string
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
import urlparse
|
||||
import time
|
||||
|
||||
from ipalib import plugable
|
||||
from ipalib.backend import Executioner
|
||||
from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError, ExecutionError
|
||||
@@ -39,15 +48,8 @@ from ipalib.session import session_mgr, AuthManager, get_ipa_ccache_name, load_c
|
||||
from ipalib.backend import Backend
|
||||
from ipalib.krb_utils import krb5_parse_ccache, KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name
|
||||
from ipapython import ipautil
|
||||
from wsgiref.util import shift_path_info
|
||||
from ipapython.version import VERSION
|
||||
import base64
|
||||
import os
|
||||
import string
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
import urlparse
|
||||
import time
|
||||
from ipalib.text import _
|
||||
|
||||
HTTP_STATUS_SUCCESS = '200 Success'
|
||||
HTTP_STATUS_SERVER_ERROR = '500 Internal Server Error'
|
||||
@@ -533,31 +535,25 @@ class jsonserver(WSGIExecutioner, HTTP_Status):
|
||||
except ValueError, e:
|
||||
raise JSONError(error=e)
|
||||
if not isinstance(d, dict):
|
||||
raise JSONError(error='Request must be a dict')
|
||||
raise JSONError(error=_('Request must be a dict'))
|
||||
if 'method' not in d:
|
||||
raise JSONError(error='Request is missing "method"')
|
||||
raise JSONError(error=_('Request is missing "method"'))
|
||||
if 'params' not in d:
|
||||
raise JSONError(error='Request is missing "params"')
|
||||
raise JSONError(error=_('Request is missing "params"'))
|
||||
d = json_decode_binary(d)
|
||||
method = d['method']
|
||||
params = d['params']
|
||||
_id = d.get('id')
|
||||
if not isinstance(params, (list, tuple)):
|
||||
raise JSONError(error='params must be a list')
|
||||
raise JSONError(error=_('params must be a list'))
|
||||
if len(params) != 2:
|
||||
raise JSONError(
|
||||
error='params must contain [args, options]'
|
||||
)
|
||||
raise JSONError(error=_('params must contain [args, options]'))
|
||||
args = params[0]
|
||||
if not isinstance(args, (list, tuple)):
|
||||
raise JSONError(
|
||||
error='params[0] (aka args) must be a list'
|
||||
)
|
||||
raise JSONError(error=_('params[0] (aka args) must be a list'))
|
||||
options = params[1]
|
||||
if not isinstance(options, dict):
|
||||
raise JSONError(
|
||||
error='params[1] (aka options) must be a dict'
|
||||
)
|
||||
raise JSONError(error=_('params[1] (aka options) must be a dict'))
|
||||
options = dict((str(k), v) for (k, v) in options.iteritems())
|
||||
return (method, args, options, _id)
|
||||
|
||||
|
||||
@@ -200,44 +200,44 @@ class test_jsonserver(PluginTester):
|
||||
# Test with invalid JSON-data:
|
||||
e = raises(errors.JSONError, o.unmarshal, 'this wont work')
|
||||
assert isinstance(e.error, ValueError)
|
||||
assert str(e.error) == 'No JSON object could be decoded'
|
||||
assert unicode(e.error) == 'No JSON object could be decoded'
|
||||
|
||||
# Test with non-dict type:
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps([1, 2, 3]))
|
||||
assert str(e.error) == 'Request must be a dict'
|
||||
assert unicode(e.error) == 'Request must be a dict'
|
||||
|
||||
params = [[1, 2], dict(three=3, four=4)]
|
||||
# Test with missing method:
|
||||
d = dict(params=params, id=18)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'Request is missing "method"'
|
||||
assert unicode(e.error) == 'Request is missing "method"'
|
||||
|
||||
# Test with missing params:
|
||||
d = dict(method='echo', id=18)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'Request is missing "params"'
|
||||
assert unicode(e.error) == 'Request is missing "params"'
|
||||
|
||||
# Test with non-list params:
|
||||
for p in ('hello', dict(args=tuple(), options=dict())):
|
||||
d = dict(method='echo', id=18, params=p)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'params must be a list'
|
||||
assert unicode(e.error) == 'params must be a list'
|
||||
|
||||
# Test with other than 2 params:
|
||||
for p in ([], [tuple()], [None, dict(), tuple()]):
|
||||
d = dict(method='echo', id=18, params=p)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'params must contain [args, options]'
|
||||
assert unicode(e.error) == 'params must contain [args, options]'
|
||||
|
||||
# Test when args is not a list:
|
||||
d = dict(method='echo', id=18, params=['args', dict()])
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'params[0] (aka args) must be a list'
|
||||
assert unicode(e.error) == 'params[0] (aka args) must be a list'
|
||||
|
||||
# Test when options is not a dict:
|
||||
d = dict(method='echo', id=18, params=[('hello', 'world'), 'options'])
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert str(e.error) == 'params[1] (aka options) must be a dict'
|
||||
assert unicode(e.error) == 'params[1] (aka options) must be a dict'
|
||||
|
||||
# Test with valid values:
|
||||
args = [u'jdoe']
|
||||
|
||||
@@ -69,14 +69,16 @@ class test_service(Declarative):
|
||||
dict(
|
||||
desc='Try to update non-existent %r' % service1,
|
||||
command=('service_mod', [service1], dict(usercertificate=servercert)),
|
||||
expected=errors.NotFound(reason='no such entry'),
|
||||
expected=errors.NotFound(
|
||||
reason=u'%s: service not found' % service1),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Try to delete non-existent %r' % service1,
|
||||
command=('service_del', [service1], {}),
|
||||
expected=errors.NotFound(reason='no such entry'),
|
||||
expected=errors.NotFound(
|
||||
reason=u'%s: service not found' % service1),
|
||||
),
|
||||
|
||||
|
||||
@@ -457,14 +459,16 @@ class test_service(Declarative):
|
||||
dict(
|
||||
desc='Try to update non-existent %r' % service1,
|
||||
command=('service_mod', [service1], dict(usercertificate=servercert)),
|
||||
expected=errors.NotFound(reason='no such entry'),
|
||||
expected=errors.NotFound(
|
||||
reason=u'%s: service not found' % service1),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Try to delete non-existent %r' % service1,
|
||||
command=('service_del', [service1], {}),
|
||||
expected=errors.NotFound(reason='no such entry'),
|
||||
expected=errors.NotFound(
|
||||
reason=u'%s: service not found' % service1),
|
||||
),
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user