python2 reduction: Merge remaining compat code into common
[samba.git] / source4 / scripting / bin / samba_dnsupdate
index b22dde8360b4b84bd88287a9f770f9bda5bc6bd5..fcd4cc5b7da326e10f2e5c323b942d4e0fc3860d 100755 (executable)
@@ -49,8 +49,7 @@ from samba.dcerpc import netlogon, winbind
 from samba.netcmd.dns import cmd_dns
 from samba import gensec
 from samba.kcc import kcc_utils
-from samba.compat import get_string
-from samba.compat import text_type
+from samba.common import get_string
 import ldb
 
 import dns.resolver
@@ -60,7 +59,7 @@ default_ttl = 900
 am_rodc = False
 error_count = 0
 
-parser = optparse.OptionParser("samba_dnsupdate")
+parser = optparse.OptionParser("samba_dnsupdate [options]")
 sambaopts = options.SambaOptions(parser)
 parser.add_option_group(sambaopts)
 parser.add_option_group(options.VersionOptions(parser))
@@ -91,35 +90,22 @@ lp = sambaopts.get_loadparm()
 
 domain = lp.get("realm")
 host = lp.get("netbios name")
-if opts.all_interfaces:
-    all_interfaces = True
-else:
-    all_interfaces = False
+all_interfaces = opts.all_interfaces
 
-if opts.current_ip:
-    IPs = opts.current_ip
-else:
-    IPs = samba.interface_ips(lp, all_interfaces)
+IPs = opts.current_ip or samba.interface_ips(lp, bool(all_interfaces)) or []
 
 nsupdate_cmd = lp.get('nsupdate command')
 dns_zone_scavenging = lp.get("dns zone scavenging")
 
 if len(IPs) == 0:
-    print("No IP interfaces - skipping DNS updates")
+    print("No IP interfaces - skipping DNS updates\n")
+    parser.print_usage()
     sys.exit(0)
 
-if opts.rpc_server_ip:
-    rpc_server_ip = opts.rpc_server_ip
-else:
-    rpc_server_ip = IPs[0]
+rpc_server_ip = opts.rpc_server_ip or IPs[0]
 
-IP6s = []
-IP4s = []
-for i in IPs:
-    if i.find(':') != -1:
-        IP6s.append(i)
-    else:
-        IP4s.append(i)
+IP6s = [ip for ip in IPs if ':' in ip]
+IP4s = [ip for ip in IPs if ':' not in ip]
 
 smb_conf = sambaopts.get_loadparm_path()
 
@@ -132,23 +118,18 @@ def get_possible_rw_dns_server(creds, domain):
        (4.6 and prior) do not maintain this value, so add NS servers
        as well"""
 
-    hostnames = []
     ans_soa = check_one_dns_name(domain, 'SOA')
-
     # Actually there is only one
-    for i in range(len(ans_soa)):
-        hostnames.append(str(ans_soa[i].mname).rstrip('.'))
+    hosts_soa = [str(a.mname).rstrip('.') for a in ans_soa]
 
     # This is not strictly legit, but old Samba domains may have an
     # unmaintained SOA record, so go for any NS that we can get a
     # ticket to.
     ans_ns = check_one_dns_name(domain, 'NS')
-
     # Actually there is only one
-    for i in range(len(ans_ns)):
-        hostnames.append(str(ans_ns[i].target).rstrip('.'))
+    hosts_ns = [str(a.target).rstrip('.') for a in ans_ns]
 
-    return hostnames
+    return hosts_soa + hosts_ns
 
 def get_krb5_rw_dns_server(creds, domain):
     """Get a list of read-write DNS servers that we can obtain a ticket
@@ -160,8 +141,7 @@ def get_krb5_rw_dns_server(creds, domain):
 
     rw_dns_servers = get_possible_rw_dns_server(creds, domain)
     # Actually there is only one
-    for i in range(len(rw_dns_servers)):
-        target_hostname = str(rw_dns_servers[i])
+    for i, target_hostname in enumerate(rw_dns_servers):
         settings = {}
         settings["lp_ctx"] = lp
         settings["target_hostname"] = target_hostname
@@ -181,9 +161,8 @@ def get_krb5_rw_dns_server(creds, domain):
             return target_hostname
         except RuntimeError:
             # Only raise an exception if they all failed
-            if i != len(rw_dns_servers) - 1:
-                pass
-            raise
+            if i == len(rw_dns_servers) - 1:
+                raise
 
 def get_credentials(lp):
     """# get credentials if we haven't got them already."""
@@ -278,9 +257,7 @@ def hostname_match(h1, h2):
     return h1.lower().rstrip('.') == h2.lower().rstrip('.')
 
 def get_resolver(d=None):
-    resolv_conf = os.getenv('RESOLV_CONF')
-    if not resolv_conf:
-        resolv_conf = '/etc/resolv.conf'
+    resolv_conf = os.getenv('RESOLV_CONF', default='/etc/resolv.conf')
     resolver = dns.resolver.Resolver(filename=resolv_conf, configure=True)
 
     if d is not None and d.nameservers != []:
@@ -290,11 +267,10 @@ def get_resolver(d=None):
 
 def check_one_dns_name(name, name_type, d=None):
     resolver = get_resolver(d)
-    if d is not None and len(d.nameservers) == 0:
+    if d and not d.nameservers:
         d.nameservers = resolver.nameservers
-
-    ans = resolver.query(name, name_type)
-    return ans
+    # dns.resolver.Answer
+    return resolver.query(name, name_type)
 
 def check_dns_name(d):
     """check that a DNS entry exists."""
@@ -443,10 +419,10 @@ def call_nsupdate(d, op="add"):
 
     assert(op in ["add", "delete"])
 
-    if opts.verbose:
-        print("Calling nsupdate for %s (%s)" % (d, op))
-
     if opts.use_file is not None:
+        if opts.verbose:
+            print("Use File instead of nsupdate for %s (%s)" % (d, op))
+
         try:
             rfile = open(opts.use_file, 'r+')
         except IOError:
@@ -471,37 +447,27 @@ def call_nsupdate(d, op="add"):
         fcntl.lockf(rfile, fcntl.LOCK_UN)
         return
 
+    if opts.verbose:
+        print("Calling nsupdate for %s (%s)" % (d, op))
+
     normalised_name = d.name.rstrip('.') + '.'
 
     (tmp_fd, tmpfile) = tempfile.mkstemp()
     f = os.fdopen(tmp_fd, 'w')
 
-    # Getting this line right is really important.  When we are under
-    # resolv_wrapper, then we want to use RESOLV_CONF and the
-    # nameserver therein. The issue is that this parameter forces us
-    # to only ever use that server, and not some other server that the
-    # NS record may point to, even as we get a ticket to that other
-    # server.
-    #
-    # Therefore we must not set this in production, instead we want
-    # to find the name of a SOA for the zone and use that server.
-
-    if os.getenv('RESOLV_CONF') and d.nameservers != []:
-        f.write('server %s\n' % d.nameservers[0])
-    else:
-        resolver = get_resolver(d)
+    resolver = get_resolver(d)
 
-        # Local the zone for this name
-        zone = dns.resolver.zone_for_name(normalised_name,
-                                          resolver=resolver)
+    # Local the zone for this name
+    zone = dns.resolver.zone_for_name(normalised_name,
+                                      resolver=resolver)
 
-        # Now find the SOA, or if we can't get a ticket to the SOA,
-        # any server with an NS record we can get a ticket for.
-        #
-        # Thanks to the Kerberos Credentials cache this is not
-        # expensive inside the loop
-        server = get_krb5_rw_dns_server(creds, zone)
-        f.write('server %s\n' % server)
+    # Now find the SOA, or if we can't get a ticket to the SOA,
+    # any server with an NS record we can get a ticket for.
+    #
+    # Thanks to the Kerberos Credentials cache this is not
+    # expensive inside the loop
+    server = get_krb5_rw_dns_server(creds, zone)
+    f.write('server %s\n' % server)
 
     if d.type == "A":
         f.write("update %s %s %u A %s\n" % (op, normalised_name, default_ttl, d.ip))
@@ -600,15 +566,10 @@ def call_samba_tool(d, op="add", zone=None):
         args = [rpc_server_ip, zone, short_name, "AAAA", d.ip]
     if d.type == "SRV":
         if op == "add" and d.existing_port is not None:
-            print("Not handling modify of exising SRV %s using samba-tool" % d)
+            print("Not handling modify of existing SRV %s using samba-tool" % d)
             return False
-            op = "update"
-            args = [rpc_server_ip, zone, short_name, "SRV",
-                    "%s %s %s %s" % (d.existing_weight,
-                                     d.existing_port, "0", "100"),
-                    "%s %s %s %s" % (d.dest, d.port, "0", "100")]
-        else:
-            args = [rpc_server_ip, zone, short_name, "SRV", "%s %s %s %s" % (d.dest, d.port, "0", "100")]
+        args = [rpc_server_ip, zone, short_name, "SRV",
+                "%s %s %s %s" % (d.dest, d.port, "0", "100")]
     if d.type == "CNAME":
         if d.existing_cname_target is None:
             args = [rpc_server_ip, zone, short_name, "CNAME", d.dest]
