1#!/usr/bin/env python
2# Copyright 2009 Google Inc. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
17"""Simple DNS server comparison benchmarking tool.
18
19Designed to assist system administrators in selection and prioritization.
20"""
21
22__author__ = 'tstromberg@google.com (Thomas Stromberg)'
23
24import datetime
25import math
26import sys
27
28import base_ui
29import conn_quality
30import nameserver_list
31
32
33class NameBenchCli(base_ui.BaseUI):
34  """A command-line implementation of the namebench workflow."""
35
36  def __init__(self, options, supplied_ns, global_ns, regional_ns, version=None):
37
38    self.options = options
39    self.supplied_ns = supplied_ns
40    self.global_ns = global_ns
41    self.include_internal = True
42    self.regional_ns = regional_ns
43    self.version = version
44    self.last_msg = (None, None, None, None)
45    self.last_msg_count_posted = 0
46    self.preferred = []
47    self.secondary = []
48    super(NameBenchCli, self).__init__()
49
50  def UpdateStatus(self, msg, count=None, total=None, error=False, debug=False):
51    """Status updates for the command-line. A lot of voodoo here."""
52    if self.last_msg == (msg, count, total, error):
53      return None
54
55    if debug:
56      return None
57
58    if error:
59      print
60      print '* ERROR: %s' % msg
61      sys.exit(2)
62    elif not total:
63      self.last_msg_count_posted = 0
64      sys.stdout.write('- %s\n' % msg)
65    elif self.last_msg[0] != msg:
66      self.last_msg_count_posted = 0
67      sys.stdout.write('- %s: %s/%s' % (msg, count, total))
68      last_count = 0
69    else:
70      last_count = self.last_msg[1]
71
72    if total:
73      if count and (count - last_count > 0):
74        # Write a few dots to catch up to where we should be.
75        catch_up = int(math.ceil((count - last_count) / 2.0))
76        sys.stdout.write('.' * catch_up)
77
78      if count == total:
79        sys.stdout.write('%s/%s\n' % (count, total))
80      elif total > 25 and count and (count - self.last_msg_count_posted > (total * 0.20)):
81        sys.stdout.write(str(count))
82        self.last_msg_count_posted = count
83    sys.stdout.flush()
84    self.last_msg = (msg, count, total, error)
85
86  def PrepareNameServers(self):
87    super(NameBenchCli, self).PrepareNameServers()
88    print ''
89    print 'Final list of nameservers considered:'
90    print '-' * 78
91    for n in self.nameservers.SortByFastest():
92      print '%-15.15s %-18.18s %-4.0fms | %s' % (n.ip, n.name, n.check_average,
93                                                 n.warnings_string)
94    print ''
95
96  def RunAndOpenReports(self):
97    self.RunBenchmark()
98    print "\n%s\n" % self.reporter.CreateReport(format='ascii')
99    self.CreateReports()
100    if self.options.open_webbrowser:
101      self.DisplayHtmlReport()
102
103  def Execute(self):
104    """Called by namebench.py to start the show."""
105    print('namebench %s - %s (%s) on %s' %
106          (self.version, self.options.input_source or 'best source',
107           self.options.select_mode, datetime.datetime.now()))
108    print ('threads=%s/%s queries=%s runs=%s timeout=%s health_timeout=%s servers=%s' %
109           (self.options.health_thread_count, self.options.benchmark_thread_count,
110            self.options.query_count,
111            self.options.run_count, self.options.timeout,
112            self.options.health_timeout, self.options.num_servers))
113    print '-' * 78
114
115    if self.options.only:
116      if not self.supplied_ns:
117        print 'If you use --only, you must provide nameservers to use.'
118        sys.exit(1)
119      self.include_internal = False
120
121    try:
122      self.LoadDataSources()
123      self.PrepareTestRecords()
124      print ''
125      self.PrepareNameServers()
126      self.PrepareBenchmark()
127      self.RunAndOpenReports()
128    except (nameserver_list.OutgoingUdpInterception,
129            nameserver_list.TooFewNameservers,
130            conn_quality.OfflineConnection):
131      (exc_type, exception) = sys.exc_info()[0:2]
132      self.UpdateStatus("%s - %s" % (exc_type, exception), error=True)
133
134
135
136