python/samba/netcmd: Fix password usage for py2/py3 compatability
[nivanova/samba-autobuild/.git] / python / samba / netcmd / domain.py
index 4bd99ba6ff5c8b2dedbdd3a36e6e4ca876900854..56386c579c1be1e4e26a36264383ccee656b3d46 100644 (file)
@@ -22,6 +22,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+from __future__ import print_function
+from __future__ import division
 import samba.getopt as options
 import ldb
 import string
@@ -33,6 +35,7 @@ import tempfile
 import logging
 import subprocess
 import time
+import shutil
 from samba import ntstatus
 from samba import NTSTATUSError
 from samba import werror
@@ -41,7 +44,7 @@ from samba.net import Net, LIBNET_JOIN_AUTOMATIC
 import samba.ntacls
 from samba.join import join_RODC, join_DC, join_subdomain
 from samba.auth import system_session
-from samba.samdb import SamDB
+from samba.samdb import SamDB, get_default_backend_store
 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
 from samba.dcerpc import drsuapi
 from samba.dcerpc import drsblobs
@@ -56,7 +59,8 @@ from samba.netcmd import (
     CommandError,
     SuperCommand,
     Option
-    )
+)
+from samba.netcmd.fsmo import get_fsmo_roleowner
 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
 from samba.samba3 import Samba3
 from samba.samba3 import param as s3param
@@ -80,12 +84,14 @@ from samba.dsdb import (
     UF_SERVER_TRUST_ACCOUNT,
     UF_TRUSTED_FOR_DELEGATION,
     UF_PARTIAL_SECRETS_ACCOUNT
-    )
+)
 
 from samba.provision import (
     provision,
-    ProvisioningError
-    )
+    ProvisioningError,
+    DEFAULT_MIN_PWD_LENGTH,
+    setup_path
+)
 
 from samba.provision.common import (
     FILL_FULL,
@@ -93,44 +99,92 @@ from samba.provision.common import (
     FILL_DRS
 )
 
+from samba.netcmd.pso import cmd_domain_passwordsettings_pso
+from samba.netcmd.domain_backup import cmd_domain_backup
+
+from samba.compat import binary_type
+
+string_version_to_constant = {
+    "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
+    "2012": DS_DOMAIN_FUNCTION_2012,
+    "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
+}
+
+common_provision_join_options = [
+    Option("--machinepass", type="string", metavar="PASSWORD",
+           help="choose machine password (otherwise random)"),
+    Option("--plaintext-secrets", action="store_true",
+           help="Store secret/sensitive values as plain text on disk" +
+           "(default is to encrypt secret/ensitive values)"),
+    Option("--backend-store", type="choice", metavar="BACKENDSTORE",
+           choices=["tdb", "mdb"],
+           help="Specify the database backend to be used "
+           "(default is %s)" % get_default_backend_store()),
+    Option("--targetdir", metavar="DIR",
+           help="Set target directory (where to store provision)", type=str),
+    Option("-q", "--quiet", help="Be quiet", action="store_true"),
+]
+
+common_join_options = [
+    Option("--server", help="DC to join", type=str),
+    Option("--site", help="site to join", type=str),
+    Option("--domain-critical-only",
+           help="only replicate critical domain objects",
+           action="store_true"),
+    Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
+           choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
+           help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
+           "BIND9_DLZ uses samba4 AD to store zone information, "
+           "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
+           default="SAMBA_INTERNAL"),
+    Option("-v", "--verbose", help="Be verbose", action="store_true")
+]
+
+common_ntvfs_options = [
+    Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
+           action="store_true")
+]
+
+
 def get_testparm_var(testparm, smbconf, varname):
     errfile = open(os.devnull, 'w')
     p = subprocess.Popen([testparm, '-s', '-l',
                           '--parameter-name=%s' % varname, smbconf],
                          stdout=subprocess.PIPE, stderr=errfile)
-    (out,err) = p.communicate()
+    (out, err) = p.communicate()
     errfile.close()
     lines = out.split('\n')
     if lines:
         return lines[0].strip()
     return ""
 
+
 try:
-   import samba.dckeytab
+    import samba.dckeytab
 except ImportError:
-   cmd_domain_export_keytab = None
+    cmd_domain_export_keytab = None
 else:
-   class cmd_domain_export_keytab(Command):
-       """Dump Kerberos keys of the domain into a keytab."""
+    class cmd_domain_export_keytab(Command):
+        """Dump Kerberos keys of the domain into a keytab."""
 
-       synopsis = "%prog <keytab> [options]"
+        synopsis = "%prog <keytab> [options]"
 
-       takes_optiongroups = {
-           "sambaopts": options.SambaOptions,
-           "credopts": options.CredentialsOptions,
-           "versionopts": options.VersionOptions,
-           }
+        takes_optiongroups = {
+            "sambaopts": options.SambaOptions,
+            "credopts": options.CredentialsOptions,
+            "versionopts": options.VersionOptions,
+        }
 
-       takes_options = [
-           Option("--principal", help="extract only this principal", type=str),
-           ]
+        takes_options = [
+            Option("--principal", help="extract only this principal", type=str),
+        ]
 
-       takes_args = ["keytab"]
+        takes_args = ["keytab"]
 
-       def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
-           lp = sambaopts.get_loadparm()
-           net = Net(None, lp)
-           net.export_keytab(keytab=keytab, principal=principal)
+        def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
+            lp = sambaopts.get_loadparm()
+            net = Net(None, lp)
+            net.export_keytab(keytab=keytab, principal=principal)
 
 
 class cmd_domain_info(Command):
@@ -139,13 +193,13 @@ class cmd_domain_info(Command):
     synopsis = "%prog <ip_address> [options]"
 
     takes_options = [
-        ]
+    ]
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
         "credopts": options.CredentialsOptions,
         "versionopts": options.VersionOptions,
-        }
+    }
 
     takes_args = ["address"]
 
@@ -175,74 +229,73 @@ class cmd_domain_provision(Command):
     }
 
     takes_options = [
-         Option("--interactive", help="Ask for names", action="store_true"),
-         Option("--domain", type="string", metavar="DOMAIN",
-                help="NetBIOS domain name to use"),
-         Option("--domain-guid", type="string", metavar="GUID",
-                help="set domainguid (otherwise random)"),
-         Option("--domain-sid", type="string", metavar="SID",
-                help="set domainsid (otherwise random)"),
-         Option("--ntds-guid", type="string", metavar="GUID",
-                help="set NTDS object GUID (otherwise random)"),
-         Option("--invocationid", type="string", metavar="GUID",
-                help="set invocationid (otherwise random)"),
-         Option("--host-name", type="string", metavar="HOSTNAME",
-                help="set hostname"),
-         Option("--host-ip", type="string", metavar="IPADDRESS",
-                help="set IPv4 ipaddress"),
-         Option("--host-ip6", type="string", metavar="IP6ADDRESS",
-                help="set IPv6 ipaddress"),
-         Option("--site", type="string", metavar="SITENAME",
-                help="set site name"),
-         Option("--adminpass", type="string", metavar="PASSWORD",
-                help="choose admin password (otherwise random)"),
-         Option("--krbtgtpass", type="string", metavar="PASSWORD",
-                help="choose krbtgt password (otherwise random)"),
-         Option("--machinepass", type="string", metavar="PASSWORD",
-                help="choose machine password (otherwise random)"),
-         Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
-                choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
-                help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
-                     "BIND9_FLATFILE uses bind9 text database to store zone information, "
-                     "BIND9_DLZ uses samba4 AD to store zone information, "
-                     "NONE skips the DNS setup entirely (not recommended)",
-                default="SAMBA_INTERNAL"),
-         Option("--dnspass", type="string", metavar="PASSWORD",
-                help="choose dns password (otherwise random)"),
-         Option("--ldapadminpass", type="string", metavar="PASSWORD",
-                help="choose password to set between Samba and its LDAP backend (otherwise random)"),
-         Option("--root", type="string", metavar="USERNAME",
-                help="choose 'root' unix username"),
-         Option("--nobody", type="string", metavar="USERNAME",
-                help="choose 'nobody' user"),
-         Option("--users", type="string", metavar="GROUPNAME",
-                help="choose 'users' group"),
-         Option("--quiet", help="Be quiet", action="store_true"),
-         Option("--blank", action="store_true",
-                help="do not add users or groups, just the structure"),
-         Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
-                help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
-                choices=["fedora-ds", "openldap"]),
-         Option("--server-role", type="choice", metavar="ROLE",
-                choices=["domain controller", "dc", "member server", "member", "standalone"],
-                help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
-                default="domain controller"),
-         Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
-                choices=["2000", "2003", "2008", "2008_R2"],
-                help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
-                default="2008_R2"),
-         Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
-                help="The initial nextRid value (only needed for upgrades).  Default is 1000."),
-         Option("--partitions-only",
-                help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
-         Option("--targetdir", type="string", metavar="DIR",
-                help="Set target directory"),
-         Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
-                help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/  (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
-         Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
-        ]
+        Option("--interactive", help="Ask for names", action="store_true"),
+        Option("--domain", type="string", metavar="DOMAIN",
+               help="NetBIOS domain name to use"),
+        Option("--domain-guid", type="string", metavar="GUID",
+               help="set domainguid (otherwise random)"),
+        Option("--domain-sid", type="string", metavar="SID",
+               help="set domainsid (otherwise random)"),
+        Option("--ntds-guid", type="string", metavar="GUID",
+               help="set NTDS object GUID (otherwise random)"),
+        Option("--invocationid", type="string", metavar="GUID",
+               help="set invocationid (otherwise random)"),
+        Option("--host-name", type="string", metavar="HOSTNAME",
+               help="set hostname"),
+        Option("--host-ip", type="string", metavar="IPADDRESS",
+               help="set IPv4 ipaddress"),
+        Option("--host-ip6", type="string", metavar="IP6ADDRESS",
+               help="set IPv6 ipaddress"),
+        Option("--site", type="string", metavar="SITENAME",
+               help="set site name"),
+        Option("--adminpass", type="string", metavar="PASSWORD",
+               help="choose admin password (otherwise random)"),
+        Option("--krbtgtpass", type="string", metavar="PASSWORD",
+               help="choose krbtgt password (otherwise random)"),
+        Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
+               choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
+               help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
+               "BIND9_FLATFILE uses bind9 text database to store zone information, "
+               "BIND9_DLZ uses samba4 AD to store zone information, "
+               "NONE skips the DNS setup entirely (not recommended)",
+               default="SAMBA_INTERNAL"),
+        Option("--dnspass", type="string", metavar="PASSWORD",
+               help="choose dns password (otherwise random)"),
+        Option("--root", type="string", metavar="USERNAME",
+               help="choose 'root' unix username"),
+        Option("--nobody", type="string", metavar="USERNAME",
+               help="choose 'nobody' user"),
+        Option("--users", type="string", metavar="GROUPNAME",
+               help="choose 'users' group"),
+        Option("--blank", action="store_true",
+               help="do not add users or groups, just the structure"),
+        Option("--server-role", type="choice", metavar="ROLE",
+               choices=["domain controller", "dc", "member server", "member", "standalone"],
+               help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
+               default="domain controller"),
+        Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
+               choices=["2000", "2003", "2008", "2008_R2"],
+               help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
+               default="2008_R2"),
+        Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
+               choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
+               help="The base schema files to use. Default is (Windows) 2008_R2.",
+               default="2008_R2"),
+        Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
+               help="The initial nextRid value (only needed for upgrades).  Default is 1000."),
+        Option("--partitions-only",
+               help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
+        Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
+    ]
 
     openldap_options = [
+        Option("--ldapadminpass", type="string", metavar="PASSWORD",
+               help="choose password to set between Samba and its LDAP backend (otherwise random)"),
+        Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
+               help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
+               choices=["fedora-ds", "openldap"]),
+        Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
+               help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/  (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
         Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early.  Used only for the test environment.  DO NOT USE",
                action="store_true"),
         Option("--slapd-path", type="string", metavar="SLAPD-PATH",
@@ -251,11 +304,10 @@ class cmd_domain_provision(Command):
         Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
                help="Force the LDAP backend connection to be to a particular URI.  Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
         Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
-        ]
+    ]
 
     ntvfs_options = [
-        Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
-        Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
+        Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
                metavar="[yes|no|auto]",
                help="Define if we should use the native fs capabilities or a tdb file for "
                "storing attributes likes ntacl when --use-ntvfs is set. "
@@ -263,11 +315,14 @@ class cmd_domain_provision(Command):
                default="auto")
     ]
 
+    takes_options.extend(common_provision_join_options)
+
     if os.getenv('TEST_LDAP', "no") == "yes":
         takes_options.extend(openldap_options)
 
     if samba.is_ntvfs_fileserver_built():
-         takes_options.extend(ntvfs_options)
+        takes_options.extend(common_ntvfs_options)
+        takes_options.extend(ntvfs_options)
 
     takes_args = []
 
@@ -308,7 +363,10 @@ class cmd_domain_provision(Command):
             ldap_backend_nosync=None,
             ldap_backend_extra_port=None,
             ldap_backend_forced_uri=None,
-            ldap_dryrun_mode=None):
+            ldap_dryrun_mode=None,
+            base_schema=None,
+            plaintext_secrets=False,
+            backend_store=None):
 
         self.logger = self.get_logger("provision")
         if quiet:
@@ -335,9 +393,9 @@ class cmd_domain_provision(Command):
 
             def ask(prompt, default=None):
                 if default is not None:
-                    print "%s [%s]: " % (prompt, default),
+                    print("%s [%s]: " % (prompt, default), end=' ')
                 else:
-                    print "%s: " % (prompt,),
+                    print("%s: " % (prompt,), end=' ')
                 return sys.stdin.readline().rstrip("\n") or default
 
             try:
@@ -370,8 +428,9 @@ class cmd_domain_provision(Command):
 
             while True:
                 adminpassplain = getpass("Administrator password: ")
-                if not adminpassplain:
-                    self.errf.write("Invalid administrator password.\n")
+                issue = self._adminpass_issue(adminpassplain)
+                if issue:
+                    self.errf.write("%s.\n" % issue)
                 else:
                     adminpassverify = getpass("Retype password: ")
                     if not adminpassplain == adminpassverify:
@@ -387,7 +446,11 @@ class cmd_domain_provision(Command):
             if domain is None:
                 raise CommandError("No domain set!")
 
-        if not adminpass:
+        if adminpass:
+            issue = self._adminpass_issue(adminpass)
+            if issue:
+                raise CommandError(issue)
+        else:
             self.logger.info("Administrator password will be set randomly!")
 
         if function_level == "2000":
@@ -451,29 +514,34 @@ class cmd_domain_provision(Command):
             domain_sid = security.dom_sid(domain_sid)
 
         session = system_session()
+        if backend_store is None:
+            backend_store = get_default_backend_store()
         try:
             result = provision(self.logger,
-                  session, smbconf=smbconf, targetdir=targetdir,
-                  samdb_fill=samdb_fill, realm=realm, domain=domain,
-                  domainguid=domain_guid, domainsid=domain_sid,
-                  hostname=host_name,
-                  hostip=host_ip, hostip6=host_ip6,
-                  sitename=site, ntdsguid=ntds_guid,
-                  invocationid=invocationid, adminpass=adminpass,
-                  krbtgtpass=krbtgtpass, machinepass=machinepass,
-                  dns_backend=dns_backend, dns_forwarder=dns_forwarder,
-                  dnspass=dnspass, root=root, nobody=nobody,
-                  users=users,
-                  serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
-                  backend_type=ldap_backend_type,
-                  ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
-                  useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
-                  use_rfc2307=use_rfc2307, skip_sysvolacl=False,
-                  ldap_backend_extra_port=ldap_backend_extra_port,
-                  ldap_backend_forced_uri=ldap_backend_forced_uri,
-                  nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
-
-        except ProvisioningError, e:
+                               session, smbconf=smbconf, targetdir=targetdir,
+                               samdb_fill=samdb_fill, realm=realm, domain=domain,
+                               domainguid=domain_guid, domainsid=domain_sid,
+                               hostname=host_name,
+                               hostip=host_ip, hostip6=host_ip6,
+                               sitename=site, ntdsguid=ntds_guid,
+                               invocationid=invocationid, adminpass=adminpass,
+                               krbtgtpass=krbtgtpass, machinepass=machinepass,
+                               dns_backend=dns_backend, dns_forwarder=dns_forwarder,
+                               dnspass=dnspass, root=root, nobody=nobody,
+                               users=users,
+                               serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
+                               backend_type=ldap_backend_type,
+                               ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
+                               useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
+                               use_rfc2307=use_rfc2307, skip_sysvolacl=False,
+                               ldap_backend_extra_port=ldap_backend_extra_port,
+                               ldap_backend_forced_uri=ldap_backend_forced_uri,
+                               nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
+                               base_schema=base_schema,
+                               plaintext_secrets=plaintext_secrets,
+                               backend_store=backend_store)
+
+        except ProvisioningError as e:
             raise CommandError("Provision failed", e)
 
         result.report_logger(self.logger)
@@ -481,7 +549,7 @@ class cmd_domain_provision(Command):
     def _get_nameserver_ip(self):
         """Grab the nameserver IP address from /etc/resolv.conf."""
         from os import path
-        RESOLV_CONF="/etc/resolv.conf"
+        RESOLV_CONF = "/etc/resolv.conf"
 
         if not path.isfile(RESOLV_CONF):
             self.logger.warning("Failed to locate %s" % RESOLV_CONF)
@@ -501,6 +569,21 @@ class cmd_domain_provision(Command):
 
         self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
 
+    def _adminpass_issue(self, adminpass):
+        """Returns error string for a bad administrator password,
+        or None if acceptable"""
+        if isinstance(adminpass, binary_type):
+            adminpass = adminpass.decode('utf8')
+        if len(adminpass) < DEFAULT_MIN_PWD_LENGTH:
+            return "Administrator password does not meet the default minimum" \
+                " password length requirement (%d characters)" \
+                % DEFAULT_MIN_PWD_LENGTH
+        elif not samba.check_password_quality(adminpass):
+            return "Administrator password does not meet the default" \
+                " quality standards"
+        else:
+            return None
+
 
 class cmd_domain_dcpromo(Command):
     """Promote an existing domain member or NT4 PDC to an AD DC."""
@@ -513,32 +596,13 @@ class cmd_domain_dcpromo(Command):
         "credopts": options.CredentialsOptions,
     }
 
-    takes_options = [
-        Option("--server", help="DC to join", type=str),
-        Option("--site", help="site to join", type=str),
-        Option("--targetdir", help="where to store provision", type=str),
-        Option("--domain-critical-only",
-               help="only replicate critical domain objects",
-               action="store_true"),
-        Option("--machinepass", type=str, metavar="PASSWORD",
-               help="choose machine password (otherwise random)"),
-        Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
-               choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
-               help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
-                   "BIND9_DLZ uses samba4 AD to store zone information, "
-                   "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
-               default="SAMBA_INTERNAL"),
-        Option("--quiet", help="Be quiet", action="store_true"),
-        Option("--verbose", help="Be verbose", action="store_true")
-        ]
+    takes_options = []
+    takes_options.extend(common_join_options)
 
-    ntvfs_options = [
-         Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
-    ]
+    takes_options.extend(common_provision_join_options)
 
     if samba.is_ntvfs_fileserver_built():
-         takes_options.extend(ntvfs_options)
-
+        takes_options.extend(common_ntvfs_options)
 
     takes_args = ["domain", "role?"]
 
@@ -546,14 +610,12 @@ class cmd_domain_dcpromo(Command):
             versionopts=None, server=None, site=None, targetdir=None,
             domain_critical_only=False, parent_domain=None, machinepass=None,
             use_ntvfs=False, dns_backend=None,
-            quiet=False, verbose=False):
+            quiet=False, verbose=False, plaintext_secrets=False,
+            backend_store=None):
         lp = sambaopts.get_loadparm()
         creds = credopts.get_credentials(lp)
         net = Net(creds, lp, server=credopts.ipaddress)
 
-        if site is None:
-            site = "Default-First-Site-Name"
-
         logger = self.get_logger()
         if verbose:
             logger.setLevel(logging.DEBUG)
@@ -564,7 +626,7 @@ class cmd_domain_dcpromo(Command):
 
         netbios_name = lp.get("netbios name")
 
-        if not role is None:
+        if role is not None:
             role = role.upper()
 
         if role == "DC":
@@ -573,13 +635,15 @@ class cmd_domain_dcpromo(Command):
                     domain_critical_only=domain_critical_only,
                     machinepass=machinepass, use_ntvfs=use_ntvfs,
                     dns_backend=dns_backend,
-                    promote_existing=True)
+                    promote_existing=True, plaintext_secrets=plaintext_secrets,
+                    backend_store=backend_store)
         elif role == "RODC":
             join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
                       site=site, netbios_name=netbios_name, targetdir=targetdir,
                       domain_critical_only=domain_critical_only,
                       machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
-                      promote_existing=True)
+                      promote_existing=True, plaintext_secrets=plaintext_secrets,
+                      backend_store=backend_store)
         else:
             raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
 
@@ -596,31 +660,18 @@ class cmd_domain_join(Command):
     }
 
     takes_options = [
-        Option("--server", help="DC to join", type=str),
-        Option("--site", help="site to join", type=str),
-        Option("--targetdir", help="where to store provision", type=str),
         Option("--parent-domain", help="parent domain to create subdomain under", type=str),
-        Option("--domain-critical-only",
-               help="only replicate critical domain objects",
-               action="store_true"),
-        Option("--machinepass", type=str, metavar="PASSWORD",
-               help="choose machine password (otherwise random)"),
         Option("--adminpass", type="string", metavar="PASSWORD",
                help="choose adminstrator password when joining as a subdomain (otherwise random)"),
-        Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
-               choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
-               help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
-                   "BIND9_DLZ uses samba4 AD to store zone information, "
-                   "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
-               default="SAMBA_INTERNAL"),
-        Option("--quiet", help="Be quiet", action="store_true"),
-        Option("--verbose", help="Be verbose", action="store_true")
-       ]
+    ]
 
     ntvfs_options = [
         Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
                action="store_true")
     ]
+    takes_options.extend(common_join_options)
+    takes_options.extend(common_provision_join_options)
+
     if samba.is_ntvfs_fileserver_built():
         takes_options.extend(ntvfs_options)
 
@@ -630,7 +681,9 @@ class cmd_domain_join(Command):
             versionopts=None, server=None, site=None, targetdir=None,
             domain_critical_only=False, parent_domain=None, machinepass=None,
             use_ntvfs=False, dns_backend=None, adminpass=None,
-            quiet=False, verbose=False):
+            quiet=False, verbose=False,
+            plaintext_secrets=False,
+            backend_store=None):
         lp = sambaopts.get_loadparm()
         creds = credopts.get_credentials(lp)
         net = Net(creds, lp, server=credopts.ipaddress)
@@ -648,7 +701,7 @@ class cmd_domain_join(Command):
 
         netbios_name = lp.get("netbios name")
 
-        if not role is None:
+        if role is not None:
             role = role.upper()
 
         if role is None or role == "MEMBER":
@@ -661,13 +714,18 @@ class cmd_domain_join(Command):
             join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
                     site=site, netbios_name=netbios_name, targetdir=targetdir,
                     domain_critical_only=domain_critical_only,
-                    machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
+                    machinepass=machinepass, use_ntvfs=use_ntvfs,
+                    dns_backend=dns_backend,
+                    plaintext_secrets=plaintext_secrets,
+                    backend_store=backend_store)
         elif role == "RODC":
             join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
                       site=site, netbios_name=netbios_name, targetdir=targetdir,
                       domain_critical_only=domain_critical_only,
                       machinepass=machinepass, use_ntvfs=use_ntvfs,
-                      dns_backend=dns_backend)
+                      dns_backend=dns_backend,
+                      plaintext_secrets=plaintext_secrets,
+                      backend_store=backend_store)
         elif role == "SUBDOMAIN":
             if not adminpass:
                 logger.info("Administrator password will be set randomly!")
@@ -680,7 +738,9 @@ class cmd_domain_join(Command):
                            netbios_name=netbios_name, netbios_domain=netbios_domain,
                            targetdir=targetdir, machinepass=machinepass,
                            use_ntvfs=use_ntvfs, dns_backend=dns_backend,
-                           adminpass=adminpass)
+                           adminpass=adminpass,
+                           plaintext_secrets=plaintext_secrets,
+                           backend_store=backend_store)
         else:
             raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
 
@@ -696,15 +756,15 @@ class cmd_domain_demote(Command):
                metavar="URL", dest="H"),
         Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
                "to remove ALL references to (rather than this DC)", type=str),
-        Option("--quiet", help="Be quiet", action="store_true"),
-        Option("--verbose", help="Be verbose", action="store_true"),
-        ]
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),
+        Option("-v", "--verbose", help="Be verbose", action="store_true"),
+    ]
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
         "credopts": options.CredentialsOptions,
         "versionopts": options.VersionOptions,
-        }
+    }
 
     def run(self, sambaopts=None, credopts=None,
             versionopts=None, server=None,
@@ -743,7 +803,7 @@ class cmd_domain_demote(Command):
                 raise CommandError("Unable to search for servers")
 
             if (len(res) == 1):
-                raise CommandError("You are the latest server in the domain")
+                raise CommandError("You are the last server in the domain")
 
             server = None
             for e in res:
@@ -753,8 +813,8 @@ class cmd_domain_demote(Command):
 
         ntds_guid = samdb.get_ntds_GUID()
         msg = samdb.search(base=str(samdb.get_config_basedn()),
-            scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
-            attrs=['options'])
+                           scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
+                           attrs=['options'])
         if len(msg) == 0 or "options" not in msg[0]:
             raise CommandError("Failed to find options on %s" % ntds_guid)
 
@@ -762,10 +822,13 @@ class cmd_domain_demote(Command):
         dsa_options = int(str(msg[0]['options']))
 
         res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
-                            controls=["search_options:1:2"])
+                           controls=["search_options:1:2"])
 
         if len(res) != 0:
-            raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
+            raise CommandError("Current DC is still the owner of %d role(s), "
+                               "use the role command to transfer roles to "
+                               "another DC" %
+                               len(res))
 
         self.errf.write("Using %s as partner server for the demotion\n" %
                         server)
@@ -781,23 +844,23 @@ class cmd_domain_demote(Command):
             nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
             samdb.modify(nmsg)
 
-
             self.errf.write("Asking partner server %s to synchronize from us\n"
                             % server)
             for part in (samdb.get_schema_basedn(),
-                            samdb.get_config_basedn(),
-                            samdb.get_root_basedn()):
+                         samdb.get_config_basedn(),
+                         samdb.get_root_basedn()):
                 nc = drsuapi.DsReplicaObjectIdentifier()
                 nc.dn = str(part)
 
                 req1 = drsuapi.DsReplicaSyncRequest1()
-                req1.naming_context = nc;
+                req1.naming_context = nc
                 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
                 req1.source_dsa_guid = misc.GUID(ntds_guid)
 
                 try:
                     drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
-                except RuntimeError as (werr, string):
+                except RuntimeError as e1:
+                    (werr, string) = e1.args
                     if werr == werror.WERR_DS_DRA_NO_REPLICA:
                         pass
                     else:
@@ -810,18 +873,18 @@ class cmd_domain_demote(Command):
                         raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
         try:
             remote_samdb = SamDB(url="ldap://%s" % server,
-                                session_info=system_session(),
-                                credentials=creds, lp=lp)
+                                 session_info=system_session(),
+                                 credentials=creds, lp=lp)
 
             self.errf.write("Changing userControl and container\n")
             res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
-                                expression="(&(objectClass=user)(sAMAccountName=%s$))" %
-                                            netbios_name.upper(),
-                                attrs=["userAccountControl"])
+                                      expression="(&(objectClass=user)(sAMAccountName=%s$))" %
+                                      netbios_name.upper(),
+                                      attrs=["userAccountControl"])
             dc_dn = res[0].dn
             uac = int(str(res[0]["userAccountControl"]))
 
-        except Exception, e:
+        except Exception as e:
             if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
                 self.errf.write(
                     "Error while demoting, re-enabling inbound replication\n")
@@ -842,18 +905,20 @@ class cmd_domain_demote(Command):
 
         olduac = uac
 
-        uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
+        uac &= ~(UF_SERVER_TRUST_ACCOUNT |
+                 UF_TRUSTED_FOR_DELEGATION |
+                 UF_PARTIAL_SECRETS_ACCOUNT)
         uac |= UF_WORKSTATION_TRUST_ACCOUNT
 
         msg = ldb.Message()
         msg.dn = dc_dn
 
         msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
-                                                        ldb.FLAG_MOD_REPLACE,
-                                                        "userAccountControl")
+                                                       ldb.FLAG_MOD_REPLACE,
+                                                       "userAccountControl")
         try:
             remote_samdb.modify(msg)
-        except Exception, e:
+        except Exception as e:
             if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
                 self.errf.write(
                     "Error while demoting, re-enabling inbound replication")
@@ -876,11 +941,11 @@ class cmd_domain_demote(Command):
 
         if (len(res) != 0):
             res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
-                                        scope=ldb.SCOPE_ONELEVEL)
+                                      scope=ldb.SCOPE_ONELEVEL)
             while(len(res) != 0 and i < 100):
                 i = i + 1
                 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
-                                            scope=ldb.SCOPE_ONELEVEL)
+                                          scope=ldb.SCOPE_ONELEVEL)
 
             if i == 100:
                 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
@@ -894,21 +959,21 @@ class cmd_domain_demote(Command):
                 msg.dn = dc_dn
 
                 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
-                                                        ldb.FLAG_MOD_REPLACE,
-                                                        "userAccountControl")
+                                                               ldb.FLAG_MOD_REPLACE,
+                                                               "userAccountControl")
 
                 remote_samdb.modify(msg)
 
                 raise CommandError("Unable to find a slot for renaming %s,"
-                                    " all names from %s-1 to %s-%d seemed used" %
-                                    (str(dc_dn), rdn, rdn, i - 9))
+                                   " all names from %s-1 to %s-%d seemed used" %
+                                   (str(dc_dn), rdn, rdn, i - 9))
 
             newrdn = "%s-%d" % (rdn, i)
 
         try:
             newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
             remote_samdb.rename(dc_dn, newdn)
-        except Exception, e:
+        except Exception as e:
             if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
                 self.errf.write(
                     "Error while demoting, re-enabling inbound replication\n")
@@ -920,13 +985,12 @@ class cmd_domain_demote(Command):
             msg.dn = dc_dn
 
             msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
-                                                    ldb.FLAG_MOD_REPLACE,
-                                                    "userAccountControl")
+                                                           ldb.FLAG_MOD_REPLACE,
+                                                           "userAccountControl")
 
             remote_samdb.modify(msg)
             raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
 
-
         server_dsa_dn = samdb.get_serverName()
         domain = remote_samdb.get_root_basedn()
 
@@ -937,7 +1001,8 @@ class cmd_domain_demote(Command):
             req1.commit = 1
 
             drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
-        except RuntimeError as (werr, string):
+        except RuntimeError as e3:
+            (werr, string) = e3.args
             if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
                 self.errf.write(
                     "Error while demoting, re-enabling inbound replication\n")
@@ -954,9 +1019,13 @@ class cmd_domain_demote(Command):
             remote_samdb.modify(msg)
             remote_samdb.rename(newdn, dc_dn)
             if werr == werror.WERR_DS_DRA_NO_REPLICA:
-                raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
+                raise CommandError("The DC %s is not present on (already "
+                                   "removed from) the remote server: %s" %
+                                   (server_dsa_dn, e3))
             else:
-                raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
+                raise CommandError("Error while sending a removeDsServer "
+                                   "of %s: %s" %
+                                   (server_dsa_dn, e3))
 
         remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
 
@@ -967,10 +1036,14 @@ class cmd_domain_demote(Command):
                   "CN=NTFRS Subscriptions"):
             try:
                 remote_samdb.delete(ldb.Dn(remote_samdb,
-                                    "%s,%s" % (s, str(newdn))))
-            except ldb.LdbError, l:
+                                           "%s,%s" % (s, str(newdn))))
+            except ldb.LdbError as l:
                 pass
 
+        # get dns host name for target server to demote, remove dns references
+        remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
+                                        ignore_no_name=True)
+
         self.errf.write("Demote successful\n")
 
 
@@ -983,17 +1056,17 @@ class cmd_domain_level(Command):
         "sambaopts": options.SambaOptions,
         "credopts": options.CredentialsOptions,
         "versionopts": options.VersionOptions,
-        }
+    }
 
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H"),
-        Option("--quiet", help="Be quiet", action="store_true"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),  # unused
         Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
-            help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
+               help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
         Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
-            help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
-            ]
+               help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
+        ]
 
     takes_args = ["subcommand"]
 
@@ -1003,21 +1076,21 @@ class cmd_domain_level(Command):
         creds = credopts.get_credentials(lp, fallback_machine=True)
 
         samdb = SamDB(url=H, session_info=system_session(),
-            credentials=creds, lp=lp)
+                      credentials=creds, lp=lp)
 
         domain_dn = samdb.domain_dn()
 
         res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
-          scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
+                                  scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
         assert len(res_forest) == 1
 
         res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
-          attrs=["msDS-Behavior-Version", "nTMixedDomain"])
+                                  attrs=["msDS-Behavior-Version", "nTMixedDomain"])
         assert len(res_domain) == 1
 
         res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
-          scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
-          attrs=["msDS-Behavior-Version"])
+                                scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
+                                attrs=["msDS-Behavior-Version"])
         assert len(res_dc_s) >= 1
 
         # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
@@ -1140,36 +1213,38 @@ class cmd_domain_level(Command):
                     m = ldb.Message()
                     m.dn = ldb.Dn(samdb, domain_dn)
                     m["nTMixedDomain"] = ldb.MessageElement("0",
-                      ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
+                                                            ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                     samdb.modify(m)
                     # Under partitions
                     m = ldb.Message()
                     m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
                     m["nTMixedDomain"] = ldb.MessageElement("0",
-                      ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
+                                                            ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                     try:
                         samdb.modify(m)
-                    except ldb.LdbError, (enum, emsg):
+                    except ldb.LdbError as e:
+                        (enum, emsg) = e.args
                         if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                             raise
 
                 # Directly on the base DN
                 m = ldb.Message()
                 m.dn = ldb.Dn(samdb, domain_dn)
-                m["msDS-Behavior-Version"]= ldb.MessageElement(
-                  str(new_level_domain), ldb.FLAG_MOD_REPLACE,
-                            "msDS-Behavior-Version")
+                m["msDS-Behavior-Version"] = ldb.MessageElement(
+                    str(new_level_domain), ldb.FLAG_MOD_REPLACE,
+                    "msDS-Behavior-Version")
                 samdb.modify(m)
                 # Under partitions
                 m = ldb.Message()
                 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
-                  + ",CN=Partitions,%s" % samdb.get_config_basedn())
-                m["msDS-Behavior-Version"]= ldb.MessageElement(
-                  str(new_level_domain), ldb.FLAG_MOD_REPLACE,
-                          "msDS-Behavior-Version")
+                              + ",CN=Partitions,%s" % samdb.get_config_basedn())
+                m["msDS-Behavior-Version"] = ldb.MessageElement(
+                    str(new_level_domain), ldb.FLAG_MOD_REPLACE,
+                    "msDS-Behavior-Version")
                 try:
                     samdb.modify(m)
-                except ldb.LdbError, (enum, emsg):
+                except ldb.LdbError as e2:
+                    (enum, emsg) = e2.args
                     if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                         raise
 
@@ -1195,9 +1270,9 @@ class cmd_domain_level(Command):
 
                 m = ldb.Message()
                 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
-                m["msDS-Behavior-Version"]= ldb.MessageElement(
-                  str(new_level_forest), ldb.FLAG_MOD_REPLACE,
-                          "msDS-Behavior-Version")
+                m["msDS-Behavior-Version"] = ldb.MessageElement(
+                    str(new_level_forest), ldb.FLAG_MOD_REPLACE,
+                    "msDS-Behavior-Version")
                 samdb.modify(m)
                 msgs.append("Forest function level changed!")
             msgs.append("All changes applied successfully!")
@@ -1206,66 +1281,34 @@ class cmd_domain_level(Command):
             raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
 
 
-class cmd_domain_passwordsettings(Command):
-    """Set password settings.
+class cmd_domain_passwordsettings_show(Command):
+    """Display current password settings for the domain."""
 
-    Password complexity, password lockout policy, history length,
-    minimum password length, the minimum and maximum password age) on
-    a Samba AD DC server.
-
-    Use against a Windows DC is possible, but group policy will override it.
-    """
-
-    synopsis = "%prog (show|set <options>) [options]"
+    synopsis = "%prog [options]"
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
         "versionopts": options.VersionOptions,
         "credopts": options.CredentialsOptions,
-        }
+    }
 
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H"),
-        Option("--quiet", help="Be quiet", action="store_true"),
-        Option("--complexity", type="choice", choices=["on","off","default"],
-          help="The password complexity (on | off | default). Default is 'on'"),
-        Option("--store-plaintext", type="choice", choices=["on","off","default"],
-          help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
-        Option("--history-length",
-          help="The password history length (<integer> | default).  Default is 24.", type=str),
-        Option("--min-pwd-length",
-          help="The minimum password length (<integer> | default).  Default is 7.", type=str),
-        Option("--min-pwd-age",
-          help="The minimum password age (<integer in days> | default).  Default is 1.", type=str),
-        Option("--max-pwd-age",
-          help="The maximum password age (<integer in days> | default).  Default is 43.", type=str),
-        Option("--account-lockout-duration",
-          help="The the length of time an account is locked out after exeeding the limit on bad password attempts (<integer in mins> | default).  Default is 30 mins.", type=str),
-        Option("--account-lockout-threshold",
-          help="The number of bad password attempts allowed before locking out the account (<integer> | default).  Default is 0 (never lock out).", type=str),
-        Option("--reset-account-lockout-after",
-          help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default).  Default is 30.", type=str),
           ]
 
-    takes_args = ["subcommand"]
-
-    def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
-            quiet=False, complexity=None, store_plaintext=None, history_length=None,
-            min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
-            reset_account_lockout_after=None, credopts=None, sambaopts=None,
-            versionopts=None):
+    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
         lp = sambaopts.get_loadparm()
         creds = credopts.get_credentials(lp)
 
         samdb = SamDB(url=H, session_info=system_session(),
-            credentials=creds, lp=lp)
+                      credentials=creds, lp=lp)
 
         domain_dn = samdb.domain_dn()
         res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
-          attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
-                 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
-                 "lockOutObservationWindow"])
+                           attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
+                                  "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
+                                  "lockOutObservationWindow"])
         assert(len(res) == 1)
         try:
             pwd_props = int(res[0]["pwdProperties"][0])
@@ -1284,171 +1327,233 @@ class cmd_domain_passwordsettings(Command):
             else:
                 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
             cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
-        except Exception, e:
+        except Exception as e:
             raise CommandError("Could not retrieve password properties!", e)
 
-        if subcommand == "show":
-            self.message("Password informations for domain '%s'" % domain_dn)
-            self.message("")
-            if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
-                self.message("Password complexity: on")
+        self.message("Password informations for domain '%s'" % domain_dn)
+        self.message("")
+        if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
+            self.message("Password complexity: on")
+        else:
+            self.message("Password complexity: off")
+        if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
+            self.message("Store plaintext passwords: on")
+        else:
+            self.message("Store plaintext passwords: off")
+        self.message("Password history length: %d" % pwd_hist_len)
+        self.message("Minimum password length: %d" % cur_min_pwd_len)
+        self.message("Minimum password age (days): %d" % cur_min_pwd_age)
+        self.message("Maximum password age (days): %d" % cur_max_pwd_age)
+        self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
+        self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
+        self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
+
+
+class cmd_domain_passwordsettings_set(Command):
+    """Set password settings.
+
+    Password complexity, password lockout policy, history length,
+    minimum password length, the minimum and maximum password age) on
+    a Samba AD DC server.
+
+    Use against a Windows DC is possible, but group policy will override it.
+    """
+
+    synopsis = "%prog <options> [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server", type=str,
+               metavar="URL", dest="H"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),  # unused
+        Option("--complexity", type="choice", choices=["on", "off", "default"],
+               help="The password complexity (on | off | default). Default is 'on'"),
+        Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
+               help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
+        Option("--history-length",
+               help="The password history length (<integer> | default).  Default is 24.", type=str),
+        Option("--min-pwd-length",
+               help="The minimum password length (<integer> | default).  Default is 7.", type=str),
+        Option("--min-pwd-age",
+               help="The minimum password age (<integer in days> | default).  Default is 1.", type=str),
+        Option("--max-pwd-age",
+               help="The maximum password age (<integer in days> | default).  Default is 43.", type=str),
+        Option("--account-lockout-duration",
+               help="The the length of time an account is locked out after exeeding the limit on bad password attempts (<integer in mins> | default).  Default is 30 mins.", type=str),
+        Option("--account-lockout-threshold",
+               help="The number of bad password attempts allowed before locking out the account (<integer> | default).  Default is 0 (never lock out).", type=str),
+        Option("--reset-account-lockout-after",
+               help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default).  Default is 30.", type=str),
+        ]
+
+    def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
+            quiet=False, complexity=None, store_plaintext=None, history_length=None,
+            min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
+            reset_account_lockout_after=None, credopts=None, sambaopts=None,
+            versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        samdb = SamDB(url=H, session_info=system_session(),
+                      credentials=creds, lp=lp)
+
+        domain_dn = samdb.domain_dn()
+        msgs = []
+        m = ldb.Message()
+        m.dn = ldb.Dn(samdb, domain_dn)
+        pwd_props = int(samdb.get_pwdProperties())
+
+        if complexity is not None:
+            if complexity == "on" or complexity == "default":
+                pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
+                msgs.append("Password complexity activated!")
+            elif complexity == "off":
+                pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
+                msgs.append("Password complexity deactivated!")
+
+        if store_plaintext is not None:
+            if store_plaintext == "on" or store_plaintext == "default":
+                pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
+                msgs.append("Plaintext password storage for changed passwords activated!")
+            elif store_plaintext == "off":
+                pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
+                msgs.append("Plaintext password storage for changed passwords deactivated!")
+
+        if complexity is not None or store_plaintext is not None:
+            m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
+                                                    ldb.FLAG_MOD_REPLACE, "pwdProperties")
+
+        if history_length is not None:
+            if history_length == "default":
+                pwd_hist_len = 24
             else:
-                self.message("Password complexity: off")
-            if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
-                self.message("Store plaintext passwords: on")
+                pwd_hist_len = int(history_length)
+
+            if pwd_hist_len < 0 or pwd_hist_len > 24:
+                raise CommandError("Password history length must be in the range of 0 to 24!")
+
+            m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
+                                                       ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
+            msgs.append("Password history length changed!")
+
+        if min_pwd_length is not None:
+            if min_pwd_length == "default":
+                min_pwd_len = 7
             else:
-                self.message("Store plaintext passwords: off")
-            self.message("Password history length: %d" % pwd_hist_len)
-            self.message("Minimum password length: %d" % cur_min_pwd_len)
-            self.message("Minimum password age (days): %d" % cur_min_pwd_age)
-            self.message("Maximum password age (days): %d" % cur_max_pwd_age)
-            self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
-            self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
-            self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
-        elif subcommand == "set":
-            msgs = []
-            m = ldb.Message()
-            m.dn = ldb.Dn(samdb, domain_dn)
-
-            if complexity is not None:
-                if complexity == "on" or complexity == "default":
-                    pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
-                    msgs.append("Password complexity activated!")
-                elif complexity == "off":
-                    pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
-                    msgs.append("Password complexity deactivated!")
-
-            if store_plaintext is not None:
-                if store_plaintext == "on" or store_plaintext == "default":
-                    pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
-                    msgs.append("Plaintext password storage for changed passwords activated!")
-                elif store_plaintext == "off":
-                    pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
-                    msgs.append("Plaintext password storage for changed passwords deactivated!")
-
-            if complexity is not None or store_plaintext is not None:
-                m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
-                  ldb.FLAG_MOD_REPLACE, "pwdProperties")
-
-            if history_length is not None:
-                if history_length == "default":
-                    pwd_hist_len = 24
-                else:
-                    pwd_hist_len = int(history_length)
+                min_pwd_len = int(min_pwd_length)
 
-                if pwd_hist_len < 0 or pwd_hist_len > 24:
-                    raise CommandError("Password history length must be in the range of 0 to 24!")
+            if min_pwd_len < 0 or min_pwd_len > 14:
+                raise CommandError("Minimum password length must be in the range of 0 to 14!")
 
-                m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
-                  ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
-                msgs.append("Password history length changed!")
+            m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
+                                                   ldb.FLAG_MOD_REPLACE, "minPwdLength")
+            msgs.append("Minimum password length changed!")
 
-            if min_pwd_length is not None:
-                if min_pwd_length == "default":
-                    min_pwd_len = 7
-                else:
-                    min_pwd_len = int(min_pwd_length)
+        if min_pwd_age is not None:
+            if min_pwd_age == "default":
+                min_pwd_age = 1
+            else:
+                min_pwd_age = int(min_pwd_age)
 
-                if min_pwd_len < 0 or min_pwd_len > 14:
-                    raise CommandError("Minimum password length must be in the range of 0 to 14!")
+            if min_pwd_age < 0 or min_pwd_age > 998:
+                raise CommandError("Minimum password age must be in the range of 0 to 998!")
 
-                m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
-                  ldb.FLAG_MOD_REPLACE, "minPwdLength")
-                msgs.append("Minimum password length changed!")
+            # days -> ticks
+            min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
 
-            if min_pwd_age is not None:
-                if min_pwd_age == "default":
-                    min_pwd_age = 1
-                else:
-                    min_pwd_age = int(min_pwd_age)
+            m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
+                                                ldb.FLAG_MOD_REPLACE, "minPwdAge")
+            msgs.append("Minimum password age changed!")
 
-                if min_pwd_age < 0 or min_pwd_age > 998:
-                    raise CommandError("Minimum password age must be in the range of 0 to 998!")
+        if max_pwd_age is not None:
+            if max_pwd_age == "default":
+                max_pwd_age = 43
+            else:
+                max_pwd_age = int(max_pwd_age)
 
-                # days -> ticks
-                min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
+            if max_pwd_age < 0 or max_pwd_age > 999:
+                raise CommandError("Maximum password age must be in the range of 0 to 999!")
 
-                m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
-                  ldb.FLAG_MOD_REPLACE, "minPwdAge")
-                msgs.append("Minimum password age changed!")
+            # days -> ticks
+            if max_pwd_age == 0:
+                max_pwd_age_ticks = -0x8000000000000000
+            else:
+                max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
 
-            if max_pwd_age is not None:
-                if max_pwd_age == "default":
-                    max_pwd_age = 43
-                else:
-                    max_pwd_age = int(max_pwd_age)
+            m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
+                                                ldb.FLAG_MOD_REPLACE, "maxPwdAge")
+            msgs.append("Maximum password age changed!")
 
-                if max_pwd_age < 0 or max_pwd_age > 999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 999!")
+        if account_lockout_duration is not None:
+            if account_lockout_duration == "default":
+                account_lockout_duration = 30
+            else:
+                account_lockout_duration = int(account_lockout_duration)
 
-                # days -> ticks
-                if max_pwd_age == 0:
-                    max_pwd_age_ticks = -0x8000000000000000
-                else:
-                    max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
+            if account_lockout_duration < 0 or account_lockout_duration > 99999:
+                raise CommandError("Maximum password age must be in the range of 0 to 99999!")
 
-                m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
-                  ldb.FLAG_MOD_REPLACE, "maxPwdAge")
-                msgs.append("Maximum password age changed!")
+            # minutes -> ticks
+            if account_lockout_duration == 0:
+                account_lockout_duration_ticks = -0x8000000000000000
+            else:
+                account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
 
-            if account_lockout_duration is not None:
-                if account_lockout_duration == "default":
-                    account_lockout_duration = 30
-                else:
-                    account_lockout_duration = int(account_lockout_duration)
+            m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
+                                                      ldb.FLAG_MOD_REPLACE, "lockoutDuration")
+            msgs.append("Account lockout duration changed!")
 
-                if account_lockout_duration < 0 or account_lockout_duration > 99999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 99999!")
+        if account_lockout_threshold is not None:
+            if account_lockout_threshold == "default":
+                account_lockout_threshold = 0
+            else:
+                account_lockout_threshold = int(account_lockout_threshold)
 
-                # days -> ticks
-                if account_lockout_duration == 0:
-                    account_lockout_duration_ticks = -0x8000000000000000
-                else:
-                    account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
+            m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
+                                                       ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
+            msgs.append("Account lockout threshold changed!")
 
-                m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
-                  ldb.FLAG_MOD_REPLACE, "lockoutDuration")
-                msgs.append("Account lockout duration changed!")
+        if reset_account_lockout_after is not None:
+            if reset_account_lockout_after == "default":
+                reset_account_lockout_after = 30
+            else:
+                reset_account_lockout_after = int(reset_account_lockout_after)
 
-            if account_lockout_threshold is not None:
-                if account_lockout_threshold == "default":
-                    account_lockout_threshold = 0
-                else:
-                    account_lockout_threshold = int(account_lockout_threshold)
+            if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
+                raise CommandError("Maximum password age must be in the range of 0 to 99999!")
 
-                m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
-                  ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
-                msgs.append("Account lockout threshold changed!")
+            # minutes -> ticks
+            if reset_account_lockout_after == 0:
+                reset_account_lockout_after_ticks = -0x8000000000000000
+            else:
+                reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
 
-            if reset_account_lockout_after is not None:
-                if reset_account_lockout_after == "default":
-                    reset_account_lockout_after = 30
-                else:
-                    reset_account_lockout_after = int(reset_account_lockout_after)
+            m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
+                                                               ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
+            msgs.append("Duration to reset account lockout after changed!")
 
-                if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 99999!")
+        if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
+            raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
 
-                # days -> ticks
-                if reset_account_lockout_after == 0:
-                    reset_account_lockout_after_ticks = -0x8000000000000000
-                else:
-                    reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
+        if len(m) == 0:
+            raise CommandError("You must specify at least one option to set. Try --help")
+        samdb.modify(m)
+        msgs.append("All changes applied successfully!")
+        self.message("\n".join(msgs))
 
-                m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
-                  ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
-                msgs.append("Duration to reset account lockout after changed!")
 
-            if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
-                raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
+class cmd_domain_passwordsettings(SuperCommand):
+    """Manage password policy settings."""
 
-            if len(m) == 0:
-                raise CommandError("You must specify at least one option to set. Try --help")
-            samdb.modify(m)
-            msgs.append("All changes applied successfully!")
-            self.message("\n".join(msgs))
-        else:
-            raise CommandError("Wrong argument '%s'!" % subcommand)
+    subcommands = {}
+    subcommands["pso"] = cmd_domain_passwordsettings_pso()
+    subcommands["show"] = cmd_domain_passwordsettings_show()
+    subcommands["set"] = cmd_domain_passwordsettings_set()
 
 
 class cmd_domain_classicupgrade(Command):
@@ -1467,26 +1572,24 @@ class cmd_domain_classicupgrade(Command):
 
     takes_options = [
         Option("--dbdir", type="string", metavar="DIR",
-                  help="Path to samba classic DC database directory"),
+               help="Path to samba classic DC database directory"),
         Option("--testparm", type="string", metavar="PATH",
-                  help="Path to samba classic DC testparm utility from the previous installation.  This allows the default paths of the previous installation to be followed"),
+               help="Path to samba classic DC testparm utility from the previous installation.  This allows the default paths of the previous installation to be followed"),
         Option("--targetdir", type="string", metavar="DIR",
-                  help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
-        Option("--quiet", help="Be quiet", action="store_true"),
-        Option("--verbose", help="Be verbose", action="store_true"),
+               help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),
+        Option("-v", "--verbose", help="Be verbose", action="store_true"),
         Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
                choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
                help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
-                   "BIND9_FLATFILE uses bind9 text database to store zone information, "
-                   "BIND9_DLZ uses samba4 AD to store zone information, "
-                   "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
+               "BIND9_FLATFILE uses bind9 text database to store zone information, "
+               "BIND9_DLZ uses samba4 AD to store zone information, "
+               "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
                default="SAMBA_INTERNAL")
     ]
 
     ntvfs_options = [
-        Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
-               action="store_true"),
-        Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
+        Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
                metavar="[yes|no|auto]",
                help="Define if we should use the native fs capabilities or a tdb file for "
                "storing attributes likes ntacl when --use-ntvfs is set. "
@@ -1494,6 +1597,7 @@ class cmd_domain_classicupgrade(Command):
                default="auto")
     ]
     if samba.is_ntvfs_fileserver_built():
+        takes_options.extend(common_ntvfs_options)
         takes_options.extend(ntvfs_options)
 
     takes_args = ["smbconf"]
@@ -1553,7 +1657,7 @@ class cmd_domain_classicupgrade(Command):
             try:
                 try:
                     samba.ntacls.setntacl(lp, tmpfile.name,
-                                "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
+                                          "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
                     eadb = False
                 except Exception:
                     # FIXME: Don't catch all exceptions here
@@ -1600,10 +1704,12 @@ class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
 
     hidden = True
 
+
 class LocalDCCredentialsOptions(options.CredentialsOptions):
     def __init__(self, parser):
         options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
 
+
 class DomainTrustCommand(Command):
     """List domain trusts."""
 
@@ -1626,7 +1732,7 @@ class DomainTrustCommand(Command):
         if runtime is None:
             return False
 
-        err32 = self._uint32(runtime[0])
+        err32 = self._uint32(runtime.args[0])
         if err32 == val:
             return True
 
@@ -1634,24 +1740,24 @@ class DomainTrustCommand(Command):
 
     class LocalRuntimeError(CommandError):
         def __init__(exception_self, self, runtime, message):
-            err32 = self._uint32(runtime[0])
-            errstr = runtime[1]
+            err32 = self._uint32(runtime.args[0])
+            errstr = runtime.args[1]
             msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
                   self.local_server, message, err32, errstr)
             CommandError.__init__(exception_self, msg)
 
     class RemoteRuntimeError(CommandError):
         def __init__(exception_self, self, runtime, message):
-            err32 = self._uint32(runtime[0])
-            errstr = runtime[1]
+            err32 = self._uint32(runtime.args[0])
+            errstr = runtime.args[1]
             msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
                   self.remote_server, message, err32, errstr)
             CommandError.__init__(exception_self, msg)
 
     class LocalLdbError(CommandError):
         def __init__(exception_self, self, ldb_error, message):
-            errval = ldb_error[0]
-            errstr = ldb_error[1]
+            errval = ldb_error.args[0]
+            errstr = ldb_error.args[1]
             msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
                   self.local_server, message, errval, errstr)
             CommandError.__init__(exception_self, msg)
@@ -1727,6 +1833,9 @@ class DomainTrustCommand(Command):
             if require_pdc:
                 remote_flags |= nbt.NBT_SERVER_PDC
             remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
+        except NTSTATUSError as error:
+            raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
+                               (domain, error[1]))
         except Exception:
             raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
         flag_map = {
@@ -1749,14 +1858,14 @@ class DomainTrustCommand(Command):
             nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
         }
         server_type_string = self.generic_bitmap_to_string(flag_map,
-                                remote_info.server_type, names_only=True)
+                                                           remote_info.server_type, names_only=True)
         self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
                         remote_info.pdc_name,
                         remote_info.pdc_dns_name,
                         server_type_string))
 
         self.remote_server = remote_info.pdc_dns_name
-        self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
+        self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
         self.remote_creds = remote_creds
         return self.remote_server
 
@@ -1777,6 +1886,15 @@ class DomainTrustCommand(Command):
 
         return (policy, info)
 
+    def get_netlogon_dc_unc(self, conn, server, domain):
+        try:
+            info = conn.netr_DsRGetDCNameEx2(server,
+                                             None, 0, None, None, None,
+                                             netlogon.DS_RETURN_DNS_NAME)
+            return info.dc_unc
+        except RuntimeError:
+            return conn.netr_GetDcName(server, domain)
+
     def get_netlogon_dc_info(self, conn, server):
         info = conn.netr_DsRGetDCNameEx2(server,
                                          None, 0, None, None, None,
@@ -1785,7 +1903,7 @@ class DomainTrustCommand(Command):
 
     def netr_DomainTrust_to_name(self, t):
         if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
-             return t.netbios_name
+            return t.netbios_name
 
         return t.dns_name
 
@@ -1793,11 +1911,11 @@ class DomainTrustCommand(Command):
         primary = None
         primary_parent = None
         for _t in a:
-             if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
-                  primary = _t
-                  if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
-                      primary_parent = a[_t.parent_index]
-                  break
+            if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
+                primary = _t
+                if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
+                    primary_parent = a[_t.parent_index]
+                break
 
         if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
             if t is primary_parent:
@@ -1850,7 +1968,7 @@ class DomainTrustCommand(Command):
             w = "__unknown__%08X__" % v32
 
         r = "0x%x (%s)" % (v, w)
-        return r;
+        return r
 
     def generic_bitmap_to_string(self, b_dict, v, names_only=False):
 
@@ -1871,50 +1989,50 @@ class DomainTrustCommand(Command):
         if names_only:
             return w
         r = "0x%x (%s)" % (v, w)
-        return r;
+        return r
 
     def trustType_string(self, v):
         types = {
-            lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
-            lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
-            lsa.LSA_TRUST_TYPE_MIT : "MIT",
-            lsa.LSA_TRUST_TYPE_DCE : "DCE",
+            lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
+            lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
+            lsa.LSA_TRUST_TYPE_MIT: "MIT",
+            lsa.LSA_TRUST_TYPE_DCE: "DCE",
         }
         return self.generic_enum_to_string(types, v)
 
     def trustDirection_string(self, v):
         directions = {
             lsa.LSA_TRUST_DIRECTION_INBOUND |
-            lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
-            lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
-            lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
+            lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
+            lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
+            lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
         }
         return self.generic_enum_to_string(directions, v)
 
     def trustAttributes_string(self, v):
         attributes = {
-            lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
-            lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
-            lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
-            lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
-            lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
-            lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
-            lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
-            lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
+            lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
+            lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
+            lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
+            lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
+            lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
+            lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
+            lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
+            lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
         }
         return self.generic_bitmap_to_string(attributes, v)
 
     def kerb_EncTypes_string(self, v):
         enctypes = {
-            security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
-            security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
-            security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
-            security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
-            security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
-            security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
-            security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
-            security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
-            security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
+            security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
+            security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
+            security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
+            security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
+            security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
+            security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
+            security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
+            security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
+            security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
         }
         return self.generic_bitmap_to_string(enctypes, v)
 
@@ -1923,9 +2041,9 @@ class DomainTrustCommand(Command):
             return "Status[Enabled]"
 
         flags = {
-            lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
-            lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
-            lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
+            lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
+            lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
+            lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
         }
         return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
 
@@ -1934,10 +2052,10 @@ class DomainTrustCommand(Command):
             return "Status[Enabled]"
 
         flags = {
-            lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
-            lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
-            lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
-            lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
+            lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
+            lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
+            lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
+            lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
         }
         return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
 
@@ -1950,8 +2068,7 @@ class DomainTrustCommand(Command):
         self.outf.write("Namespaces[%d]%s:\n" % (
                         len(fti.entries), tln_string))
 
-        for i in xrange(0, len(fti.entries)):
-            e = fti.entries[i]
+        for i, e in enumerate(fti.entries):
 
             flags = e.flags
             collision_string = ""
@@ -1979,6 +2096,7 @@ class DomainTrustCommand(Command):
                                 d.domain_sid, collision_string))
         return
 
+
 class cmd_domain_trust_list(DomainTrustCommand):
     """List domain trusts."""
 
@@ -1991,7 +2109,7 @@ class cmd_domain_trust_list(DomainTrustCommand):
     }
 
     takes_options = [
-       ]
+    ]
 
     def run(self, sambaopts=None, versionopts=None, localdcopts=None):
 
@@ -2002,10 +2120,11 @@ class cmd_domain_trust_list(DomainTrustCommand):
             raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
 
         try:
-            local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
-                                    netlogon.NETR_TRUST_FLAG_IN_FOREST |
-                                    netlogon.NETR_TRUST_FLAG_OUTBOUND |
-                                    netlogon.NETR_TRUST_FLAG_INBOUND)
+            local_netlogon_trusts = \
+                local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
+                                                             netlogon.NETR_TRUST_FLAG_IN_FOREST |
+                                                             netlogon.NETR_TRUST_FLAG_OUTBOUND |
+                                                             netlogon.NETR_TRUST_FLAG_INBOUND)
         except RuntimeError as error:
             if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
                 # TODO: we could implement a fallback to lsa.EnumTrustDom()
@@ -2024,6 +2143,7 @@ class cmd_domain_trust_list(DomainTrustCommand):
                             "Name[%s]" % self.netr_DomainTrust_to_name(t)))
         return
 
+
 class cmd_domain_trust_show(DomainTrustCommand):
     """Show trusted domain details."""
 
@@ -2036,7 +2156,7 @@ class cmd_domain_trust_show(DomainTrustCommand):
     }
 
     takes_options = [
-       ]
+    ]
 
     takes_args = ["domain"]
 
@@ -2062,8 +2182,10 @@ class cmd_domain_trust_show(DomainTrustCommand):
         lsaString = lsa.String()
         lsaString.string = domain
         try:
-            local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+            local_tdo_full = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
             local_tdo_info = local_tdo_full.info_ex
             local_tdo_posix = local_tdo_full.posix_offset
         except NTSTATUSError as error:
@@ -2073,8 +2195,10 @@ class cmd_domain_trust_show(DomainTrustCommand):
             raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
 
         try:
