xref: /netbsd-src/external/bsd/nsd/dist/contrib/bind2nsd/scripts/s64-sync (revision d83a80ee7fb31190352cf1f781441e06ca6a86db)
1*d83a80eeSchristos#!/usr/bin/env python
2*d83a80eeSchristos# Copyright (c) 2007, Secure64 Software Corporation
3*d83a80eeSchristos#
4*d83a80eeSchristos# Permission is hereby granted, free of charge, to any person obtaining a copy
5*d83a80eeSchristos# of this software and associated documentation files (the "Software"), to deal
6*d83a80eeSchristos# in the Software without restriction, including without limitation the rights
7*d83a80eeSchristos# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8*d83a80eeSchristos# copies of the Software, and to permit persons to whom the Software is
9*d83a80eeSchristos# furnished to do so, subject to the following conditions:
10*d83a80eeSchristos#
11*d83a80eeSchristos# The above copyright notice and this permission notice shall be included in
12*d83a80eeSchristos# all copies or substantial portions of the Software.
13*d83a80eeSchristos#
14*d83a80eeSchristos# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*d83a80eeSchristos# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*d83a80eeSchristos# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17*d83a80eeSchristos# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18*d83a80eeSchristos# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19*d83a80eeSchristos# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20*d83a80eeSchristos# THE SOFTWARE.
21*d83a80eeSchristos#
22*d83a80eeSchristos#
23*d83a80eeSchristos#
24*d83a80eeSchristos#	When named.conf changes, update the DNS machine
25*d83a80eeSchristos#
26*d83a80eeSchristos#
27*d83a80eeSchristos
28*d83a80eeSchristos#-- imports
29*d83a80eeSchristosimport getopt
30*d83a80eeSchristosimport os
31*d83a80eeSchristosimport os.path
32*d83a80eeSchristosimport popen2
33*d83a80eeSchristosimport re
34*d83a80eeSchristosimport sys
35*d83a80eeSchristosimport time
36*d83a80eeSchristos
37*d83a80eeSchristosif os.path.exists('../bind2nsd/Config.py'):
38*d83a80eeSchristos   sys.path.append('../bind2nsd')
39*d83a80eeSchristos   from Config import *
40*d83a80eeSchristos   from Utils import *
41*d83a80eeSchristos   from NamedConf import *
42*d83a80eeSchristos   from NsdConf import *
43*d83a80eeSchristoselse:
44*d83a80eeSchristos   from bind2nsd.Config import *
45*d83a80eeSchristos   from bind2nsd.Utils import *
46*d83a80eeSchristos   from bind2nsd.NamedConf import *
47*d83a80eeSchristos   from bind2nsd.NsdConf import *
48*d83a80eeSchristos
49*d83a80eeSchristosif os.path.exists('../pexpect-2.1'):
50*d83a80eeSchristos   sys.path.append('../pexpect-2.1')
51*d83a80eeSchristosimport pexpect
52*d83a80eeSchristosimport pxssh
53*d83a80eeSchristos
54*d83a80eeSchristos#-- globals
55*d83a80eeSchristosconf = Config()
56*d83a80eeSchristos
57*d83a80eeSchristos#-- useful functions
58*d83a80eeSchristosdef usage():
59*d83a80eeSchristos   print 's64-sync %s, copyright(c) 2007, Secure64 Software Corporation' \
60*d83a80eeSchristos               % (conf.getValue('version'))
61*d83a80eeSchristos   print
62*d83a80eeSchristos   print 'usage: s64-sync [-a|--analyze-only] [-h|--help] [-s|--sync-only]'
63*d83a80eeSchristos   print '                [-n|--now]'
64*d83a80eeSchristos   print '   -a | --analyze-only => look for and report errors, but do'
65*d83a80eeSchristos   print '                          not sync with the Secure64 server'
66*d83a80eeSchristos   print '   -h | --help         => print this message and quit'
67*d83a80eeSchristos   print '   -n | --now          => do not poll, sync immediately'
68*d83a80eeSchristos   print '   -s | --sync-only    => sync without translating BIND files'
69*d83a80eeSchristos   print '   -v | --verbose      => output lots of info'
70*d83a80eeSchristos   return
71*d83a80eeSchristos
72*d83a80eeSchristosdef rebuild_nsd_files():
73*d83a80eeSchristos   result = False
74*d83a80eeSchristos   xlate = conf.getValue('bind2nsd')
75*d83a80eeSchristos   if os.path.exists(xlate):
76*d83a80eeSchristos      result = run_cmd(xlate, 'running bind2nsd...')
77*d83a80eeSchristos   else:
78*d83a80eeSchristos      report_error('? could not find "%s" and have got to have it' % (xlate))
79*d83a80eeSchristos      report_error('  skipping rebuild of NSD files')
80*d83a80eeSchristos   return result
81*d83a80eeSchristos
82*d83a80eeSchristosdef scp_s64():
83*d83a80eeSchristos   #-- do the scp to an actual Secure64 server
84*d83a80eeSchristos   report_info('=> using scp to transfer to Secure64 system...')
85*d83a80eeSchristos   tmpdir = conf.getValue('tmpdir') 	# must have trailing '/'
86*d83a80eeSchristos   if not os.path.exists(tmpdir) and not os.path.isdir(tmpdir):
87*d83a80eeSchristos      bail('? cannot find "%s"...' % (tmpdir))
88*d83a80eeSchristos
89*d83a80eeSchristos   #-- this feels a bit dodgy due to issues in pexpect when it goes
90*d83a80eeSchristos   #   up against passwd and other such nastiness from scp/ssh -- all
91*d83a80eeSchristos   #   we should have to do is child.wait() really, but that does not
92*d83a80eeSchristos   #   work as it should.
93*d83a80eeSchristos   #
94*d83a80eeSchristos   #   NB: it turn out that you can _not_ put a '*' at the end of the
95*d83a80eeSchristos   #   source path; pexpect.spawn() screws up and the parsing of the string
96*d83a80eeSchristos   #   and ends up ignoring everything up to the '*', meaning the command
97*d83a80eeSchristos   #   does not have the 'scp' part in it and does not get executed properly.
98*d83a80eeSchristos   #
99*d83a80eeSchristos   pwd = os.getcwd()
100*d83a80eeSchristos   os.chdir(tmpdir)
101*d83a80eeSchristos   #-- this is what i wanted to do...
102*d83a80eeSchristos   #cmd = 'scp -r ' + tmpdir + ' dns@' + conf.getValue('dest-ip') + ':'
103*d83a80eeSchristos   #-- this is what works...
104*d83a80eeSchristos   flist = os.listdir('.')
105*d83a80eeSchristos   fnames = ' '.join(flist)
106*d83a80eeSchristos   cmd  = 'scp -r ' + fnames
107*d83a80eeSchristos   cmd += ' ' + conf.getValue('destuser') + '@'
108*d83a80eeSchristos   cmd += conf.getValue('dest-ip') + ':'
109*d83a80eeSchristos   report_info('=> ' + cmd)
110*d83a80eeSchristos   child = pexpect.spawn(cmd)
111*d83a80eeSchristos   if len(conf.getValue('dnspw')) > 0:
112*d83a80eeSchristos      child.expect('.*ssword:')
113*d83a80eeSchristos      child.sendline(conf.getValue('dnspw'))
114*d83a80eeSchristos   child.expect('.*' + conf.getValue('nsd_conf') + '.*')
115*d83a80eeSchristos   child.expect(pexpect.EOF)
116*d83a80eeSchristos   child.close()
117*d83a80eeSchristos   os.chdir(pwd)
118*d83a80eeSchristos
119*d83a80eeSchristos   return
120*d83a80eeSchristos
121*d83a80eeSchristosdef cp_files(analyze):
122*d83a80eeSchristos   #-- we assume everything has already been copied to the tmpdir by bind2nsd
123*d83a80eeSchristos
124*d83a80eeSchristos   if analyze:
125*d83a80eeSchristos      return
126*d83a80eeSchristos
127*d83a80eeSchristos   tmpdir = conf.getValue('tmpdir') 	# must have trailing '/'
128*d83a80eeSchristos   if not os.path.exists(tmpdir) and not os.path.isdir(tmpdir):
129*d83a80eeSchristos      bail('? cannot find "%s"...' % (tmpdir))
130*d83a80eeSchristos
131*d83a80eeSchristos   #-- scp the entire tmp directory
132*d83a80eeSchristos   if conf.getValue('DEMO-MODE'):
133*d83a80eeSchristos      report_info('** scp would go here, but cp -r for demonstration purposes')
134*d83a80eeSchristos      cmd = 'cp -r ' + tmpdir + '* ' + conf.getValue('destdir')
135*d83a80eeSchristos      run_cmd(cmd, 'using cp to transfer to demo system...')
136*d83a80eeSchristos   else:
137*d83a80eeSchristos      scp_s64()
138*d83a80eeSchristos
139*d83a80eeSchristos   return
140*d83a80eeSchristos
141*d83a80eeSchristosdef restart_nsd():
142*d83a80eeSchristos   if conf.getValue('DEMO-MODE'):
143*d83a80eeSchristos      cmd = conf.getValue('stop_cmd')
144*d83a80eeSchristos      run_cmd(cmd, 'stopping nsd...')
145*d83a80eeSchristos
146*d83a80eeSchristos      # BOZO: rebuild is not behaving when there are errors, so the hack is
147*d83a80eeSchristos      # to remove the existing db, run the zone compiler and restart nsd
148*d83a80eeSchristos      #cmd = conf.getValue('rebuild_cmd')
149*d83a80eeSchristos      #os.system(cmd)
150*d83a80eeSchristos      cmd = 'rm -f ' + conf.getValue('database')
151*d83a80eeSchristos      run_cmd(cmd, 'removing old zonedb...')
152*d83a80eeSchristos      cmd = conf.getValue('zonec_cmd')
153*d83a80eeSchristos      run_cmd(cmd, 'rebuilding zonedb...')
154*d83a80eeSchristos
155*d83a80eeSchristos      cmd = conf.getValue('start_cmd')
156*d83a80eeSchristos      run_cmd(cmd, 'starting nsd...')
157*d83a80eeSchristos   else:
158*d83a80eeSchristos      cmd = 'ssh -a -x dns@' + conf.getValue('dest-ip')
159*d83a80eeSchristos      child = pexpect.spawn(cmd)
160*d83a80eeSchristos      if not child.isalive():
161*d83a80eeSchristos         bail('? cannot login to Secure64 system at %s' % \
162*d83a80eeSchristos	      (conf.getValue('dest-ip')))
163*d83a80eeSchristos      else:
164*d83a80eeSchristos         report_info('=> restarting Secure64 NSD on %s' % \
165*d83a80eeSchristos	             (conf.getValue('dest-ip')))
166*d83a80eeSchristos	 child.expect('.*ssword:')
167*d83a80eeSchristos	 child.sendline(conf.getValue('syspw'))
168*d83a80eeSchristos	 child.expect('\[view@.*> ')
169*d83a80eeSchristos	 report_info('=> now logged in')
170*d83a80eeSchristos	 child.sendline('enable dnsconfig')
171*d83a80eeSchristos	 child.expect('.*ssword:')
172*d83a80eeSchristos	 child.sendline(conf.getValue('dnspw'))
173*d83a80eeSchristos	 child.expect('\*.*')
174*d83a80eeSchristos	 report_info('=> issuing zonec')
175*d83a80eeSchristos	 child.sendline('zonec')
176*d83a80eeSchristos	 if isVerbose():
177*d83a80eeSchristos	    child.logfile = sys.stdout
178*d83a80eeSchristos	 child.expect('\[dnsconfig@.*# ')
179*d83a80eeSchristos	 report_info('=> issuing stop')
180*d83a80eeSchristos	 child.sendline('stop')
181*d83a80eeSchristos	 child.expect('\[dnsconfig@.*# ')
182*d83a80eeSchristos	 report_info('=> issuing start')
183*d83a80eeSchristos	 child.sendline('start')
184*d83a80eeSchristos	 child.expect('\[dnsconfig@.*# ')
185*d83a80eeSchristos	 child.sendline('exit')
186*d83a80eeSchristos	 child.expect('\[view@.*> ')
187*d83a80eeSchristos	 child.sendline('exit')
188*d83a80eeSchristos	 child.close()
189*d83a80eeSchristos         report_info('=> restart done')
190*d83a80eeSchristos
191*d83a80eeSchristos   return
192*d83a80eeSchristos
193*d83a80eeSchristosdef quick_parse():
194*d83a80eeSchristos   #-- build an in-core representation of the named.conf file
195*d83a80eeSchristos   named_root  = conf.getValue('named_root')
196*d83a80eeSchristos   named_fname = conf.getValue('named_conf')
197*d83a80eeSchristos   report_info('=> parsing named.conf file \"%s\"...' % (named_fname))
198*d83a80eeSchristos
199*d83a80eeSchristos   pwd = os.getcwd()
200*d83a80eeSchristos   if os.path.exists(named_root) and os.path.isdir(named_root):
201*d83a80eeSchristos      os.chdir(named_root)
202*d83a80eeSchristos   else:
203*d83a80eeSchristos      bail('? cannot find the named root directory "%s"' % (named_root))
204*d83a80eeSchristos   named = NamedConf(named_fname)
205*d83a80eeSchristos   os.chdir(pwd)
206*d83a80eeSchristos
207*d83a80eeSchristos   return named
208*d83a80eeSchristos
209*d83a80eeSchristosdef run_named_check(named):
210*d83a80eeSchristos   #-- run named-checkconf on the config file and then run named-checkzone
211*d83a80eeSchristos   #   on each zone file
212*d83a80eeSchristos   chkconf = conf.getValue('named-checkconf')
213*d83a80eeSchristos   if os.path.exists(chkconf):
214*d83a80eeSchristos      fname = conf.getValue('named_root')
215*d83a80eeSchristos      fname += '/' + conf.getValue('named_conf')
216*d83a80eeSchristos      report_info('=> running "%s" on "%s"...' % (chkconf, fname))
217*d83a80eeSchristos      (output, errors) = run_cmd_capture(chkconf + ' ' + fname)
218*d83a80eeSchristos      if len(errors) > 0:
219*d83a80eeSchristos         report_info('? errors found --->')
220*d83a80eeSchristos	 report_info(errors)
221*d83a80eeSchristos      else:
222*d83a80eeSchristos         report_info('   all is well.')
223*d83a80eeSchristos   else:
224*d83a80eeSchristos      report_error("? wanted to run named-checkconf, dude, but it's not there.")
225*d83a80eeSchristos
226*d83a80eeSchristos   chkzone = conf.getValue('named-checkzone')
227*d83a80eeSchristos   if os.path.exists(chkzone):
228*d83a80eeSchristos      zdict = named.getZones()
229*d83a80eeSchristos      zlist = zdict.keys()
230*d83a80eeSchristos      zlist.sort()
231*d83a80eeSchristos      rname = named.getOptions().getDirectory().replace('"','')
232*d83a80eeSchristos      report_info('=> running "%s" on all zones...' % (chkzone))
233*d83a80eeSchristos      prog = re.compile(':[0-9][0-9]*:')
234*d83a80eeSchristos      for ii in zlist:
235*d83a80eeSchristos         zone = zdict[ii].getName()
236*d83a80eeSchristos	 zfile = rname + '/' + zdict[ii].getFile()
237*d83a80eeSchristos         (output, errors) = run_cmd_capture(chkzone + ' ' + zone + ' ' + zfile)
238*d83a80eeSchristos         if len(output) > 0 and prog.search(output) != None:
239*d83a80eeSchristos	    report_info(output.strip())
240*d83a80eeSchristos   else:
241*d83a80eeSchristos      report_error("? wanted to run named-checkzone, dude, but it's not there.")
242*d83a80eeSchristos
243*d83a80eeSchristos   return
244*d83a80eeSchristos
245*d83a80eeSchristosdef run_zonec():
246*d83a80eeSchristos   zonec = conf.getValue('zonec_cmd')
247*d83a80eeSchristos   if os.path.exists(zonec):
248*d83a80eeSchristos      report_info('=> running the zone compiler "%s"...' % (zonec))
249*d83a80eeSchristos      fname = conf.getValue('nsd_conf')
250*d83a80eeSchristos      tmpdir = conf.getValue('tmpdir')
251*d83a80eeSchristos      cmd = zonec + ' -c ' + tmpdir + '/' + fname + ' -d ' + tmpdir
252*d83a80eeSchristos      cmd += ' -f ' + tmpdir + '/zone.db'
253*d83a80eeSchristos      os.system('rm -f ' + tmpdir + '/zone.db')
254*d83a80eeSchristos      (output, errors) = run_cmd_capture(cmd)
255*d83a80eeSchristos      if len(errors) > 0:
256*d83a80eeSchristos         report_info('? errors found --->')
257*d83a80eeSchristos	 report_info(errors)
258*d83a80eeSchristos      else:
259*d83a80eeSchristos         report_info('   all is well.')
260*d83a80eeSchristos   else:
261*d83a80eeSchristos      report_error("? hmph.  wanted to run zonec, but it's not there.")
262*d83a80eeSchristos
263*d83a80eeSchristos   return
264*d83a80eeSchristos
265*d83a80eeSchristos
266*d83a80eeSchristos#-- main ---------------------------------------------------------------
267*d83a80eeSchristosdef main():
268*d83a80eeSchristos
269*d83a80eeSchristos   try:
270*d83a80eeSchristos      opts, args = getopt.getopt(sys.argv[1:],
271*d83a80eeSchristos                                 'ahnsv',
272*d83a80eeSchristos				 ['analyze-only', 'help', 'now', 'sync-only',
273*d83a80eeSchristos				  'verbose']
274*d83a80eeSchristos				)
275*d83a80eeSchristos   except getopt.GetoptError:
276*d83a80eeSchristos      usage()
277*d83a80eeSchristos      sys.exit(1)
278*d83a80eeSchristos
279*d83a80eeSchristos   now = False
280*d83a80eeSchristos   analyze_only = False
281*d83a80eeSchristos   sync_only = False
282*d83a80eeSchristos   for ii, val in opts:
283*d83a80eeSchristos      if ii in ('-a', '--analyze-only'):
284*d83a80eeSchristos         analyze_only = True
285*d83a80eeSchristos      if ii in ('-h', '--help'):
286*d83a80eeSchristos         usage()
287*d83a80eeSchristos	 sys.exit(0)
288*d83a80eeSchristos      if ii in ('-n', '--now'):
289*d83a80eeSchristos         now = True
290*d83a80eeSchristos      if ii in ('-s', '--sync-only'):
291*d83a80eeSchristos         sync_only = True
292*d83a80eeSchristos      if ii in ('-v', '--verbose'):
293*d83a80eeSchristos         set_verbosity(True)
294*d83a80eeSchristos
295*d83a80eeSchristos   last_stat = {}
296*d83a80eeSchristos   this_stat = {}
297*d83a80eeSchristos
298*d83a80eeSchristos   #-- don't poll unless we need to...
299*d83a80eeSchristos   if now:
300*d83a80eeSchristos      rebuild_nsd_files()
301*d83a80eeSchristos      cp_files(analyze_only)
302*d83a80eeSchristos      restart_nsd()
303*d83a80eeSchristos      sys.exit(0)
304*d83a80eeSchristos
305*d83a80eeSchristos   #-- ...and don't poll if we just need to sync up to the Secure64 machine...
306*d83a80eeSchristos   if sync_only:
307*d83a80eeSchristos      cp_files(analyze_only)
308*d83a80eeSchristos      restart_nsd()
309*d83a80eeSchristos      sys.exit(0)
310*d83a80eeSchristos
311*d83a80eeSchristos   #-- ...and don't poll if we're just checking things out...
312*d83a80eeSchristos   if analyze_only:
313*d83a80eeSchristos      #-- well, and do a couple of extra things, too
314*d83a80eeSchristos      set_verbosity(True)
315*d83a80eeSchristos      report_info( \
316*d83a80eeSchristos            's64-sync %s, copyright(c) 2007, Secure64 Software Corporation' \
317*d83a80eeSchristos            % (conf.getValue('version')))
318*d83a80eeSchristos      named = quick_parse()
319*d83a80eeSchristos      rebuild_nsd_files()
320*d83a80eeSchristos      run_named_check(named)
321*d83a80eeSchristos      cp_files(analyze_only)
322*d83a80eeSchristos      run_zonec()
323*d83a80eeSchristos      sys.exit(0)
324*d83a80eeSchristos
325*d83a80eeSchristos   #-- apparently we need to poll...
326*d83a80eeSchristos   tmplist = conf.getValue('named_watchlist').split()
327*d83a80eeSchristos   watchlist = []
328*d83a80eeSchristos   for ii in tmplist:
329*d83a80eeSchristos      watchlist.append(ii.strip())
330*d83a80eeSchristos   while True:
331*d83a80eeSchristos      for ii in watchlist:
332*d83a80eeSchristos         if ii in last_stat.keys():
333*d83a80eeSchristos            statinfo = os.stat(ii)
334*d83a80eeSchristos            this_stat[ii] = (statinfo.st_size, statinfo.st_mtime)
335*d83a80eeSchristos            (old_size, old_mtime) = last_stat[ii]
336*d83a80eeSchristos            (new_size, new_mtime) = this_stat[ii]
337*d83a80eeSchristos            if old_size != new_size or old_mtime != new_mtime:
338*d83a80eeSchristos               report_info('aha! "%s" has changed!' % (ii))
339*d83a80eeSchristos	       last_stat[ii] = (new_size, new_mtime)
340*d83a80eeSchristos	       rebuild_nsd_files()
341*d83a80eeSchristos               cp_files(analyze_only)
342*d83a80eeSchristos	       restart_nsd()
343*d83a80eeSchristos         else:
344*d83a80eeSchristos            statinfo = os.stat(ii)
345*d83a80eeSchristos            last_stat[ii] = (statinfo.st_size, statinfo.st_mtime)
346*d83a80eeSchristos            this_stat[ii] = last_stat[ii]
347*d83a80eeSchristos
348*d83a80eeSchristos      time.sleep(int(conf.getValue('sleep_time')))
349*d83a80eeSchristos
350*d83a80eeSchristos   sys.exit(0)
351*d83a80eeSchristos
352*d83a80eeSchristos#-- just in case
353*d83a80eeSchristosif __name__ == '__main__':
354*d83a80eeSchristos   main()
355