summaryrefslogtreecommitdiffstats
path: root/ipaserver/install
diff options
context:
space:
mode:
Diffstat (limited to 'ipaserver/install')
-rw-r--r--ipaserver/install/adtrustinstance.py63
-rw-r--r--ipaserver/install/bindinstance.py28
-rw-r--r--ipaserver/install/cainstance.py64
-rw-r--r--ipaserver/install/certs.py26
-rw-r--r--ipaserver/install/dsinstance.py42
-rw-r--r--ipaserver/install/httpinstance.py2
-rw-r--r--ipaserver/install/installutils.py5
-rw-r--r--ipaserver/install/ipa_ldap_updater.py2
-rw-r--r--ipaserver/install/krbinstance.py23
-rw-r--r--ipaserver/install/ldapupdate.py688
-rw-r--r--ipaserver/install/plugins/adtrust.py16
-rw-r--r--ipaserver/install/plugins/dns.py41
-rw-r--r--ipaserver/install/plugins/fix_replica_memberof.py4
-rw-r--r--ipaserver/install/plugins/rename_managed.py159
-rw-r--r--ipaserver/install/plugins/updateclient.py23
-rw-r--r--ipaserver/install/replication.py204
-rw-r--r--ipaserver/install/service.py26
17 files changed, 748 insertions, 668 deletions
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index 9dcbec2d6..078c54dbe 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -29,11 +29,12 @@ from ipaserver.install.dsinstance import realm_to_serverid
from ipaserver.install.bindinstance import get_rr, add_rr, del_rr, \
dns_zone_exists
from ipalib import errors, api
-from ipalib.dn import DN
+from ipapython.dn import DN
from ipapython import sysrestore
from ipapython import ipautil
from ipapython.ipa_log_manager import *
from ipapython import services as ipaservices
+from ipapython.dn import DN
import string
import struct
@@ -103,7 +104,7 @@ class ADTRUSTInstance(service.Service):
self.netbios_name = None
self.no_msdcs = None
self.smbd_user = None
- self.suffix = None
+ self.suffix = DN()
self.ldapi_socket = None
self.smb_conf = None
self.smb_dn = None
@@ -129,11 +130,10 @@ class ADTRUSTInstance(service.Service):
return "S-1-5-21-%d-%d-%d" % (sub_ids[0], sub_ids[1], sub_ids[2])
def __add_admin_sids(self):
- admin_dn = str(DN(('uid', 'admin'), api.env.container_user,
- self.suffix))
- admin_group_dn = str(DN(('cn', 'admins'), api.env.container_group,
- self.suffix))
-
+ admin_dn = DN(('uid', 'admin'), api.env.container_user,
+ self.suffix)
+ admin_group_dn = DN(('cn', 'admins'), api.env.container_group,
+ self.suffix)
try:
dom_entry = self.admin_conn.getEntry(self.smb_dom_dn, \
ldap.SCOPE_BASE)
@@ -186,10 +186,9 @@ class ADTRUSTInstance(service.Service):
"""
try:
- res = self.admin_conn.search_s(str(DN(api.env.container_ranges,
- self.suffix)),
- ldap.SCOPE_ONELEVEL,
- "(objectclass=ipaDomainIDRange)")
+ res = self.admin_conn.getList(DN(api.env.container_ranges, self.suffix),
+ ldap.SCOPE_ONELEVEL,
+ "(objectclass=ipaDomainIDRange)")
if len(res) != 1:
root_logger.critical("Found more than one ID range for the " \
"local domain.")
@@ -230,17 +229,18 @@ class ADTRUSTInstance(service.Service):
pass
for new_dn in (self.trust_dn, \
- str(DN(('cn', 'ad'), self.trust_dn)), \
- str(DN(api.env.container_cifsdomains, self.suffix))):
+ DN(('cn', 'ad'), self.trust_dn), \
+ DN(api.env.container_cifsdomains, self.suffix)):
try:
self.admin_conn.getEntry(new_dn, ldap.SCOPE_BASE)
except errors.NotFound:
entry = ipaldap.Entry(new_dn)
entry.setValues("objectclass", ["nsContainer"])
- name = new_dn.split('=')[1].split(',')[0]
- if not name:
- print "Cannot extract RDN attribute value from [%s]" % \
- new_dn
+ try:
+ name = new_dn[1].attr
+ except Exception, e:
+ print 'Cannot extract RDN attribute value from "%s": %s' % \
+ (new_dn, e)
return
entry.setValues("cn", name)
self.admin_conn.addEntry(entry)
@@ -474,16 +474,16 @@ class ADTRUSTInstance(service.Service):
self.smb_conf = "/etc/samba/smb.conf"
- self.smb_dn = str(DN(('cn', 'adtrust agents'), ('cn', 'sysaccounts'),
- ('cn', 'etc'), self.suffix))
+ self.smb_dn = DN(('cn', 'adtrust agents'), ('cn', 'sysaccounts'),
+ ('cn', 'etc'), self.suffix)
- self.trust_dn = str(DN(api.env.container_trusts, self.suffix))
- self.smb_dom_dn = str(DN(('cn', self.domain_name),
- api.env.container_cifsdomains, self.suffix))
+ self.trust_dn = DN(api.env.container_trusts, self.suffix)
+ self.smb_dom_dn = DN(('cn', self.domain_name),
+ api.env.container_cifsdomains, self.suffix)
self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm
- self.cifs_agent = str(DN(('krbprincipalname', self.cifs_principal.lower()),
- api.env.container_service,
- self.suffix))
+ self.cifs_agent = DN(('krbprincipalname', self.cifs_principal.lower()),
+ api.env.container_service,
+ self.suffix)
self.selinux_booleans = ["samba_portmapper"]
self.__setup_sub_dict()
@@ -491,16 +491,13 @@ class ADTRUSTInstance(service.Service):
def find_local_id_range(self):
self.ldap_connect()
- if self.admin_conn.search_s(str(DN(api.env.container_ranges,
- self.suffix)),
+ if self.admin_conn.search_s(DN(api.env.container_ranges, self.suffix),
ldap.SCOPE_ONELEVEL,
"objectclass=ipaDomainIDRange"):
return
try:
- entry = self.admin_conn.getEntry(str(DN(('cn', 'admins'),
- api.env.container_group,
- self.suffix)),
+ entry = self.admin_conn.getEntry(DN(('cn', 'admins'), api.env.container_group, self.suffix),
ldap.SCOPE_BASE)
except errors.NotFound:
raise ValueError("No local ID range and no admins group found.\n" \
@@ -523,9 +520,9 @@ class ADTRUSTInstance(service.Service):
"range.\nAdd local ID range manually and try " \
"again!")
- entry = ipaldap.Entry(str(DN(('cn', ('%s_id_range' % self.realm)),
- api.env.container_ranges,
- self.suffix)))
+ entry = ipaldap.Entry(DN(('cn', ('%s_id_range' % self.realm)),
+ api.env.container_ranges,
+ self.suffix))
entry.setValue('objectclass', 'ipaDomainIDRange')
entry.setValue('cn', ('%s_id_range' % self.realm))
entry.setValue('ipaBaseID', str(base_id))
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index f320202ea..2e00f70b1 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -38,6 +38,7 @@ from ipapython.ipa_log_manager import *
import ipalib
from ipalib import api, util, errors
+from ipapython.dn import DN
NAMED_CONF = '/etc/named.conf'
RESOLV_CONF = '/etc/resolv.conf'
@@ -166,10 +167,11 @@ def dns_container_exists(fqdn, suffix, dm_password=None, ldapi=False, realm=None
Test whether the dns container exists.
"""
- def object_exists(dn):
+ def object_exists(dn): # FIXME, this should be a IPAdmin/ldap2 method so it can be shared
"""
Test whether the given object exists in LDAP.
"""
+ assert isinstance(dn, DN)
try:
conn.search_ext_s(dn, ldap.SCOPE_BASE)
except ldap.NO_SUCH_OBJECT:
@@ -177,6 +179,7 @@ def dns_container_exists(fqdn, suffix, dm_password=None, ldapi=False, realm=None
else:
return True
+ assert isinstance(suffix, DN)
try:
# At install time we may need to use LDAPI to avoid chicken/egg
# issues with SSL certs and truting CAs
@@ -192,7 +195,7 @@ def dns_container_exists(fqdn, suffix, dm_password=None, ldapi=False, realm=None
except ldap.SERVER_DOWN:
raise RuntimeError('LDAP server on %s is not responding. Is IPA installed?' % fqdn)
- ret = object_exists("cn=dns,%s" % suffix)
+ ret = object_exists(DN(('cn', 'dns'), suffix))
conn.unbind_s()
return ret
@@ -288,11 +291,14 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
ns_main = ns_hostname
ns_replicas = []
+ if ns_ip_address is not None:
+ ns_ip_address = unicode(ns_ip_address)
+
try:
api.Command.dnszone_add(unicode(name),
idnssoamname=unicode(ns_main+'.'),
idnssoarname=unicode(zonemgr),
- ip_address=unicode(ns_ip_address),
+ ip_address=ns_ip_address,
idnsallowdynupdate=True,
idnsupdatepolicy=unicode(update_policy),
idnsallowquery=u'any',
@@ -329,11 +335,14 @@ def add_reverse_zone(zone, ns_hostname=None, ns_ip_address=None,
ns_main = ns_hostname
ns_replicas = []
+ if ns_ip_address is not None:
+ ns_ip_address = unicode(ns_ip_address)
+
try:
api.Command.dnszone_add(unicode(zone),
idnssoamname=unicode(ns_main+'.'),
idnsallowdynupdate=True,
- ip_address=unicode(ns_ip_address),
+ ip_address=ns_ip_address,
idnsupdatepolicy=unicode(update_policy),
idnsallowquery=u'any',
idnsallowtransfer=u'none',)
@@ -465,6 +474,8 @@ class BindInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ suffix = ipautil.dn_attribute_property('_suffix')
+
def setup(self, fqdn, ip_address, realm_name, domain_name, forwarders, ntp,
reverse_zone, named_user="named", zonemgr=None,
zone_refresh=0, persistent_search=True, serial_autoincrement=True):
@@ -574,7 +585,7 @@ class BindInstance(service.Service):
if self.ntp:
optional_ntp = "\n;ntp server\n"
- optional_ntp += "_ntp._udp\t\tIN SRV 0 100 123\t%s""" % self.host_in_rr
+ optional_ntp += "_ntp._udp\t\tIN SRV 0 100 123\t%s" % self.host_in_rr
else:
optional_ntp = ""
@@ -654,7 +665,8 @@ class BindInstance(service.Service):
p = self.move_service(dns_principal)
if p is None:
# the service has already been moved, perhaps we're doing a DNS reinstall
- dns_principal = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (dns_principal, self.suffix)
+ dns_principal = DN(('krbprincipalname', dns_principal),
+ ('cn', 'services'), ('cn', 'accounts'), self.suffix)
else:
dns_principal = p
@@ -667,9 +679,7 @@ class BindInstance(service.Service):
# it can host the memberof attribute, then also add it to the
# dnsserver role group, this way the DNS is allowed to perform
# DNS Updates
- dns_group = "cn=DNS Servers,cn=privileges,cn=pbac,%s" % self.suffix
- if isinstance(dns_principal, unicode):
- dns_principal = dns_principal.encode('utf-8')
+ dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'), ('cn', 'pbac'), self.suffix)
mod = [(ldap.MOD_ADD, 'member', dns_principal)]
try:
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index dc4374cce..b00ceeaed 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -39,7 +39,7 @@ from ipapython import dogtag
from ipapython.certdb import get_ca_nickname
from ipapython import certmonger
from ipalib import pkcs10, x509
-from ipalib.dn import DN
+from ipapython.dn import DN
import subprocess
from nss.error import NSPRError
@@ -243,7 +243,9 @@ class CADSInstance(service.Service):
self.suffix = ipautil.realm_to_suffix(self.realm_name)
self.__setup_sub_dict()
else:
- self.suffix = None
+ self.suffix = DN()
+
+ subject_base = ipautil.dn_attribute_property('_subject_base')
def create_instance(self, realm_name, host_name, domain_name,
dm_password, pkcs12_info=None, ds_port=DEFAULT_DSPORT,
@@ -268,7 +270,7 @@ class CADSInstance(service.Service):
def __setup_sub_dict(self):
server_root = dsinstance.find_server_root()
self.sub_dict = dict(FQDN=self.fqdn, SERVERID=self.serverid,
- PASSWORD=self.dm_password, SUFFIX=self.suffix.lower(),
+ PASSWORD=self.dm_password, SUFFIX=self.suffix,
REALM=self.realm_name, USER=PKI_DS_USER,
SERVER_ROOT=server_root, DOMAIN=self.domain,
TIME=int(time.time()), DSPORT=self.ds_port,
@@ -342,7 +344,7 @@ class CADSInstance(service.Service):
def enable_ssl(self):
conn = ipaldap.IPAdmin("127.0.0.1", port=DEFAULT_DSPORT)
- conn.simple_bind_s("cn=directory manager", self.dm_password)
+ conn.simple_bind_s(DN(('cn', 'directory manager')), self.dm_password)
mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
(ldap.MOD_REPLACE, "nsSSL3Ciphers",
@@ -350,13 +352,13 @@ class CADSInstance(service.Service):
+rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,\
+fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,\
+tls_rsa_export1024_with_des_cbc_sha")]
- conn.modify_s("cn=encryption,cn=config", mod)
+ conn.modify_s(DN(('cn', 'encryption'), ('cn', 'config')), mod)
mod = [(ldap.MOD_ADD, "nsslapd-security", "on"),
(ldap.MOD_ADD, "nsslapd-secureport", str(DEFAULT_DSPORT+1))]
- conn.modify_s("cn=config", mod)
+ conn.modify_s(DN(('cn', 'config')), mod)
- entry = ipaldap.Entry("cn=RSA,cn=encryption,cn=config")
+ entry = ipaldap.Entry(DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')))
entry.setValues("objectclass", "top", "nsEncryptionModule")
entry.setValues("cn", "RSA")
@@ -460,7 +462,7 @@ class CAInstance(service.Service):
# will already have been initialized by Apache by the time
# mod_python wants to do things.
self.canickname = get_ca_nickname(realm)
- self.basedn = "o=ipaca"
+ self.basedn = DN(('o', 'ipaca'))
self.ca_agent_db = tempfile.mkdtemp(prefix = "tmp-")
self.ra_agent_db = ra_db
self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
@@ -506,7 +508,7 @@ class CAInstance(service.Service):
self.clone = True
self.master_host = master_host
if subject_base is None:
- self.subject_base = "O=%s" % self.realm
+ self.subject_base = DN(('O', self.realm))
else:
self.subject_base = subject_base
@@ -615,12 +617,12 @@ class CAInstance(service.Service):
"-agent_name", "ipa-ca-agent",
"-agent_key_size", "2048",
"-agent_key_type", "rsa",
- "-agent_cert_subject", "CN=ipa-ca-agent,%s" % self.subject_base,
+ "-agent_cert_subject", str(DN(('CN', 'ipa-ca-agent'), self.subject_base)),
"-ldap_host", self.fqdn,
"-ldap_port", str(self.ds_port),
"-bind_dn", "cn=Directory Manager",
"-bind_password", self.dm_password,
- "-base_dn", self.basedn,
+ "-base_dn", str(self.basedn),
"-db_name", "ipaca",
"-key_size", "2048",
"-key_type", "rsa",
@@ -629,11 +631,12 @@ class CAInstance(service.Service):
"-backup_pwd", self.admin_password,
"-subsystem_name", self.service_name,
"-token_name", "internal",
- "-ca_subsystem_cert_subject_name", "CN=CA Subsystem,%s" % self.subject_base,
- "-ca_ocsp_cert_subject_name", "CN=OCSP Subsystem,%s" % self.subject_base,
- "-ca_server_cert_subject_name", "CN=%s,%s" % (self.fqdn, self.subject_base),
- "-ca_audit_signing_cert_subject_name", "CN=CA Audit,%s" % self.subject_base,
- "-ca_sign_cert_subject_name", "CN=Certificate Authority,%s" % self.subject_base ]
+ "-ca_subsystem_cert_subject_name", str(DN(('CN', 'CA Subsystem'), self.subject_base)),
+ "-ca_subsystem_cert_subject_name", str(DN(('CN', 'CA Subsystem'), self.subject_base)),
+ "-ca_ocsp_cert_subject_name", str(DN(('CN', 'OCSP Subsystem'), self.subject_base)),
+ "-ca_server_cert_subject_name", str(DN(('CN', self.fqdn), self.subject_base)),
+ "-ca_audit_signing_cert_subject_name", str(DN(('CN', 'CA Audit'), self.subject_base)),
+ "-ca_sign_cert_subject_name", str(DN(('CN', 'Certificate Authority'), self.subject_base)) ]
if self.external == 1:
args.append("-external")
args.append("true")
@@ -836,13 +839,12 @@ class CAInstance(service.Service):
# Create an RA user in the CA LDAP server and add that user to
# the appropriate groups so it can issue certificates without
# manual intervention.
- ld = ldap.initialize("ldap://%s" % ipautil.format_netloc(self.fqdn, self.ds_port))
- ld.protocol_version=ldap.VERSION3
- ld.simple_bind_s("cn=Directory Manager", self.dm_password)
+ conn = ipaldap.IPAdmin(self.fqdn, self.ds_port)
+ conn.simple_bind_s(DN(('cn', 'Directory Manager')), self.dm_password)
decoded = base64.b64decode(self.ra_cert)
- entry_dn = "uid=%s,ou=People,%s" % ("ipara", self.basedn)
+ entry_dn = DN(('uid', "ipara"), ('ou', 'People'), self.basedn)
entry = [
('objectClass', ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'cmsuser']),
('uid', "ipara"),
@@ -851,19 +853,23 @@ class CAInstance(service.Service):
('usertype', "agentType"),
('userstate', "1"),
('userCertificate', decoded),
- ('description', '2;%s;CN=Certificate Authority,%s;CN=IPA RA,%s' % (str(self.requestId), self.subject_base, self.subject_base)),]
+ ('description', '2;%s;%s;%s' % \
+ (str(self.requestId),
+ DN(('CN', 'Certificate Authority'), self.subject_base),
+ DN(('CN', 'IPA RA'), self.subject_base))),
+ ]
- ld.add_s(entry_dn, entry)
+ conn.add_s(entry_dn, entry)
- dn = "cn=Certificate Manager Agents,ou=groups,%s" % self.basedn
+ dn = DN(('cn', 'Certificate Manager Agents'), ('ou', 'groups'), self.basedn)
modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
- ld.modify_s(dn, modlist)
+ conn.modify_s(dn, modlist)
- dn = "cn=Registration Manager Agents,ou=groups,%s" % self.basedn
+ dn = DN(('cn', 'Registration Manager Agents'), ('ou', 'groups'), self.basedn)
modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
- ld.modify_s(dn, modlist)
+ conn.modify_s(dn, modlist)
- ld.unbind_s()
+ conn.unbind_s()
def __run_certutil(self, args, database=None, pwd_file=None,stdin=None):
if not database:
@@ -969,7 +975,7 @@ class CAInstance(service.Service):
# Generate our CSR. The result gets put into stdout
try:
- (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=IPA RA,%s" % self.subject_base, "-z", noise_name, "-a"])
+ (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", str(DN(('CN', 'IPA RA'), self.subject_base)), "-z", noise_name, "-a"])
finally:
os.remove(noise_name)
@@ -1071,7 +1077,7 @@ class CAInstance(service.Service):
def __set_subject_in_config(self):
# dogtag ships with an IPA-specific profile that forces a subject
# format. We need to update that template with our base subject
- if installutils.update_file(IPA_SERVICE_PROFILE, 'OU=pki-ipa, O=IPA', self.subject_base):
+ if installutils.update_file(IPA_SERVICE_PROFILE, 'OU=pki-ipa, O=IPA', str(self.subject_base)):
print "Updating subject_base in CA template failed"
def uninstall(self):
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index d25a471ea..eebaa48c4 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -39,7 +39,7 @@ from ipalib import pkcs10
from ConfigParser import RawConfigParser, MissingSectionHeaderError
from ipapython import services as ipaservices
from ipalib import x509
-from ipalib.dn import DN
+from ipapython.dn import DN
from ipalib.errors import CertificateOperationError
from nss.error import NSPRError
@@ -224,8 +224,7 @@ class CertDB(object):
self.self_signed_ca = ipa_self_signed()
if not subject_base:
- self.subject_base = "O=IPA"
- self.subject_format = "CN=%%s,%s" % self.subject_base
+ self.subject_base = DN(('O', 'IPA'))
self.cacert_name = get_ca_nickname(self.realm)
self.valid_months = "120"
@@ -245,6 +244,8 @@ class CertDB(object):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ subject_base = ipautil.dn_attribute_property('_subject_base')
+
def __del__(self):
if self.reqdir is not None:
shutil.rmtree(self.reqdir, ignore_errors=True)
@@ -381,11 +382,11 @@ class CertDB(object):
def create_ca_cert(self):
os.chdir(self.secdir)
- subject = "cn=%s Certificate Authority" % self.realm
+ subject = DN(('cn', '%s Certificate Authority' % self.realm))
p = subprocess.Popen(["/usr/bin/certutil",
"-d", self.secdir,
"-S", "-n", self.cacert_name,
- "-s", subject,
+ "-s", str(subject),
"-x",
"-t", "CT,,C",
"-1",
@@ -565,7 +566,7 @@ class CertDB(object):
if not cdb:
cdb = self
if subject is None:
- subject=self.subject_format % hostname
+ subject=DN(('CN', hostname), self.subject_base)
self.request_cert(subject)
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
@@ -583,7 +584,7 @@ class CertDB(object):
if not cdb:
cdb = self
if subject is None:
- subject=self.subject_format % hostname
+ subject=DN(('CN', hostname), self.subject_base)
self.request_cert(subject)
cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
@@ -591,9 +592,10 @@ class CertDB(object):
os.unlink(self.certder_fname)
def request_cert(self, subject, certtype="rsa", keysize="2048"):
+ assert isinstance(subject, DN)
self.create_noise_file()
self.setup_cert_request()
- args = ["-R", "-s", subject,
+ args = ["-R", "-s", str(subject),
"-o", self.certreq_fname,
"-k", certtype,
"-g", keysize,
@@ -1046,19 +1048,19 @@ class CertDB(object):
# Prepare a simple cert request
req_dict = dict(PASSWORD=self.gen_password(),
SUBJBASE=self.subject_base,
- CERTNAME="CN="+nickname)
+ CERTNAME=DN(('CN', nickname)))
req_template = ipautil.SHARE_DIR + reqcfg + ".template"
conf = ipautil.template_file(req_template, req_dict)
fd = open(reqcfg, "w+")
fd.write(conf)
fd.close()
- base = self.subject_base.replace(",", "/")
- esc_subject = "CN=%s/%s" % (nickname, base)
+ base = str(self.subject_base).replace(",", "/")
+ esc_subject = DN(('CN', '%s/%s' % (nickname, base)))
ipautil.run(["/usr/bin/openssl", "req", "-new",
"-config", reqcfg,
- "-subj", esc_subject,
+ "-subj", str(esc_subject),
"-key", key_fname,
"-out", "kdc.req"])
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 9f3ae7252..bf6677381 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -36,12 +36,12 @@ import service
import installutils
import certs
import ldap
-from ldap.dn import escape_dn_chars
from ipaserver import ipaldap
from ipaserver.install import ldapupdate
from ipaserver.install import httpinstance
from ipaserver.install import replication
from ipalib import util, errors
+from ipapython.dn import DN
from ipaserver.plugins.ldap2 import ldap2
SERVER_ROOT_64 = "/usr/lib64/dirsrv"
@@ -177,7 +177,7 @@ class DsInstance(service.Service):
self.suffix = ipautil.realm_to_suffix(self.realm_name)
self.__setup_sub_dict()
else:
- self.suffix = None
+ self.suffix = DN()
if fstore:
self.fstore = fstore
@@ -185,6 +185,8 @@ class DsInstance(service.Service):
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ subject_base = ipautil.dn_attribute_property('_subject_base')
+
def __common_setup(self):
self.step("creating directory server user", self.__create_ds_user)
@@ -299,7 +301,7 @@ class DsInstance(service.Service):
self.fqdn,
self.dm_password)
repl.setup_replication(self.master_fqdn,
- r_binddn="cn=Directory Manager",
+ r_binddn=DN(('cn', 'Directory Manager')),
r_bindpw=self.dm_password)
self.run_init_memberof = repl.needs_memberof_fixup()
@@ -314,12 +316,12 @@ class DsInstance(service.Service):
self.sub_dict = dict(FQDN=self.fqdn, SERVERID=self.serverid,
PASSWORD=self.dm_password,
RANDOM_PASSWORD=self.generate_random(),
- SUFFIX=self.suffix.lower(),
+ SUFFIX=self.suffix,
REALM=self.realm_name, USER=DS_USER,
SERVER_ROOT=server_root, DOMAIN=self.domain,
TIME=int(time.time()), IDSTART=self.idstart,
IDMAX=self.idmax, HOST=self.fqdn,
- ESCAPED_SUFFIX= escape_dn_chars(self.suffix.lower()),
+ ESCAPED_SUFFIX=str(self.suffix),
GROUP=DS_GROUP,
IDRANGE_SIZE=self.idmax-self.idstart+1
)
@@ -445,11 +447,12 @@ class DsInstance(service.Service):
self._ldap_mod("memberof-task.ldif", self.sub_dict)
# Note, keep dn in sync with dn in install/share/memberof-task.ldif
- dn = "cn=IPA install %s,cn=memberof task,cn=tasks,cn=config" % self.sub_dict["TIME"]
+ dn = DN(('cn', 'IPA install %s' % self.sub_dict["TIME"]), ('cn', 'memberof task'),
+ ('cn', 'tasks'), ('cn', 'config'))
root_logger.debug("Waiting for memberof task to complete.")
conn = ipaldap.IPAdmin("127.0.0.1")
if self.dm_password:
- conn.simple_bind_s("cn=directory manager", self.dm_password)
+ conn.simple_bind_s(DN(('cn', 'directory manager')), self.dm_password)
else:
conn.do_sasl_gssapi_bind()
conn.checkTask(dn, dowait=True)
@@ -543,7 +546,7 @@ class DsInstance(service.Service):
dsdb.create_pin_file()
conn = ipaldap.IPAdmin("127.0.0.1")
- conn.simple_bind_s("cn=directory manager", self.dm_password)
+ conn.simple_bind_s(DN(('cn', 'directory manager')), self.dm_password)
mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
(ldap.MOD_REPLACE, "nsSSL3Ciphers",
@@ -551,12 +554,12 @@ class DsInstance(service.Service):
+rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,\
+fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,\
+tls_rsa_export1024_with_des_cbc_sha")]
- conn.modify_s("cn=encryption,cn=config", mod)
+ conn.modify_s(DN(('cn', 'encryption'), ('cn', 'config')), mod)
mod = [(ldap.MOD_ADD, "nsslapd-security", "on")]
- conn.modify_s("cn=config", mod)
+ conn.modify_s(DN(('cn', 'config')), mod)
- entry = ipaldap.Entry("cn=RSA,cn=encryption,cn=config")
+ entry = ipaldap.Entry(DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')))
entry.setValues("objectclass", "top", "nsEncryptionModule")
entry.setValues("cn", "RSA")
@@ -612,9 +615,9 @@ class DsInstance(service.Service):
os.close(admpwdfd)
args = ["/usr/bin/ldappasswd", "-h", self.fqdn,
- "-ZZ", "-x", "-D", "cn=Directory Manager",
+ "-ZZ", "-x", "-D", str(DN(('cn', 'Directory Manager'))),
"-y", dmpwdfile, "-T", admpwdfile,
- "uid=admin,cn=users,cn=accounts,"+self.suffix]
+ str(DN(('uid', 'admin'), ('cn', 'users'), ('cn', 'accounts'), self.suffix))]
try:
env = { 'LDAPTLS_CACERTDIR':os.path.dirname(CACERT),
'LDAPTLS_CACERT':CACERT }
@@ -801,22 +804,19 @@ class DsInstance(service.Service):
def replica_populate(self):
self.ldap_connect()
- dn = "cn=default,ou=profile,%s" % self.suffix
+ dn = DN(('cn', 'default'), ('ou', 'profile'), self.suffix)
try:
- ret = self.admin_conn.search_s(dn, ldap.SCOPE_BASE,
- '(objectclass=*)')[0]
- srvlist = ret.data.get('defaultServerList')
- if len(srvlist) > 0:
- srvlist = srvlist[0].split()
+ entry = self.admin_conn.getEntry(dn, ldap.SCOPE_BASE, '(objectclass=*)')
+ srvlist = entry.getValue('defaultServerList', '')
+ srvlist = srvlist.split()
if not self.fqdn in srvlist:
srvlist.append(self.fqdn)
attr = ' '.join(srvlist)
mod = [(ldap.MOD_REPLACE, 'defaultServerList', attr)]
self.admin_conn.modify_s(dn, mod)
- except ldap.NO_SUCH_OBJECT:
+ except errors.NotFound:
pass
except ldap.TYPE_OR_VALUE_EXISTS:
pass
self.ldap_disconnect()
-
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 601f76bb7..e5d9f080b 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -57,6 +57,8 @@ class HTTPInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ subject_base = ipautil.dn_attribute_property('_subject_base')
+
def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False, subject_base=None, auto_redirect=True):
self.fqdn = fqdn
self.realm = realm
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 388a11e26..048706523 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -44,6 +44,7 @@ from ipapython.ipa_log_manager import *
from ipalib.util import validate_hostname
from ipapython import config
from ipalib import errors
+from ipapython.dn import DN
# Used to determine install status
IPA_MODULES = ['httpd', 'kadmin', 'dirsrv', 'pki-cad', 'pkids', 'install', 'krb5kdc', 'ntpd', 'named', 'ipa_memcached']
@@ -71,9 +72,11 @@ class ReplicaConfig:
self.dirman_password = ""
self.host_name = ""
self.dir = ""
- self.subject_base = ""
+ self.subject_base = None
self.setup_ca = False
+ subject_base = ipautil.dn_attribute_property('_subject_base')
+
def get_fqdn():
fqdn = ""
try:
diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index 794ea28b5..d9f680927 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -150,6 +150,8 @@ class LDAPUpdater_Upgrade(LDAPUpdater):
class LDAPUpdater_NonUpgrade(LDAPUpdater):
+ log_file_name = '/var/log/ipaupgrade.log'
+
def validate_options(self):
super(LDAPUpdater_NonUpgrade, self).validate_options()
options = self.options
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 8cc50fba4..9101e6fcc 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -33,6 +33,7 @@ from ipapython import services as ipaservices
from ipalib import util
from ipalib import errors
from ipapython.ipa_log_manager import *
+from ipapython.dn import DN
from ipaserver import ipaldap
from ipaserver.install import replication
@@ -84,6 +85,7 @@ class KrbInstance(service.Service):
self.admin_password = None
self.master_password = None
self.suffix = None
+ self.subject_base = None
self.kdc_password = None
self.sub_dict = None
self.pkcs12_info = None
@@ -94,8 +96,11 @@ class KrbInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ suffix = ipautil.dn_attribute_property('_suffix')
+ subject_base = ipautil.dn_attribute_property('_subject_base')
+
def get_realm_suffix(self):
- return "cn=%s,cn=kerberos,%s" % (self.realm, self.suffix)
+ return DN(('cn', self.realm), ('cn', 'kerberos'), self.suffix)
def move_service_to_host(self, principal):
"""
@@ -103,12 +108,12 @@ class KrbInstance(service.Service):
cn=kerberos to reside under the host entry.
"""
- service_dn = "krbprincipalname=%s,%s" % (principal, self.get_realm_suffix())
+ service_dn = DN(('krbprincipalname', principal), self.get_realm_suffix())
service_entry = self.admin_conn.getEntry(service_dn, ldap.SCOPE_BASE)
self.admin_conn.deleteEntry(service_dn)
# Create a host entry for this master
- host_dn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
+ host_dn = DN(('fqdn', self.fqdn), ('cn', 'computers'), ('cn', 'accounts'), self.suffix)
host_entry = ipaldap.Entry(host_dn)
host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'ipaservice', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux', 'ipasshhost'])
host_entry.setValues('krbextradata', service_entry.getValues('krbextradata'))
@@ -251,7 +256,7 @@ class KrbInstance(service.Service):
# they may conflict.
try:
- res = self.admin_conn.search_s("cn=mapping,cn=sasl,cn=config",
+ res = self.admin_conn.search_s(DN(('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')),
ldap.SCOPE_ONELEVEL,
"(objectclass=nsSaslMapping)")
for r in res:
@@ -264,7 +269,7 @@ class KrbInstance(service.Service):
root_logger.critical("Error while enumerating SASL mappings %s" % str(e))
raise e
- entry = ipaldap.Entry("cn=Full Principal,cn=mapping,cn=sasl,cn=config")
+ entry = ipaldap.Entry(DN(('cn', 'Full Principal'), ('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')))
entry.setValues("objectclass", "top", "nsSaslMapping")
entry.setValues("cn", "Full Principal")
entry.setValues("nsSaslMapRegexString", '\(.*\)@\(.*\)')
@@ -277,7 +282,7 @@ class KrbInstance(service.Service):
root_logger.critical("failed to add Full Principal Sasl mapping")
raise e
- entry = ipaldap.Entry("cn=Name Only,cn=mapping,cn=sasl,cn=config")
+ entry = ipaldap.Entry(DN(('cn', 'Name Only'), ('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')))
entry.setValues("objectclass", "top", "nsSaslMapping")
entry.setValues("cn", "Name Only")
entry.setValues("nsSaslMapRegexString", '^[^:@]+$')
@@ -358,7 +363,7 @@ class KrbInstance(service.Service):
root_logger.critical("Could not find master key in DS")
raise e
- krbMKey = pyasn1.codec.ber.decoder.decode(entry.krbmkey)
+ krbMKey = pyasn1.codec.ber.decoder.decode(entry.getValue('krbmkey'))
keytype = int(krbMKey[0][1][0])
keydata = str(krbMKey[0][1][1])
@@ -431,7 +436,7 @@ class KrbInstance(service.Service):
# Create the special anonymous principal
installutils.kadmin_addprinc(princ_realm)
- dn = "krbprincipalname=%s,%s" % (princ_realm, self.get_realm_suffix())
+ dn = DN(('krbprincipalname', princ_realm), self.get_realm_suffix())
self.admin_conn.inactivateEntry(dn, False)
def __convert_to_gssapi_replication(self):
@@ -439,7 +444,7 @@ class KrbInstance(service.Service):
self.fqdn,
self.dm_password)
repl.convert_to_gssapi_replication(self.master_fqdn,
- r_binddn="cn=Directory Manager",
+ r_binddn=DN(('cn', 'Directory Manager')),
r_bindpw=self.dm_password)
def uninstall(self):
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 949b86ad9..a51edff99 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -33,9 +33,8 @@ import uuid
from ipalib import util
from ipalib import errors
from ipalib import api
-from ipalib.dn import DN
+from ipapython.dn import DN
import ldap
-from ldap.dn import escape_dn_chars
from ipapython.ipa_log_manager import *
import krbV
import platform
@@ -52,22 +51,62 @@ from ipaserver.install.plugins import FIRST, MIDDLE, LAST
class BadSyntax(installutils.ScriptError):
def __init__(self, value):
self.value = value
- self.msg = "There is a syntax error in this update file: \n %s" % value
+ self.msg = "LDAPUpdate: syntax error: \n %s" % value
self.rval = 1
def __str__(self):
return repr(self.value)
class LDAPUpdate:
+ action_keywords = ["default", "add", "remove", "only", "deleteentry", "replace", "addifnew", "addifexist"]
+
def __init__(self, dm_password, sub_dict={}, live_run=True,
online=True, ldapi=False, plugins=False):
- """dm_password = Directory Manager password
- sub_dict = substitution dictionary
- live_run = Apply the changes or just test
- online = do an online LDAP update or use an experimental LDIF updater
- ldapi = bind using ldapi. This assumes autobind is enabled.
- plugins = execute the pre/post update plugins
- """
+ '''
+ :parameters:
+ dm_password
+ Directory Manager password
+ sub_dict
+ substitution dictionary
+ live_run
+ Apply the changes or just test
+ online
+ Do an online LDAP update or use an experimental LDIF updater
+ ldapi
+ Bind using ldapi. This assumes autobind is enabled.
+ plugins
+ execute the pre/post update plugins
+
+ Data Structure Example:
+ -----------------------
+
+ dn_by_rdn_count = {
+ 3: 'cn=config,dc=example,dc=com':
+ 4: 'cn=bob,ou=people,dc=example,dc=com',
+ }
+
+ all_updates = {
+ 'dn': 'cn=config,dc=example,dc=com':
+ {
+ 'dn': 'cn=config,dc=example,dc=com',
+ 'default': ['attr1':default1'],
+ 'updates': ['action:attr1:value1',
+ 'action:attr2:value2]
+ },
+ 'dn': 'cn=bob,ou=people,dc=example,dc=com':
+ {
+ 'dn': 'cn=bob,ou=people,dc=example,dc=com',
+ 'default': ['attr3':default3'],
+ 'updates': ['action:attr3:value3',
+ 'action:attr4:value4],
+ }
+ }
+
+ The default and update lists are "dispositions"
+
+
+ '''
+ log_mgr.get_logger(self, True)
self.sub_dict = sub_dict
self.live_run = live_run
self.dm_password = dm_password
@@ -77,6 +116,8 @@ class LDAPUpdate:
self.ldapi = ldapi
self.plugins = plugins
self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
+ self.realm = None
+ suffix = None
if sub_dict.get("REALM"):
self.realm = sub_dict["REALM"]
@@ -89,8 +130,10 @@ class LDAPUpdate:
self.realm = None
suffix = None
+ if suffix is not None:
+ assert isinstance(suffix, DN)
domain = ipautil.get_domain_name()
- libarch = self.__identify_arch()
+ libarch = self._identify_arch()
fqdn = installutils.get_fqdn()
if fqdn is None:
@@ -110,7 +153,7 @@ class LDAPUpdate:
if not self.sub_dict.get("SUFFIX") and suffix is not None:
self.sub_dict["SUFFIX"] = suffix
if not self.sub_dict.get("ESCAPED_SUFFIX"):
- self.sub_dict["ESCAPED_SUFFIX"] = escape_dn_chars(suffix)
+ self.sub_dict["ESCAPED_SUFFIX"] = str(suffix)
if not self.sub_dict.get("LIBARCH"):
self.sub_dict["LIBARCH"] = libarch
if not self.sub_dict.get("TIME"):
@@ -123,7 +166,7 @@ class LDAPUpdate:
try:
conn = ipaldap.IPAdmin(fqdn, ldapi=self.ldapi, realm=self.realm)
if self.dm_password:
- conn.do_simple_bind(binddn="cn=directory manager", bindpw=self.dm_password)
+ conn.do_simple_bind(binddn=DN(('cn', 'directory manager')), bindpw=self.dm_password)
elif os.getegid() == 0:
try:
# autobind
@@ -145,13 +188,13 @@ class LDAPUpdate:
# The following 2 functions were taken from the Python
# documentation at http://docs.python.org/library/csv.html
- def __utf_8_encoder(self, unicode_csv_data):
+ def _utf_8_encoder(self, unicode_csv_data):
for line in unicode_csv_data:
yield line.encode('utf-8')
- def __unicode_csv_reader(self, unicode_csv_data, quote_char="'", dialect=csv.excel, **kwargs):
+ def _unicode_csv_reader(self, unicode_csv_data, quote_char="'", dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
- csv_reader = csv.reader(self.__utf_8_encoder(unicode_csv_data),
+ csv_reader = csv.reader(self._utf_8_encoder(unicode_csv_data),
dialect=dialect, delimiter=',',
quotechar=quote_char,
skipinitialspace=True,
@@ -160,7 +203,7 @@ class LDAPUpdate:
# decode UTF-8 back to Unicode, cell by cell:
yield [unicode(cell, 'utf-8') for cell in row]
- def __identify_arch(self):
+ def _identify_arch(self):
"""On multi-arch systems some libraries may be in /lib64, /usr/lib64,
etc. Determine if a suffix is needed based on the current
architecture.
@@ -178,7 +221,7 @@ class LDAPUpdate:
except KeyError, e:
raise BadSyntax("Unknown template keyword %s" % e)
- def __parse_values(self, line):
+ def _parse_values(self, line):
"""Parse a comma-separated string into separate values and convert them
into a list. This should handle quoted-strings with embedded commas
"""
@@ -186,7 +229,7 @@ class LDAPUpdate:
quote_char = "'"
else:
quote_char = '"'
- reader = self.__unicode_csv_reader([line], quote_char)
+ reader = self._unicode_csv_reader([line], quote_char)
value = []
for row in reader:
value = value + row
@@ -201,7 +244,7 @@ class LDAPUpdate:
if fd != sys.stdin: fd.close()
return text
- def __entry_to_entity(self, ent):
+ def _entry_to_entity(self, ent):
"""Tne Entry class is a bare LDAP entry. The Entity class has a lot more
helper functions that we need, so convert to dict and then to Entity.
"""
@@ -215,113 +258,158 @@ class LDAPUpdate:
entry[key] = value[0]
return entity.Entity(entry)
- def __combine_updates(self, dn_list, all_updates, update):
- """Combine a new update with the list of total updates
-
- Updates are stored in 2 lists:
- dn_list: contains a unique list of DNs in the updates
- all_updates: the actual updates that need to be applied
-
- We want to apply the updates from the shortest to the longest
- path so if new child and parent entries are in different updates
- we can be sure the parent gets written first. This also lets
- us apply any schema first since it is in the very short cn=schema.
- """
+ def _combine_updates(self, all_updates, update):
+ 'Combine a new update with the list of total updates'
dn = update.get('dn')
- dns = ldap.explode_dn(dn.lower())
- l = len(dns)
- if dn_list.get(l):
- if dn not in dn_list[l]:
- dn_list[l].append(dn)
- else:
- dn_list[l] = [dn]
+ assert isinstance(dn, DN)
+
if not all_updates.get(dn):
all_updates[dn] = update
return all_updates
- e = all_updates[dn]
+ existing_update = all_updates[dn]
if 'default' in update:
- if 'default' in e:
- e['default'] = e['default'] + update['default']
- else:
- e['default'] = update['default']
+ disposition_list = existing_update.setdefault('default', [])
+ disposition_list.extend(update['default'])
elif 'updates' in update:
- if 'updates' in e:
- e['updates'] = e['updates'] + update['updates']
- else:
- e['updates'] = update['updates']
+ disposition_list = existing_update.setdefault('updates', [])
+ disposition_list.extend(update['updates'])
else:
- root_logger.debug("Unknown key in updates %s" % update.keys())
-
- all_updates[dn] = e
+ self.debug("Unknown key in updates %s" % update.keys())
+
+ def merge_updates(self, all_updates, updates):
+ '''
+ Add the new_update dict to the all_updates dict. If an entry
+ in the new_update already has an entry in all_updates merge
+ the two entries sensibly assuming the new entries take
+ precedence. Otherwise just add the new entry.
+ '''
+
+ for new_update in updates:
+ for new_dn, new_entry in new_update.iteritems():
+ existing_entry = all_updates.get(new_dn)
+ if existing_entry:
+ # If the existing entry is marked for deletion but the
+ # new entry is not also a delete then clear the delete
+ # flag otherwise the newer update will be lost.
+ if existing_entry.has_key('deleteentry') and not new_entry.has_key('deleteentry'):
+ self.warning("ldapupdate: entry '%s' previously marked for deletion but" +
+ " this subsequent update reestablishes it: %s", new_dn, new_entry)
+ del existing_entry['deleteentry']
+ existing_entry.update(new_entry)
+ else:
+ all_updates[new_dn] = new_entry
- return all_updates
- def parse_update_file(self, data, all_updates, dn_list):
+ def parse_update_file(self, data_source_name, source_data, all_updates):
"""Parse the update file into a dictonary of lists and apply the update
for each DN in the file."""
- valid_keywords = ["default", "add", "remove", "only", "deleteentry", "replace", "addifnew", "addifexist"]
update = {}
- d = ""
- index = ""
+ logical_line = ""
+ action = ""
dn = None
lcount = 0
- for line in data:
- # Strip out \n and extra white space
- lcount = lcount + 1
- # skip comments and empty lines
- line = line.rstrip()
- if line.startswith('#') or line == '': continue
+ def emit_item(logical_line):
+ '''
+ Given a logical line containing an item to process perform the following:
- if line.lower().startswith('dn:'):
- if dn is not None:
- all_updates = self.__combine_updates(dn_list, all_updates, update)
+ * Strip leading & trailing whitespace
+ * Substitute any variables
+ * Get the action, attribute, and value
+ * Each update has one list per disposition, append to specified disposition list
+ '''
- update = {}
- dn = line[3:].strip()
- update['dn'] = self._template_str(dn)
- else:
- if dn is None:
- raise BadSyntax, "dn is not defined in the update"
-
- line = self._template_str(line)
- if line.startswith(' '):
- v = d[len(d) - 1]
- v = v + line[1:]
- d[len(d) - 1] = v
- update[index] = d
- continue
- line = line.strip()
- values = line.split(':', 2)
- if len(values) != 3:
- raise BadSyntax, "Bad formatting on line %d: %s" % (lcount,line)
+ logical_line = logical_line.strip()
+ if logical_line == '':
+ return
+
+ # Perform variable substitution on constructued line
+ logical_line = self._template_str(logical_line)
- index = values[0].strip().lower()
+ items = logical_line.split(':', 2)
- if index not in valid_keywords:
- raise BadSyntax, "Unknown keyword %s" % index
+ if len(items) == 0:
+ raise BadSyntax, "Bad formatting on line %s:%d: %s" % (data_source_name, lcount, logical_line)
- attr = values[1].strip()
- value = values[2].strip()
+ action = items[0].strip().lower()
- new_value = ""
- if index == "default":
+ if action not in self.action_keywords:
+ raise BadSyntax, "Unknown update action '%s', data source=%s" % (action, data_source_name)
+
+ if action == 'deleteentry':
+ new_value = None
+ disposition = "deleteentry"
+ else:
+ if len(items) != 3:
+ raise BadSyntax, "Bad formatting on line %s:%d: %s" % (data_source_name, lcount, logical_line)
+
+ attr = items[1].strip()
+ value = items[2].strip()
+
+ if action == "default":
new_value = attr + ":" + value
+ disposition = "default"
else:
- new_value = index + ":" + attr + ":" + value
- index = "updates"
+ new_value = action + ":" + attr + ":" + value
+ disposition = "updates"
+
+ disposition_list = update.setdefault(disposition, [])
+ disposition_list.append(new_value)
+
+ def emit_update(update):
+ '''
+ When processing a dn is completed emit the update by merging it into
+ the set of all updates.
+ '''
+
+ self._combine_updates(all_updates, update)
+
+ # Iterate over source input lines
+ for source_line in source_data:
+ lcount += 1
+
+ # strip trailing whitespace and newline
+ source_line = source_line.rstrip()
- d = update.get(index, [])
+ # skip comments and empty lines
+ if source_line.startswith('#') or source_line == '':
+ continue
- d.append(new_value)
+ if source_line.lower().startswith('dn:'):
+ # Starting new dn
+ if dn is not None:
+ # Emit previous dn
+ emit_item(logical_line)
+ logical_line = ''
+ emit_update(update)
+ update = {}
+
+ dn = source_line[3:].strip()
+ dn = DN(self._template_str(dn))
+ update['dn'] = dn
+ else:
+ # Process items belonging to dn
+ if dn is None:
+ raise BadSyntax, "dn is not defined in the update, data source=%s" % (data_source_name)
- update[index] = d
+ # If continuation line, append to existing logical line & continue,
+ # otherwise flush the previous item.
+ if source_line.startswith(' '):
+ logical_line += source_line[1:]
+ continue
+ else:
+ emit_item(logical_line)
+ logical_line = ''
+ logical_line = source_line
if dn is not None:
- all_updates = self.__combine_updates(dn_list, all_updates, update)
+ emit_item(logical_line)
+ logical_line = ''
+ emit_update(update)
+ update = {}
- return (all_updates, dn_list)
+ return all_updates
def create_index_task(self, attribute):
"""Create a task to update an index for an attribute"""
@@ -337,15 +425,15 @@ class LDAPUpdate:
cn = "indextask_%s_%s_%s" % (attribute, cn_uuid.time, cn_uuid.clock_seq)
dn = DN(('cn', cn), ('cn', 'index'), ('cn', 'tasks'), ('cn', 'config'))
- e = ipaldap.Entry(str(dn))
+ e = ipaldap.Entry(dn)
e.setValues('objectClass', ['top', 'extensibleObject'])
e.setValue('cn', cn)
e.setValue('nsInstance', 'userRoot')
e.setValues('nsIndexAttribute', attribute)
- root_logger.info("Creating task to index attribute: %s", attribute)
- root_logger.debug("Task id: %s", dn)
+ self.info("Creating task to index attribute: %s", attribute)
+ self.debug("Task id: %s", dn)
if self.live_run:
self.conn.addEntry(e)
@@ -356,6 +444,8 @@ class LDAPUpdate:
"""Give a task DN monitor it and wait until it has completed (or failed)
"""
+ assert isinstance(dn, DN)
+
if not self.live_run:
# If not doing this live there is nothing to monitor
return
@@ -370,10 +460,10 @@ class LDAPUpdate:
try:
entry = self.conn.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)", attrlist)
except errors.NotFound, e:
- root_logger.error("Task not found: %s", dn)
+ self.error("Task not found: %s", dn)
return
except errors.DatabaseError, e:
- root_logger.error("Task lookup failure %s", e)
+ self.error("Task lookup failure %s", e)
return
status = entry.getValue('nstaskstatus')
@@ -383,200 +473,158 @@ class LDAPUpdate:
continue
if status.lower().find("finished") > -1:
- root_logger.info("Indexing finished")
+ self.info("Indexing finished")
break
- root_logger.debug("Indexing in progress")
+ self.debug("Indexing in progress")
time.sleep(1)
return
- def __create_default_entry(self, dn, default):
+ def _create_default_entry(self, dn, default):
"""Create the default entry from the values provided.
The return type is entity.Entity
"""
+ assert isinstance(dn, DN)
entry = ipaldap.Entry(dn)
if not default:
# This means that the entire entry needs to be created with add
- return self.__entry_to_entity(entry)
+ return self._entry_to_entity(entry)
- for line in default:
+ for item in default:
# We already do syntax-parsing so this is safe
- (k, v) = line.split(':',1)
- e = entry.getValues(k)
+ (attr, value) = item.split(':',1)
+ e = entry.getValues(attr)
if e:
# multi-valued attribute
e = list(e)
- e.append(v)
+ e.append(value)
else:
- e = v
- entry.setValues(k, e)
+ e = value
+ entry.setValues(attr, e)
- return self.__entry_to_entity(entry)
+ return self._entry_to_entity(entry)
- def __get_entry(self, dn):
+ def _get_entry(self, dn):
"""Retrieve an object from LDAP.
The return type is ipaldap.Entry
"""
+ assert isinstance(dn, DN)
searchfilter="objectclass=*"
sattrs = ["*", "aci", "attributeTypes", "objectClasses"]
scope = ldap.SCOPE_BASE
return self.conn.getList(dn, scope, searchfilter, sattrs)
- def __update_managed_entries(self):
- """Update and move legacy Managed Entry Plugins."""
-
- suffix = ipautil.realm_to_suffix(self.realm)
- searchfilter = '(objectclass=*)'
- definitions_managed_entries = []
- old_template_container = 'cn=etc,%s' % suffix
- old_definition_container = 'cn=Managed Entries,cn=plugins,cn=config'
- new = 'cn=Managed Entries,cn=etc,%s' % suffix
- sub = ['cn=Definitions,', 'cn=Templates,']
- new_managed_entries = []
- old_templates = []
- template = None
- try:
- definitions_managed_entries = self.conn.getList(old_definition_container, ldap.SCOPE_ONELEVEL, searchfilter,[])
- except errors.NotFound, e:
- return new_managed_entries
- for entry in definitions_managed_entries:
- new_definition = {}
- definition_managed_entry_updates = {}
- definitions_managed_entries
- old_definition = {'dn': entry.dn, 'deleteentry': ['dn: %s' % entry.dn]}
- old_template = entry.getValue('managedtemplate')
- entry.setValues('managedtemplate', entry.getValue('managedtemplate').replace(old_template_container, sub[1] + new))
- new_definition['dn'] = entry.dn.replace(old_definition_container, sub[0] + new)
- new_definition['default'] = str(entry).strip().replace(': ', ':').split('\n')[1:]
- definition_managed_entry_updates[new_definition['dn']] = new_definition
- definition_managed_entry_updates[old_definition['dn']] = old_definition
- old_templates.append(old_template)
- new_managed_entries.append(definition_managed_entry_updates)
- for old_template in old_templates:
- try:
- template = self.conn.getEntry(old_template, ldap.SCOPE_BASE, searchfilter,[])
- new_template = {}
- template_managed_entry_updates = {}
- old_template = {'dn': template.dn, 'deleteentry': ['dn: %s' % template.dn]}
- new_template['dn'] = template.dn.replace(old_template_container, sub[1] + new)
- new_template['default'] = str(template).strip().replace(': ', ':').split('\n')[1:]
- template_managed_entry_updates[new_template['dn']] = new_template
- template_managed_entry_updates[old_template['dn']] = old_template
- new_managed_entries.append(template_managed_entry_updates)
- except errors.NotFound, e:
- pass
- if len(new_managed_entries) > 0:
- new_managed_entries.sort(reverse=True)
-
- return new_managed_entries
-
- def __apply_updates(self, updates, entry):
- """updates is a list of changes to apply
- entry is the thing to apply them to
+ def _apply_update_disposition(self, updates, entry):
+ """
+ updates is a list of changes to apply
+ entry is the thing to apply them to
- Returns the modified entry
+ Returns the modified entry
"""
if not updates:
return entry
only = {}
- for u in updates:
+ for update in updates:
# We already do syntax-parsing so this is safe
- (utype, k, values) = u.split(':',2)
- values = self.__parse_values(values)
-
- e = entry.getValues(k)
- if not isinstance(e, list):
- if e is None:
- e = []
+ (action, attr, update_values) = update.split(':',2)
+ update_values = self._parse_values(update_values)
+
+ # If the attribute is known to be a DN convert it to a DN object.
+ # This has to be done after _parse_values() due to quoting and comma separated lists.
+ if self.conn.has_dn_syntax(attr):
+ update_values = [DN(x) for x in update_values]
+
+ entry_values = entry.getValues(attr)
+ if not isinstance(entry_values, list):
+ if entry_values is None:
+ entry_values = []
else:
- e = [e]
- for v in values:
- if utype == 'remove':
- root_logger.debug("remove: '%s' from %s, current value %s", v, k, e)
+ entry_values = [entry_values]
+ for update_value in update_values:
+ if action == 'remove':
+ self.debug("remove: '%s' from %s, current value %s", update_value, attr, entry_values)
try:
- e.remove(v)
+ entry_values.remove(update_value)
except ValueError:
- root_logger.warning("remove: '%s' not in %s", v, k)
+ self.warning("remove: '%s' not in %s", update_value, attr)
pass
- entry.setValues(k, e)
- root_logger.debug('remove: updated value %s', e)
- elif utype == 'add':
- root_logger.debug("add: '%s' to %s, current value %s", v, k, e)
+ entry.setValues(attr, entry_values)
+ self.debug('remove: updated value %s', entry_values)
+ elif action == 'add':
+ self.debug("add: '%s' to %s, current value %s", update_value, attr, entry_values)
# Remove it, ignoring errors so we can blindly add it later
try:
- e.remove(v)
+ entry_values.remove(update_value)
except ValueError:
pass
- e.append(v)
- root_logger.debug('add: updated value %s', e)
- entry.setValues(k, e)
- elif utype == 'addifnew':
- root_logger.debug("addifnew: '%s' to %s, current value %s", v, k, e)
+ entry_values.append(update_value)
+ self.debug('add: updated value %s', entry_values)
+ entry.setValues(attr, entry_values)
+ elif action == 'addifnew':
+ self.debug("addifnew: '%s' to %s, current value %s", update_value, attr, entry_values)
# Only add the attribute if it doesn't exist. Only works
# with single-value attributes.
- if len(e) == 0:
- e.append(v)
- root_logger.debug('addifnew: set %s to %s', k, e)
- entry.setValues(k, e)
- elif utype == 'addifexist':
- root_logger.debug("addifexist: '%s' to %s, current value %s", v, k, e)
+ if len(entry_values) == 0:
+ entry_values.append(update_value)
+ self.debug('addifnew: set %s to %s', attr, entry_values)
+ entry.setValues(attr, entry_values)
+ elif action == 'addifexist':
+ self.debug("addifexist: '%s' to %s, current value %s", update_value, attr, entry_values)
# Only add the attribute if the entry doesn't exist. We
# determine this based on whether it has an objectclass
if entry.getValues('objectclass'):
- e.append(v)
- root_logger.debug('addifexist: set %s to %s', k, e)
- entry.setValues(k, e)
- elif utype == 'only':
- root_logger.debug("only: set %s to '%s', current value %s", k, v, e)
- if only.get(k):
- e.append(v)
+ entry_values.append(update_value)
+ self.debug('addifexist: set %s to %s', attr, entry_values)
+ entry.setValues(attr, entry_values)
+ elif action == 'only':
+ self.debug("only: set %s to '%s', current value %s", attr, update_value, entry_values)
+ if only.get(attr):
+ entry_values.append(update_value)
else:
- e = [v]
- only[k] = True
- entry.setValues(k, e)
- root_logger.debug('only: updated value %s', e)
- elif utype == 'deleteentry':
+ entry_values = [update_value]
+ only[attr] = True
+ entry.setValues(attr, entry_values)
+ self.debug('only: updated value %s', entry_values)
+ elif action == 'deleteentry':
# skip this update type, it occurs in __delete_entries()
return None
- elif utype == 'replace':
- # v has the format "old::new"
+ elif action == 'replace':
+ # value has the format "old::new"
try:
- (old, new) = v.split('::', 1)
+ (old, new) = update_value.split('::', 1)
except ValueError:
- raise BadSyntax, "bad syntax in replace, needs to be in the format old::new in %s" % v
+ raise BadSyntax, "bad syntax in replace, needs to be in the format old::new in %s" % update_value
try:
- e.remove(old)
- e.append(new)
- root_logger.debug('replace: updated value %s', e)
- entry.setValues(k, e)
+ entry_values.remove(old)
+ entry_values.append(new)
+ self.debug('replace: updated value %s', entry_values)
+ entry.setValues(attr, entry_values)
except ValueError:
- root_logger.debug('replace: %s not found, skipping', old)
-
- self.print_entity(entry)
+ self.debug('replace: %s not found, skipping', old)
return entry
def print_entity(self, e, message=None):
"""The entity object currently lacks a str() method"""
- root_logger.debug("---------------------------------------------")
+ self.debug("---------------------------------------------")
if message:
- root_logger.debug("%s", message)
- root_logger.debug("dn: " + e.dn)
+ self.debug("%s", message)
+ self.debug("dn: %s", e.dn)
attr = e.attrList()
for a in attr:
value = e.getValues(a)
- if isinstance(value,str):
- root_logger.debug(a + ": " + value)
- else:
- root_logger.debug(a + ": ")
+ if isinstance(value, (list, tuple)):
+ self.debug('%s:', a)
for l in value:
- root_logger.debug("\t" + l)
+ self.debug("\t%s", l)
+ else:
+ self.debug('%s: %s', a, value)
def is_schema_updated(self, s):
"""Compare the schema in 's' with the current schema in the DS to
@@ -598,48 +646,59 @@ class LDAPUpdate:
s = s.ldap_entry()
# Get a fresh copy and convert into a SubSchema
- n = self.__get_entry("cn=schema")[0]
- n = dict(n.data)
- n = ldap.schema.SubSchema(n)
+ n = self._get_entry(DN(('cn', 'schema')))[0]
+
+ # Convert IPA data types back to strings
+ d = dict()
+ for k,v in n.data.items():
+ d[k] = [str(x) for x in v]
+
+ # Convert to subschema dict
+ n = ldap.schema.SubSchema(d)
n = n.ldap_entry()
+ # Are they equal?
if s == n:
return False
else:
return True
- def __update_record(self, update):
+ def _update_record(self, update):
found = False
- new_entry = self.__create_default_entry(update.get('dn'),
- update.get('default'))
+ # If the entry is going to be deleted no point in processing it.
+ if update.has_key('deleteentry'):
+ return
+
+ new_entry = self._create_default_entry(update.get('dn'),
+ update.get('default'))
try:
- e = self.__get_entry(new_entry.dn)
+ e = self._get_entry(new_entry.dn)
if len(e) > 1:
# we should only ever get back one entry
raise BadSyntax, "More than 1 entry returned on a dn search!? %s" % new_entry.dn
- entry = self.__entry_to_entity(e[0])
+ entry = self._entry_to_entity(e[0])
found = True
- root_logger.info("Updating existing entry: %s", entry.dn)
+ self.info("Updating existing entry: %s", entry.dn)
except errors.NotFound:
# Doesn't exist, start with the default entry
entry = new_entry
- root_logger.info("New entry: %s", entry.dn)
+ self.info("New entry: %s", entry.dn)
except errors.DatabaseError:
# Doesn't exist, start with the default entry
entry = new_entry
- root_logger.info("New entry, using default value: %s", entry.dn)
+ self.info("New entry, using default value: %s", entry.dn)
- self.print_entity(entry)
+ self.print_entity(entry, "Initial value")
# Bring this entry up to date
- entry = self.__apply_updates(update.get('updates'), entry)
+ entry = self._apply_update_disposition(update.get('updates'), entry)
if entry is None:
# It might be None if it is just deleting an entry
return
- self.print_entity(entry, "Final value")
+ self.print_entity(entry, "Final value after applying updates")
if not found:
# New entries get their orig_data set to the entry itself. We want to
@@ -657,47 +716,47 @@ class LDAPUpdate:
except errors.NotFound:
# parent entry of the added entry does not exist
# this may not be an error (e.g. entries in NIS container)
- root_logger.info("Parent DN of %s may not exist, cannot create the entry",
+ self.info("Parent DN of %s may not exist, cannot create the entry",
entry.dn)
return
self.modified = True
except Exception, e:
- root_logger.error("Add failure %s", e)
+ self.error("Add failure %s", e)
else:
# Update LDAP
try:
updated = False
changes = self.conn.generateModList(entry.origDataDict(), entry.toDict())
- if (entry.dn == "cn=schema"):
+ if (entry.dn == DN(('cn', 'schema'))):
updated = self.is_schema_updated(entry.toDict())
else:
if len(changes) >= 1:
updated = True
- root_logger.debug("%s" % changes)
- root_logger.debug("Live %d, updated %d" % (self.live_run, updated))
+ self.debug("%s" % changes)
+ self.debug("Live %d, updated %d" % (self.live_run, updated))
if self.live_run and updated:
self.conn.updateEntry(entry.dn, entry.origDataDict(), entry.toDict())
- root_logger.info("Done")
+ self.info("Done")
except errors.EmptyModlist:
- root_logger.info("Entry already up-to-date")
+ self.info("Entry already up-to-date")
updated = False
except errors.DatabaseError, e:
- root_logger.error("Update failed: %s", e)
+ self.error("Update failed: %s", e)
updated = False
except errors.ACIError, e:
- root_logger.error("Update failed: %s", e)
+ self.error("Update failed: %s", e)
updated = False
- if ("cn=index" in entry.dn and
- "cn=userRoot" in entry.dn):
- taskid = self.create_index_task(entry.cn)
+ if (DN(('cn', 'index')) in entry.dn and
+ DN(('cn', 'userRoot')) in entry.dn):
+ taskid = self.create_index_task(entry.getValue('cn'))
self.monitor_index_task(taskid)
if updated:
self.modified = True
return
- def __delete_record(self, updates):
+ def _delete_record(self, updates):
"""
Run through all the updates again looking for any that should be
deleted.
@@ -706,38 +765,21 @@ class LDAPUpdate:
considered first so we don't end up trying to delete a parent
and child in the wrong order.
"""
- dn = updates['dn']
- deletes = updates.get('deleteentry', [])
- for d in deletes:
- try:
- root_logger.info("Deleting entry %s", dn)
- if self.live_run:
- self.conn.deleteEntry(dn)
- self.modified = True
- except errors.NotFound, e:
- root_logger.info("%s did not exist:%s", dn, e)
- self.modified = True
- except errors.DatabaseError, e:
- root_logger.error("Delete failed: %s", e)
-
- updates = updates.get('updates', [])
- for u in updates:
- # We already do syntax-parsing so this is safe
- (utype, k, values) = u.split(':',2)
- if utype == 'deleteentry':
- try:
- root_logger.info("Deleting entry %s", dn)
- if self.live_run:
- self.conn.deleteEntry(dn)
- self.modified = True
- except errors.NotFound, e:
- root_logger.info("%s did not exist:%s", dn, e)
- self.modified = True
- except errors.DatabaseError, e:
- root_logger.error("Delete failed: %s", e)
+ if not updates.has_key('deleteentry'):
+ return
- return
+ dn = updates['dn']
+ try:
+ self.info("Deleting entry %s", dn)
+ if self.live_run:
+ self.conn.deleteEntry(dn)
+ self.modified = True
+ except errors.NotFound, e:
+ self.info("%s did not exist:%s", dn, e)
+ self.modified = True
+ except errors.DatabaseError, e:
+ self.error("Delete failed: %s", e)
def get_all_files(self, root, recursive=False):
"""Get all update files"""
@@ -761,7 +803,7 @@ class LDAPUpdate:
realm=self.realm)
try:
if self.dm_password:
- self.conn.do_simple_bind(binddn="cn=directory manager", bindpw=self.dm_password)
+ self.conn.do_simple_bind(binddn=DN(('cn', 'directory manager')), bindpw=self.dm_password)
elif os.getegid() == 0:
try:
# autobind
@@ -776,19 +818,28 @@ class LDAPUpdate:
else:
raise RuntimeError("Offline updates are not supported.")
- def __run_updates(self, dn_list, all_updates):
+ def _run_updates(self, all_updates):
# For adds and updates we want to apply updates from shortest
# to greatest length of the DN. For deletes we want the reverse.
- sortedkeys = dn_list.keys()
+
+ dn_by_rdn_count = {}
+ for dn in all_updates.keys():
+ assert isinstance(dn, DN)
+ rdn_count = len(dn)
+ rdn_count_list = dn_by_rdn_count.setdefault(rdn_count, [])
+ if dn not in rdn_count_list:
+ rdn_count_list.append(dn)
+
+ sortedkeys = dn_by_rdn_count.keys()
sortedkeys.sort()
- for k in sortedkeys:
- for dn in dn_list[k]:
- self.__update_record(all_updates[dn])
+ for rdn_count in sortedkeys:
+ for dn in dn_by_rdn_count[rdn_count]:
+ self._update_record(all_updates[dn])
sortedkeys.reverse()
- for k in sortedkeys:
- for dn in dn_list[k]:
- self.__delete_record(all_updates[dn])
+ for rdn_count in sortedkeys:
+ for dn in dn_by_rdn_count[rdn_count]:
+ self._delete_record(all_updates[dn])
def update(self, files):
"""Execute the update. files is a list of the update files to use.
@@ -796,71 +847,46 @@ class LDAPUpdate:
returns True if anything was changed, otherwise False
"""
- updates = None
+ all_updates = {}
if self.plugins:
- root_logger.info('PRE_UPDATE')
+ self.info('PRE_UPDATE')
updates = api.Backend.updateclient.update(PRE_UPDATE, self.dm_password, self.ldapi, self.live_run)
-
+ self.merge_updates(all_updates, updates)
try:
self.create_connection()
- all_updates = {}
- dn_list = {}
- # Start with any updates passed in from pre-update plugins
- if updates:
- for entry in updates:
- all_updates.update(entry)
- for upd in updates:
- for dn in upd:
- dn_explode = ldap.explode_dn(dn.lower())
- l = len(dn_explode)
- if dn_list.get(l):
- if dn not in dn_list[l]:
- dn_list[l].append(dn)
- else:
- dn_list[l] = [dn]
for f in files:
try:
- root_logger.info("Parsing file %s" % f)
+ self.info("Parsing update file '%s'" % f)
data = self.read_file(f)
except Exception, e:
- print e
+ self.error("error reading update file '%s'", f)
sys.exit(e)
- (all_updates, dn_list) = self.parse_update_file(data, all_updates, dn_list)
+ self.parse_update_file(f, data, all_updates)
- self.__run_updates(dn_list, all_updates)
+ self._run_updates(all_updates)
finally:
if self.conn: self.conn.unbind()
if self.plugins:
- root_logger.info('POST_UPDATE')
+ self.info('POST_UPDATE')
+ all_updates = {}
updates = api.Backend.updateclient.update(POST_UPDATE, self.dm_password, self.ldapi, self.live_run)
- dn_list = {}
- for upd in updates:
- for dn in upd:
- dn_explode = ldap.explode_dn(dn.lower())
- l = len(dn_explode)
- if dn_list.get(l):
- if dn not in dn_list[l]:
- dn_list[l].append(dn)
- else:
- dn_list[l] = [dn]
- self.__run_updates(dn_list, updates)
+ self.merge_updates(all_updates, updates)
+ self._run_updates(all_updates)
return self.modified
- def update_from_dict(self, dn_list, updates):
+ def update_from_dict(self, updates):
"""
Apply updates internally as opposed to from a file.
-
- dn_list is a list of dns to be updated
updates is a dictionary containing the updates
"""
if not self.conn:
self.create_connection()
- self.__run_updates(dn_list, updates)
+ self._run_updates(updates)
return self.modified
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index c32d82e3a..555a28b8f 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -20,7 +20,7 @@
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipalib import api, errors
-from ipalib.dn import DN
+from ipapython.dn import DN
from ipapython.ipa_log_manager import *
DEFAULT_ID_RANGE_SIZE = 200000
@@ -34,7 +34,7 @@ class update_default_range(PostUpdate):
def execute(self, **options):
ldap = self.obj.backend
- dn = str(DN(api.env.container_ranges, api.env.basedn))
+ dn = DN(api.env.container_ranges, api.env.basedn)
search_filter = "objectclass=ipaDomainIDRange"
try:
(entries, truncated) = ldap.find_entries(search_filter, [], dn)
@@ -44,7 +44,7 @@ class update_default_range(PostUpdate):
root_logger.debug("default_range: ipaDomainIDRange entry found, skip plugin")
return (False, False, [])
- dn = str(DN(('cn', 'admins'), api.env.container_group, api.env.basedn))
+ dn = DN(('cn', 'admins'), api.env.container_group, api.env.basedn)
try:
(dn, admins_entry) = ldap.get_entry(dn, ['gidnumber'])
except errors.NotFound:
@@ -65,12 +65,10 @@ class update_default_range(PostUpdate):
]
updates = {}
- dn = str(DN(('cn', '%s_id_range' % api.env.realm),
- api.env.container_ranges, api.env.basedn))
+ dn = DN(('cn', '%s_id_range' % api.env.realm),
+ api.env.container_ranges, api.env.basedn)
- # make sure everything is str or otherwise python-ldap would complain
- range_entry = map(str, range_entry)
- updates[dn] = {'dn' : dn, 'default' : range_entry}
+ updates[dn] = {'dn': dn, 'default': range_entry}
# Default range entry has a hard-coded range size to 200000 which is
# a default range size in ipa-server-install. This could cause issues
@@ -78,7 +76,7 @@ class update_default_range(PostUpdate):
# bigger range (option --idmax).
# We should make our best to check if this is the case and provide
# user with an information how to fix it.
- dn = str(DN(api.env.container_dna_posix_ids, api.env.basedn))
+ dn = DN(api.env.container_dna_posix_ids, api.env.basedn)
search_filter = "objectclass=dnaSharedConfig"
attrs = ['dnaHostname', 'dnaRemainingValues']
try:
diff --git a/ipaserver/install/plugins/dns.py b/ipaserver/install/plugins/dns.py
index e11c331a4..d55596704 100644
--- a/ipaserver/install/plugins/dns.py
+++ b/ipaserver/install/plugins/dns.py
@@ -21,7 +21,7 @@ from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipaserver.install.plugins import baseupdate
from ipalib import api, errors, util
-from ipalib.dn import DN
+from ipapython.dn import DN
from ipalib.plugins.dns import dns_container_exists
from ipapython.ipa_log_manager import *
@@ -89,31 +89,29 @@ class update_dns_permissions(PostUpdate):
entries otherwise.
"""
- _write_dns_perm_dn = DN('cn=Write DNS Configuration',
- api.env.container_permission,
- api.env.basedn)
+ _write_dns_perm_dn = DN(('cn', 'Write DNS Configuration'),
+ api.env.container_permission, api.env.basedn)
_write_dns_perm_entry = ['objectClass:groupofnames',
'objectClass:top',
'cn:Write DNS Configuration',
'description:Write DNS Configuration',
- 'member:cn=DNS Administrators,cn=privileges,cn=pbac,%s' \
- % api.env.basedn,
- 'member:cn=DNS Servers,cn=privileges,cn=pbac,%s' \
- % api.env.basedn]
-
- _read_dns_perm_dn = DN('cn=Read DNS Entries',
- api.env.container_permission,
- api.env.basedn)
+ 'member:%s' % DN(('cn', 'DNS Administrators'), ('cn', 'privileges'), ('cn', 'pbac'),
+ api.env.basedn),
+ 'member:%s' % DN(('cn', 'DNS Servers'), ('cn', 'privileges'), ('cn', 'pbac'),
+ api.env.basedn)]
+
+ _read_dns_perm_dn = DN(('cn', 'Read DNS Entries'),
+ api.env.container_permission, api.env.basedn)
_read_dns_perm_entry = ['objectClass:top',
'objectClass:groupofnames',
'objectClass:ipapermission',
'cn:Read DNS Entries',
'description:Read DNS entries',
'ipapermissiontype:SYSTEM',
- 'member:cn=DNS Administrators,cn=privileges,cn=pbac,%s' \
- % api.env.basedn,
- 'member:cn=DNS Servers,cn=privileges,cn=pbac,%s' \
- % api.env.basedn,]
+ 'member:%s' % DN(('cn', 'DNS Administrators'), ('cn', 'privileges'), ('cn', 'pbac'),
+ api.env.basedn),
+ 'member:%s' % DN(('cn', 'DNS Servers'), ('cn', 'privileges'), ('cn', 'pbac'),
+ api.env.basedn),]
_write_dns_aci_dn = DN(api.env.basedn)
_write_dns_aci_entry = ['add:aci:\'(targetattr = "idnsforwardpolicy || idnsforwarders || idnsallowsyncptr || idnszonerefresh || idnspersistentsearch")(target = "ldap:///cn=dns,%(realm)s")(version 3.0;acl "permission:Write DNS Configuration";allow (write) groupdn = "ldap:///cn=Write DNS Configuration,cn=permissions,cn=pbac,%(realm)s";)\'' % dict(realm=api.env.basedn)]
@@ -135,10 +133,7 @@ class update_dns_permissions(PostUpdate):
(self._write_dns_aci_dn, 'updates', self._write_dns_aci_entry),
(self._read_dns_aci_dn, 'updates', self._read_dns_aci_entry)):
- dn = str(dn)
- # make sure everything is str or otherwise python-ldap would complain
- entry = map(str, entry)
- dnsupdates[dn] = {'dn' : dn, container : entry}
+ dnsupdates[dn] = {'dn': dn, container: entry}
return (False, True, [dnsupdates])
@@ -161,9 +156,9 @@ class update_dns_limits(PostUpdate):
return (False, False, [])
dns_principal = 'DNS/%s@%s' % (self.env.host, self.env.realm)
- dns_service_dn = str(DN(('krbprincipalname', dns_principal),
- self.env.container_service,
- self.env.basedn))
+ dns_service_dn = DN(('krbprincipalname', dns_principal),
+ self.env.container_service,
+ self.env.basedn)
try:
(dn, entry) = ldap.get_entry(dns_service_dn, self.limit_attributes)
diff --git a/ipaserver/install/plugins/fix_replica_memberof.py b/ipaserver/install/plugins/fix_replica_memberof.py
index 23bde0c9f..d4ab75348 100644
--- a/ipaserver/install/plugins/fix_replica_memberof.py
+++ b/ipaserver/install/plugins/fix_replica_memberof.py
@@ -44,7 +44,7 @@ class update_replica_exclude_attribute_list(PreUpdate):
entries = repl.find_replication_agreements()
self.log.debug("Found %d agreement(s)", len(entries))
for replica in entries:
- self.log.debug(replica.description)
+ self.log.debug(replica.getValue('description'))
attrlist = replica.getValue('nsDS5ReplicatedAttributeList')
if attrlist is None:
self.log.debug("Adding nsDS5ReplicatedAttributeList and nsDS5ReplicatedAttributeListTotal")
@@ -68,7 +68,7 @@ class update_replica_exclude_attribute_list(PreUpdate):
self.log.debug("Attribute list needs updating")
current = replica.toDict()
replica.setValue('nsDS5ReplicatedAttributeList',
- replica.nsDS5ReplicatedAttributeList + ' %s' % ' '.join(missing))
+ replica.getValue('nsDS5ReplicatedAttributeList') + ' %s' % ' '.join(missing))
try:
repl.conn.updateEntry(replica.dn, current, replica.toDict())
self.log.debug("Updated")
diff --git a/ipaserver/install/plugins/rename_managed.py b/ipaserver/install/plugins/rename_managed.py
index a9eed0be3..99dac8148 100644
--- a/ipaserver/install/plugins/rename_managed.py
+++ b/ipaserver/install/plugins/rename_managed.py
@@ -24,6 +24,7 @@ from ipalib.frontend import Updater
from ipaserver.install.plugins import baseupdate
from ipalib import api, errors
from ipapython import ipautil
+from ipapython.dn import DN, EditableDN
import ldap as _ldap
def entry_to_update(entry):
@@ -44,67 +45,99 @@ def entry_to_update(entry):
return update
-def generate_update(ldap, deletes=False):
- """
- We need to separate the deletes that need to happen from the
- new entries that need to be added.
- """
- suffix = ipautil.realm_to_suffix(api.env.realm)
- searchfilter = '(objectclass=*)'
- definitions_managed_entries = []
- old_template_container = 'cn=etc,%s' % suffix
- old_definition_container = 'cn=managed entries,cn=plugins,cn=config'
- new = 'cn=Managed Entries,cn=etc,%s' % suffix
- sub = ['cn=Definitions,', 'cn=Templates,']
- new_managed_entries = []
- old_templates = []
- template = None
- restart = False
-
- # If the old entries don't exist the server has already been updated.
- try:
- (definitions_managed_entries, truncated) = ldap.find_entries(
- searchfilter, ['*'], old_definition_container, _ldap.SCOPE_ONELEVEL, normalize=False
- )
- except errors.NotFound, e:
- return (False, new_managed_entries)
-
- for entry in definitions_managed_entries:
- new_definition = {}
- definition_managed_entry_updates = {}
- if deletes:
- old_definition = {'dn': str(entry[0]), 'deleteentry': ['dn: %s' % str(entry[0])]}
- old_template = str(entry[1]['managedtemplate'][0])
- definition_managed_entry_updates[old_definition['dn']] = old_definition
- old_templates.append(old_template)
- else:
- entry[1]['managedtemplate'] = str(entry[1]['managedtemplate'][0].replace(old_template_container, sub[1] + new))
- new_definition['dn'] = str(entry[0].replace(old_definition_container, sub[0] + new))
- new_definition['default'] = entry_to_update(entry[1])
- definition_managed_entry_updates[new_definition['dn']] = new_definition
- new_managed_entries.append(definition_managed_entry_updates)
- for old_template in old_templates: # Only happens when deletes is True
- try:
- (dn, template) = ldap.get_entry(old_template, ['*'], normalize=False)
- dn = str(dn)
- new_template = {}
- template_managed_entry_updates = {}
- old_template = {'dn': dn, 'deleteentry': ['dn: %s' % dn]}
- new_template['dn'] = str(dn.replace(old_template_container, sub[1] + new))
- new_template['default'] = entry_to_update(template)
- template_managed_entry_updates[new_template['dn']] = new_template
- template_managed_entry_updates[old_template['dn']] = old_template
- new_managed_entries.append(template_managed_entry_updates)
- except errors.NotFound, e:
- pass
+class GenerateUpdateMixin(object):
+ def generate_update(self, deletes=False):
+ """
+ We need to separate the deletes that need to happen from the
+ new entries that need to be added.
+ """
+ ldap = self.obj.backend
- if len(new_managed_entries) > 0:
- restart = True
- new_managed_entries.sort(reverse=True)
+ suffix = ipautil.realm_to_suffix(api.env.realm)
+ searchfilter = '(objectclass=*)'
+ definitions_managed_entries = []
- return (restart, new_managed_entries)
+ old_template_container = DN(('cn', 'etc'), suffix)
+ new_template_container = DN(('cn', 'Templates'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix)
-class update_managed_post_first(PreUpdate):
+ old_definition_container = DN(('cn', 'managed entries'), ('cn', 'plugins'), ('cn', 'config'), suffix)
+ new_definition_container = DN(('cn', 'Definitions'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix)
+
+ definitions_dn = DN(('cn', 'Definitions'))
+ update_list = []
+ restart = False
+
+ # If the old entries don't exist the server has already been updated.
+ try:
+ (definitions_managed_entries, truncated) = ldap.find_entries(
+ searchfilter, ['*'], old_definition_container, _ldap.SCOPE_ONELEVEL, normalize=False
+ )
+ except errors.NotFound, e:
+ return (False, update_list)
+
+ for entry in definitions_managed_entries:
+ assert isinstance(entry.dn, DN)
+ if deletes:
+ old_dn = entry.data['managedtemplate'][0]
+ assert isinstance(old_dn, DN)
+ try:
+ (old_dn, entry) = ldap.get_entry(old_dn, ['*'], normalize=False)
+ except errors.NotFound, e:
+ pass
+ else:
+ # Compute the new dn by replacing the old container with the new container
+ new_dn = EditableDN(old_dn)
+ if new_dn.replace(old_template_container, new_template_container) != 1:
+ self.error("unable to replace '%s' with '%s' in '%s'",
+ old_template_container, new_template_container, old_dn)
+ continue
+
+ new_dn = DN(new_dn)
+
+ # The old attributes become defaults for the new entry
+ new_update = {'dn': new_dn,
+ 'default': entry_to_update(entry)}
+
+ # Delete the old entry
+ old_update = {'dn': old_dn, 'deleteentry': None}
+
+ # Add the delete and replacement updates to the list of all updates
+ update_list.append({old_dn: old_update, new_dn: new_update})
+
+ else:
+ # Update the template dn by replacing the old containter with the new container
+ old_dn = entry.data['managedtemplate'][0]
+ new_dn = EditableDN(old_dn)
+ if new_dn.replace(old_template_container, new_template_container) != 1:
+ self.error("unable to replace '%s' with '%s' in '%s'",
+ old_template_container, new_template_container, old_dn)
+ continue
+ new_dn = DN(new_dn)
+ entry.data['managedtemplate'] = new_dn
+
+ # Edit the dn, then convert it back to an immutable DN
+ old_dn = entry.dn
+ new_dn = EditableDN(old_dn)
+ if new_dn.replace(old_definition_container, new_definition_container) != 1:
+ self.error("unable to replace '%s' with '%s' in '%s'",
+ old_definition_container, new_definition_container, old_dn)
+ continue
+ new_dn = DN(new_dn)
+
+ # The old attributes become defaults for the new entry
+ new_update = {'dn': new_dn,
+ 'default': entry_to_update(entry.data)}
+
+ # Add the replacement update to the collection of all updates
+ update_list.append({new_dn: new_update})
+
+ if len(update_list) > 0:
+ restart = True
+ update_list.sort(reverse=True)
+
+ return (restart, update_list)
+
+class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
"""
Update managed entries
"""
@@ -112,21 +145,21 @@ class update_managed_post_first(PreUpdate):
def execute(self, **options):
# Never need to restart with the pre-update changes
- (ignore, new_managed_entries) = generate_update(self.obj.backend, False)
+ (ignore, update_list) = self.generate_update(False)
- return (False, True, new_managed_entries)
+ return (False, True, update_list)
api.register(update_managed_post_first)
-class update_managed_post(PostUpdate):
+class update_managed_post(PostUpdate, GenerateUpdateMixin):
"""
Update managed entries
"""
order=LAST
def execute(self, **options):
- (restart, new_managed_entries) = generate_update(self.obj.backend, True)
+ (restart, update_list) = self.generate_update(True)
- return (restart, True, new_managed_entries)
+ return (restart, True, update_list)
api.register(update_managed_post)
diff --git a/ipaserver/install/plugins/updateclient.py b/ipaserver/install/plugins/updateclient.py
index e23769471..dca2c75dd 100644
--- a/ipaserver/install/plugins/updateclient.py
+++ b/ipaserver/install/plugins/updateclient.py
@@ -25,6 +25,7 @@ from ipaserver.install.ldapupdate import LDAPUpdate
from ipapython.ipautil import wait_for_open_socket
from ipalib import api
from ipalib import backend
+from ipapython.dn import DN
import ldap as _ldap
class updateclient(backend.Executioner):
@@ -44,7 +45,7 @@ class updateclient(backend.Executioner):
updates is a dictionary keyed on dn. The value of an update is a
dictionary with the following possible values:
- - dn: str, duplicate of the key
+ - dn: DN, equal to the dn attribute
- updates: list of updates against the dn
- default: list of the default entry to be added if it doesn't
exist
@@ -103,7 +104,7 @@ class updateclient(backend.Executioner):
autobind = False
else:
autobind = True
- self.Backend.ldap2.connect(bind_dn='cn=Directory Manager', bind_pw=dm_password, autobind=autobind)
+ self.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password, autobind=autobind)
def order(self, updatetype):
"""Return plugins of the given updatetype in sorted order.
@@ -125,22 +126,12 @@ class updateclient(backend.Executioner):
(restart, apply_now, res) = self.run(update.name, **kw)
if restart:
self.restart(dm_password, live_run)
- dn_list = {}
- for upd in res:
- for dn in upd:
- dn_explode = _ldap.explode_dn(dn.lower())
- l = len(dn_explode)
- if dn_list.get(l):
- if dn not in dn_list[l]:
- dn_list[l].append(dn)
- else:
- dn_list[l] = [dn]
- updates = {}
- for entry in res:
- updates.update(entry)
if apply_now:
- ld.update_from_dict(dn_list, updates)
+ updates = {}
+ for entry in res:
+ updates.update(entry)
+ ld.update_from_dict(updates)
elif res:
result.extend(res)
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 38abfe210..8fe73ca77 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -28,17 +28,16 @@ from ipapython import services as ipaservices
from ldap import modlist
from ipalib import api, util, errors
from ipapython import ipautil
-from ipalib.dn import DN
+from ipapython.dn import DN
-DIRMAN_CN = "cn=directory manager"
CACERT = "/etc/ipa/ca.crt"
# the default container used by AD for user entries
-WIN_USER_CONTAINER = "cn=Users"
+WIN_USER_CONTAINER = DN(('cn', 'Users'))
# the default container used by IPA for user entries
-IPA_USER_CONTAINER = "cn=users,cn=accounts"
+IPA_USER_CONTAINER = DN(('cn', 'users'), ('cn', 'accounts'))
PORT = 636
TIMEOUT = 120
-REPL_MAN_DN = "cn=replication manager,cn=config"
+REPL_MAN_DN = DN(('cn', 'replication manager'), ('cn', 'config'))
IPA_REPLICA = 1
WINSYNC = 2
@@ -94,9 +93,10 @@ def enable_replication_version_checking(hostname, realm, dirman_passwd):
conn.do_simple_bind(bindpw=dirman_passwd)
else:
conn.do_sasl_gssapi_bind()
- entry = conn.search_s('cn=IPA Version Replication,cn=plugins,cn=config', ldap.SCOPE_BASE, 'objectclass=*')
- if entry[0].getValue('nsslapd-pluginenabled') == 'off':
- conn.modify_s(entry[0].dn, [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'on')])
+ entry = conn.getEntry(DN(('cn', 'IPA Version Replication'), ('cn', 'plugins'), ('cn', 'config')),
+ ldap.SCOPE_BASE, 'objectclass=*')
+ if entry.getValue('nsslapd-pluginenabled') == 'off':
+ conn.modify_s(entry.dn, [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'on')])
conn.unbind()
serverid = "-".join(realm.split("."))
ipaservices.knownservices.dirsrv.restart(instance_name=serverid)
@@ -112,8 +112,7 @@ class ReplicationManager(object):
self.dirman_passwd = dirman_passwd
self.realm = realm
self.starttls = starttls
- tmp = ipautil.realm_to_suffix(realm)
- self.suffix = str(DN(tmp)).lower()
+ self.suffix = ipautil.realm_to_suffix(realm)
self.need_memberof_fixup = False
# The caller is allowed to pass in an existing IPAdmin connection.
@@ -150,25 +149,28 @@ class ReplicationManager(object):
"""
# First see if there is already one set
dn = self.replica_dn()
+ assert isinstance(dn, DN)
try:
- replica = conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0]
+ replica = conn.getEntry(dn, ldap.SCOPE_BASE, "objectclass=*")
+ except errors.NotFound:
+ pass
+ else:
if replica.getValue('nsDS5ReplicaId'):
return int(replica.getValue('nsDS5ReplicaId'))
- except ldap.NO_SUCH_OBJECT:
- pass
# Ok, either the entry doesn't exist or the attribute isn't set
# so get it from the other master
retval = -1
- dn = str(DN(('cn','replication'),('cn','etc'), self.suffix))
+ dn = DN(('cn','replication'),('cn','etc'), self.suffix)
try:
- replica = master_conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0]
- if not replica.getValue('nsDS5ReplicaId'):
- root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
- raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server")
- except ldap.NO_SUCH_OBJECT:
+ replica = master_conn.getEntry(dn, ldap.SCOPE_BASE, "objectclass=*")
+ except errors.NotFound:
root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
raise
+ else:
+ if replica.getValue('nsDS5ReplicaId') is None:
+ root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
+ raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server")
# Now update the value on the master
retval = int(replica.getValue('nsDS5ReplicaId'))
@@ -195,8 +197,9 @@ class ReplicationManager(object):
"""
filt = "(|(objectclass=nsDSWindowsReplicationAgreement)(objectclass=nsds5ReplicationAgreement))"
try:
- ents = self.conn.search_s("cn=mapping tree,cn=config", ldap.SCOPE_SUBTREE, filt)
- except ldap.NO_SUCH_OBJECT:
+ ents = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')),
+ ldap.SCOPE_SUBTREE, filt)
+ except errors.NotFound:
ents = []
return ents
@@ -212,13 +215,13 @@ class ReplicationManager(object):
filt = "(objectclass=nsds5ReplicationAgreement)"
try:
- ents = self.conn.search_s("cn=mapping tree,cn=config",
- ldap.SCOPE_SUBTREE, filt)
- except ldap.NO_SUCH_OBJECT:
+ ents = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')),
+ ldap.SCOPE_SUBTREE, filt)
+ except errors.NotFound:
return res
for ent in ents:
- res.append(ent.nsds5replicahost)
+ res.append(ent.getValue('nsds5replicahost'))
return res
@@ -234,24 +237,23 @@ class ReplicationManager(object):
filt = "(&(|(objectclass=nsds5ReplicationAgreement)(objectclass=nsDSWindowsReplicationAgreement))(nsDS5ReplicaHost=%s))" % hostname
try:
- entry = self.conn.search_s("cn=mapping tree,cn=config",
- ldap.SCOPE_SUBTREE, filt)
- except ldap.NO_SUCH_OBJECT:
+ entries = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')),
+ ldap.SCOPE_SUBTREE, filt)
+ except errors.NotFound:
return None
- if len(entry) == 0:
+ if len(entries) == 0:
return None
else:
- return entry[0] # There can be only one
+ return entries[0] # There can be only one
def add_replication_manager(self, conn, dn, pw):
"""
Create a pseudo user to use for replication.
"""
-
- edn = ldap.dn.str2dn(dn)
- rdn_attr = edn[0][0][0]
- rdn_val = edn[0][0][1]
+ assert isinstance(dn, DN)
+ rdn_attr = dn[0].attr
+ rdn_val = dn[0].value
ent = ipaldap.Entry(dn)
ent.setValues("objectclass", "top", "person")
@@ -266,6 +268,7 @@ class ReplicationManager(object):
pass
def delete_replication_manager(self, conn, dn=REPL_MAN_DN):
+ assert isinstance(dn, DN)
try:
conn.delete_s(dn)
except ldap.NO_SUCH_OBJECT:
@@ -278,16 +281,18 @@ class ReplicationManager(object):
return "2"
def replica_dn(self):
- return str(DN(('cn','replica'),('cn',self.suffix),('cn','mapping tree'),('cn','config')))
+ return DN(('cn','replica'),('cn',self.suffix),('cn','mapping tree'),('cn','config'))
def replica_config(self, conn, replica_id, replica_binddn):
+ assert isinstance(replica_binddn, DN)
dn = self.replica_dn()
+ assert isinstance(dn, DN)
try:
entry = conn.getEntry(dn, ldap.SCOPE_BASE)
managers = entry.getValues('nsDS5ReplicaBindDN')
for m in managers:
- if DN(replica_binddn) == DN(m):
+ if replica_binddn == DN(m):
return
# Add the new replication manager
mod = [(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn)]
@@ -303,7 +308,7 @@ class ReplicationManager(object):
entry = ipaldap.Entry(dn)
entry.setValues('objectclass', "top", "nsds5replica", "extensibleobject")
entry.setValues('cn', "replica")
- entry.setValues('nsds5replicaroot', self.suffix)
+ entry.setValues('nsds5replicaroot', str(self.suffix))
entry.setValues('nsds5replicaid', str(replica_id))
entry.setValues('nsds5replicatype', replica_type)
entry.setValues('nsds5flags', "1")
@@ -313,7 +318,7 @@ class ReplicationManager(object):
conn.addEntry(entry)
def setup_changelog(self, conn):
- dn = "cn=changelog5, cn=config"
+ dn = DN(('cn', 'changelog5'), ('cn', 'config'))
dirpath = conn.dbdir + "/cldb"
entry = ipaldap.Entry(dn)
entry.setValues('objectclass', "top", "extensibleobject")
@@ -325,7 +330,7 @@ class ReplicationManager(object):
return
def setup_chaining_backend(self, conn):
- chaindn = "cn=chaining database, cn=plugins, cn=config"
+ chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config'))
benamebase = "chaindb"
urls = [self.to_ldap_url(conn)]
cn = ""
@@ -334,11 +339,11 @@ class ReplicationManager(object):
while not done:
try:
cn = benamebase + str(benum) # e.g. localdb1
- dn = "cn=" + cn + ", " + chaindn
+ dn = DN(('cn', cn), chaindn)
entry = ipaldap.Entry(dn)
entry.setValues('objectclass', 'top', 'extensibleObject', 'nsBackendInstance')
entry.setValues('cn', cn)
- entry.setValues('nsslapd-suffix', self.suffix)
+ entry.setValues('nsslapd-suffix', str(self.suffix))
entry.setValues('nsfarmserverurl', urls)
entry.setValues('nsmultiplexorbinddn', self.repl_man_dn)
entry.setValues('nsmultiplexorcredentials', self.repl_man_passwd)
@@ -365,7 +370,7 @@ class ReplicationManager(object):
def get_mapping_tree_entry(self):
try:
- entry = self.conn.getEntry("cn=mapping tree,cn=config", ldap.SCOPE_ONELEVEL,
+ entry = self.conn.getEntry(DN(('cn', 'mapping tree'), ('cn', 'config')), ldap.SCOPE_ONELEVEL,
"(cn=\"%s\")" % (self.suffix))
except errors.NotFound, e:
root_logger.debug("failed to find mappting tree entry for %s" % self.suffix)
@@ -378,7 +383,7 @@ class ReplicationManager(object):
mtent = self.get_mapping_tree_entry()
dn = mtent.dn
- plgent = self.conn.getEntry("cn=Multimaster Replication Plugin,cn=plugins,cn=config",
+ plgent = self.conn.getEntry(DN(('cn', 'Multimaster Replication Plugin'), ('cn', 'plugins'), ('cn', 'config')),
ldap.SCOPE_BASE, "(objectclass=*)", ['nsslapd-pluginPath'])
path = plgent.getValue('nsslapd-pluginPath')
@@ -397,7 +402,7 @@ class ReplicationManager(object):
self.enable_chain_on_update(chainbe)
def add_passsync_user(self, conn, password):
- pass_dn = "uid=passsync,cn=sysaccounts,cn=etc,%s" % self.suffix
+ pass_dn = DN(('uid', 'passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'), self.suffix)
print "The user for the Windows PassSync service is %s" % pass_dn
try:
conn.getEntry(pass_dn, ldap.SCOPE_BASE)
@@ -414,7 +419,7 @@ class ReplicationManager(object):
conn.addEntry(entry)
# Add it to the list of users allowed to bypass password policy
- extop_dn = "cn=ipa_pwd_extop,cn=plugins,cn=config"
+ extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config'))
entry = conn.getEntry(extop_dn, ldap.SCOPE_BASE)
pass_mgrs = entry.getValues('passSyncManagersDNs')
if not pass_mgrs:
@@ -435,9 +440,9 @@ class ReplicationManager(object):
def setup_winsync_agmt(self, entry, win_subtree=None):
if win_subtree is None:
- win_subtree = WIN_USER_CONTAINER + "," + self.ad_suffix
- ds_subtree = IPA_USER_CONTAINER + "," + self.suffix
- windomain = '.'.join(ldap.explode_dn(self.suffix, 1))
+ win_subtree = DN(WIN_USER_CONTAINER, self.ad_suffix)
+ ds_subtree = DN(IPA_USER_CONTAINER, self.suffix)
+ windomain = ipautil.suffix_to_realm(self.suffix)
entry.setValues("objectclass", "nsDSWindowsReplicationAgreement")
entry.setValues("nsds7WindowsReplicaSubtree", win_subtree)
@@ -454,7 +459,7 @@ class ReplicationManager(object):
tell which side we want.
"""
cn = "meTo%s" % (hostname)
- dn = "cn=%s, %s" % (cn, self.replica_dn())
+ dn = DN(('cn', cn), self.replica_dn())
return (cn, dn)
@@ -469,6 +474,9 @@ class ReplicationManager(object):
isn't a dogtag replication agreement.
"""
+ if repl_man_dn is not None:
+ assert isinstance(repl_man_dn, DN)
+
cn, dn = self.agreement_dn(b_hostname, master=master)
try:
a_conn.getEntry(dn, ldap.SCOPE_BASE)
@@ -482,7 +490,7 @@ class ReplicationManager(object):
entry.setValues('nsds5replicahost', b_hostname)
entry.setValues('nsds5replicaport', str(port))
entry.setValues('nsds5replicatimeout', str(TIMEOUT))
- entry.setValues('nsds5replicaroot', self.suffix)
+ entry.setValues('nsds5replicaroot', str(self.suffix))
if master is None:
entry.setValues('nsDS5ReplicatedAttributeList',
'(objectclass=*) $ EXCLUDE %s' % " ".join(EXCLUDES))
@@ -538,10 +546,15 @@ class ReplicationManager(object):
while (retries > 0 ):
root_logger.info('Getting ldap service principals for conversion: %s and %s' % (filter_a, filter_b))
- a_entry = b.search_s(self.suffix, ldap.SCOPE_SUBTREE,
- filterstr=filter_a)
- b_entry = a.search_s(self.suffix, ldap.SCOPE_SUBTREE,
- filterstr=filter_b)
+ try:
+ a_entry = b.search_s(self.suffix, ldap.SCOPE_SUBTREE, filterstr=filter_a)
+ except errors.NotFound:
+ pass
+
+ try:
+ b_entry = a.search_s(self.suffix, ldap.SCOPE_SUBTREE, filterstr=filter_b)
+ except errors.NotFound:
+ pass
if a_entry and b_entry:
root_logger.debug('Found both principals.')
@@ -578,7 +591,10 @@ class ReplicationManager(object):
"""
rep_dn = self.replica_dn()
+ assert isinstance(rep_dn, DN)
(a_dn, b_dn) = self.get_replica_principal_dns(a, b, retries=10)
+ assert isinstance(a_dn, DN)
+ assert isinstance(b_dn, DN)
# Add kerberos principal DNs as valid bindDNs for replication
try:
@@ -623,12 +639,10 @@ class ReplicationManager(object):
return self.conn.deleteEntry(dn)
def delete_referral(self, hostname):
- esc1_suffix = self.suffix.replace('=', '\\3D').replace(',', '\\2C')
- esc2_suffix = self.suffix.replace('=', '%3D').replace(',', '%2C')
- dn = 'cn=%s,cn=mapping tree,cn=config' % esc1_suffix
+ dn = DN(('cn', self.suffix), ('cn', 'mapping tree'), ('cn', 'config'))
# TODO: should we detect proto/port somehow ?
mod = [(ldap.MOD_DELETE, 'nsslapd-referral',
- 'ldap://%s/%s' % (ipautil.format_netloc(hostname, 389), esc2_suffix))]
+ 'ldap://%s/%s' % (ipautil.format_netloc(hostname, 389), self.suffix))]
try:
self.conn.modify_s(dn, mod)
@@ -648,9 +662,9 @@ class ReplicationManager(object):
print "Error reading status from agreement", agmtdn
hasError = 1
else:
- refresh = entry.nsds5BeginReplicaRefresh
- inprogress = entry.nsds5replicaUpdateInProgress
- status = entry.nsds5ReplicaLastInitStatus
+ refresh = entry.getValue('nsds5BeginReplicaRefresh')
+ inprogress = entry.getValue('nsds5replicaUpdateInProgress')
+ status = entry.getValue('nsds5ReplicaLastInitStatus')
if not refresh: # done - check status
if not status:
print "No status yet"
@@ -683,10 +697,10 @@ class ReplicationManager(object):
print "Error reading status from agreement", agmtdn
hasError = 1
else:
- inprogress = entry.nsds5replicaUpdateInProgress
- status = entry.nsds5ReplicaLastUpdateStatus
- start = entry.nsds5ReplicaLastUpdateStart
- end = entry.nsds5ReplicaLastUpdateEnd
+ inprogress = entry.getValue('nsds5replicaUpdateInProgress')
+ status = entry.getValue('nsds5ReplicaLastUpdateStatus')
+ start = entry.getValue('nsds5ReplicaLastUpdateStart')
+ end = entry.getValue('nsds5ReplicaLastUpdateEnd')
# incremental update is done if inprogress is false and end >= start
done = inprogress and inprogress.lower() == 'false' and start and end and (start <= end)
root_logger.info("Replication Update in progress: %s: status: %s: start: %s: end: %s" %
@@ -733,6 +747,7 @@ class ReplicationManager(object):
return self.wait_for_repl_init(conn, dn)
def basic_replication_setup(self, conn, replica_id, repldn, replpw):
+ assert isinstance(repldn, DN)
if replpw is not None:
self.add_replication_manager(conn, repldn, replpw)
self.replica_config(conn, replica_id, repldn)
@@ -741,6 +756,7 @@ class ReplicationManager(object):
def setup_replication(self, r_hostname, r_port=389, r_sslport=636,
r_binddn=None, r_bindpw=None, starttls=False,
is_cs_replica=False):
+ assert isinstance(r_binddn, DN)
# note - there appears to be a bug in python-ldap - it does not
# allow connections using two different CA certs
if starttls:
@@ -836,7 +852,7 @@ class ReplicationManager(object):
root_logger.info("Agreement is ready, starting replication . . .")
# Add winsync replica to the public DIT
- dn = str(DN(('cn',ad_dc_name),('cn','replicas'),('cn','ipa'),('cn','etc'), self.suffix))
+ dn = DN(('cn',ad_dc_name),('cn','replicas'),('cn','ipa'),('cn','etc'), self.suffix)
entry = ipaldap.Entry(dn)
entry.setValues("objectclass", ["nsContainer", "ipaConfigObject"])
entry.setValues("cn", ad_dc_name)
@@ -910,17 +926,17 @@ class ReplicationManager(object):
filter = '(&(nsDS5ReplicaHost=%s)' \
'(|(objectclass=nsDSWindowsReplicationAgreement)' \
'(objectclass=nsds5ReplicationAgreement)))' % hostname
- entry = conn.search_s("cn=config", ldap.SCOPE_SUBTREE, filter)
- if len(entry) == 0:
+ entries = conn.getList(DN(('cn', 'config')), ldap.SCOPE_SUBTREE, filter)
+ if len(entries) == 0:
root_logger.error("Unable to find replication agreement for %s" %
(hostname))
raise RuntimeError("Unable to proceed")
- if len(entry) > 1:
+ if len(entries) > 1:
root_logger.error("Found multiple agreements for %s" % hostname)
- root_logger.error("Using the first one only (%s)" % entry[0].dn)
+ root_logger.error("Using the first one only (%s)" % entries[0].dn)
- dn = entry[0].dn
- schedule = entry[0].nsds5replicaupdateschedule
+ dn = entries[0].dn
+ schedule = entries[0].getValue('nsds5replicaupdateschedule')
# On the remote chance of a match. We force a synch to happen right
# now by setting the schedule to something and quickly removing it.
@@ -965,16 +981,14 @@ class ReplicationManager(object):
# delete master kerberos key and all its svc principals
try:
filter='(krbprincipalname=*/%s@%s)' % (replica, realm)
- entries = self.conn.search_s(self.suffix, ldap.SCOPE_SUBTREE,
- filterstr=filter)
+ entries = self.conn.getList(self.suffix, ldap.SCOPE_SUBTREE,
+ filterstr=filter)
if len(entries) != 0:
dnset = self.conn.get_dns_sorted_by_length(entries,
reverse=True)
for dns in dnset:
for dn in dns:
self.conn.deleteEntry(dn)
- except ldap.NO_SUCH_OBJECT:
- pass
except errors.NotFound:
pass
except Exception, e:
@@ -984,18 +998,18 @@ class ReplicationManager(object):
err = e
# remove replica memberPrincipal from s4u2proxy configuration
- dn1 = DN(u'cn=ipa-http-delegation', api.env.container_s4u2proxy, self.suffix)
+ dn1 = DN(('cn', 'ipa-http-delegation'), api.env.container_s4u2proxy, self.suffix)
member_principal1 = "HTTP/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm)
- dn2 = DN(u'cn=ipa-ldap-delegation-targets', api.env.container_s4u2proxy, self.suffix)
+ dn2 = DN(('cn', 'ipa-ldap-delegation-targets'), api.env.container_s4u2proxy, self.suffix)
member_principal2 = "ldap/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm)
- dn3 = DN(u'cn=ipa-cifs-delegation-targets', api.env.container_s4u2proxy, self.suffix)
+ dn3 = DN(('cn', 'ipa-cifs-delegation-targets'), api.env.container_s4u2proxy, self.suffix)
member_principal3 = "cifs/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm)
- for (dn, member_principal) in ((str(dn1), member_principal1),
- (str(dn2), member_principal2),
- (str(dn3), member_principal3)):
+ for (dn, member_principal) in ((dn1, member_principal1),
+ (dn2, member_principal2),
+ (dn3, member_principal3)):
try:
mod = [(ldap.MOD_DELETE, 'memberPrincipal', member_principal)]
self.conn.modify_s(dn, mod)
@@ -1010,16 +1024,14 @@ class ReplicationManager(object):
# delete master entry with all active services
try:
- dn = 'cn=%s,cn=masters,cn=ipa,cn=etc,%s' % (replica, self.suffix)
- entries = self.conn.search_s(dn, ldap.SCOPE_SUBTREE)
+ dn = DN(('cn', replica), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), self.suffix)
+ entries = self.conn.getList(dn, ldap.SCOPE_SUBTREE)
if len(entries) != 0:
dnset = self.conn.get_dns_sorted_by_length(entries,
reverse=True)
for dns in dnset:
for dn in dns:
self.conn.deleteEntry(dn)
- except ldap.NO_SUCH_OBJECT:
- pass
except errors.NotFound:
pass
except Exception, e:
@@ -1029,15 +1041,13 @@ class ReplicationManager(object):
err = e
try:
- basedn = 'cn=etc,%s' % self.suffix
+ basedn = DN(('cn', 'etc'), self.suffix)
filter = '(dnaHostname=%s)' % replica
- entries = self.conn.search_s(basedn, ldap.SCOPE_SUBTREE,
- filterstr=filter)
+ entries = self.conn.getList(basedn, ldap.SCOPE_SUBTREE,
+ filterstr=filter)
if len(entries) != 0:
for e in entries:
self.conn.deleteEntry(e.dn)
- except ldap.NO_SUCH_OBJECT:
- pass
except errors.NotFound:
pass
except Exception, e:
@@ -1047,18 +1057,16 @@ class ReplicationManager(object):
err = e
try:
- dn = 'cn=default,ou=profile,%s' % self.suffix
- ret = self.conn.search_s(dn, ldap.SCOPE_BASE,
- '(objectclass=*)')[0]
- srvlist = ret.data.get('defaultServerList')
- if len(srvlist) > 0:
- srvlist = srvlist[0].split()
+ dn = DN(('cn', 'default'), ('ou', 'profile'), self.suffix)
+ ret = self.conn.getEntry(dn, ldap.SCOPE_BASE, '(objectclass=*)')
+ srvlist = ret.getValue('defaultServerList', '')
+ srvlist = srvlist[0].split()
if replica in srvlist:
srvlist.remove(replica)
attr = ' '.join(srvlist)
mod = [(ldap.MOD_REPLACE, 'defaultServerList', attr)]
self.conn.modify_s(dn, mod)
- except ldap.NO_SUCH_OBJECT:
+ except errors.NotFound:
pass
except ldap.NO_SUCH_ATTRIBUTE:
pass
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index 198cb3870..dcfcbc27f 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -25,6 +25,7 @@ from ipapython import sysrestore
from ipapython import ipautil
from ipapython import services as ipaservices
from ipalib import errors
+from ipapython.dn import DN
import ldap
from ipaserver import ipaldap
import base64
@@ -78,7 +79,7 @@ class Service(object):
self.sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
self.realm = None
- self.suffix = None
+ self.suffix = DN()
self.principal = None
self.dercert = None
@@ -150,7 +151,7 @@ class Service(object):
# use URI of admin connection
if not self.admin_conn:
self.ldap_connect()
- args += ["-H", self.admin_conn._uri]
+ args += ["-H", self.admin_conn.uri]
auth_parms = []
if self.dm_password:
@@ -183,15 +184,15 @@ class Service(object):
cn=kerberos to cn=services
"""
- dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, self.realm, self.suffix)
+ dn = DN(('krbprincipalname', principal), ('cn', self.realm), ('cn', 'kerberos'), self.suffix)
try:
entry = self.admin_conn.getEntry(dn, ldap.SCOPE_BASE)
except errors.NotFound:
# There is no service in the wrong location, nothing to do.
# This can happen when installing a replica
- return
- newdn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, self.suffix)
- hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
+ return None
+ newdn = DN(('krbprincipalname', principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix)
+ hostdn = DN(('fqdn', self.fqdn), ('cn', 'computers'), ('cn', 'accounts'), self.suffix)
self.admin_conn.deleteEntry(dn)
entry.dn = newdn
classes = entry.getValues("objectclass")
@@ -211,8 +212,8 @@ class Service(object):
if not self.admin_conn:
self.ldap_connect()
- dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, self.suffix)
- hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
+ dn = DN(('krbprincipalname', principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix)
+ hostdn = DN(('fqdn', self.fqdn), ('cn', 'computers'), ('cn', 'accounts'), self.suffix)
entry = ipaldap.Entry(dn)
entry.setValues("objectclass", ["krbprincipal", "krbprincipalaux", "krbticketpolicyaux", "ipaobject", "ipaservice", "pkiuser"])
entry.setValue("krbprincipalname", principal)
@@ -242,7 +243,7 @@ class Service(object):
self.ldap_disconnect()
self.ldap_connect()
- dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (self.principal, self.suffix)
+ dn = DN(('krbprincipalname', self.principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix)
mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
try:
self.admin_conn.modify_s(dn, mod)
@@ -327,13 +328,12 @@ class Service(object):
self.steps = []
def ldap_enable(self, name, fqdn, dm_password, ldap_suffix):
+ assert isinstance(ldap_suffix, DN)
self.disable()
if not self.admin_conn:
self.ldap_connect()
- entry_name = "cn=%s,cn=%s,%s,%s" % (name, fqdn,
- "cn=masters,cn=ipa,cn=etc",
- ldap_suffix)
+ entry_name = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix)
order = SERVICE_LIST[name][1]
entry = ipaldap.Entry(entry_name)
entry.setValues("objectclass",
@@ -362,6 +362,8 @@ class SimpleServiceInstance(Service):
self.step("configuring %s to start on boot" % self.service_name, self.__enable)
self.start_creation("Configuring %s" % self.service_name)
+ suffix = ipautil.dn_attribute_property('_ldap_suffix')
+
def __start(self):
self.backup_state("running", self.is_running())
self.restart()