-            local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
+            local_tdo_enctypes = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
         except NTSTATUSError as error:
             if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
                 error = None
@@ -2083,7 +2207,7 @@ class cmd_domain_trust_show(DomainTrustCommand):
 
             if error is not None:
                 raise self.LocalRuntimeError(self, error,
-                           "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
+                                             "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
 
             local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
             local_tdo_enctypes.enc_types = 0
@@ -2091,8 +2215,10 @@ class cmd_domain_trust_show(DomainTrustCommand):
         try:
             local_tdo_forest = None
             if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
-                local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
-                                        lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
+                local_tdo_forest = \
+                    local_lsa.lsaRQueryForestTrustInformation(local_policy,
+                                                              lsaString,
+                                                              lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
         except RuntimeError as error:
             if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
                 error = None
@@ -2105,7 +2231,7 @@ class cmd_domain_trust_show(DomainTrustCommand):
             local_tdo_forest.count = 0
             local_tdo_forest.entries = []
 
-        self.outf.write("TrusteDomain:\n\n");
+        self.outf.write("TrustedDomain:\n\n")
         self.outf.write("NetbiosName:    %s\n" % local_tdo_info.netbios_name.string)
         if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
             self.outf.write("DnsName:        %s\n" % local_tdo_info.domain_name.string)
@@ -2124,6 +2250,7 @@ class cmd_domain_trust_show(DomainTrustCommand):
 
         return
 
+
 class cmd_domain_trust_create(DomainTrustCommand):
     """Create a domain or forest trust."""
 
@@ -2179,7 +2306,7 @@ class cmd_domain_trust_create(DomainTrustCommand):
                help="Skip validation of the trust.",
                dest='validate',
                default=True),
-       ]
+    ]
 
     takes_args = ["domain"]
 
@@ -2210,7 +2337,7 @@ class cmd_domain_trust_create(DomainTrustCommand):
             enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
             enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
 
-        local_policy_access =  lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
+        local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
         local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
         local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
 
@@ -2271,7 +2398,7 @@ class cmd_domain_trust_create(DomainTrustCommand):
             # 512 bytes and a 2 bytes confounder is required.
             #
             def random_trust_secret(length):
-                pw = samba.generate_random_machine_password(length/2, length/2)
+                pw = samba.generate_random_machine_password(length // 2, length // 2)
                 return string_to_byte_array(pw.encode('utf-16-le'))
 
             if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
@@ -2351,48 +2478,56 @@ class cmd_domain_trust_create(DomainTrustCommand):
 
         try:
             lsaString.string = local_trust_info.domain_name.string
-            local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+            local_old_netbios = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
             raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
         except NTSTATUSError as error:
             if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                 raise self.LocalRuntimeError(self, error,
-                                "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
-                                lsaString.string))
+                                             "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
+                                    lsaString.string))
 
         try:
             lsaString.string = local_trust_info.netbios_name.string
-            local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+            local_old_dns = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
             raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
         except NTSTATUSError as error:
             if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                 raise self.LocalRuntimeError(self, error,
-                                "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
-                                lsaString.string))
+                                             "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
+                                    lsaString.string))
 
         if remote_trust_info:
             try:
                 lsaString.string = remote_trust_info.domain_name.string
-                remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
-                                            lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+                remote_old_netbios = \
+                    remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
+                                                            lsaString,
+                                                            lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
                 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
             except NTSTATUSError as error:
                 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                     raise self.RemoteRuntimeError(self, error,
-                                    "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
-                                    lsaString.string))
+                                                  "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
+                                        lsaString.string))
 
             try:
                 lsaString.string = remote_trust_info.netbios_name.string
-                remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
-                                            lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+                remote_old_dns = \
+                    remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
+                                                            lsaString,
+                                                            lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
                 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
             except NTSTATUSError as error:
                 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                     raise self.RemoteRuntimeError(self, error,
-                                    "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
-                                    lsaString.string))
+                                                  "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
+                                        lsaString.string))
 
         try:
             local_netlogon = self.new_local_netlogon_connection()
@@ -2411,7 +2546,8 @@ class cmd_domain_trust_create(DomainTrustCommand):
                 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
 
             try:
-                remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
+                remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
+                                                                  remote_server, domain)
             except RuntimeError as error:
                 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
 
@@ -2483,29 +2619,30 @@ class cmd_domain_trust_create(DomainTrustCommand):
         try:
             if remote_trust_info:
                 self.outf.write("Creating remote TDO.\n")
-                current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
-                remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
-                                                                      remote_trust_info,
-                                                                      remote_auth_info,
-                                                                      lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
+                current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
+                remote_tdo_handle = \
+                    remote_lsa.CreateTrustedDomainEx2(remote_policy,
+                                                      remote_trust_info,
+                                                      remote_auth_info,
+                                                      lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
                 self.outf.write("Remote TDO created.\n")
                 if enc_types:
                     self.outf.write("Setting supported encryption types on remote TDO.\n")
-                    current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
+                    current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
                     remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
                                                            lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
                                                            enc_types)
 
             self.outf.write("Creating local TDO.\n")
-            current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
+            current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
             local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
-                                                                  local_trust_info,
-                                                                  local_auth_info,
-                                                                  lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
+                                                                local_trust_info,
+                                                                local_auth_info,
+                                                                lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
             self.outf.write("Local TDO created\n")
             if enc_types:
                 self.outf.write("Setting supported encryption types on local TDO.\n")
-                current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
+                current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
                 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
                                                       lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
                                                       enc_types)
@@ -2534,19 +2671,21 @@ class cmd_domain_trust_create(DomainTrustCommand):
                     # this triggers netr_GetForestTrustInformation to the remote domain
                     # and lsaRSetForestTrustInformation() locally, but new top level
                     # names are disabled by default.
-                    local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
-                                                                  remote_lsa_info.dns_domain.string,
-                                                                  netlogon.DS_GFTI_UPDATE_TDO)
+                    local_forest_info = \
+                        local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
+                                                                         remote_lsa_info.dns_domain.string,
+                                                                         netlogon.DS_GFTI_UPDATE_TDO)
                 except RuntimeError as error:
                     raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
 
                 try:
                     # here we try to enable all top level names
-                    local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
-                                                                  remote_lsa_info.dns_domain,
-                                                                  lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
-                                                                  local_forest_info,
-                                                                  0)
+                    local_forest_collision = \
+                        local_lsa.lsaRSetForestTrustInformation(local_policy,
+                                                                remote_lsa_info.dns_domain,
+                                                                lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
+                                                                local_forest_info,
+                                                                0)
                 except RuntimeError as error:
                     raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
 
@@ -2561,19 +2700,21 @@ class cmd_domain_trust_create(DomainTrustCommand):
                         # this triggers netr_GetForestTrustInformation to our domain.
                         # and lsaRSetForestTrustInformation() remotely, but new top level
                         # names are disabled by default.
-                        remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
-                                                                      local_lsa_info.dns_domain.string,
-                                                                      netlogon.DS_GFTI_UPDATE_TDO)
+                        remote_forest_info = \
+                            remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
+                                                                              local_lsa_info.dns_domain.string,
+                                                                              netlogon.DS_GFTI_UPDATE_TDO)
                     except RuntimeError as error:
                         raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
 
                     try:
                         # here we try to enable all top level names
-                        remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
-                                                                      local_lsa_info.dns_domain,
-                                                                      lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
-                                                                      remote_forest_info,
-                                                                      0)
+                        remote_forest_collision = \
+                            remote_lsa.lsaRSetForestTrustInformation(remote_policy,
+                                                                     local_lsa_info.dns_domain,
+                                                                     lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
+                                                                     remote_forest_info,
+                                                                     0)
                     except RuntimeError as error:
                         raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
 
@@ -2585,9 +2726,9 @@ class cmd_domain_trust_create(DomainTrustCommand):
                 self.outf.write("Validating outgoing trust...\n")
                 try:
                     local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
-                                                                      netlogon.NETLOGON_CONTROL_TC_VERIFY,
-                                                                      2,
-                                                                      remote_lsa_info.dns_domain.string)
+                                                                             netlogon.NETLOGON_CONTROL_TC_VERIFY,
+                                                                             2,
+                                                                             remote_lsa_info.dns_domain.string)
                 except RuntimeError as error:
                     raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
 
@@ -2614,10 +2755,11 @@ class cmd_domain_trust_create(DomainTrustCommand):
                 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
                     self.outf.write("Validating incoming trust...\n")
                     try:
-                        remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
-                                                                      netlogon.NETLOGON_CONTROL_TC_VERIFY,
-                                                                      2,
-                                                                      local_lsa_info.dns_domain.string)
+                        remote_trust_verify = \
+                            remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
+                                                                 netlogon.NETLOGON_CONTROL_TC_VERIFY,
+                                                                 2,
+                                                                 local_lsa_info.dns_domain.string)
                     except RuntimeError as error:
                         raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
 
@@ -2656,6 +2798,7 @@ class cmd_domain_trust_create(DomainTrustCommand):
         self.outf.write("Success.\n")
         return
 
+
 class cmd_domain_trust_delete(DomainTrustCommand):
     """Delete a domain trust."""
 
@@ -2674,21 +2817,21 @@ class cmd_domain_trust_delete(DomainTrustCommand):
                help="Where to delete the trusted domain object: 'local' or 'both'.",
                dest='delete_location',
                default="both"),
-       ]
+    ]
 
     takes_args = ["domain"]
 
     def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
             delete_location=None):
 
-        local_policy_access =  lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
+        local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
         local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
         local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
 
         if delete_location == "local":
             remote_policy_access = None
         else:
-            remote_policy_access =  lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
+            remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
             remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
             remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
 
@@ -2717,13 +2860,12 @@ class cmd_domain_trust_delete(DomainTrustCommand):
         try:
             lsaString.string = domain
             local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
+                                                                    lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
         except NTSTATUSError as error:
             if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                 raise CommandError("Failed to find trust for domain '%s'" % domain)
             raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
 
-
         if remote_policy_access is not None:
             try:
                 remote_server = self.setup_remote_server(credopts, domain)
@@ -2755,8 +2897,10 @@ class cmd_domain_trust_delete(DomainTrustCommand):
 
             try:
                 lsaString.string = local_lsa_info.dns_domain.string
-                remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
-                                            lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
+                remote_tdo_info = \
+                    remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
+                                                            lsaString,
+                                                            lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
             except NTSTATUSError as error:
                 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                     raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
@@ -2775,9 +2919,10 @@ class cmd_domain_trust_delete(DomainTrustCommand):
         if local_tdo_info is not None:
             try:
                 lsaString.string = local_tdo_info.domain_name.string
-                local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
-                                                                     lsaString,
-                                                                     security.SEC_STD_DELETE)
+                local_tdo_handle = \
+                    local_lsa.OpenTrustedDomainByName(local_policy,
+                                                      lsaString,
+                                                      security.SEC_STD_DELETE)
             except RuntimeError as error:
                 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
                                              lsaString.string))
@@ -2788,9 +2933,10 @@ class cmd_domain_trust_delete(DomainTrustCommand):
         if remote_tdo_info is not None:
             try:
                 lsaString.string = remote_tdo_info.domain_name.string
-                remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
-                                                                       lsaString,
-                                                                       security.SEC_STD_DELETE)
+                remote_tdo_handle = \
+                    remote_lsa.OpenTrustedDomainByName(remote_policy,
+                                                       lsaString,
+                                                       security.SEC_STD_DELETE)
             except RuntimeError as error:
                 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
                                               lsaString.string))
@@ -2813,6 +2959,7 @@ class cmd_domain_trust_delete(DomainTrustCommand):
 
         return
 
+
 class cmd_domain_trust_validate(DomainTrustCommand):
     """Validate a domain trust."""
 
@@ -2831,14 +2978,14 @@ class cmd_domain_trust_validate(DomainTrustCommand):
                help="Where to validate the trusted domain object: 'local' or 'both'.",
                dest='validate_location',
                default="both"),
-       ]
+    ]
 
     takes_args = ["domain"]
 
     def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
             validate_location=None):
 
-        local_policy_access =  lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
+        local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
 
         local_server = self.setup_local_server(sambaopts, localdcopts)
         try:
@@ -2859,8 +3006,10 @@ class cmd_domain_trust_validate(DomainTrustCommand):
         try:
             lsaString = lsa.String()
             lsaString.string = domain
-            local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
+            local_tdo_info = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
         except NTSTATUSError as error:
             if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
@@ -2878,10 +3027,11 @@ class cmd_domain_trust_validate(DomainTrustCommand):
             raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
 
         try:
-            local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
-                                                                 netlogon.NETLOGON_CONTROL_TC_VERIFY,
-                                                                 2,
-                                                                 local_tdo_info.domain_name.string)
+            local_trust_verify = \
+                local_netlogon.netr_LogonControl2Ex(local_server,
+                                                    netlogon.NETLOGON_CONTROL_TC_VERIFY,
+                                                    2,
+                                                    local_tdo_info.domain_name.string)
         except RuntimeError as error:
             raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
 
@@ -2907,10 +3057,11 @@ class cmd_domain_trust_validate(DomainTrustCommand):
         try:
             server = local_trust_verify.trusted_dc_name.replace('\\', '')
             domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
-            local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
-                                                                 netlogon.NETLOGON_CONTROL_REDISCOVER,
-                                                                 2,
-                                                                 domain_and_server)
+            local_trust_rediscover = \
+                local_netlogon.netr_LogonControl2Ex(local_server,
+                                                    netlogon.NETLOGON_CONTROL_REDISCOVER,
+                                                    2,
+                                                    domain_and_server)
         except RuntimeError as error:
             raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
 
@@ -2936,10 +3087,11 @@ class cmd_domain_trust_validate(DomainTrustCommand):
                 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
 
             try:
-                remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
-                                                                  netlogon.NETLOGON_CONTROL_TC_VERIFY,
-                                                                  2,
-                                                                  local_lsa_info.dns_domain.string)
+                remote_trust_verify = \
+                    remote_netlogon.netr_LogonControl2Ex(remote_server,
+                                                         netlogon.NETLOGON_CONTROL_TC_VERIFY,
+                                                         2,
+                                                         local_lsa_info.dns_domain.string)
             except RuntimeError as error:
                 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
 
@@ -2965,10 +3117,11 @@ class cmd_domain_trust_validate(DomainTrustCommand):
             try:
                 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
                 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
-                remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
-                                                                     netlogon.NETLOGON_CONTROL_REDISCOVER,
-                                                                     2,
-                                                                     domain_and_server)
+                remote_trust_rediscover = \
+                    remote_netlogon.netr_LogonControl2Ex(remote_server,
+                                                         netlogon.NETLOGON_CONTROL_REDISCOVER,
+                                                         2,
+                                                         domain_and_server)
             except RuntimeError as error:
                 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
 
@@ -2985,6 +3138,7 @@ class cmd_domain_trust_validate(DomainTrustCommand):
 
         return
 
+
 class cmd_domain_trust_namespaces(DomainTrustCommand):
     """Manage forest trust namespaces."""
 
@@ -3054,7 +3208,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
                dest='delete_spn',
                default=[]),
-       ]
+    ]
 
     takes_args = ["domain?"]
 
@@ -3239,7 +3393,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                         continue
                     raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
 
-        local_policy_access =  lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
+        local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
         if require_update:
             local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
 
@@ -3324,10 +3478,10 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
             self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
             for v in stored_upn_vals:
-                  self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
+                self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
             self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
             for v in stored_spn_vals:
-                  self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
+                self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
 
             if not require_update:
                 return
@@ -3341,22 +3495,17 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
             update_spn_vals.extend(stored_spn_vals)
 
             for upn in add_upn:
-                idx = None
-                for i in xrange(0, len(update_upn_vals)):
-                    v = update_upn_vals[i]
-                    if v.lower() != upn.lower():
-                        continue
-                    idx = i
-                    break
-                if idx is not None:
-                    raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
+                for i, v in enumerate(update_upn_vals):
+                    if v.lower() == upn.lower():
+                        raise CommandError("Entry already present for "
+                                           "value[%s] specified for "
+                                           "--add-upn-suffix" % upn)
                 update_upn_vals.append(upn)
                 replace_upn = True
 
             for upn in delete_upn:
                 idx = None
-                for i in xrange(0, len(update_upn_vals)):
-                    v = update_upn_vals[i]
+                for i, v in enumerate(update_upn_vals):
                     if v.lower() != upn.lower():
                         continue
                     idx = i
@@ -3368,22 +3517,17 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                 replace_upn = True
 
             for spn in add_spn:
-                idx = None
-                for i in xrange(0, len(update_spn_vals)):
-                    v = update_spn_vals[i]
-                    if v.lower() != spn.lower():
-                        continue
-                    idx = i
-                    break
-                if idx is not None:
-                    raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
+                for i, v in enumerate(update_spn_vals):
+                    if v.lower() == spn.lower():
+                        raise CommandError("Entry already present for "
+                                           "value[%s] specified for "
+                                           "--add-spn-suffix" % spn)
                 update_spn_vals.append(spn)
                 replace_spn = True
 
             for spn in delete_spn:
                 idx = None
-                for i in xrange(0, len(update_spn_vals)):
-                    v = update_spn_vals[i]
+                for i, v in enumerate(update_spn_vals):
                     if v.lower() != spn.lower():
                         continue
                     idx = i
@@ -3396,18 +3540,18 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
             self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
             for v in update_upn_vals:
-                  self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
+                self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
             self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
             for v in update_spn_vals:
-                  self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
+                self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
 
             update_msg = ldb.Message()
             update_msg.dn = stored_msg.dn
 
             if replace_upn:
                 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
-                                                                    ldb.FLAG_MOD_REPLACE,
-                                                                    'uPNSuffixes')
+                                                               ldb.FLAG_MOD_REPLACE,
+                                                               'uPNSuffixes')
             if replace_spn:
                 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
                                                                     ldb.FLAG_MOD_REPLACE,
@@ -3419,7 +3563,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
             try:
                 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
-                                                                                       None, 0)
+                                                                                      None, 0)
             except RuntimeError as error:
                 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
 
@@ -3431,8 +3575,10 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
         try:
             lsaString = lsa.String()
             lsaString.string = domain
-            local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
-                                        lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
+            local_tdo_info = \
+                local_lsa.QueryTrustedDomainInfoByName(local_policy,
+                                                       lsaString,
+                                                       lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
         except NTSTATUSError as error:
             if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
                 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
@@ -3471,18 +3617,20 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                 # this triggers netr_GetForestTrustInformation to the remote domain
                 # and lsaRSetForestTrustInformation() locally, but new top level
                 # names are disabled by default.
-                fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
-                                                              local_tdo_info.domain_name.string,
-                                                              netlogon_update_tdo)
+                fresh_forest_info = \
+                    local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
+                                                                     local_tdo_info.domain_name.string,
+                                                                     netlogon_update_tdo)
             except RuntimeError as error:
                 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
 
             try:
-                fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
-                                                              local_tdo_info.domain_name,
-                                                              lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
-                                                              fresh_forest_info,
-                                                              lsa_update_check)
+                fresh_forest_collision = \
+                    local_lsa.lsaRSetForestTrustInformation(local_policy,
+                                                            local_tdo_info.domain_name,
+                                                            lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
+                                                            fresh_forest_info,
+                                                            lsa_update_check)
             except RuntimeError as error:
                 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
 
@@ -3495,7 +3643,8 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                 try:
                     lsaString = lsa.String()
                     lsaString.string = local_tdo_info.domain_name.string
-                    stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
+                    stored_forest_info = \
+                        local_lsa.lsaRQueryForestTrustInformation(local_policy,
                                                                   lsaString,
                                                                   lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
                 except RuntimeError as error:
@@ -3514,9 +3663,10 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
         try:
             lsaString = lsa.String()
             lsaString.string = local_tdo_info.domain_name.string
-            local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
-                                                      lsaString,
-                                                      lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
+            local_forest_info = \
+                local_lsa.lsaRQueryForestTrustInformation(local_policy,
+                                                          lsaString,
+                                                          lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
         except RuntimeError as error:
             raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
 
@@ -3534,16 +3684,14 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
         update_forest_info.entries = entries
 
         if enable_all:
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
                     continue
                 if update_forest_info.entries[i].flags == 0:
                     continue
                 update_forest_info.entries[i].time = 0
                 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
                     continue
                 if update_forest_info.entries[i].flags == 0:
@@ -3554,8 +3702,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for tln in enable_tln:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
                     continue
                 if r.forest_trust_data.string.lower() != tln.lower():
@@ -3571,8 +3718,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for tln in disable_tln:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
                     continue
                 if r.forest_trust_data.string.lower() != tln.lower():
@@ -3589,8 +3735,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for tln_ex in add_tln_ex:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
                     continue
                 if r.forest_trust_data.string.lower() != tln_ex.lower():
@@ -3602,8 +3747,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
             tln_dot = ".%s" % tln_ex.lower()
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
                     continue
                 r_dot = ".%s" % r.forest_trust_data.string.lower()
@@ -3631,8 +3775,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for tln_ex in delete_tln_ex:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
                     continue
                 if r.forest_trust_data.string.lower() != tln_ex.lower():
@@ -3650,8 +3793,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for nb in enable_nb:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
                     continue
                 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
@@ -3667,8 +3809,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for nb in disable_nb:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
                     continue
                 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
@@ -3685,8 +3826,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for sid in enable_sid:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
                     continue
                 if r.forest_trust_data.domain_sid != sid:
@@ -3702,8 +3842,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         for sid in disable_sid:
             idx = None
-            for i in xrange(0, len(update_forest_info.entries)):
-                r = update_forest_info.entries[i]
+            for i, r in enumerate(update_forest_info.entries):
                 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
                     continue
                 if r.forest_trust_data.domain_sid != sid:
@@ -3720,9 +3859,9 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
 
         try:
             update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
-                                                          local_tdo_info.domain_name,
-                                                          lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
-                                                          update_forest_info, 0)
+                                                                              local_tdo_info.domain_name,
+                                                                              lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
+                                                                              update_forest_info, 0)
         except RuntimeError as error:
             raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
 
@@ -3735,8 +3874,8 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
             lsaString = lsa.String()
             lsaString.string = local_tdo_info.domain_name.string
             stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
-                                                          lsaString,
-                                                          lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
+                                                                           lsaString,
+                                                                           lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
         except RuntimeError as error:
             raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
 
@@ -3745,6 +3884,7 @@ class cmd_domain_trust_namespaces(DomainTrustCommand):
                                      tln=local_tdo_info.domain_name.string)
         return
 
+
 class cmd_domain_tombstones_expunge(Command):
     """Expunge tombstones from the database.
 
@@ -3753,10 +3893,10 @@ This command expunges tombstones from the database."""
 
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
-                metavar="URL", dest="H"),
+               metavar="URL", dest="H"),
         Option("--current-time",
-                help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
-                type=str),
+               help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
+               type=str),
         Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
     ]
 
@@ -3766,7 +3906,7 @@ This command expunges tombstones from the database."""
         "sambaopts": options.SambaOptions,
         "credopts": options.CredentialsOptions,
         "versionopts": options.VersionOptions,
-        }
+    }
 
     def run(self, *ncs, **kwargs):
         sambaopts = kwargs.get("sambaopts")
@@ -3789,7 +3929,7 @@ This command expunges tombstones from the database."""
 
         if len(ncs) == 0:
             res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
-                         attrs=["namingContexts"])
+                               attrs=["namingContexts"])
 
             ncs = []
             for nc in res[0]["namingContexts"]:
@@ -3806,7 +3946,7 @@ This command expunges tombstones from the database."""
                                                                current_time=current_time,
                                                                tombstone_lifetime=tombstone_lifetime)
 
-        except Exception, err:
+        except Exception as err:
             if started_transaction:
                 samdb.transaction_cancel()
             raise CommandError("Failed to expunge / garbage collect tombstones", err)
@@ -3817,7 +3957,6 @@ This command expunges tombstones from the database."""
                         % (removed_objects, removed_links))
 
 
-
 class cmd_domain_trust(SuperCommand):
     """Domain and forest trust management."""
 
@@ -3829,12 +3968,427 @@ class cmd_domain_trust(SuperCommand):
     subcommands["validate"] = cmd_domain_trust_validate()
     subcommands["namespaces"] = cmd_domain_trust_namespaces()
 
+
 class cmd_domain_tombstones(SuperCommand):
     """Domain tombstone and recycled object management."""
 
     subcommands = {}
     subcommands["expunge"] = cmd_domain_tombstones_expunge()
 
+
+class ldif_schema_update:
+    """Helper class for applying LDIF schema updates"""
+
+    def __init__(self):
+        self.is_defunct = False
+        self.unknown_oid = None
+        self.dn = None
+        self.ldif = ""
+
+    def can_ignore_failure(self, error):
+        """Checks if we can safely ignore failure to apply an LDIF update"""
+        (num, errstr) = error.args
+
+        # Microsoft has marked objects as defunct that Samba doesn't know about
+        if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
+            print("Defunct object %s doesn't exist, skipping" % self.dn)
+            return True
+        elif self.unknown_oid is not None:
+            print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
+            return True
+
+        return False
+
+    def apply(self, samdb):
+        """Applies a single LDIF update to the schema"""
+
+        try:
+            try:
+                samdb.modify_ldif(self.ldif, controls=['relax:0'])
+            except ldb.LdbError as e:
+                if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
+
+                    # REFRESH after a failed change
+
+                    # Otherwise the OID-to-attribute mapping in
+                    # _apply_updates_in_file() won't work, because it
+                    # can't lookup the new OID in the schema
+                    samdb.set_schema_update_now()
+
+                    samdb.modify_ldif(self.ldif, controls=['relax:0'])
+                else:
+                    raise
+        except ldb.LdbError as e:
+            if self.can_ignore_failure(e):
+                return 0
+            else:
+                print("Exception: %s" % e)
+                print("Encountered while trying to apply the following LDIF")
+                print("----------------------------------------------------")
+                print("%s" % self.ldif)
+
+                raise
+
+        return 1
+
+
+class cmd_domain_schema_upgrade(Command):
+    """Domain schema upgrading"""
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server", type=str,
+               metavar="URL", dest="H"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),  # unused
+        Option("-v", "--verbose", help="Be verbose", action="store_true"),
+        Option("--schema", type="choice", metavar="SCHEMA",
+               choices=["2012", "2012_R2"],
+               help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
+               default="2012_R2"),
+        Option("--ldf-file", type=str, default=None,
+               help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
+        Option("--base-dir", type=str, default=None,
+               help="Location of ldf files Default is ${SETUPDIR}/adprep.")
+    ]
+
+    def _apply_updates_in_file(self, samdb, ldif_file):
+        """
+        Applies a series of updates specified in an .LDIF file. The .LDIF file
+        is based on the adprep Schema updates provided by Microsoft.
+        """
+        count = 0
+        ldif_op = ldif_schema_update()
+
+        # parse the file line by line and work out each update operation to apply
+        for line in ldif_file:
+
+            line = line.rstrip()
+
+            # the operations in the .LDIF file are separated by blank lines. If
+            # we hit a blank line, try to apply the update we've parsed so far
+            if line == '':
+
+                # keep going if we haven't parsed anything yet
+                if ldif_op.ldif == '':
+                    continue
+
+                # Apply the individual change
+                count += ldif_op.apply(samdb)
+
+                # start storing the next operation from scratch again
+                ldif_op = ldif_schema_update()
+                continue
+
+            # replace the placeholder domain name in the .ldif file with the real domain
+            if line.upper().endswith('DC=X'):
+                line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
+            elif line.upper().endswith('CN=X'):
+                line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
+
+            values = line.split(':')
+
+            if values[0].lower() == 'dn':
+                ldif_op.dn = values[1].strip()
+
+            # replace the Windows-specific operation with the Samba one
+            if values[0].lower() == 'changetype':
+                line = line.lower().replace(': ntdsschemaadd',
+                                            ': add')
+                line = line.lower().replace(': ntdsschemamodify',
+                                            ': modify')
+
+            if values[0].lower() in ['rdnattid', 'subclassof',
+                                     'systemposssuperiors',
+                                     'systemmaycontain',
+                                     'systemauxiliaryclass']:
+                _, value = values
+
+                # The Microsoft updates contain some OIDs we don't recognize.
+                # Query the DB to see if we can work out the OID this update is
+                # referring to. If we find a match, then replace the OID with
+                # the ldapDisplayname
+                if '.' in value:
+                    res = samdb.search(base=samdb.get_schema_basedn(),
+                                       expression="(|(attributeId=%s)(governsId=%s))" %
+                                       (value, value),
+                                       attrs=['ldapDisplayName'])
+
+                    if len(res) != 1:
+                        ldif_op.unknown_oid = value
+                    else:
+                        display_name = res[0]['ldapDisplayName'][0]
+                        line = line.replace(value, ' ' + display_name)
+
+            # Microsoft has marked objects as defunct that Samba doesn't know about
+            if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
+                ldif_op.is_defunct = True
+
+            # Samba has added the showInAdvancedViewOnly attribute to all objects,
+            # so rather than doing an add, we need to do a replace
+            if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
+                line = 'replace: showInAdvancedViewOnly'
+
+            # Add the line to the current LDIF operation (including the newline
+            # we stripped off at the start of the loop)
+            ldif_op.ldif += line + '\n'
+
+        return count
+
+    def _apply_update(self, samdb, update_file, base_dir):
+        """Wrapper function for parsing an LDIF file and applying the updates"""
+
+        print("Applying %s updates..." % update_file)
+
+        ldif_file = None
+        try:
+            ldif_file = open(os.path.join(base_dir, update_file))
+
+            count = self._apply_updates_in_file(samdb, ldif_file)
+
+        finally:
+            if ldif_file:
+                ldif_file.close()
+
+        print("%u changes applied" % count)
+
+        return count
+
+    def run(self, **kwargs):
+        from samba.ms_schema_markdown import read_ms_markdown
+        from samba.schema import Schema
+
+        updates_allowed_overriden = False
+        sambaopts = kwargs.get("sambaopts")
+        credopts = kwargs.get("credopts")
+        versionpts = kwargs.get("versionopts")
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        H = kwargs.get("H")
+        target_schema = kwargs.get("schema")
+        ldf_files = kwargs.get("ldf_file")
+        base_dir = kwargs.get("base_dir")
+
+        temp_folder = None
+
+        samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
+
+        # we're not going to get far if the config doesn't allow schema updates
+        if lp.get("dsdb:schema update allowed") is None:
+            lp.set("dsdb:schema update allowed", "yes")
+            print("Temporarily overriding 'dsdb:schema update allowed' setting")
+            updates_allowed_overriden = True
+
+        own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
+        master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
+                                    'schema')
+        if own_dn != master:
+            raise CommandError("This server is not the schema master.")
+
+        # if specific LDIF files were specified, just apply them
+        if ldf_files:
+            schema_updates = ldf_files.split(",")
+        else:
+            schema_updates = []
+
+            # work out the version of the target schema we're upgrading to
+            end = Schema.get_version(target_schema)
+
+            # work out the version of the schema we're currently using
+            res = samdb.search(base=samdb.get_schema_basedn(),
+                               scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
+
+            if len(res) != 1:
+                raise CommandError('Could not determine current schema version')
+            start = int(res[0]['objectVersion'][0]) + 1
+
+            diff_dir = setup_path("adprep/WindowsServerDocs")
+            if base_dir is None:
+                # Read from the Schema-Updates.md file
+                temp_folder = tempfile.mkdtemp()
+
+                update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
+
+                try:
+                    read_ms_markdown(update_file, temp_folder)
+                except Exception as e:
+                    print("Exception in markdown parsing: %s" % e)
+                    shutil.rmtree(temp_folder)
+                    raise CommandError('Failed to upgrade schema')
+
+                base_dir = temp_folder
+
+            for version in range(start, end + 1):
+                update = 'Sch%d.ldf' % version
+                schema_updates.append(update)
+
+                # Apply patches if we parsed the Schema-Updates.md file
+                diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
+                if temp_folder and os.path.exists(diff):
+                    try:
+                        p = subprocess.Popen(['patch', update, '-i', diff],
+                                             stdout=subprocess.PIPE,
+                                             stderr=subprocess.PIPE, cwd=temp_folder)
+                    except (OSError, IOError):
+                        shutil.rmtree(temp_folder)
+                        raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
+
+                    stdout, stderr = p.communicate()
+
+                    if p.returncode:
+                        print("Exception in patch: %s\n%s" % (stdout, stderr))
+                        shutil.rmtree(temp_folder)
+                        raise CommandError('Failed to upgrade schema')
+
+                    print("Patched %s using %s" % (update, diff))
+
+        if base_dir is None:
+            base_dir = setup_path("adprep")
+
+        samdb.transaction_start()
+        count = 0
+        error_encountered = False
+
+        try:
+            # Apply the schema updates needed to move to the new schema version
+            for ldif_file in schema_updates:
+                count += self._apply_update(samdb, ldif_file, base_dir)
+
+            if count > 0:
+                samdb.transaction_commit()
+                print("Schema successfully updated")
+            else:
+                print("No changes applied to schema")
+                samdb.transaction_cancel()
+        except Exception as e:
+            print("Exception: %s" % e)
+            print("Error encountered, aborting schema upgrade")
+            samdb.transaction_cancel()
+            error_encountered = True
+
+        if updates_allowed_overriden:
+            lp.set("dsdb:schema update allowed", "no")
+
+        if temp_folder:
+            shutil.rmtree(temp_folder)
+
+        if error_encountered:
+            raise CommandError('Failed to upgrade schema')
+
+
+class cmd_domain_functional_prep(Command):
+    """Domain functional level preparation"""
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server", type=str,
+               metavar="URL", dest="H"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),
+        Option("-v", "--verbose", help="Be verbose", action="store_true"),
+        Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
+               choices=["2008_R2", "2012", "2012_R2"],
+               help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
+               default="2012_R2"),
+        Option("--forest-prep", action="store_true",
+               help="Run the forest prep (by default, both the domain and forest prep are run)."),
+        Option("--domain-prep", action="store_true",
+               help="Run the domain prep (by default, both the domain and forest prep are run).")
+    ]
+
+    def run(self, **kwargs):
+        updates_allowed_overriden = False
+        sambaopts = kwargs.get("sambaopts")
+        credopts = kwargs.get("credopts")
+        versionpts = kwargs.get("versionopts")
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        H = kwargs.get("H")
+        target_level = string_version_to_constant[kwargs.get("function_level")]
+        forest_prep = kwargs.get("forest_prep")
+        domain_prep = kwargs.get("domain_prep")
+
+        samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
+
+        # we're not going to get far if the config doesn't allow schema updates
+        if lp.get("dsdb:schema update allowed") is None:
+            lp.set("dsdb:schema update allowed", "yes")
+            print("Temporarily overriding 'dsdb:schema update allowed' setting")
+            updates_allowed_overriden = True
+
+        if forest_prep is None and domain_prep is None:
+            forest_prep = True
+            domain_prep = True
+
+        own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
+        if forest_prep:
+            master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
+                                        'schema')
+            if own_dn != master:
+                raise CommandError("This server is not the schema master.")
+
+        if domain_prep:
+            domain_dn = samdb.domain_dn()
+            infrastructure_dn = "CN=Infrastructure," + domain_dn
+            master = get_fsmo_roleowner(samdb, infrastructure_dn,
+                                        'infrastructure')
+            if own_dn != master:
+                raise CommandError("This server is not the infrastructure master.")
+
+        if forest_prep:
+            samdb.transaction_start()
+            error_encountered = False
+            try:
+                from samba.forest_update import ForestUpdate
+                forest = ForestUpdate(samdb, fix=True)
+
+                forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
+                forest.check_updates_functional_level(target_level,
+                                                      DS_DOMAIN_FUNCTION_2008_R2,
+                                                      update_revision=True)
+
+                samdb.transaction_commit()
+            except Exception as e:
+                print("Exception: %s" % e)
+                samdb.transaction_cancel()
+                error_encountered = True
+
+        if domain_prep:
+            samdb.transaction_start()
+            error_encountered = False
+            try:
+                from samba.domain_update import DomainUpdate
+
+                domain = DomainUpdate(samdb, fix=True)
+                domain.check_updates_functional_level(target_level,
+                                                      DS_DOMAIN_FUNCTION_2008,
+                                                      update_revision=True)
+
+                samdb.transaction_commit()
+            except Exception as e:
+                print("Exception: %s" % e)
+                samdb.transaction_cancel()
+                error_encountered = True
+
+        if updates_allowed_overriden:
+            lp.set("dsdb:schema update allowed", "no")
+
+        if error_encountered:
+            raise CommandError('Failed to perform functional prep')
+
+
 class cmd_domain(SuperCommand):
     """Domain management."""
 
@@ -3852,3 +4406,6 @@ class cmd_domain(SuperCommand):
     subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
     subcommands["trust"] = cmd_domain_trust()
     subcommands["tombstones"] = cmd_domain_tombstones()
+    subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
+    subcommands["functionalprep"] = cmd_domain_functional_prep()
+    subcommands["backup"] = cmd_domain_backup()