Source code for roster_user_tools.cli_common_lib

#!/usr/bin/python

# Copyright (c) 2009, Purdue University
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# Neither the name of the Purdue University nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Common library for DNS management command line tools."""

__copyright__ = 'Copyright (C) 2009, Purdue University'
__license__ = 'BSD'
__version__ = '#TRUNK#'


import ConfigParser
import sys
import os
import roster_client_lib

[docs]class ArgumentError(Exception): pass
[docs]class HostsError(Exception): pass
DEFAULT_CRED_FILE = '~/.dnscred'
[docs]class CliCommonLib: """Command line common library""" def __init__(self, options): """Retrieves configuration Inputs: options: Options object from optparse """ self.options = options self.config_file = ConfigParser.SafeConfigParser() if( hasattr(options, 'config_file') and options.config_file is not None ): config_file = self.options.config_file self.config_file.read(config_file) if( hasattr(self.options, 'server') ): if( not self.options.server ): self.options.server = self.config_file.get('user_tools', 'server') if( hasattr(self.options, 'credfile') ): if( not options.credfile ): self.options.credfile = self.config_file.get('user_tools', 'cred_file') else: config_file = '' if( 'ROSTER_USER_CONFIG' in os.environ ): file_locations = [os.environ['ROSTER_USER_CONFIG']] else: file_locations = [] file_locations.extend([os.path.expanduser('~/.rosterrc'), '/etc/roster/roster_user_tools.conf']) for config_file in file_locations: if( os.path.exists(config_file) ): self.config_file.read(config_file) if( hasattr(self.options, 'server') ): if( not self.options.server ): self.options.server = self.config_file.get('user_tools', 'server') if( hasattr(self.options, 'credfile') ): if( not options.credfile ): self.options.credfile = self.config_file.get('user_tools', 'cred_file') break else: if( hasattr(self.options, 'server') ): if( not self.options.server ): raise ArgumentError('A server must be specified.') if( hasattr(self.options, 'credfile') ): if( not self.options.credfile ): self.options.credfile = os.path.expanduser(DEFAULT_CRED_FILE) roster_client_lib.CheckServerVersionMatch(self.options.server) roster_client_lib.CheckCredentials( self.options.username, self.options.credfile, self.options.server, password=self.options.password)
[docs] def DisallowFlags(self, disallow_list, parser): """Dissallows certain command line flags. Inputs: disallow_list: list of command line flags to block parser: parser object from optparse """ defaults = parser.defaults error = False for flag in parser.option_list[1:]: combo = 'self.options.%s' % flag.dest if( flag.dest in disallow_list ): if( eval(combo) != defaults[flag.dest] ): self.DnsError('The %s flag cannot be used.' % flag, 0) error = True if( error ): sys.exit(1) ## Function accessors, need to be removed at some point
[docs]def SortRecordsDict(records_dictionary, view_name): """Retries records from database and sorts them Inputs: records_dictionary: dictionary of records from core view_name: string of view name Outputs: dict: sorted dictionary of records """ sorted_records = {} for ip_address in records_dictionary[view_name]: for record in records_dictionary[view_name][ip_address]: record['view'] = view_name if( ip_address not in sorted_records ): sorted_records[ip_address] = {'forward': [], 'reverse': []} if( record['forward'] ): sorted_records[ip_address]['forward'].append(record) else: sorted_records[ip_address]['reverse'].append(record) return sorted_records
[docs]def DnsError(message, exit_status=0): """Prints standardized client error message to screen. Inputs: message: string of message to be displayed on screen exit_status: integer of retrun code, assumed not exit if 0 """ print "CLIENT ERROR: %s" % message if( exit_status ): sys.exit(exit_status)
[docs]def ServerError(message, uuid_string, exit_status=0): """Prints standardized server error message to screen. Inputs: message: string of message to be displayed on screen exit_status: integer of retrun code, assumed not exit if 0 """ print "SERVER ERROR: (%s) %s" % (uuid_string, message) if( exit_status ): sys.exit(exit_status)
[docs]def UserError(message, exit_status=0): """Prints standardized user error message to screen. Inputs: message: string of message to be displayed on screen exit_status: integer of retrun code, assumed not exit if 0 """ print "USER ERROR: %s" % (message) if( exit_status ): sys.exit(exit_status)
[docs]def UnknownError(error_class, uuid_string, message, exit_status=0): """Prints standardized unknown error message to screen. Inputs: message: string of message to be displayed on screen exit_status: integer of retrun code, assumed not exit if 0 """ print "UNKNOWN ERROR(%s): (%s) %s" % (error_class, uuid_string, message) if( exit_status ): sys.exit(exit_status)
[docs]def DnsWarning(message): """Prints standardized warning message to screen. Inputs: message: string of message to be displayed on screen """ print "WARNING: %s" % message
[docs]def PrintColumns(print_list, first_line_header=False): """Prints a table with aligned columns. Inputs: print_list: list of lists of columns file: string of filename """ ## Construct zeros for lengths lengths = [] if( print_list ): for column in print_list[0]: lengths.append(0) ## Get sizes of strings for row in print_list: for column_index, column in enumerate(row): if( len(str(column)) > lengths[column_index] ): lengths[column_index] = len(str(column)) ## Construct string print_string_list = [] for row in print_list: string_list = [] string_args_list = [] for column_index, column in enumerate(print_list[0]): string_list.append('%*s') string_args_list.extend([lengths[column_index] * -1, row[column_index]]) print_line = ' '.join(string_list) % tuple(string_args_list) print_string_list.append('%s\n' % print_line.strip()) if( first_line_header and (print_list.index(row) == 0) ): hyphen_list = [] for _ in range(len(print_line.strip())): hyphen_list.append('-') print_string_list.append('%s\n' % ''.join(hyphen_list)) return ''.join(print_string_list)
[docs]def PrintSortedHosts(hosts_dict, print_headers=True): """Prints sorted hosts in a nice usable format Inputs: hosts_dict: A dictionary of sort host lists keyed by views. """ print_list = [] for view in hosts_dict: if( print_headers ): print_list.append(['View:', view, '', '', '']) for host in hosts_dict[view]: print_list.append([host['ip_address'], host['direction'], host['host'], host['record_zone_name'], view]) return PrintColumns(print_list)
[docs]def PrintHosts(records_dictionary, ip_address_list, view_name=None): """Prints hosts in an /etc/hosts format Inputs: records_dictionary: dictionary of records ip_address_list: list of ip_addresses view_name: string of view_name """ print_list = [] sorted_records = SortRecordsDict(records_dictionary, view_name) for ip_address in ip_address_list: if( ip_address not in sorted_records ): print_list.append(['#%s' % ip_address, '', '', '']) continue if( len(sorted_records[ip_address]['reverse']) > 1 ): raise HostsError( 'Multiple reverse records found for %s' % ( str(sorted_records[ip_address]))) elif( len(sorted_records[ip_address]['reverse']) == 0 ): for record in sorted_records[ip_address]['forward']: forward_zone_origin = record['zone_origin'].rstrip('.') shorthost = record['host'].rsplit('.%s' % forward_zone_origin, 1)[0] longhost = record['host'] if( longhost.startswith('@.') ): longhost = record['host'].lstrip('@.') print_list.append(['#%s' % ip_address, longhost, shorthost, '# No reverse assignment']) else: reverse_record = sorted_records[ip_address]['reverse'][0] for record_number, record in enumerate( sorted_records[ip_address]['forward']): if( record['host'].replace('@.', '') == reverse_record['host'].replace('@.', '') ): forward_zone_origin = record['zone_origin'].rstrip('.') shorthost = record['host'].rsplit('.%s' % forward_zone_origin, 1)[0] longhost = record['host'] if( longhost.startswith('@.') ): longhost = record['host'].lstrip('@.') print_list.append([ip_address, longhost, shorthost, '']) sorted_records[ip_address]['forward'].pop(record_number) break else: print_list.append(['#%s' % ip_address, reverse_record['host'], '', '# No forward assignment']) for record in sorted_records[ip_address]['forward']: forward_zone_origin = record['zone_origin'].rstrip('.') shorthost = record['host'].rsplit('.%s' % forward_zone_origin, 1)[0] longhost = record['host'] if( longhost.startswith('@.') ): longhost = record['host'].lstrip('@.') print_list.append(['#%s' % ip_address, longhost, shorthost, '# No reverse assignment']) return PrintColumns(print_list)
[docs]def EditFile(fname): """Opens a file in a text editor in the EDITOR env variable for editing Inputs: fname: string of filename Outputs: int: return code from editor """ if( 'EDITOR' not in os.environ ): DnsError('EDITOR environment variable not set.', 1) closenum = os.system('%s %s' % (os.environ['EDITOR'], fname)) if( closenum is None ): return_code = 0 else: return_code = os.WEXITSTATUS(closenum) if( return_code != 0 ): DnsError('Error editing file.', 1) return return_code