import time
import re
import argparse
+import datetime
from numbers import Number
# 3rd party modules
+import pytz
+
+from tzlocal import get_localzone
+
from ldap3 import Server, Connection, ALL, DSA, IP_V4_PREFERRED, SAFE_SYNC
from ldap3 import BASE, LEVEL, SUBTREE, DEREF_NEVER, DEREF_SEARCH, DEREF_BASE, DEREF_ALWAYS
from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES
from .idict import CaseInsensitiveDict
from .istringset import CaseInsensitiveStringSet
-__version__ = '0.6.10'
+__version__ = '0.7.0'
LOG = logging.getLogger(__name__)
CFG_BASENAME = 'ldap-migration.ini'
re_dn_split = re.compile(r'\s*,\s*')
re_token_split = re.compile(r'^\s*([a-z0-9]+)\s*=\s*(\S(?:.*\S)?)\s*$', re.IGNORECASE)
+ tz = get_localzone()
+
# -------------------------------------------------------------------------
def __init__(
self, appname=None, verbose=0, version=__version__, base_dir=None):
self.tmp_dir = None
self.all_dns_file = None
self.structural_dns_file = None
+ self.migrated_file = None
self.limit = 0
self.tmp_dir = self.base_dir / 'tmp'
self.all_dns_file = self.tmp_dir / 'all-dns.txt'
self.structural_dns_file = self.tmp_dir / 'structural-dns.txt'
+ self.migrated_file = self.tmp_dir / 'migrated-entries.txt'
self.initialized = True
# -------------------------------------------------------------------------
print()
LOG.info("Migrating all entries from source to target LDAP cluster.")
- if not self.migrate_structural_entries():
- return False
+ open_args = {
+ 'encoding': 'utf-8',
+ 'errors': 'surrogateescape',
+ 'mode': 'w',
+ }
+
+ with self.migrated_file.open(**open_args) as fh:
+
+ if not self.migrate_structural_entries(fh):
+ return False
print()
return True
# -------------------------------------------------------------------------
- def migrate_structural_entries(self):
+ def migrate_structural_entries(self, fh):
print()
LOG.info("Migrating all structural entries from source to target LDAP cluster.")
self.count_modified = 0
try:
- self._migrate_entries(self.struct_dns, is_root=True, with_acl=False)
+ self._migrate_entries(self.struct_dns, fh=fh, is_root=True, with_acl=False)
except ReadLDAPItemError as e:
msg = "Abort migration: " + str(e)
LOG.error(msg)
return None
# -------------------------------------------------------------------------
- def _migrate_entries(self, cur_hash, is_root=False, with_acl=False):
+ def _migrate_entries(self, cur_hash, fh, is_root=False, with_acl=False):
wait = self.config.wait_after_write
if not is_root:
src_dn = cur_hash['dn']
- if self.migrate_entry(src_dn, with_acl=with_acl):
+ if self.migrate_entry(src_dn, fh=fh, with_acl=with_acl):
if wait:
time.sleep(wait)
for key in cur_hash['childs'].keys():
- self._migrate_entries(cur_hash['childs'][key], is_root=False, with_acl=with_acl)
+ self._migrate_entries(cur_hash['childs'][key], fh=fh, is_root=False, with_acl=with_acl)
# -------------------------------------------------------------------------
- def migrate_entry(self, src_dn, with_acl=False):
+ def migrate_entry(self, src_dn, fh, with_acl=False):
tgt_dn = self.mangle_dn(src_dn)
rev_dn = self.get_reverse_dn(tgt_dn)
if not self.simulate:
self.target.modify(tgt_dn, changes)
self.migrated_entries[rev_dn] = tgt_dn
+ ts = datetime.datetime.now(self.tz).isoformat(' ', timespec='seconds')
+ line = "{dn}: {ts}".format(dn=tgt_dn, ts=ts)
+ print(line, file=fh, flush=True)
return True
else:
return False
if not self.simulate:
self.target.add(tgt_dn, object_class=tgt_obj_classes, attributes=tgt_entry)
self.migrated_entries[rev_dn] = tgt_dn
+ ts = datetime.datetime.now(self.tz).isoformat(' ', timespec='seconds')
+ line = "{dn}: {ts}".format(dn=tgt_dn, ts=ts)
+ print(line, file=fh, flush=True)
return True
# -------------------------------------------------------------------------