Index: conffiles/etc/univention/s4connector/s4/mapping.py =================================================================== --- conffiles/etc/univention/s4connector/s4/mapping.py (Revision 61284) +++ conffiles/etc/univention/s4connector/s4/mapping.py (Arbeitskopie) @@ -59,6 +59,8 @@ 'CN=DomainUpdates,CN=System,@%@connector/s4/ldap/base@%@', 'CN=Password Settings Container,CN=System,@%@connector/s4/ldap/base@%@', 'DC=RootDNSServers,CN=MicrosoftDNS,CN=System,@%@connector/s4/ldap/base@%@', + 'DC=RootDNSServers,CN=MicrosoftDNS,DC=DomainDnsZones,@%@connector/s4/ldap/base@%@', + 'DC=RootDNSServers,CN=MicrosoftDNS,DC=ForestDnsZones,@%@connector/s4/ldap/base@%@', 'CN=File Replication Service,CN=System,@%@connector/s4/ldap/base@%@', 'CN=RpcServices,CN=System,@%@connector/s4/ldap/base@%@', 'CN=Meetings,CN=System,@%@connector/s4/ldap/base@%@', @@ -600,42 +602,56 @@ }, ), +@!@ + +sync_mode_dns = configRegistry.get('connector/s4/mapping/dns/syncmode') +if not sync_mode_dns: + sync_mode_dns = configRegistry.get('connector/s4/mapping/syncmode') + +if configRegistry.get('connector/s4/mapping/dns/position') == 'legacy': + s4_dns_ldap_base = "CN=System,%s" % (configRegistry['connector/s4/ldap/base'],) +else: + s4_dns_ldap_base = "DC=DomainDnsZones,%s" % (configRegistry['connector/s4/ldap/base'],) + +dns_section = ''' 'dns': univention.s4connector.property ( - ucs_default_dn='cn=dns,@%@ldap/base@%@', - con_default_dn='CN=MicrosoftDNS,CN=System,@%@connector/s4/ldap/base@%@', + ucs_default_dn='cn=dns,%(ldap_base)s', + con_default_dn='CN=MicrosoftDNS,%(s4_dns_ldap_base)s', ucs_module='dns/dns', identify=univention.s4connector.s4.dns.identify, + sync_mode='%(sync_mode_dns)s', - @!@ -if configRegistry.get('connector/s4/mapping/dns/syncmode'): - print "sync_mode='%s'," % configRegistry.get('connector/s4/mapping/dns/syncmode') -else: - print "sync_mode='%s'," % configRegistry.get('connector/s4/mapping/syncmode') -@!@ - scope='sub', con_search_filter='(|(objectClass=dnsNode)(objectClass=dnsZone))', - position_mapping = [( ',cn=dns,@%@ldap/base@%@', ',CN=MicrosoftDNS,CN=System,@%@connector/s4/ldap/base@%@' )], + dn_mapping_function=[ univention.s4connector.s4.dns.dns_dn_mapping ], +''' % { + 'ldap_base': configRegistry['ldap/base'], + 'sync_mode_dns': sync_mode_dns, + 's4_dns_ldap_base': s4_dns_ldap_base, + } -@!@ ignore_filter = '' for dns in configRegistry.get('connector/s4/mapping/dns/ignorelist', '').split(','): if dns: ignore_filter += '(%s)' % (dns) if ignore_filter: - print " ignore_filter='(|%s)'," % ignore_filter -@!@ + dns_section = dns_section + ''' + ignore_filter='(|%s)',''' % ignore_filter + dns_section = dns_section + ''' ignore_subtree = global_ignore_subtree, con_sync_function = univention.s4connector.s4.dns.ucs2con, ucs_sync_function = univention.s4connector.s4.dns.con2ucs, - ), -@!@ + ),''' + +print dns_section + + if configRegistry.is_true('connector/s4/mapping/gpo', True): ignore_filter = '' for gpo in configRegistry.get('connector/s4/mapping/gpo/ignorelist', '').split(','): Index: debian/univention-s4-connector.postinst =================================================================== --- debian/univention-s4-connector.postinst (Revision 61284) +++ debian/univention-s4-connector.postinst (Arbeitskopie) @@ -134,6 +134,10 @@ fi fi + if [ "$1" = "configure" -a -n "$2" ] && dpkg --compare-versions "$2" lt 9.0.16-11; then + univention-config-registry set connector/s4/mapping/dns/position?'legacy' + fi + if [ "$skip_final_restart" != "true" ]; then /etc/init.d/univention-s4-connector restart fi Index: debian/univention-s4-connector.univention-config-registry-variables =================================================================== --- debian/univention-s4-connector.univention-config-registry-variables (Revision 61284) +++ debian/univention-s4-connector.univention-config-registry-variables (Arbeitskopie) @@ -267,3 +267,9 @@ Description[en]=Group policies are stored in Group Policy Objects (GPOs) in the directory /var/lib/samba/sysvol/ on all Samba 4 domain controllers. The GPOs and the corresponding access rights are referenced in Samba 4 LDAP. If this option is activated, the access rights of the GPO references are synchronised to the UCS LDAP along with the references. If the variable is unset, the references are not synchronised. Type=bool Categories=service-s4con + +[connector/s4/mapping/dns/position] +Description[de]=Diese Variable bestimmt die Basis-DN der DNS Objekte im Samba Verzeichnisdienst. Falls sie auf dem Wert 'legacy' steht, dann schreibt der S4-Connector neue DNS-Zonen in Samba4 unter CN=System statt unter DC=DomainDnsZones. Diese Variable sollte nur einmalig nach manueller Migration der DNS-Objekte angepasst werden, falls sie noch auf 'legacy' steht. +Description[en]=This variable determins the base DN of DNS objects in the Samba directory service. When set to 'legacy', the S4 Connector writes new DNS zones in Samba4 below CN=System instead of below DC=DomainDnsZones. This variable should only be modified once after manual migration of the DNS objects, if it still has the value 'legacy'. +Type=str +Categories=service-s4con Index: modules/univention/s4connector/__init__.py =================================================================== --- modules/univention/s4connector/__init__.py (Revision 61284) +++ modules/univention/s4connector/__init__.py (Arbeitskopie) @@ -825,6 +825,26 @@ ud.debug(ud.LDAP, ud.INFO, "__sync_file_from_ucs: No mapping was found for dn: %s" % dn) return True + def get_ucs_ldap_object_dn(self, dn): + _d=ud.function('ldap.get_ucs_ldap_object_dn') + + for i in [0, 1]: # do it twice if the LDAP connection was closed + if type(dn) == type(u''): + searchdn = dn + else: + searchdn = unicode(dn) + try: + return self.lo.lo.lo.search_s( searchdn, ldap.SCOPE_BASE, '(objectClass=*)', ('dn',))[0][0] + except ldap.NO_SUCH_OBJECT: + return None + except ldap.INVALID_DN_SYNTAX: + return None + except ldap.INVALID_SYNTAX: + return None + except (ldap.SERVER_DOWN, SystemExit): + self.open_ucs() + continue + def get_ucs_ldap_object(self, dn): _d=ud.function('ldap.get_ucs_ldap_object') Index: modules/univention/s4connector/s4/__init__.py =================================================================== --- modules/univention/s4connector/s4/__init__.py (Revision 61284) +++ modules/univention/s4connector/s4/__init__.py (Arbeitskopie) @@ -38,7 +38,7 @@ import univention.s4connector import univention.debug2 as ud from ldap.controls import LDAPControl -from ldap.controls import SimplePagedResultsControl +from ldap.controls import SimplePagedResultsControl, LDAPControl from samba.dcerpc import security from samba.ndr import ndr_pack, ndr_unpack from samba.dcerpc import misc @@ -45,6 +45,11 @@ DECODE_IGNORELIST=['objectSid', 'objectGUID', 'repsFrom', 'replUpToDateVector', 'ipsecData', 'logonHours', 'userCertificate', 'dNSProperty', 'dnsRecord'] +LDAP_SERVER_SHOW_DELETED_OID = "1.2.840.113556.1.4.417" +LDB_CONTROL_DOMAIN_SCOPE_OID = "1.2.840.113556.1.4.1339" +LDB_CONTROL_RELAX_OID = "1.3.6.1.4.1.4203.666.5.12" +LDB_CONTROL_PROVISION_OID = '1.3.6.1.4.1.7165.4.3.16' + # page results PAGE_SIZE = 1000 @@ -110,7 +115,6 @@ ud.debug(ud.LDAP, ud.INFO, 'add_primary_group_to_addlist: Set primary group to %s (rid) for %s' % (primary_group_rid, object.get('dn'))) addlist.append(('primaryGroupID', [primary_group_rid])) - LDB_CONTROL_RELAX_OID = '1.3.6.1.4.1.4203.666.5.12' serverctrls.append(LDAPControl(LDB_CONTROL_RELAX_OID,criticality=0)) def __is_groupType_local(groupType): @@ -126,7 +130,6 @@ ud.debug(ud.LDAP, ud.INFO, "groupType: %s" % groupType) if __is_groupType_local(groupType): - LDB_CONTROL_RELAX_OID = '1.3.6.1.4.1.4203.666.5.12' serverctrls.append(LDAPControl(LDB_CONTROL_RELAX_OID,criticality=0)) sambaSID = object.get('attributes', {}).get('sambaSID', [])[0] @@ -266,7 +269,7 @@ try: if value.lower() == ucsval.lower(): value = conval - ud.debug(ud.LDAP, ud.INFO, "samaccount_dn_mapping: map samaccountanme regarding to mapping-table") + ud.debug(ud.LDAP, ud.INFO, "samaccount_dn_mapping: map samaccountanme according to mapping-table") continue except UnicodeDecodeError: pass # values are not the same codec @@ -316,7 +319,7 @@ for ucsval, conval in s4connector.property[propertyname].mapping_table[propertyattrib]: if samaccountname.lower() == conval.lower(): samaccountname = ucsval - ud.debug(ud.LDAP, ud.INFO, "samaccount_dn_mapping: map samaccountanme regarding to mapping-table") + ud.debug(ud.LDAP, ud.INFO, "samaccount_dn_mapping: map samaccountanme according to mapping-table") continue else: ud.debug(ud.LDAP, ud.INFO, "samaccount_dn_mapping: samaccountname not in mapping-table") @@ -384,8 +387,7 @@ def old_user_dn_mapping(s4connector, given_object): object = copy.deepcopy(given_object) - # LDAP_SERVER_SHOW_DELETED_OID -> 1.2.840.113556.1.4.417 - ctrls = [LDAPControl('1.2.840.113556.1.4.417',criticality=1)] + ctrls = [LDAPControl(LDAP_SERVER_SHOW_DELETED_OID, criticality=1)] samaccountname = '' if object.has_key('sAMAccountName'): @@ -753,8 +755,7 @@ ud.debug(ud.LDAP, ud.INFO,"__init__: init add config section 'S4 GUID'") self.config.add_section('S4 GUID') try: - # LDAP_SERVER_SHOW_DELETED_OID -> 1.2.840.113556.1.4.417 - self.ctrl_show_deleted = LDAPControl('1.2.840.113556.1.4.417',criticality=1) + self.ctrl_show_deleted = LDAPControl(LDAP_SERVER_SHOW_DELETED_OID, criticality=1) res = self.lo_s4.lo.search_ext_s('',ldap.SCOPE_BASE, 'objectclass=*',[], serverctrls=[ self.ctrl_show_deleted ], timeout=-1, sizelimit=0) @@ -773,7 +774,6 @@ # objectSid modification for an Samba4 object is only possible with the "provision" control: if self.configRegistry.is_true('connector/s4/mapping/sid_to_s4', False): - LDB_CONTROL_PROVISION_OID = '1.3.6.1.4.1.7165.4.3.16' self.serverctrls_for_add_and_modify.append(LDAPControl(LDB_CONTROL_PROVISION_OID,criticality=0) ) # Save a list of objects just created, this is needed to @@ -865,6 +865,12 @@ self.lo_s4.lo.set_option(ldap.OPT_REFERRALS,0) + if self.configRegistry.get('connector/s4/mapping/dns/position') == 'legacy': + self.s4_ldap_partitions = (self.s4_ldap_base,) + else: + self.s4_ldap_partitions = (self.s4_ldap_base, "DC=DomainDnsZones,%s" % self.s4_ldap_base, "DC=ForestDnsZones,%s" % self.s4_ldap_base) + + # encode string to unicode def encode(self, string): try: @@ -968,6 +974,24 @@ def isInCreationList(self, dn): return dn.lower() in self.creation_list + def get_object_dn(self, dn): + _d=ud.function('ldap.get_object_dn') + for i in [0, 1]: # do it twice if the LDAP connection was closed + try: + dn, s4_object=self.lo_s4.lo.search_ext_s(compatible_modstring(dn),ldap.SCOPE_BASE,'(objectClass=*)', ('dn',))[0] + try: + ud.debug(ud.LDAP, ud.INFO,"get_object: got object: %s" % dn) + except: # FIXME: which exception is to be caught? + ud.debug(ud.LDAP, ud.INFO,"get_object: got object: ") + return dn + except (ldap.SERVER_DOWN, SystemExit): + if i == 0: + self.open_s4() + continue + raise + except: # FIXME: which exception is to be caught? + pass + def get_object(self, dn): _d=ud.function('ldap.get_object') for i in [0, 1]: # do it twice if the LDAP connection was closed @@ -1003,6 +1027,17 @@ return max(usnchanged,usncreated) + def __search_s4_partitions(self, scope=ldap.SCOPE_SUBTREE, filter='', attrlist= [], show_deleted=False): + ''' + search s4 across all partitions listed in self.s4_ldap_partitions + ''' + _d=ud.function('ldap.__search_s4_partitions') + res = [] + for base in self.s4_ldap_partitions: + res += self.__search_s4(base, scope, filter, attrlist, show_deleted) + + return res + def __search_s4(self, base=None, scope=ldap.SCOPE_SUBTREE, filter='', attrlist= [], show_deleted=False): ''' search s4 @@ -1012,12 +1047,13 @@ if not base: base=self.lo_s4.base - ctrls=[] - ctrls.append(SimplePagedResultsControl(True, PAGE_SIZE, '')) + ctrls=[ + LDAPControl(LDB_CONTROL_DOMAIN_SCOPE_OID, criticality=0), ## Don't show referrals + SimplePagedResultsControl(True, PAGE_SIZE, ''), + ] if show_deleted: - # LDAP_SERVER_SHOW_DELETED_OID -> 1.2.840.113556.1.4.417 - ctrls.append(LDAPControl('1.2.840.113556.1.4.417',criticality=1)) + ctrls.append(LDAPControl(LDAP_SERVER_SHOW_DELETED_OID, criticality=1)) ud.debug(ud.LDAP, ud.INFO, "Search S4 with filter: %s" % filter) msgid = self.lo_s4.lo.search_ext(base, scope, filter, attrlist, serverctrls=ctrls, timeout=-1, sizelimit=0) @@ -1046,7 +1082,6 @@ else: ud.debug(ud.LDAP, ud.WARN, "S4 ignores PAGE_RESULTS") break - return encode_s4_resultlist(res) @@ -1078,7 +1113,8 @@ if filter !='': usnFilter = '(&(%s)(%s))' % ( filter, usnFilter ) - return self.__search_s4( filter=usnFilter, show_deleted=show_deleted) + res = self.__search_s4_partitions(filter=usnFilter, show_deleted=show_deleted) + return sorted(res, key=lambda element: element[1][attribute][0]) # search fpr objects with uSNCreated and uSNChanged in the known range @@ -1132,9 +1168,10 @@ filter = '(&(%s)(|(uSNChanged=%s)(uSNCreated=%s)))' % (filter,changeUSN,changeUSN) else: filter = '(|(uSNChanged=%s)(uSNCreated=%s))' % (changeUSN,changeUSN) - return self.__search_s4(filter=filter, show_deleted=show_deleted) + return self.__search_s4_partitions(filter=filter, show_deleted=show_deleted) + def __dn_from_deleted_object(self, object, GUID): ''' gets dn for deleted object (original dn before the object was moved into the deleted objects container) Index: modules/univention/s4connector/s4/dns.py =================================================================== --- modules/univention/s4connector/s4/dns.py (Revision 61284) +++ modules/univention/s4connector/s4/dns.py (Arbeitskopie) @@ -35,13 +35,13 @@ import univention.debug2 as ud import univention.s4connector.s4 import univention.admin.uldap - -from samba.dcerpc import dnsp -from samba.ndr import ndr_print -from samba.ndr import ndr_pack -from samba.ndr import ndr_unpack -import binascii +from samba.dcerpc import dnsp +from samba.ndr import ndr_print, ndr_pack, ndr_unpack +import binascii +import copy +import time + from samba.provision.sambadns import ARecord # def __init__(self, ip_addr, serial=1, ttl=3600): @@ -90,6 +90,261 @@ import univention.admin.handlers.dns.reverse_zone import univention.admin.handlers.dns.ptr_record +# mapping funtions +def dns_dn_mapping(s4connector, given_object, dn_mapping_stored, isUCSobject): + ''' + map dn of given object (which must have an s4_RR_attr in S4) + ol_oc_filter and s4_RR_filter are objectclass filters in UCS and S4 + ''' + obj = copy.deepcopy(given_object) + + propertyname = 'dns' + propertyattrib = u'relativeDomainName' ## using LDAP name here, for simplicity + ol_oc_filter = '(objectClass=dNSZone)' ## all OpenLDAP DNS records match + ol_RR_attr = 'relativeDomainName' + s4_RR_filter = u'(objectClass=dnsNode)' ## This also matches the DC=@ SOA object + s4_RR_attr = 'dc' ## Note: the S4 attribute itself is lowercase + + if obj['dn'] != None: + try: + s4_RR_val = obj['attributes'][s4_RR_attr][0] + except (KeyError, IndexError): + s4_RR_val = '' + + def dn_premapped(given_object, dn_key, dn_mapping_stored): + if (not dn_key in dn_mapping_stored) or (not given_object[dn_key]): + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: not premapped (in first instance)") + return False + else: # check if DN exists + if isUCSobject: + premapped_dn = s4connector.get_object_dn(given_object[dn_key]) + if premapped_dn != None: + # ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped S4 object found") + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped S4 object: %s" % premapped_dn) + return True + else: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped S4 object not found") + return False + else: + premapped_dn = s4connector.get_ucs_ldap_object_dn(given_object[dn_key]) + if premapped_dn != None: + # ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped UCS object found") + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped UCS object: %s" % premapped_dn) + return True + else: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: premapped UCS object not found") + return False + + for dn_key in ['dn', 'olddn']: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: check newdn for key '%s'" % dn_key) + if dn_key in obj and not dn_premapped(obj, dn_key, dn_mapping_stored): + + dn = obj[dn_key] + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: dn: %s" % dn) + + # Skip Configuration objects with empty DNs + if dn == None: + break + + pos = string.find(dn,'=') + rdn = univention.s4connector.s4.explode_unicode_dn(dn) + pos2 = len(rdn[0]) + + if isUCSobject: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: got an UCS-Object") + # lookup the relativeDomainName as DC/dnsNode in S4 to get corresponding DN, if not found create new + + try: + relativeDomainName = obj['attributes'][ol_RR_attr][0] + except (KeyError, IndexError): + ### Safety fallback for the unexpected case, where relativeDomainName would not be set + rdn0_attrib = dn[:pos] + if 'zoneName' == rdn0_attrib: + relativeDomainName = '@' + else: + raise ## can't determine relativeDomainName + + if s4connector.property[propertyname].mapping_table and propertyattrib in s4connector.property[propertyname].mapping_table.keys(): + for ucsval, conval in s4connector.property[propertyname].mapping_table[propertyattrib]: + try: + if relativeDomainName.lower() == ucsval.lower(): + relativeDomainName = conval + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: map relativeDomainName according to mapping-table") + continue + except UnicodeDecodeError: + pass # values are not the same codec + + try: + ol_zone_name = obj['attributes']['zoneName'][0] + except (KeyError, IndexError): + ### Safety fallback for the unexpected case, where zoneName would not be set + rdn0_attrib = dn[:pos] + if ol_RR_attr == rdn0_attrib: + ## get parent following the recipe from __split_s4_dns_dn: + rdn1_tmp = rdn[1].split('=') + rdn1_key, rdn1_val = (rdn1_tmp[0], string.join(rdn1_tmp[1:], '=')) + if 'zoneName' == rdn1_key: + ol_zone_name = rdn1_val + else: + raise ## can't determine zoneName for this relativeDomainName + + if '@' == relativeDomainName: ## or dn starts with 'zoneName=' + s4_filter = '(&(objectClass=dnsZone)(%s=%s))' % (s4_RR_attr, ol_zone_name) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: search in S4 %s" % s4_filter) + for base in s4connector.s4_ldap_partitions: + result = s4connector._s4__search_s4( + base, + ldap.SCOPE_SUBTREE, + univention.s4connector.s4.compatible_modstring(s4_filter), + attrlist=(s4_RR_attr,), + show_deleted=False) + + if result: + break + else: + ## identify position by parent zone name + ol_zone_dn = s4connector.lo.parentDn(dn) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: get dns_dn_mapping for %s" % ol_zone_dn) + fake_ol_zone_object = { + 'dn': ol_zone_dn, + 'attributes': { + 'objectClass': ['top', 'dNSZone'], + 'relativeDomainName': ['@'], + 'zoneName': [ol_zone_name], + }, + 'olddn': None, ## Just fake, not used + } + s4_zone_object = dns_dn_mapping(s4connector, fake_ol_zone_object, dn_mapping_stored, isUCSobject) + ## and use its parent as the search base + s4_zone_dn = s4connector.lo_s4.parentDn(s4_zone_object['dn']) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: search in s4 %s=%s" % (s4_RR_attr, relativeDomainName)) + result = s4connector._s4__search_s4( + s4_zone_dn, + ldap.SCOPE_SUBTREE, + univention.s4connector.s4.compatible_modstring('(&%s(%s=%s))' % (s4_RR_filter, s4_RR_attr, relativeDomainName)), + attrlist=('dn',), + show_deleted=False) + + try: + s4dn_utf16_le = result[0][0] + except (IndexError, TypeError): + s4dn_utf16_le = None + + if s4dn_utf16_le: # no referral, so we've got a valid result + s4dn = univention.s4connector.s4.encode_attrib(result[0][0]) + s4pos2 = len(univention.s4connector.s4.explode_unicode_dn(s4dn)[0]) + if dn_key == 'olddn' or (dn_key == 'dn' and not 'olddn' in obj): + ## TODO: Why do we differenciate here? + newdn = s4dn + else: + ## TODO: Why do we need to patch the DNs here? Rename case? Modify case? + s4dn = s4dn[:s4pos2] + dn[pos2:] + newdn = s4dn.lower().replace(s4connector.lo_s4.base.lower(), s4connector.lo.base.lower()) + else: + ## Ok, it's a new object, so propose a S4 DN for it: + if '@' == relativeDomainName: ## or dn starts with 'zoneName=' + new_rdn = 'dc=%s' % ol_zone_name + else: + new_rdn = 'dc=%s,dc=%s' % (relativeDomainName, ol_zone_name) + newdn = new_rdn + ',' + s4connector.property['dns'].con_default_dn + + else: + # get the object to read the s4_RR_attr in S4 and use it as name + # we have no fallback here, the given dn must be found in S4 or we've got an error + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: got an S4-Object") + i = 0 + + while ( not s4_RR_val ): # in case of olddn this is already set + i = i + 1 + search_dn = obj.get('deleted_dn', dn) + try: + s4_RR_val = univention.s4connector.s4.encode_attrib( + s4connector.lo_s4.lo.search_ext_s(univention.s4connector.s4.compatible_modstring(search_dn), ldap.SCOPE_BASE, + s4_RR_filter, [s4_RR_attr]) [0][1][s4_RR_attr][0]) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: got %s from S4" % s4_RR_attr) + except ldap.NO_SUCH_OBJECT: # S4 may need time + if i > 5: + raise + time.sleep(1) # S4 may need some time... + + if s4connector.property[propertyname].mapping_table and propertyattrib in s4connector.property[propertyname].mapping_table.keys(): + for ucsval, conval in s4connector.property[propertyname].mapping_table[propertyattrib]: + if s4_RR_val.lower() == conval.lower(): + s4_RR_val = ucsval + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: map %s according to mapping-table" % s4_RR_attr) + continue + else: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: %s not in mapping-table" % s4_RR_attr) + + # search for object with this dn in ucs, needed if it is located in a different container + try: + s4_ocs = obj['attributes']['objectClass'] + except (KeyError, TypeError): + s4_ocs = [] + + if 'dnsZone' in s4_ocs: + s4_zone_name = s4_RR_val + base = s4connector.lo.base + ol_search_attr = 'zoneName' + ## could use a specific LDAP filter here, but not necessary: + # ol_oc_filter = '(&(objectClass=dNSZone)(|(univentionObjectType=dns/forward_zone)(univentionObjectType=dns/reverse_zone)))' + elif 'dnsNode' in s4_ocs: + ## identify position of the parent zone + s4pos = string.find(rdn[1], '=') + s4_zone_name = rdn[1][s4pos+1:] + s4_zone_dn = s4connector.lo_s4.parentDn(dn) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: get dns_dn_mapping for %s" % s4_zone_dn) + fake_s4_zone_object = { + 'dn': s4_zone_dn, + 'attributes': { + 'objectClass': ['top', 'dnsZone'], + 'dc': [s4_zone_name], + }, + 'olddn': None, ## Just fake, not used + } + ol_zone_object = dns_dn_mapping(s4connector, fake_s4_zone_object, dn_mapping_stored, isUCSobject) + ## and use that as the search base + base = ol_zone_object['dn'] + ol_search_attr = ol_RR_attr + ## could use a specific LDAP filter here, but not necessary: + # ol_oc_filter = '(&(objectClass=dNSZone)(!(|(univentionObjectType=dns/forward_zone)(univentionObjectType=dns/reverse_zone))))' + + ud.debug(ud.LDAP, ud.WARN, "dns_dn_mapping: UCS filter: (&%s(%s=%s))" % (ol_oc_filter, ol_search_attr, s4_RR_val)) + ucsdn_result = s4connector.search_ucs(filter=u'(&%s(%s=%s))' % (ol_oc_filter, ol_search_attr, s4_RR_val), + base=base, scope='sub', attr=('dn',)) + + try: + ucsdn = ucsdn_result[0][0] + except (IndexError, TypeError): + ucsdn = None + + ud.debug(ud.LDAP, ud.ALL, "dns_dn_mapping: Found ucsdn: %s" % ucsdn) + if ucsdn: + ## In this case we have found the old or first known DN: + newdn = ucsdn + ## HACK: + # pos = string.find(ucsdn, '=') + # newdn = ol_search_attr + ucsdn[pos:] ## adjust for zoneName + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: newdn is ucsdn") + else: + ## Ok, it's a new object, so propose a S4 DN for it: + if 'dnsZone' in s4_ocs or '@' == s4_RR_val: + new_rdn = 'zoneName=%s' % s4_zone_name + else: + new_rdn = 'relativeDomainName=%s,zoneName=%s' % (s4_RR_val, s4_zone_name) + newdn = new_rdn + ',' + s4connector.property['dns'].ucs_default_dn + + try: + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: mapping for key '%s':" % dn_key) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: source DN: %s" % dn) + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: mapped DN: %s" % newdn) + except: # FIXME: which exception is to be caught? + ud.debug(ud.LDAP, ud.INFO, "dns_dn_mapping: dn-print failed") + + obj[dn_key]=newdn + + return obj + ''' HELPER functions ''' def __append_dot(str): if str[-1] != '.': @@ -109,21 +364,15 @@ raise return zoneName -def __create_default_s4_zone_dn(s4connector, object): - zoneName=__get_zone_name(object) - dn='DC=%s,%s' % (zoneName[0], s4connector.property['dns'].con_default_dn) - return (dn, zoneName) - -def __create_s4_forward_zone(s4connector, zoneDn, zoneName): +def __create_s4_forward_zone(s4connector, zoneDn): al=[] al.append(('objectClass', ['top', 'dnsZone'])) - al.append(('DC', univention.s4connector.s4.compatible_list(zoneName))) ud.debug(ud.LDAP, ud.INFO, '_dns_zone_forward_con_create: dn: %s' % zoneDn) ud.debug(ud.LDAP, ud.INFO, '_dns_zone_forward_con_create: al: %s' % al) s4connector.lo_s4.lo.add_s(zoneDn, al) -def __create_s4_forward_zone_at(s4connector, zoneDnAt, zoneName): +def __create_s4_forward_zone_at(s4connector, zoneDnAt): al=[] al.append(('objectClass', ['top', 'dnsNode'])) al.append(('dc', ['@'])) @@ -295,13 +544,14 @@ def s4_zone_create(s4connector, object): _d=ud.function('s4_zone_create') - zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) + zoneDn = object['dn'] + zoneName = __get_zone_name(object) # Create the forward zone in S4 if it does not exist try: searchResult=s4connector.lo_s4.lo.search_s(zoneDn, ldap.SCOPE_BASE, '(objectClass=*)',['dn']) except ldap.NO_SUCH_OBJECT: - __create_s4_forward_zone(s4connector, zoneDn, zoneName) + __create_s4_forward_zone(s4connector, zoneDn) # Create @ object zoneDnAt='DC=@,%s' % zoneDn @@ -313,7 +563,7 @@ if searchResult and searchResult[0][1]: old_dnsRecords=searchResult[0][1].get('dnsRecord') except ldap.NO_SUCH_OBJECT: - __create_s4_forward_zone_at(s4connector, zoneDnAt, zoneName) + __create_s4_forward_zone_at(s4connector, zoneDnAt) dnsRecords=[] @@ -354,7 +604,7 @@ def s4_zone_delete(s4connector, object): _d=ud.function('s4_zone_delete') - zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) + zoneDn = object['dn'] zoneDnAt='DC=@,%s' % zoneDn @@ -373,8 +623,6 @@ def s4_dns_node_base_create(s4connector, object, dnsRecords): _d=ud.function('s4_dns_node_base_create') - zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) - relativeDomainNames=object['attributes'].get('relativeDomainName') relativeDomainNames=univention.s4connector.s4.compatible_list(relativeDomainNames) @@ -381,7 +629,7 @@ old_dnsRecords=[] # Create dnsNode object - dnsNodeDn='DC=%s,%s' % (relativeDomainNames[0],zoneDn) + dnsNodeDn=object['dn'] try: searchResult=s4connector.lo_s4.lo.search_s(dnsNodeDn, ldap.SCOPE_BASE, '(objectClass=*)') if searchResult and searchResult[0][1]: @@ -396,12 +644,10 @@ def s4_dns_node_base_delete(s4connector, object): _d=ud.function('s4_dns_node_base_delete') - zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) - relativeDomainNames=object['attributes'].get('relativeDomainName') relativeDomainNames=univention.s4connector.s4.compatible_list(relativeDomainNames) - dnsNodeDn='DC=%s,%s' % (relativeDomainNames[0],zoneDn) + dnsNodeDn=object['dn'] try: res=s4connector.lo_s4.lo.delete_s(dnsNodeDn) except ldap.NO_SUCH_OBJECT: @@ -422,7 +668,7 @@ return True -def __split_s4_dn(dn): +def __split_s4_dns_dn(dn): # split zone dn=ldap.explode_dn(dn) @@ -436,7 +682,7 @@ _d=ud.function('ucs_host_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_create: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) # unpack the host record a=__unpack_aRecord(object) @@ -472,7 +718,7 @@ _d=ud.function('ucs_host_record_delete') ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_delete: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: @@ -501,7 +747,7 @@ _d=ud.function('ucs_ptr_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_ptr_record_create: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) # unpack the host record ptr=__unpack_ptrRecord(object) @@ -536,7 +782,7 @@ _d=ud.function('ucs_ptr_record_delete') ud.debug(ud.LDAP, ud.INFO, 'ucs_ptr_record_delete: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: @@ -553,7 +799,7 @@ _d=ud.function('ucs_cname_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_cname_create: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) # unpack the host record c=__unpack_cName(object) @@ -588,7 +834,7 @@ _d=ud.function('ucs_cname_delete') ud.debug(ud.LDAP, ud.INFO, 'ucs_cname_delete: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: @@ -614,7 +860,7 @@ _d=ud.function('ucs_srv_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) # unpack the host record srv=__unpack_sRVrecord(object) @@ -673,7 +919,7 @@ _d=ud.function('ucs_srv_record_delete') ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_delete: object: %s' % object) - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: @@ -692,7 +938,8 @@ dnsRecords=[] - zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) + zoneDn = object['dn'] + zoneName = __get_zone_name(object) relativeDomainName=object['attributes'].get('relativeDomainName') relativeDomainName=univention.s4connector.s4.compatible_list(relativeDomainName) @@ -727,7 +974,7 @@ def ucs_zone_create(s4connector, object, dns_type): _d=ud.function('ucs_zone_create') - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) # create the zone if the dc=@ object was created if relativeDomainName != '@': @@ -804,7 +1051,7 @@ def ucs_zone_delete(s4connector, object, dns_type): _d=ud.function('ucs_zone_delete') - zoneName, relativeDomainName=__split_s4_dn(object['dn']) + zoneName, relativeDomainName=__split_s4_dns_dn(object['dn']) if relativeDomainName == '@': searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) @@ -890,7 +1137,7 @@ ud.debug(ud.LDAP, ud.INFO, 'dns ucs2con: Ignore unkown dns object: %s' % object['dn']) return True - ud.debug(ud.LDAP, ud.INFO, 'dns ucs2con: Object (%s) is from type %s' % (object['dn'], dns_type)) + ud.debug(ud.LDAP, ud.INFO, 'dns ucs2con: Object (%s) is of type %s' % (object['dn'], dns_type)) if dns_type == 'forward_zone' or dns_type == 'reverse_zone': if object['modtype'] in ['add', 'modify']: