From: Garming Sam Date: Mon, 28 May 2018 23:57:26 +0000 (+1200) Subject: gpo: Add a --generalize to the backup command X-Git-Tag: tdb-1.3.17~2151 X-Git-Url: http://git.samba.org/samba.git/?a=commitdiff_plain;h=07de156598b614aa0ecbbc542ff87dae43a0e08f;p=bbaumbach%2Fsamba-autobuild%2F.git gpo: Add a --generalize to the backup command This normally prints out the entities in DTD form to be given to the restore command with --entities. Specifying --entities during the backup conveniently writes these entities to a file. Generalizing occurs after the standard backup on the XML files, which will then re-write the XML file. There are a number of files which can be further handled, including many of the preferences XML files. This will require more annotation and parsing. Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett --- diff --git a/python/samba/netcmd/gpo.py b/python/samba/netcmd/gpo.py index 9706ca8a833..92526965ccc 100644 --- a/python/samba/netcmd/gpo.py +++ b/python/samba/netcmd/gpo.py @@ -48,7 +48,7 @@ import uuid from samba.ntacls import dsacl2fsacl from samba.dcerpc import nbt from samba.net import Net -from samba.gp_parse import GPParser, GPNoParserException +from samba.gp_parse import GPParser, GPNoParserException, GPGeneralizeException from samba.gp_parse.gp_pol import GPPolParser from samba.gp_parse.gp_ini import ( GPIniParser, @@ -965,10 +965,15 @@ class cmd_backup(Command): takes_options = [ Option("-H", help="LDB URL for database or target server", type=str), - Option("--tmpdir", help="Temporary directory for copying policy files", type=str) + Option("--tmpdir", help="Temporary directory for copying policy files", type=str), + Option("--generalize", help="Generalize XML entities to restore", + default=False, action='store_true'), + Option("--entities", help="File to export defining XML entities for the restore", + dest='ent_file', type=str) ] - def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None): + def run(self, gpo, H=None, tmpdir=None, generalize=False, sambaopts=None, + credopts=None, versionopts=None, ent_file=None): self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) @@ -1020,8 +1025,79 @@ class cmd_backup(Command): except Exception as e: # FIXME: Catch more specific exception raise CommandError("Error copying GPO from DC", e) + self.outf.write('GPO copied to %s\n' % gpodir) + if generalize: + self.outf.write('\nAttempting to generalize XML entities:\n') + entities = cmd_backup.generalize_xml_entities(self.outf, gpodir, + gpodir) + import operator + ents = '' + for ent in sorted(entities.items(), key=operator.itemgetter(1)): + ents += '\n'.format(ent[1].strip('&;'), ent[0]) + + if ent_file: + with open(ent_file, 'w') as f: + f.write(ents) + self.outf.write('Entities successfully written to %s\n' % + ent_file) + else: + self.outf.write('\nEntities:\n') + self.outf.write(ents) + + @staticmethod + def generalize_xml_entities(outf, sourcedir, targetdir): + entities = {} + + if not os.path.exists(targetdir): + os.mkdir(targetdir) + + l_dirs = [ sourcedir ] + r_dirs = [ targetdir ] + while l_dirs: + l_dir = l_dirs.pop() + r_dir = r_dirs.pop() + + dirlist = os.listdir(l_dir) + dirlist.sort() + for e in dirlist: + l_name = os.path.join(l_dir, e) + r_name = os.path.join(r_dir, e) + + if os.path.isdir(l_name): + l_dirs.append(l_name) + r_dirs.append(r_name) + if not os.path.exists(r_name): + os.mkdir(r_name) + else: + if l_name.endswith('.xml'): + # Restore the xml file if possible + + # Get the filename to find the parser + to_parse = os.path.basename(l_name)[:-4] + + parser = find_parser(to_parse) + try: + with open(l_name, 'r') as ltemp: + data = ltemp.read() + + concrete_xml = ET.fromstring(data) + found_entities = parser.generalize_xml(concrete_xml, r_name, entities) + except GPGeneralizeException: + outf.write('SKIPPING: Generalizing failed for %s\n' % to_parse) + + else: + # No need to generalize non-xml files. + # + # TODO This could be improved with xml files stored in + # the renamed backup file (with custom extension) by + # inlining them into the exported backups. + if not os.path.samefile(l_name, r_name): + shutil.copy2(l_name, r_name) + + return entities + class cmd_create(Command): """Create an empty GPO.""" @@ -1213,6 +1289,7 @@ class cmd_restore(cmd_create): r_dir = r_dirs.pop() dirlist = os.listdir(l_dir) + dirlist.sort() for e in dirlist: l_name = os.path.join(l_dir, e) r_name = os.path.join(r_dir, e)