Merge pull request #27548 from lomeroe/add_infoblox

Add infoblox DNS mgmt
This commit is contained in:
Nicole Thomas 2015-10-02 09:36:38 -06:00
commit 5fed31b823
2 changed files with 746 additions and 0 deletions

518
salt/modules/infoblox.py Normal file
View File

@ -0,0 +1,518 @@
# -*- coding: utf-8 -*-
'''
Module for managing Infoblox
Will look for pillar data infoblox:server, infoblox:user, infoblox:password if not passed to functions
.. versionadded:: Boron
:depends:
- requests
'''
from __future__ import absolute_import
# Import salt libs
from salt.exceptions import CommandExecutionError
from salt.exceptions import SaltInvocationError
import logging
log = logging.getLogger(__name__)
try:
import json
import requests
HAS_IMPORTS = True
except ImportError:
HAS_IMPORTS = False
def __virtual__():
if HAS_IMPORTS:
return True
return False
def _conn_info_check(infoblox_server=None,
infoblox_user=None,
infoblox_password=None):
'''
get infoblox stuff from pillar if not passed
'''
if infoblox_server is None:
infoblox_server = __salt__['pillar.get']('infoblox:server', None)
if infoblox_user is None:
infoblox_user = __salt__['pillar.get']('infoblox:user', None)
log.debug('Infoblox username is "{0}"'.format(infoblox_user))
if infoblox_password is None:
infoblox_password = __salt__['pillar.get']('infoblox:password', None)
return infoblox_server, infoblox_user, infoblox_password
def _process_return_data(retData):
'''
generic return processing
'''
if retData.status_code == 200:
if retData.json():
return retData
else:
log.debug('no data returned from infoblox')
return None
else:
msg = 'Unsuccessful error code {0} returned'.format(retData.status_code)
log.error(msg)
return None
def delete_record(name,
dns_view,
record_type,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
delete a record
name
name of the record
dns_view
the DNS view to remove the record from
record_type
the record type (a, cname, host, etc)
infoblox_server
the infoblox server hostname (can also use the infoblox:server pillar)
infoblox_user
the infoblox user to connect with (can also use the infoblox:user pillar)
infoblox_password
the infoblox user's password (can also use the infolblox:password pillar)
infoblox_api_version
the infoblox api verison to use
sslVerify
should ssl verification be done on the connection to the Infoblox REST API
CLI Example:
.. code-block:: bash
salt my-minion infoblox.delete_record some.dns.record MyInfobloxView A sslVerify=False
'''
infoblox_server, infoblox_user, infoblox_password = _conn_info_check(infoblox_server,
infoblox_user,
infoblox_password)
if infoblox_server is None and infoblox_user is None and infoblox_password is None:
_throw_no_creds()
return None
currentRecords = get_record(name,
record_type,
infoblox_server,
infoblox_user,
infoblox_password,
dns_view,
infoblox_api_version,
sslVerify)
if currentRecords:
for currentRecord in currentRecords:
url = 'https://{0}/wapi/{1}/{2}'.format(infoblox_server,
infoblox_api_version,
currentRecord['Record ID'])
ret = requests.delete(url,
auth=(infoblox_user, infoblox_password),
headers={'Content-Type': 'application/json'},
verify=sslVerify)
if ret.status_code == 200:
return True
else:
msg = 'Unsuccessful error code {0} returned -- full json dump {1}'.format(ret.status_code, ret.json())
raise CommandExecutionError(msg)
return False
def update_record(name,
value,
dns_view,
record_type,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
update an entry to an infoblox dns view
name
the dns name
value
the value for the record
record_type
the record type (a, cname, etc)
dns_view
the DNS view to add the record to
infoblox_server
the infoblox server hostname (can also use the infoblox:server pillar)
infoblox_user
the infoblox user to connect with (can also use the infoblox:user pillar)
infoblox_password
the infoblox user's password (can also use the infolblox:password pillar)
infoblox_api_version
the infoblox api verison to use
sslVerify
should ssl verification be done on the connection to the Infoblox REST API
CLI Example:
.. code-block:: bash
salt '*' infoblox.update_record alias.network.name canonical.network.name MyInfobloxView cname sslVerify=False
'''
infoblox_server, infoblox_user, infoblox_password = _conn_info_check(infoblox_server,
infoblox_user,
infoblox_password)
if infoblox_server is None and infoblox_user is None and infoblox_password is None:
_throw_no_creds()
return None
currentRecords = get_record(name,
record_type,
infoblox_server,
infoblox_user,
infoblox_password,
dns_view,
infoblox_api_version,
sslVerify)
if currentRecords:
for currentRecord in currentRecords:
url = 'https://{0}/wapi/{1}/{2}'.format(
infoblox_server,
infoblox_api_version,
currentRecord['Record ID'])
data = None
if record_type == 'cname':
data = json.dumps({'canonical': value})
elif record_type == 'a':
data = {'ipv4addrs': []}
for i in value:
data['ipv4addrs'].append({'ipv4addr': i})
data = json.dumps(data)
ret = requests.put(url,
data,
auth=(infoblox_user, infoblox_password),
headers={'Content-Type': 'application/json'},
verify=sslVerify)
if ret.status_code == 200:
return True
else:
msg = 'Unsuccessful status code {0} returned.'.format(ret.status_code)
raise CommandExecutionError(msg)
else:
msg = 'Record {0} of type {1} was not found'.format(name, record_type)
log.error(msg)
return False
def add_record(name,
value,
record_type,
dns_view,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
add a record to an infoblox dns view
name
the record name
value
the value for the entry
can make use of infoblox functions for next available IP, like 'func:nextavailableip:10.1.0.0/24'
record_type
the record type (cname, a, host, etc)
dns_view
the DNS view to add the record to
infoblox_server
the infoblox server hostname (can also use the infoblox:server pillar)
infoblox_user
the infoblox user to connect with (can also use the infoblox:user pillar)
infoblox_password
the infoblox user's password (can also use the infolblox:password pillar)
infoblox_api_version
the infoblox api verison to use
sslVerify
should ssl verification be done on the connection to the Infoblox REST API
CLI Example:
.. code-block:: bash
salt 'myminion' infoblox.add_record alias.network.name canonical.network.name MyView
'''
infoblox_server, infoblox_user, infoblox_password = _conn_info_check(infoblox_server,
infoblox_user,
infoblox_password)
if infoblox_server is None and infoblox_user is None and infoblox_password is None:
_throw_no_creds()
return None
record_type = record_type.lower()
data = None
url = None
if record_type == 'cname':
data = json.dumps({'name': name, 'canonical': value, 'view': dns_view})
if record_type == 'host' or record_type == 'a':
data = json.dumps({'name': name, 'ipv4addrs': [{'ipv4addr': value}], 'view': dns_view})
#if record_type == 'alias':
# data = json.dumps({'name': name, 'aliases': [value], 'view': dns_view})
# record_type = 'host'
# tRec = get_record(name,
# record_type,
# infoblox_server,
# infoblox_user,
# infoblox_password,
# dns_view,
# infoblox_api_version,
# sslVerify)
# if not tRec:
# log.error('A host record matching {0} was not found to add the alias to.'.format(name))
# return False
# else:
# for _rec in tRec:
# url = 'https://{0}/wapi/{1}/{2}'.format(
# infoblox_server,
# infoblox_api_version,
# _rec['Record ID'])
url = 'https://{0}/wapi/{1}/record:{2}'.format(infoblox_server,
infoblox_api_version,
record_type)
ret = requests.post(url,
data,
auth=(infoblox_user, infoblox_password),
headers={'Content-Type': 'application/json'},
verify=sslVerify)
if ret.status_code == 201:
return True
else:
msg = 'Unsuccessful error code {0} returned -- full json dump {1}'.format(ret.status_code, ret.json())
raise CommandExecutionError(msg)
def _throw_no_creds():
'''
helper function to log no credentials found error
'''
msg = 'An infoblox server, username, and password must be specified or configured via pillar'
raise SaltInvocationError(msg)
def get_network(network_name,
network_view=None,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
get a network from infoblox
network_name
The name of the network in IPAM
network_view
The name of the network view the network belongs to
infoblox_server
the infoblox server hostname (can also use the infoblox:server pillar)
infoblox_user
the infoblox user to connect with (can also use the infoblox:user pillar)
infoblox_password
the infoblox user's password (can also use the infolblox:password pillar)
infoblox_api_version
the infoblox api verison to use
sslVerify
should ssl verification be done on the connection to the Infoblox REST API
CLI Example:
.. code-block:: bash
salt myminion infoblox.get_network '10.0.0.0/8'
'''
records = []
infoblox_server, infoblox_user, infoblox_password = _conn_info_check(infoblox_server,
infoblox_user,
infoblox_password)
if infoblox_server is None and infoblox_user is None and infoblox_password is None:
_throw_no_creds()
return None
url = 'https://{0}/wapi/{1}/network?network={2}{3}'.format(
infoblox_server,
infoblox_api_version,
network_name,
('' if network_view is None else '&network_view=' + network_view))
log.debug('Requst url is "{0}"'.format(url))
ret = _process_return_data(requests.get(url,
auth=(infoblox_user, infoblox_password),
verify=sslVerify))
if ret:
for entry in ret.json():
log.debug('Infoblox record returned: {0}'.format(entry))
tEntry = {}
data = _parse_record_data(entry)
for key in data.keys():
tEntry[key] = data[key]
records.append(tEntry)
return records
else:
return False
return False
def get_record(record_name,
record_type='host',
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
dns_view=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
get a record from infoblox
record_name
name of the record to search for
record_type
type of reacord to search for (host, cname, a, etc...defaults to host)
infoblox_server
the infoblox server hostname (can also use the infoblox:server pillar)
infoblox_user
the infoblox user to connect with (can also use the infoblox:user pillar)
infoblox_password
the infoblox user's password (can also use the infolblox:password pillar)
dns_view
the infoblox DNS view to search, if not specified all views are searched
infoblox_api_version
the infoblox api verison to use
sslVerify
should ssl verification be done on the connection to the Infoblox REST API
CLI Example:
.. code-block:: bash
salt myminion infoblox.get_record some.host.com A sslVerify=False
'''
#TODO - verify record type (A, AAAA, CNAME< HOST, MX, PTR, SVR, TXT, host_ipv4addr, host_ipv6addr, naptr)
records = []
infoblox_server, infoblox_user, infoblox_password = _conn_info_check(infoblox_server,
infoblox_user,
infoblox_password)
if infoblox_server is None and infoblox_user is None and infoblox_password is None:
_throw_no_creds()
return None
url = 'https://{0}/wapi/{1}/record:{3}?name:={2}{4}{5}'.format(
infoblox_server,
infoblox_api_version,
record_name,
record_type,
('' if dns_view is None else '&view=' + dns_view),
('&_return_fields%2B=aliases' if record_type == 'host' else '')
)
log.debug('Requst url is "{0}"'.format(url))
ret = _process_return_data(requests.get(url,
auth=(infoblox_user, infoblox_password),
verify=sslVerify))
if ret:
for entry in ret.json():
log.debug('Infoblox record returned: {0}'.format(entry))
tEntry = {}
data = _parse_record_data(entry)
for key in data.keys():
tEntry[key] = data[key]
records.append(tEntry)
return records
else:
return False
return False
def _parse_record_data(entry_data):
'''
returns the right value data we'd be interested in for the specified record type
'''
ret = {}
ipv4addrs = []
aliases = []
if 'canonical' in entry_data:
ret['Canonical Name'] = entry_data['canonical']
if 'ipv4addrs' in entry_data:
for ipaddrs in entry_data['ipv4addrs']:
ipv4addrs.append(ipaddrs['ipv4addr'])
ret['IP Addresses'] = ipv4addrs
if 'aliases' in entry_data:
for alias in entry_data['aliases']:
aliases.append(alias)
ret['Aliases'] = aliases
if 'name' in entry_data:
ret['Name'] = entry_data['name']
if 'view' in entry_data:
ret['DNS View'] = entry_data['view']
if 'network_view' in entry_data:
ret['Network View'] = entry_data['network_view']
if 'comment' in entry_data:
ret['Comment'] = entry_data['comment']
if 'network' in entry_data:
ret['Network'] = entry_data['network']
if '_ref' in entry_data:
ret['Record ID'] = entry_data['_ref']
return ret

228
salt/states/infoblox.py Normal file
View File

@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
'''
states for infoblox stuff
ensures a record is either present or absent in an Infoblox DNS system
.. versionadded:: Boron
'''
from __future__ import absolute_import
# Import Python libs
import logging
log = logging.getLogger(__name__)
def __virtual__():
'''
make sure the infoblox module is available
'''
return True if 'infoblox.get_record' in __salt__ else False
def present(name,
value,
record_type,
dns_view,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
Ensure a record exists
name
Name of the record
value
Value of the record
record_type
record type (host, a, cname, etc)
dns_view
DNS View
infoblox_server
infoblox server to connect to (will try pillar if not specified)
infoblox_user
username to use to connect to infoblox (will try pillar if not specified)
infoblox_password
password to use to connect to infoblox (will try pillar if not specified)
verify_ssl
verify SSL certificates
Example:
.. code-block:: yaml
some-state:
infoblox.present:
- name: some.dns.record
- value: 10.1.1.3
- record_type: host
- sslVerify: False
'''
record_type = record_type.lower()
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
records = __salt__['infoblox.get_record'](name,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
dns_view=dns_view,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify)
if records:
#check records for updates
for record in records:
if record_type == 'cname':
if record['Canonical Name'] != value:
if __opts__['test']:
ret['result'] = None
ret['comment'] = ' '.join([ret['comment'],
'DNS {0} record {1} in view {2} will be update'.format(record_type,
name,
dns_view)])
else:
retval = __salt__['infoblox.update_record'](name,
value,
dns_view,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify)
if retval:
if 'old' not in ret['changes']:
ret['changes']['old'] = []
if 'new' not in ret['changes']:
ret['changes']['new'] = []
ret['changes']['old'].append(record)
ret['changes']['new'].append(__salt__['infoblox.get_record'](name,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
dns_view=dns_view,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify))
else:
ret['result'] = False
return ret
else:
#no records
if __opts__['test']:
ret['result'] = None
ret['comment'] = ' '.join([ret['comment'],
'DNS {0} record {1} set to be added to view {2}'.format(record_type,
name,
dns_view)])
return ret
retval = __salt__['infoblox.add_record'](name,
value,
record_type,
dns_view,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=sslVerify)
if retval:
ret['result'] = True
ret['changes']['old'] = None
ret['changes']['new'] = __salt__['infoblox.get_record'](name,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
dns_view=dns_view,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify)
return ret
def absent(name,
record_type,
dns_view,
infoblox_server=None,
infoblox_user=None,
infoblox_password=None,
infoblox_api_version='v1.4.2',
sslVerify=True):
'''
Ensure a record does not exists
name
Name of the record
record_type
record type (host, a, cname, etc)
dns_view
DNS View
infoblox_server
infoblox server to connect to (will try pillar if not specified)
infoblox_user
username to use to connect to infoblox (will try pillar if not specified)
infoblox_password
password to use to connect to infoblox (will try pillar if not specified)
verify_ssl
verify SSL certificates
Example:
.. code-block:: yaml
some-state:
infoblox.absent:
- name: some.dns.record
- record_type: host
- dns_view: MyView
- sslVerify: False
'''
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
record = __salt__['infoblox.get_record'](name,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
dns_view=dns_view,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify)
if record:
if __opts__['test']:
ret['result'] = None
ret['comment'] = ' '.join([ret['comment'],
'DNS {0} record {1} in view {2} will be removed'.format(record_type,
name,
dns_view)])
else:
retval = __salt__['infoblox.delete_record'](name,
dns_view,
record_type,
infoblox_server=infoblox_server,
infoblox_user=infoblox_user,
infoblox_password=infoblox_password,
infoblox_api_version=infoblox_api_version,
sslVerify=sslVerify)
if retval:
if 'old' not in ret['changes']:
ret['changes']['old'] = []
ret['changes']['new'] = None
ret['changes']['old'].append(record)
else:
ret['result'] = False
return ret
else:
#record not found
ret['result'] = True
ret['changes']['old'] = None
ret['changes']['new'] = None
ret['comment'] = 'DNS record does not exist'
return ret