@@ -643,6 +604,14 @@ def call_samba_tool(d, op="add", zone=None):
             print("Failed 'samba-tool dns' based update: %s : %s" % (str(d), estr))
         raise
 
+irpc_wb = None
+def cached_irpc_wb(lp):
+    global irpc_wb
+    if irpc_wb is not None:
+        return irpc_wb
+    irpc_wb = winbind.winbind("irpc:winbind_server", lp)
+    return irpc_wb
+
 def rodc_dns_update(d, t, op):
     '''a single DNS update via the RODC netlogon call'''
     global sub_vars
@@ -662,7 +631,7 @@ def rodc_dns_update(d, t, op):
         netlogon.NlDnsGenericGcAtSite  : netlogon.NlDnsDomainNameAlias
         }
 
-    w = winbind.winbind("irpc:winbind_server", lp)
+    w = cached_irpc_wb(lp)
     dns_names = netlogon.NL_DNS_NAME_INFO_ARRAY()
     dns_names.count = 1
     name = netlogon.NL_DNS_NAME_INFO()
@@ -677,7 +646,7 @@ def rodc_dns_update(d, t, op):
     else:
         name.dns_register = False
     dns_names.names = [ name ]
-    site_name = text_type(sub_vars['SITE'])
+    site_name = sub_vars['SITE']
 
     global error_count
 
@@ -690,6 +659,9 @@ def rodc_dns_update(d, t, op):
         print("Error setting DNS entry of type %u: %s: %s" % (t, d, reason))
         error_count = error_count + 1
 
+    if opts.verbose:
+        print("Called netlogon RODC update for %s" % d)
+
     if error_count != 0 and opts.fail_immediately:
         sys.exit(1)
 
@@ -726,15 +698,9 @@ def call_rodc_update(d, op="add"):
 
 
 # get the list of DNS entries we should have
-if opts.update_list:
-    dns_update_list = opts.update_list
-else:
-    dns_update_list = lp.private_path('dns_update_list')
+dns_update_list = opts.update_list or lp.private_path('dns_update_list')
 
-if opts.update_cache:
-    dns_update_cache = opts.update_cache
-else:
-    dns_update_cache = lp.private_path('dns_update_cache')
+dns_update_cache = opts.update_cache or lp.private_path('dns_update_cache')
 
 krb5conf = None
 # only change the krb5.conf if we are not in selftest
@@ -744,7 +710,18 @@ if 'SOCKET_WRAPPER_DIR' not in os.environ:
     krb5conf = lp.private_path('krb5.conf')
     os.environ['KRB5_CONFIG'] = krb5conf
 
-file = open(dns_update_list, "r")
+try:
+    file = open(dns_update_list, "r")
+except OSError as e:
+    if opts.update_cache:
+        print("The specified update list does not exist")
+    else:
+        print("The server update list was not found, "
+              "and --update-list was not provided.")
+    print(e)
+    print()
+    parser.print_usage()
+    sys.exit(1)
 
 if opts.nosubs:
     sub_vars = {}
@@ -894,7 +871,7 @@ else:
 use_samba_tool = opts.use_samba_tool
 use_nsupdate = opts.use_nsupdate
 # get our krb5 creds
-if len(delete_list) != 0 or len(update_list) != 0 and not opts.nocreds:
+if (delete_list or update_list) and not opts.nocreds:
     try:
         creds = get_credentials(lp)
     except RuntimeError as e:
@@ -914,7 +891,7 @@ if len(delete_list) != 0 or len(update_list) != 0 and not opts.nocreds:
 for d in delete_list:
     if d.rpc or (not use_nsupdate and use_samba_tool):
         if opts.verbose:
-            print("update (samba-tool): %s" % d)
+            print("delete (samba-tool): %s" % d)
         call_samba_tool(d, op="delete", zone=d.zone)
 
     elif am_rodc:
@@ -969,6 +946,7 @@ if rebuild_cache:
         if opts.verbose:
             print("Adding %s to %s" % (str(d), file_name))
         wfile.write(str(d)+"\n")
+    wfile.flush()
     os.rename(tmpfile, dns_update_cache)
 fcntl.lockf(cfile, fcntl.LOCK_UN)