#!/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.
"""Core helper functions."""
__copyright__ = 'Copyright (C) 2009, Purdue University'
__license__ = 'BSD'
__version__ = '#TRUNK#'
import constants
import errors
import helpers_lib
import datetime
import dns.zone
import IPy
[docs]class CoreHelpers(object):
"""Library of helper functions that extend the core functions."""
def __init__(self, core_instance):
"""Sets up core instance
Inputs:
core_instance: instance of RosterCore
"""
self.core_instance = core_instance
self.db_instance = core_instance.db_instance
self.user_instance = core_instance.user_instance
self.log_instance = core_instance.log_instance
### These functions just expose helpers_lib functions for the
### XML-RPC server. For doc strings see helpers_lib
[docs] def ListGroupPermissions(self):
return self.db_instance.data_validation_instance.ListGroupPermissions()
[docs] def ReverseIP(self, ip_address):
return helpers_lib.ReverseIP(ip_address)
[docs] def UnReverseIP(self, ip_address):
return helpers_lib.UnReverseIP(ip_address)
[docs] def CIDRExpand(self, cidr_block, begin=None, end=None):
return helpers_lib.CIDRExpand(cidr_block, begin, end)
[docs] def ExpandIPV6(self, ip_address):
return helpers_lib.ExpandIPV6(ip_address)
[docs] def GetViewsByUser(self, username):
"""Lists view names available to given username
Inputs:
username: string of user name
Outputs:
list: list of view name strings
"""
function_name, current_args = helpers_lib.GetFunctionNameAndArgs()
self.user_instance.Authorize(function_name)
views = set([])
success = False
users_dict = self.db_instance.GetEmptyRowDict('users')
users_dict['user_name'] = username
user_group_assignments_dict = self.db_instance.GetEmptyRowDict(
'user_group_assignments')
groups_dict = self.db_instance.GetEmptyRowDict('groups')
forward_zone_permissions_dict = self.db_instance.GetEmptyRowDict(
'forward_zone_permissions')
zones_dict = self.db_instance.GetEmptyRowDict('zones')
zone_view_assignments_dict = self.db_instance.GetEmptyRowDict(
'zone_view_assignments')
self.db_instance.StartTransaction()
try:
joined_list = self.db_instance.ListRow(
'users', users_dict, 'user_group_assignments',
user_group_assignments_dict,
'groups', groups_dict, 'forward_zone_permissions',
forward_zone_permissions_dict, 'zones', zones_dict,
'zone_view_assignments', zone_view_assignments_dict)
finally:
self.db_instance.EndTransaction()
for view_dict in joined_list:
views.add(view_dict['zone_view_assignments_view_dependency'].split(
'_dep')[0])
success = True
return views
def _FixHostname(self, host_name, origin):
"""Checks name and returns fqdn.
Inputs:
host_name: string of host name
origin: string of the zone origin
Outputs:
string of fully qualified domain name
"""
if( host_name == u'@' ):
host_name = origin
elif( not host_name.endswith('.') ):
host_name = '%s.%s' % (host_name, origin)
return unicode(host_name)
[docs] def GetCIDRBlocksByView(self, view, username):
"""Lists CIDR blocks available to a username in a given view
Inputs:
view: string of view name
username: string of user name
Outputs:
list: list of cidr block strings
"""
self.user_instance.Authorize('GetCIDRBlocksByView')
cidrs = set([])
users_dict = self.db_instance.GetEmptyRowDict('users')
users_dict['user_name'] = username
views_dict = self.db_instance.GetEmptyRowDict('views')
views_dict['view_name'] = view
user_group_assignments_dict = self.db_instance.GetEmptyRowDict(
'user_group_assignments')
groups_dict = self.db_instance.GetEmptyRowDict('groups')
reverse_range_permissions_dict = self.db_instance.GetEmptyRowDict(
'reverse_range_permissions')
zones_dict = self.db_instance.GetEmptyRowDict('zones')
reverse_range_zone_assignments_dict = self.db_instance.GetEmptyRowDict(
'reverse_range_zone_assignments')
zone_view_assignments_dict = self.db_instance.GetEmptyRowDict(
'zone_view_assignments')
self.db_instance.StartTransaction()
try:
joined_list = self.db_instance.ListRow(
'views', views_dict,
'users', users_dict, 'user_group_assignments',
user_group_assignments_dict,
'groups', groups_dict, 'reverse_range_permissions',
reverse_range_permissions_dict, 'zones', zones_dict,
'zone_view_assignments', zone_view_assignments_dict,
'reverse_range_zone_assignments', reverse_range_zone_assignments_dict)
finally:
self.db_instance.EndTransaction()
for cidr_dict in joined_list:
cidrs.add(cidr_dict['reverse_range_zone_assignments_cidr_block'])
return cidrs
[docs] def GetAssociatedCNAMEs(self, hostname, view_name, zone_name,
recursive=False):
"""Lists cname's by assignment hostname.
Inputs:
hostname: string of hostname
view_name: string of view name
zone_name: string of zone name
Outputs:
list: list of found cname dictionaries
"""
self.user_instance.Authorize('GetAssociatedCNAMEs')
record_arguments_record_assignments_dict = (
self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments'))
zone_view_assignments_dict = self.db_instance.GetEmptyRowDict(
'zone_view_assignments')
zone_view_assignments_dict['zone_view_assignments_zone_name'] = unicode(
zone_name)
zone_view_assignments_dict['zone_view_assignments_view_dependency'] = (
unicode('%s_dep' % view_name))
record_arguments_record_assignments_dict[
'record_arguments_records_assignments_type'] = u'cname'
record_arguments_record_assignments_dict[
'argument_value'] = hostname
records_dict = self.db_instance.GetEmptyRowDict(
'records')
records_dict['record_type'] = u'cname'
records_dict['record_view_dependency'] = '%s_dep' % view_name
records_dict['record_zone_name'] = zone_name
self.db_instance.StartTransaction()
try:
found_records = self.db_instance.ListRow(
'records', records_dict,
'record_arguments_records_assignments',
record_arguments_record_assignments_dict,
'zone_view_assignments', zone_view_assignments_dict)
finally:
self.db_instance.EndTransaction()
cnames = []
for record in found_records:
new_record = {}
new_record['record_type'] = record[
'record_arguments_records_assignments_type']
new_record['zone_name'] = record['record_zone_name']
new_record['target'] = record['record_target']
new_record['ttl'] = record['record_ttl']
new_record['view_name'] = record[
'record_view_dependency'].rsplit('_dep')[0]
new_record['assignment_host'] = record['argument_value']
new_record['last_user'] = record['record_last_user']
new_record['zone_origin'] = record['zone_origin']
cnames.append(new_record)
if( not recursive ):
return cnames
new_cnames = []
for record in cnames:
new_cnames.extend(self.GetAssociatedCNAMEs(
'%s.%s' % (record['target'], record['zone_origin']),
record['view_name'], record['zone_name'], recursive=recursive))
cnames.extend(new_cnames)
del new_cnames
del found_records
return cnames
[docs] def ListLatestNamedConfig(self, dns_server_set):
"""Lists the latest named config string given dns server set
This function is duplicated in
roster-config-manager/roster_config_manager/tree_exporter.py
Inputs:
dns_server_set: string of dns server set name
Outputs:
dict: dictionary of latest named config
"""
named_configs = self.core_instance.ListNamedConfGlobalOptions(
dns_server_set=dns_server_set)
current_timestamp = datetime.datetime.now()
smallest_time_differential = datetime.timedelta(weeks=100000)
newest_config = None
for named_config in named_configs:
time_differential = current_timestamp - named_config['timestamp']
if( time_differential < smallest_time_differential ):
smallest_time_differential = time_differential
newest_config = named_config
return newest_config
[docs] def RevertNamedConfig(self, dns_server_set, option_id):
"""Revert a Named Config file
Inputs:
option_id: the id of config to replicate
dns_server_set: string of dns server set name
Raises:
InvalidInputError: DNS server set does not contain id.
UnexpectedDataError: Multiple configurations found.
"""
named_config = self.core_instance.ListNamedConfGlobalOptions(
dns_server_set=dns_server_set, option_id=option_id)
if( len(named_config) == 0 ):
raise errors.InvalidInputError(
'DNS server set "%s" does not contain id "%s"' % (
dns_server_set, option_id))
elif( len(named_config) == 1 ):
self.core_instance.MakeNamedConfGlobalOption(
dns_server_set, named_config[0]['options'])
else:
raise errors.UnexpectedDataError('Multiple configurations found.')
[docs] def MakeAAAARecord(self, target, zone_name, record_args_dict,
view_name=None, ttl=None):
"""Makes an AAAA record.
Inputs:
target: string of target
zone_name: string of zone name
record_args_dict: dictionary of record arguments
view_name: string of view name
ttl: time to live
"""
record_args_dict['assignment_ip'] = unicode(IPy.IP(record_args_dict[
'assignment_ip']).strFullsize())
self.core_instance.MakeRecord(u'aaaa', target, zone_name, record_args_dict,
view_name, ttl)
[docs] def MakeSubdomainDelegation(self, zone_name, subdomain_name, nameserver,
view_name=u'any'):
""""Makes a Delegated Subdomain
Assumes delegation zone is created
Inputs:
view_name: string of view name
zone_name: string of zone name
subdomain_name: string of subdomain name
nameserver: string of fully qualified nameserver
Raises:
InvalidInputError: Zone does not exist.
"""
self.core_instance.MakeRecord(u'ns', subdomain_name, zone_name,
{u'name_server':nameserver}, view_name)
[docs] def GetPTRTarget(self, long_target, view_name=u'any'):
"""Gets the short PTR target given the long PTR target
Inputs:
long_target: String of long PTR target
view_name: String of view name
Raises:
InvalidInputError: No suitable reverse range zone assignments found.
Outputs:
string: String of short PTR target
"""
if( not long_target.endswith('in-addr.arpa.') and not
long_target.endswith('ip6.arpa.') ):
long_target = self.ReverseIP(long_target)
zone_assignment = None
reverse_range_zone_assignments = (
self.core_instance.ListReverseRangeZoneAssignments())
ip_address = IPy.IP(self.UnReverseIP(long_target))
for zone_assignment in reverse_range_zone_assignments:
if( zone_assignment in reverse_range_zone_assignments ):
if( ip_address in IPy.IP(
reverse_range_zone_assignments[zone_assignment]) ):
break
else:
raise errors.InvalidInputError(
'No suitable reverse range zone assignments found.')
zone_detail = self.core_instance.ListZones(view_name=view_name)
zone_origin = zone_detail[zone_assignment][view_name]['zone_origin']
# Count number of characters in zone origin, add one to count the extra
# period and remove that number of characters from the target.
zone_origin_length = len(zone_origin) + 1
short_target = long_target[:-zone_origin_length]
return (short_target, zone_assignment)
[docs] def MakePTRRecord(self, target, record_args_dict,
view_name=u'any', ttl=None):
"""Makes a ptr record.
Inputs:
target: string of target
record_args_dict: dictionary of record arguments
view_name: string of view name
ttl: string of ttl
"""
target, zone_assignment = self.GetPTRTarget(target, view_name)
if( record_args_dict['assignment_host'].startswith('@.') ):
record_args_dict['assignment_host'] = record_args_dict[
'assignment_host'].lstrip('@.')
self.core_instance.MakeRecord(u'ptr', target, zone_assignment,
record_args_dict, view_name, ttl)
[docs] def RemovePTRRecord(self, record_type, target, zone_name, record_args_dict,
view_name, ttl=None):
"""Removes a ptr record.
Inputs:
target: string of target
record_args_dict: dictionary of record arguments
view_name: string of view name
ttl: string of ttl
"""
if( record_args_dict['assignment_host'].startswith('@.') ):
record_args_dict['assignment_host'] = record_args_dict[
'assignment_host'].lstrip('@.')
self.core_instance.RemoveRecord(record_type, target, zone_name,
record_args_dict, view_name, ttl)
[docs] def MakeIPv4ClasslessReverseDelegation(self, name_server, cidr_block,
view_name=u'any', ttl=None):
"""Creates classless forwarding for reverse DNS lookups
Inputs:
name_server: nameserver to add for reverse delegation
cidr_block: string of CIDR block
view_name: string of view for the reverse zone, defaults to 'any'
ttl: time-to-live for the newly added records, defaults to zone ttl
Raises:
InvalidInputError: nameserver required
InvalidInputError: cidr block range required
InvalidInputError: Not a valid zone name or CIDR block
"""
view_dep = '%s_dep' % view_name
cidr_octets = cidr_block.split('.')
num_octets = len(cidr_octets)
if( num_octets != 4 ):
raise errors.InvalidInputError('Invalid CIDR octet number')
cidr_block_target = cidr_octets[-1]
broadcast_last_octet = cidr_block_target.split('/')[0]
netmask = cidr_block_target.split('/')[1]
if( str.isdigit(str(broadcast_last_octet)) and str.isdigit(str(netmask))):
if( int(netmask) < 25 or int(netmask) > 31 ):
raise errors.InvalidInputError('Invalid CIDR netmask: %s' % netmask)
if( int(broadcast_last_octet) < 0 or int(broadcast_last_octet) > 255):
raise errors.InvalidInputError('Invalid CIDR last octet')
else:
raise errors.InvalidInputError('Invalid CIDR last octet/netmask')
for i in range(1, len(cidr_octets) - 1):
if( str.isdigit(str(cidr_octets[i])) ):
if( int(cidr_octets[i]) < 0 or int(cidr_octets[i]) > 255):
raise errors.InvalidInputError('Invalid CIDR octet %s' %
cidr_octets[i])
else:
raise errors.InvalidInputError('Invalid CIDR octet %s' %
cidr_octets[i])
cidr_for_ipy = cidr_octets[0]
for i in range(1, num_octets - 1):
cidr_for_ipy = '%s.%s' % (cidr_for_ipy, cidr_octets[i])
cidr_for_ipy = '%s.%s/%s' % (cidr_for_ipy, '0', netmask)
expanded_cidr = self.CIDRExpand(cidr_for_ipy,
begin=long(broadcast_last_octet))
expanded_cidr.remove(expanded_cidr[-1])
zone_name = self.ListZoneByIPAddress(cidr_block)
if( zone_name is None ):
raise errors.InvalidInputError(
'ERROR: zone that includes cidr block %s not found' % cidr_block)
records = self.core_instance.ListRecords(zone_name=zone_name)
for record in records:
for ip in expanded_cidr:
if( self.ReverseIP(ip).split('.')[0] == record['target'] ):
raise errors.InvalidInputError('ERROR: existing record(s) with '
'target: %s overlaps given cidr: %s' %
(record['target'], cidr_block))
records_batch = []
cidr_last_target = int(broadcast_last_octet) + pow(2, 32 - int(netmask))
ns_target = cidr_block_target
ns_args_dict = self.core_instance.GetEmptyRecordArgsDict(u'ns')
ns_args_dict['name_server'] = name_server
ns_record = {'record_type': u'ns', 'record_target': ns_target,
'record_zone_name': zone_name, 'view_name': view_name,
'record_view_dependency': view_dep,
'record_arguments': ns_args_dict}
records_batch.append(ns_record)
for ip in expanded_cidr:
reverse_ip = self.ReverseIP(ip)
split_reverse_ip = reverse_ip.split('.')
target = split_reverse_ip[0]
reverse_ip_for_record = '%s.%s' % (target, cidr_block_target)
for i in range(1, len(split_reverse_ip)):
reverse_ip_for_record = '%s.%s' % (reverse_ip_for_record,
split_reverse_ip[i])
if( target == broadcast_last_octet or target == unicode(
cidr_last_target - 1) ):
continue
cname_args_dict = self.core_instance.GetEmptyRecordArgsDict(u'cname')
cname_args_dict['assignment_host'] = unicode(reverse_ip_for_record)
cname_record = {'record_type': u'cname', 'record_target': unicode(target),
'record_zone_name': zone_name, 'view_name': view_name,
'record_view_dependency': view_dep,
'record_arguments': cname_args_dict}
records_batch.append(cname_record)
self.ProcessRecordsBatch(add_records=records_batch)
[docs] def MakeIPv4ClasslessReverseDelegatedTargetZone(self, cidr_block):
"""Creates a delegated reverse zone
Inputs:
cidr_block: string of IPv4 cidr block
Raises:
InvalidInputError: Not a valid cidr block
"""
cidr_octets = cidr_block.split('.')
cidr_block_target = cidr_octets[-1]
broadcast_last_octet = cidr_block_target.split('/')[0]
netmask = cidr_block_target.split('/')[1]
if( str.isdigit(str(broadcast_last_octet)) and str.isdigit(str(netmask))):
if( int(netmask) < 25 or int(netmask) > 31 or
int(broadcast_last_octet) < 0 or int(broadcast_last_octet) > 255 ):
raise errors.InvalidInputError('Invalid CIDR block')
else:
raise errors.InvalidInputError('Invalid CIDR block')
for i in range(1, len(cidr_octets) - 1):
if( str.isdigit(str(cidr_octets[i])) ):
if( int(cidr_octets[i]) < 0 or int(cidr_octets[i]) > 255):
raise errors.InvalidInputError('Invalid CIDR block')
else:
raise errors.InvalidInputError('Invalid CIDR block')
zone_name = u'in-addr.arpa'
for i in range(0, len(cidr_octets)):
zone_name = u'%s.%s' % (cidr_octets[i], zone_name)
zone_type = u'master'
zone_origin = u'%s.' % zone_name
self.core_instance.MakeZone(zone_name, zone_type, zone_origin)
[docs] def ListAccessLevels(self):
"""Lists access levels from constants for both integer and string keys
Outputs:
dict: dictionary of access levels with both string and integer-string keys
Example:
{'32': 32, '64': 64, '128': 128, 'user': 32, 'unlocked_user': 64,
'dns_admin': 128}
"""
access_levels_dict = {}
for key, value in constants.ACCESS_LEVELS.iteritems():
access_levels_dict[str(value)] = value
access_levels_dict[key] = value
return access_levels_dict
[docs] def ListAvailableIpsInCIDR(self, cidr_block, num_ips=1, view_name=None,
zone_name=None):
"""Finds first available ips. Only lists as many IPs as are available.
Returns empty list if no IPs are available in given cidr block and a
truncated list if only a portion of IPs are available.
Inputs:
cidr_block: string of ipv4 or ipv6 cidr block
Raises:
InvalidInputError: IP is in a reserved IP space.
InvalidInputError: Not a valid cidr block
Outputs:
list: list of strings of ip addresses
"""
try:
cidr_block_ipy = IPy.IP(cidr_block)
except ValueError:
raise errors.InvalidInputError(
'%s is not a valid cidr block' % cidr_block)
reserved_ips = []
if( cidr_block_ipy.version() == 6 ):
reserved = constants.RESERVED_IPV6
elif( cidr_block_ipy.version() == 4 ):
reserved = constants.RESERVED_IPV4
for cidr in reserved:
reserved_ips.append(IPy.IP(cidr))
for reserved_ip in reserved_ips:
if( IPy.IP(cidr_block) in reserved_ip ):
raise errors.InvalidInputError(
'%s is in a reserved IP space' % cidr_block)
records = self.ListRecordsByCIDRBlock(cidr_block, view_name=view_name,
zone_name=zone_name)
taken_ips = []
avail_ips = []
for view in records:
for ip in records[view]:
taken_ips.append(ip)
count = 0L
while( count < cidr_block_ipy.len() ):
if( len(avail_ips) >= num_ips ):
break
if( cidr_block_ipy[count].strFullsize() not in taken_ips ):
avail_ips.append(cidr_block_ipy[count].strFullsize())
count += 1L
return avail_ips
[docs] def ListRecordsByCIDRBlock(self, cidr_block, view_name=None, zone_name=None):
"""Lists records in a given cidr block.
Inputs:
cidr_block: string of ipv4 or ipv6 cidr block
view_name: string of the view
zone_name: string of the zone
Raise:
InvalidInputError: The CIDR block specified does not contain a valid IP
IPIndexError: Record type not indexable by IP
IPIndexError: Record type unknown. Missing ipv4 or ipv6 dec index
Outputs:
dict: A dictionary Keyed by view, keyed by IP, listed by record.
example:
{u'test_view':
{u'192.168.1.8':
[{u'forward': True,
u'host': u'host6.university.edu',
u'zone': u'forward_zone',
u'zone_origin': u'university.edu.'},
{u'forward': False,
u'host': u'host6.university.edu',
u'zone': u'reverse_zone',
u'zone_origin': u'1.168.192.in-addr.arpa.'}]}}
"""
self.user_instance.Authorize('ListRecordsByCIDRBlock')
record_list = {}
try:
IPy.IP(cidr_block)
except ValueError:
raise errors.InvalidInputError(
'The CIDR block specified does not contain a valid IP: %s' % (
cidr_block))
cidr_block = IPy.IP(cidr_block).strFullsize(1)
if( cidr_block.find('/') != -1 ):
cidr_ip = IPy.IP(cidr_block.split('/')[0])
cidr_size = int(cidr_block.split('/')[1])
else:
cidr_ip = IPy.IP(cidr_block)
if( cidr_ip.version() == 4 ):
cidr_size = 32
elif( cidr_ip.version() == 6 ):
cidr_size = 128
else:
raise errors.InvalidInputError(
'The CIDR block specified does not contain a valid IP: %s' % (
cidr_block))
records_dict = self.db_instance.GetEmptyRowDict('records')
zone_view_assignments_dict = self.db_instance.GetEmptyRowDict(
'zone_view_assignments')
zone_dict = self.db_instance.GetEmptyRowDict('zones')
record_arguments_records_assignments_dict = (
self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments'))
if( view_name is not None and
view_name.endswith('_dep') or view_name == u'any' ):
records_dict['record_view_dependency'] = view_name
elif( view_name is not None ):
records_dict['record_view_dependency'] = '%s_dep' % view_name
zone_dict['zone_name'] = zone_name
if( cidr_ip.version() == 4 ):
decimal_ip = int( cidr_ip.strDec() )
decimal_ip_lower = (
(decimal_ip >> (32 - cidr_size) ) << (32 - cidr_size))
decimal_ip_upper = ( pow(2, 32 - cidr_size) - 1 ) | decimal_ip
self.db_instance.StartTransaction()
ip_index_dict = self.db_instance.GetEmptyRowDict('ipv4_index')
try:
record_list = self.db_instance.ListRow(
'ipv4_index', ip_index_dict,
'records', records_dict,
'zones', zone_dict,
'zone_view_assignments', zone_view_assignments_dict,
'record_arguments_records_assignments',
record_arguments_records_assignments_dict,
column='ipv4_dec_address',
range_values=(decimal_ip_lower, decimal_ip_upper))
finally:
self.db_instance.EndTransaction()
elif( cidr_ip.version() == 6 ):
ip_index_dict = self.db_instance.GetEmptyRowDict('ipv6_index')
if( cidr_size >= 64 ):
try:
ip_index_dict[u'ipv6_dec_upper'] = int(cidr_ip.strHex(0)[:-16], 0)
except ValueError:
ip_index_dict[u'ipv6_dec_upper'] = 0
decimal_ip_lower = int('0x%s' % cidr_ip.strHex(0)[18:], 0)
decimal_ip_lower_lower = (
(decimal_ip_lower >> (128 - cidr_size)) <<
(128 - cidr_size))
decimal_ip_lower_upper = (
(pow(2,128 - cidr_size) - 1 ) | decimal_ip_lower)
column = 'ipv6_dec_lower'
range_values = (decimal_ip_lower_lower, decimal_ip_lower_upper)
elif( cidr_size < 64 ):
try:
decimal_ip_upper = int(cidr_ip.strHex()[:-16], 0)
except ValueError:
decimal_ip_upper = 0
decimal_ip_upper_lower = (
(decimal_ip_upper >> (64 - cidr_size)) << (64 - cidr_size))
decimal_ip_upper_upper = (
(pow(2,64 - cidr_size) - 1 ) | decimal_ip_upper)
column = 'ipv6_dec_upper'
range_values = (decimal_ip_upper_lower, decimal_ip_upper_upper)
self.db_instance.StartTransaction()
try:
record_list = self.db_instance.ListRow(
'ipv6_index', ip_index_dict,
'records', records_dict,
'zones', zone_dict,
'zone_view_assignments', zone_view_assignments_dict,
'record_arguments_records_assignments',
record_arguments_records_assignments_dict,
column=column,
range_values=range_values)
finally:
self.db_instance.EndTransaction()
## Parse returned list
parsed_record_dict = {}
for _, record_entry in enumerate(record_list):
if( record_entry[u'record_type'] not in
constants.RECORD_TYPES_INDEXED_BY_IP ):
raise errors.IPIndexError('Record type not indexable by '
'IP: %s' % record_entry)
if( record_entry[u'record_view_dependency'].endswith('_dep') ):
record_view = record_entry[u'record_view_dependency'][:-4]
else:
record_view = record_entry[u'record_view_dependency']
if( record_view not in parsed_record_dict ):
parsed_record_dict[record_view] = {}
if( u'ipv4_dec_address' in record_entry ):
record_ip = u'%s' % (
IPy.IP(record_entry[u'ipv4_dec_address']).strNormal(1))
if( record_ip not in parsed_record_dict[record_view] ):
parsed_record_dict[record_view][record_ip] = []
elif( u'ipv6_dec_upper' in record_entry ):
decimal_ip = (
(record_entry[u'ipv6_dec_upper'] << 64) +
(record_entry[u'ipv6_dec_lower']) )
record_ip = u'%s' % IPy.IP(decimal_ip).strFullsize(0)
if( record_ip not in parsed_record_dict[record_view] ):
parsed_record_dict[record_view][record_ip] = []
else:
raise errors.IPIndexError(
'Record type unknown. Missing ipv4 or ipv6 dec index: %s' % (
record_entry))
record_item = {}
record_item['records_id'] = record_entry['records_id']
record_item['record_type'] = record_entry['record_type']
record_item['record_target'] = record_entry['record_target']
record_item['record_ttl'] = record_entry['record_ttl']
record_item['record_zone_name'] = record_entry['record_zone_name']
record_item[u'zone_origin'] = record_entry[u'zone_origin']
record_item['record_view_dependency'] = record_entry[
'record_view_dependency']
#record_item['record_last_updated'] = record_entry['record_last_updated']
record_item['record_last_user'] = record_entry['record_last_user']
if record_entry[u'record_view_dependency'].endswith('_dep'):
record_item[u'view_name'] = record_entry[u'record_view_dependency'][:-4]
else:
record_item[u'view_name'] = record_entry[u'record_view_dependency']
if( record_entry[u'record_type'] == u'a' or
record_entry[u'record_type'] == u'aaaa' ):
record_item[u'forward'] = True
record_item[u'host'] = '%s.%s' % (
record_entry[u'record_target'],
record_entry[u'zone_origin'][:-1])
record_item[u'zone_origin'] = record_entry['zone_origin']
record_item[u'record_target'] = record_entry['record_target']
record_item[u'record_args_dict'] = {
'assignment_ip': record_entry['argument_value']}
parsed_record_dict[record_view][record_ip].append( record_item )
elif( record_entry[u'record_type'] == u'ptr' ):
record_item[u'zone_origin'] = record_entry['zone_origin']
record_item[u'record_target'] = record_entry['record_target']
record_item[u'forward'] = False
record_item[u'host'] = record_entry[u'argument_value'][:-1]
assignment_ip = helpers_lib.UnReverseIP(
'%s.%s' % (
record_entry['record_target'],record_entry['zone_origin']))
record_item[u'record_args_dict'] = {'assignment_ip': assignment_ip}
parsed_record_dict[record_view][record_ip].insert(0, record_item )
return parsed_record_dict
[docs] def ListRecordsByZone(self, zone_name, view_name=None):
"""Lists records in a given zone.
Inputs:
zone_name: name of the zone
view_name: name of the view
Output:
dict: A dictionary Keyed by view, keyed by IP, listed by record.
example:
{u'test_view':
{u'192.168.1.8':
[{u'forward': True,
u'host': u'host6.university.edu',
u'zone': u'forward_zone',
u'zone_origin': u'university.edu.'},
{u'forward': False,
u'host': u'host6.university.edu',
u'zone': u'reverse_zone',
u'zone_origin': u'1.168.192.in-addr.arpa.'}]}}
"""
self.user_instance.Authorize('ListRecordsByZone')
record_list = {}
records_dict = self.db_instance.GetEmptyRowDict('records')
zone_view_assignments_dict = self.db_instance.GetEmptyRowDict(
'zone_view_assignments')
zone_dict = self.db_instance.GetEmptyRowDict('zones')
zone_dict['zone_name'] = zone_name
record_arguments_records_assignments_dict = (
self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments'))
ipv4_index_dict = self.db_instance.GetEmptyRowDict('ipv4_index')
ipv6_index_dict = self.db_instance.GetEmptyRowDict('ipv6_index')
if( view_name is not None and
view_name.endswith('_dep') or view_name == u'any' ):
records_dict['record_view_dependency'] = view_name
elif( view_name is not None ):
records_dict['record_view_dependency'] = '%s_dep' % view_name
args_ipv4 = ['zone_view_assignments', zone_view_assignments_dict,
'zones', zone_dict, 'records', records_dict,
'record_arguments_records_assignments',
record_arguments_records_assignments_dict]
args_ipv6 = args_ipv4 + ['ipv6_index', ipv6_index_dict]
args_ipv4.append('ipv4_index')
args_ipv4.append(ipv4_index_dict)
self.db_instance.StartTransaction()
try:
record_list = self.db_instance.ListRow(*args_ipv4)
record_list = record_list + self.db_instance.ListRow(*args_ipv6)
finally:
self.db_instance.EndTransaction()
#Parsing Records
parsed_record_dict = {}
for record_entry in record_list:
if( record_entry[u'record_type'] not in
constants.RECORD_TYPES_INDEXED_BY_IP ):
raise errors.IPIndexError('Record type not indexable by '
'IP: %s' % record_entry)
if( record_entry[u'record_view_dependency'].endswith('_dep') ):
record_view = record_entry[u'record_view_dependency'][:-4]
else:
record_view = record_entry[u'record_view_dependency']
if( record_view not in parsed_record_dict ):
parsed_record_dict[record_view] = {}
if( u'ipv4_dec_address' in record_entry ):
record_ip = u'%s' % (
IPy.IP(record_entry[u'ipv4_dec_address']).strNormal(1))
if( record_ip not in parsed_record_dict[record_view] ):
parsed_record_dict[record_view][record_ip] = []
elif( u'ipv6_dec_upper' in record_entry ):
decimal_ip = (
(record_entry[u'ipv6_dec_upper'] << 64) +
(record_entry[u'ipv6_dec_lower']) )
record_ip = u'%s' % IPy.IP(decimal_ip).strFullsize(0)
if( record_ip not in parsed_record_dict[record_view] ):
parsed_record_dict[record_view][record_ip] = []
else:
raise errors.IPIndexError(
'Record type unknown. Missing ipv4 or ipv6 dec index: %s' % (
record_entry))
record_item = {}
record_item['records_id'] = record_entry['records_id']
record_item['record_type'] = record_entry['record_type']
record_item['record_target'] = record_entry['record_target']
record_item['record_ttl'] = record_entry['record_ttl']
record_item['record_zone_name'] = record_entry['record_zone_name']
record_item[u'zone_origin'] = record_entry[u'zone_origin']
record_item['record_view_dependency'] = record_entry[
'record_view_dependency']
record_item['record_last_user'] = record_entry['record_last_user']
if record_entry[u'record_view_dependency'].endswith('_dep'):
record_item[u'view_name'] = record_entry[u'record_view_dependency'][:-4]
else:
record_item[u'view_name'] = record_entry[u'record_view_dependency']
if( record_entry[u'record_type'] == u'a' or
record_entry[u'record_type'] == u'aaaa' ):
record_item[u'forward'] = True
record_item[u'host'] = '%s.%s' % (
record_entry[u'record_target'],
record_entry[u'zone_origin'][:-1])
record_item[u'zone_origin'] = record_entry['zone_origin']
record_item[u'record_target'] = record_entry['record_target']
record_item[u'record_args_dict'] = {
'assignment_ip': record_entry['argument_value']}
parsed_record_dict[record_view][record_ip].append(record_item)
elif( record_entry[u'record_type'] == u'ptr' ):
record_item[u'zone_origin'] = record_entry['zone_origin']
record_item[u'record_target'] = record_entry['record_target']
record_item[u'forward'] = False
record_item[u'host'] = record_entry[u'argument_value'][:-1]
assignment_ip = helpers_lib.UnReverseIP(
'%s.%s' % (
record_entry['record_target'],record_entry['zone_origin']))
record_item[u'record_args_dict'] = {'assignment_ip': assignment_ip}
parsed_record_dict[record_view][record_ip].insert(0, record_item )
return parsed_record_dict
[docs] def SortRecordsByHost(self, records_dict):
"""Generates an IP list sorted by record's host
Inputs:
record_dict: dictionary keyed by view, then keyed by IP
dictionary from ListRecordsByCIDRBlock
and from ListRecordsByZone
Outputs:
sorted_list: list of sorted records
"""
sorted_list = []
target_sort = []
for view in records_dict:
for ip in records_dict[view]:
for record in records_dict[view][ip]:
target_sort.append(dict({'ip_address':IPy.IP(ip).strFullsize()}.items() +
record.items()))
sorted_list = sorted(target_sort, key=lambda x: x['host'])
return sorted_list
[docs] def ListNamedConfGlobalOptionsClient(self, option_id=None,
dns_server_set=None, timestamp=None):
"""Converts XMLRPC datetime to datetime object and runs
ListNamedConfGlobalOptions
Inputs:
option_id: integer of the option id
dns_server_set: string of the dns server set name
timestamp: XMLRPC datetime timestamp
Outputs:
list: list of dictionarires from ListNamedConfGlobalOptions
"""
success = False
named_conf_global_options = None
named_conf_global_options = self.core_instance.ListNamedConfGlobalOptions(
option_id, dns_server_set, timestamp)
success = True
return named_conf_global_options
[docs] def ListZoneByIPAddress(self, ip_address):
"""Lists zone name given ip_address
Inputs:
ip_address: string of ip address
Outputs:
string: string of zone name, ex: 'test_zone'
"""
user_ip = IPy.IP(ip_address)
reverse_range_zone_assignments = (
self.core_instance.ListReverseRangeZoneAssignments())
for reverse_range_zone_assignment in reverse_range_zone_assignments:
db_cidr = IPy.IP(reverse_range_zone_assignments[
reverse_range_zone_assignment])
if( user_ip in db_cidr ):
return reverse_range_zone_assignment
[docs] def RemoveCNamesByAssignmentHost(self, hostname, view_name, zone_name):
"""Removes cname's by assignment hostname, will not remove cnames
that the user does not have permissin to remove. The function will continue
and pass over that cname.
Inputs:
hostname: string of hostname
view_name: string of view name
zone_name: string of zone name
Raises:
UnexpectedDataError: Incorrect number of records found
Outputs:
int: number of rows modified
"""
function_name, current_args = helpers_lib.GetFunctionNameAndArgs()
row_count = 0
record_arguments_record_assignments_dict = (
self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments'))
record_arguments_record_assignments_dict[
'record_arguments_records_assignments_type'] = u'cname'
record_arguments_record_assignments_dict[
'argument_value'] = hostname
records_dict = self.db_instance.GetEmptyRowDict(
'records')
records_dict['record_type'] = u'cname'
records_dict['record_view_dependency'] = '%s_dep' % view_name
records_dict['record_zone_name'] = zone_name
success = False
try:
self.db_instance.StartTransaction()
try:
found_record_arguments = self.db_instance.ListRow(
'record_arguments_records_assignments',
record_arguments_record_assignments_dict)
remove_record_dict = {}
for record_argument in found_record_arguments:
remove_record_dict[record_argument[
'record_arguments_records_assignments_record_id']] = {
'assignment_host': record_argument['argument_value']}
for record_id in remove_record_dict:
records_dict['records_id'] = record_id
found_records_dict = self.db_instance.ListRow(
'records', records_dict)
if( len(found_records_dict) != 1 ):
raise errors.UnexpectedDataError(
'Incorrect number of records found!')
try:
self.core_instance.user_instance.Authorize(
function_name,
record_data=
{'target': found_records_dict[0]['record_target'],
'zone_name': records_dict['record_zone_name'],
'view_name': records_dict['record_view_dependency'],
'record_type': records_dict['record_type']},
current_transaction=True)
except errors.AuthorizationError:
continue
row_count += self.db_instance.RemoveRow(
'records', found_records_dict[0])
remove_record_dict[record_id].update({
'cname_host': found_records_dict[0]['record_target']})
except:
self.db_instance.EndTransaction(rollback=True)
raise
self.db_instance.EndTransaction()
success = True
log_list = []
for record_id in remove_record_dict:
log_list.append('record_id:')
log_list.append(str(record_id))
for record in remove_record_dict[record_id]:
log_list.append('%s:' % record)
log_list.append(remove_record_dict[record_id][record])
success = True
finally:
self.log_instance.LogAction(self.user_instance.user_name, function_name,
current_args, success)
return row_count
[docs] def ConstructRecordArgsDictFromRecordID(self, record_id):
"""Constructs the records_arg_dict from the Roster database given only
the record id.
Inputs:
record_id: int of record id
Outputs:
record_args_dict: dictionary of arguments and their values
"""
record_args_db_row = self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments')
record_args_db_row[
'record_arguments_records_assignments_record_id'] = record_id
record_args_db_dict = self.db_instance.ListRow(
'record_arguments_records_assignments', record_args_db_row)
record_args_dict = {}
for key in record_args_db_dict:
key_entry = key['argument_value']
if( key_entry.isdigit() ):
key_entry = int(key_entry)
record_args_dict[
key['record_arguments_records_assignments_argument_name']] = key_entry
return record_args_dict
[docs] def ProcessRecordsBatch(self, delete_records=None, add_records=None,
zone_import=False):
"""Proccess batches of records
Inputs:
delete_records: list of dictionaries of records
ex: {'record_ttl': 3600, 'record_type': u'a',
'records_id': 10, 'record_target': u'host1',
'record_zone_name': u'forward_zone',
'record_last_user': u'sharrell',
'record_view_dependency': u'test_view_dep'}
{'record_type': 'ptr', 'record_target': 'target',
'view_name': 'view', 'zone_name': 'zone'}
add_records: list of dictionaries of records
Raises:
RecordsBatchError: Record specification too broad
RecordsBatchError: No record found
RecordsBatchError: Record already exists
RecordsBatchError: CNAME already exists
RecordsBatchError: Duplicate record found
Outputs:
int: row count
"""
if delete_records is None:
delete_records = []
if add_records is None:
add_records = []
function_name, current_args = helpers_lib.GetFunctionNameAndArgs()
log_dict = {'delete': [], 'add': []}
row_count = 0
changed_view_dep = []
success = False
try:
for record in add_records:
self.db_instance.ValidateRecordArgsDict(record[u'record_type'],
record[u'record_arguments'])
self.db_instance.StartTransaction()
try:
# REMOVE RECORDS
for record in delete_records:
record_dict = self.db_instance.GetEmptyRowDict('records')
record_dict['records_id'] = record['records_id']
record_dict['record_type'] = record['record_type']
record_dict['record_target'] = record['record_target']
record_dict['record_ttl'] = record['record_ttl']
if( record['record_view_dependency'].endswith('_dep') or
record['record_view_dependency'] == u'any' ):
record_dict['record_view_dependency'] = record[
'record_view_dependency']
else:
record_dict['record_view_dependency'] = (
'%s_dep' % record['record_view_dependency'])
record_dict['record_zone_name'] = record['record_zone_name']
record_dict['record_last_user'] = record['record_last_user']
record_args_dict = self.ConstructRecordArgsDictFromRecordID(
record['records_id'])
self.user_instance.Authorize('ProcessRecordsBatch',
record_data = {
'target': record['record_target'],
'zone_name': record['record_zone_name'],
'view_name': record_dict['record_view_dependency'],
'record_type': record['record_type'],
'record_args_dict': record_args_dict},
current_transaction=True)
rows_deleted = self.db_instance.RemoveRow('records', record_dict)
log_dict['delete'].append(record)
row_count += 1
if( rows_deleted > 1 ):
raise errors.RecordsBatchError(
'Record specification too broad, '
'found %d matching records for %s.' % (
rows_deleted, record_dict))
elif( rows_deleted == 0 ):
raise errors.RecordsBatchError(
'No record found for :%s' % record_dict)
# ADD RECORDS
for record in add_records:
#Target length check
if( not self.db_instance.data_validation_instance.isTarget(
record[u'record_target']) ):
raise errors.InvalidInputError('Target hostname is invalid. %s' % (
record[u'record_target']))
view_name = record['record_view_dependency']
if( not record['record_view_dependency'].endswith('_dep') and record[
'record_view_dependency'] != u'any'):
view_name = '%s_dep' % record['record_view_dependency']
self.user_instance.Authorize('ProcessRecordsBatch',
record_data = {
'target': record['record_target'],
'zone_name': record['record_zone_name'],
'view_name': view_name,
'record_type': record['record_type'],
'record_args_dict': record['record_arguments']},
current_transaction=True)
if( record['record_type'] == u'ptr' ):
if( record['record_arguments'][
'assignment_host'].startswith('@.') ):
record['record_arguments']['assignment_host'] = record[
'record_arguments']['assignment_host'].lstrip('@.')
changed_view_dep.append((view_name, record['record_zone_name']))
ttl = None
if( 'ttl' in record ):
ttl = record['ttl']
if( ttl is None ):
ttl = constants.DEFAULT_TTL
records_dict = {'records_id': None,
'record_target': record['record_target'],
'record_type': None,
'record_ttl': None,
'record_zone_name': record['record_zone_name'],
'record_view_dependency': view_name,
'record_last_user': None}
if( record['record_type'] == 'cname' ):
all_records = self.db_instance.ListRow('records', records_dict)
if( len(all_records) > 0 ):
raise errors.RecordsBatchError(
'Record already exists with target %s.' % (
record['record_target']))
records_dict['record_type'] = u'cname'
cname_records = self.db_instance.ListRow('records', records_dict)
if( len(cname_records) > 0 ):
raise errors.RecordsBatchError('CNAME already exists with target '
'%s.' % (record['record_target']))
record_args_assignment_dict = self.db_instance.GetEmptyRowDict(
'record_arguments_records_assignments')
records_dict['record_type'] = record['record_type']
raw_records = self.db_instance.ListRow(
'records', records_dict, 'record_arguments_records_assignments',
record_args_assignment_dict)
records_dict['record_last_user'] = self.user_instance.GetUserName()
records_dict['record_ttl'] = ttl
current_records = (
helpers_lib.GetRecordsFromRecordRowsAndArgumentRows(
raw_records, record['record_arguments']))
for current_record in current_records:
for arg in record['record_arguments'].keys():
if( arg not in current_record ):
break
if( record['record_arguments'][arg] is None ):
continue
if( record['record_arguments'][arg] != current_record[arg] ):
break
else:
raise errors.RecordsBatchError('Duplicate record found: %s' %
current_record)
records_dict['record_type'] = record['record_type']
record_id = self.db_instance.MakeRow('records', records_dict)
for arg in record['record_arguments'].keys():
record_argument_assignments_dict = {
'record_arguments_records_assignments_record_id': record_id,
'record_arguments_records_assignments_type': record[
'record_type'],
'record_arguments_records_assignments_argument_name': arg,
'argument_value': unicode(record['record_arguments'][arg])}
self.db_instance.MakeRow('record_arguments_records_assignments',
record_argument_assignments_dict)
log_dict['add'].append(record)
row_count += 1
if( records_dict['record_type'] in
constants.RECORD_TYPES_INDEXED_BY_IP ):
self.core_instance._AddRecordToIpIndex(
records_dict['record_type'], records_dict['record_zone_name'],
records_dict['record_view_dependency'],
record_id, records_dict['record_target'],
record['record_arguments'])
changed_view_dep = set(changed_view_dep)
for view_dep_pair in changed_view_dep:
self.core_instance._IncrementSoa(*view_dep_pair, missing_ok=zone_import)
except:
self.db_instance.EndTransaction(rollback=True)
raise
self.db_instance.EndTransaction()
success = True
finally:
self.log_instance.LogAction(self.user_instance.user_name, function_name,
current_args, success)
return row_count
[docs] def ListSortedHostsByZone(self, zone_name, view_name=None):
records_dict = self.ListRecordsByZone(zone_name, view_name=view_name)
sorted_records = self.SortRecordsByHost(records_dict)
hosts_dict = {}
for record in sorted_records:
direction = 'Reverse'
if( record['forward'] ):
direction = 'Forward'
if( not hosts_dict.has_key(record[u'view_name']) ):
hosts_dict.update({record[u'view_name']: []})
new_record = record.copy()
new_record['direction'] = direction
hosts_dict[record[u'view_name']].append(new_record)
return hosts_dict
[docs] def ListSortedHostsByCIDR(self, cidr, zone_name=None, view_name=None):
records_dict = self.ListRecordsByCIDRBlock(cidr, zone_name=zone_name,
view_name=view_name)
ip_address_list = self.CIDRExpand(cidr)
if ip_address_list is None:
ip_address_list = []
if( ip_address_list == [] ):
for view in records_dict:
ip_address_list.extend(records_dict[view].keys())
ip_address_list = list(set(ip_address_list))
hosts_dict = {}
if( len(records_dict) == 0 ):
hosts_dict['--'] = []
for ip_address in ip_address_list:
hosts_dict['--'].append(
{'host': '--', 'direction': '--',
'ip_address': ip_address, 'record_zone_name': '--'})
else:
for view in records_dict:
if( not hosts_dict.has_key(view) ):
hosts_dict.update({view: []})
for ip_address in ip_address_list:
if( ip_address in records_dict[view] ):
for record in records_dict[view][ip_address]:
direction = 'Reverse'
if( record['forward'] ):
direction = 'Forward'
new_record = record.copy()
new_record['direction'] = direction
new_record['ip_address'] = ip_address
hosts_dict[view].append(new_record)
else:
hosts_dict[view].append(
{'ip_address': ip_address, 'direction': '--', 'host': '--',
'record_zone_name': '--'})
return hosts_dict
# vi: set ai aw sw=2: