269 lines
9.0 KiB
Python
269 lines
9.0 KiB
Python
# Copyright 2015 Infoblox Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import json as jsonutils
|
|
|
|
from oslo_log import log
|
|
from oslo_utils import strutils
|
|
from six.moves.urllib import parse
|
|
import requests
|
|
|
|
from designate.backend.impl_infoblox.config import cfg
|
|
from designate.backend.impl_infoblox import ibexceptions as exc
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class Infoblox(object):
|
|
"""Infoblox class
|
|
|
|
Defines methods for getting, creating, updating and
|
|
removing objects from an Infoblox server instance.
|
|
"""
|
|
|
|
def __init__(self, options):
|
|
"""Initialize a new Infoblox object instance
|
|
|
|
Args:
|
|
options (dict): Target options dictionary
|
|
"""
|
|
|
|
config = cfg.CONF['backend:infoblox']
|
|
|
|
reqd_opts = ['wapi_url', 'username', 'password', 'ns_group']
|
|
other_opts = ['sslverify', 'network_view', 'dns_view', 'multi_tenant']
|
|
|
|
for opt in reqd_opts + other_opts:
|
|
if opt == 'sslverify' or opt == 'multi_tenant':
|
|
# NOTE(selvakumar): This check is for sslverify option.
|
|
# type of sslverify is unicode string from designate DB
|
|
# if the value is 0 getattr called for setting default values.
|
|
# to avoid setting default values we use oslo strutils
|
|
if not strutils.is_int_like(options.get(opt)):
|
|
option_value = options.get(opt)
|
|
else:
|
|
option_value = strutils.bool_from_string(options.get(opt),
|
|
default=True)
|
|
setattr(self, opt, option_value)
|
|
continue
|
|
setattr(self, opt, options.get(opt) or getattr(config, opt))
|
|
|
|
for opt in reqd_opts:
|
|
LOG.debug("self.%s = %s" % (opt, getattr(self, opt)))
|
|
if not getattr(self, opt):
|
|
raise exc.InfobloxIsMisconfigured(option=opt)
|
|
|
|
self.session = requests.Session()
|
|
adapter = requests.adapters.HTTPAdapter(
|
|
pool_connections=config.http_pool_connections,
|
|
pool_maxsize=config.http_pool_maxsize)
|
|
self.session.mount('http://', adapter)
|
|
self.session.mount('https://', adapter)
|
|
self.session.auth = (self.username, self.password)
|
|
self.session.verify = self.sslverify
|
|
|
|
def _construct_url(self, relative_path, query_params=None, extattrs=None):
|
|
if query_params is None:
|
|
query_params = {}
|
|
if extattrs is None:
|
|
extattrs = {}
|
|
|
|
if not relative_path or relative_path[0] == '/':
|
|
raise ValueError('Path in request must be relative.')
|
|
query = ''
|
|
if query_params or extattrs:
|
|
query = '?'
|
|
|
|
if extattrs:
|
|
attrs_queries = []
|
|
for key, value in extattrs.items():
|
|
LOG.debug("key: %s, value: %s" % (key, value))
|
|
attrs_queries.append('*' + key + '=' + value['value'])
|
|
query += '&'.join(attrs_queries)
|
|
if query_params:
|
|
if len(query) > 1:
|
|
query += '&'
|
|
query += parse.urlencode(query_params)
|
|
|
|
baseurl = parse.urljoin(self.wapi_url, parse.quote(relative_path))
|
|
return baseurl + query
|
|
|
|
def _validate_objtype_or_die(self, objtype):
|
|
if not objtype:
|
|
raise ValueError('WAPI object type can\'t be empty.')
|
|
if '/' in objtype:
|
|
raise ValueError('WAPI object type can\'t contains slash.')
|
|
|
|
def get_object(self, objtype, payload=None, return_fields=None,
|
|
extattrs=None):
|
|
"""Retrieve a list of Infoblox objects of type 'objtype'
|
|
|
|
Args:
|
|
objtype (str): Infoblox object type, e.g. 'view', 'tsig', etc.
|
|
payload (dict): Payload with data to send
|
|
Returns:
|
|
A list of the Infoblox objects requested
|
|
Raises:
|
|
InfobloxObjectNotFound
|
|
"""
|
|
if return_fields is None:
|
|
return_fields = []
|
|
if extattrs is None:
|
|
extattrs = {}
|
|
|
|
self._validate_objtype_or_die(objtype)
|
|
|
|
query_params = dict()
|
|
if return_fields:
|
|
query_params['_return_fields'] = ','.join(return_fields)
|
|
|
|
headers = {'Content-type': 'application/json'}
|
|
|
|
data = jsonutils.dumps(payload)
|
|
url = self._construct_url(objtype, query_params, extattrs)
|
|
|
|
r = self.session.get(url,
|
|
data=data,
|
|
verify=self.sslverify,
|
|
headers=headers)
|
|
|
|
if r.status_code != requests.codes.ok:
|
|
raise exc.InfobloxSearchError(
|
|
response=jsonutils.loads(r.content),
|
|
objtype=objtype,
|
|
content=r.content,
|
|
code=r.status_code)
|
|
|
|
return jsonutils.loads(r.content)
|
|
|
|
def create_object(self, objtype, payload, return_fields=None):
|
|
"""Create an Infoblox object of type 'objtype'
|
|
|
|
Args:
|
|
objtype (str): Infoblox object type, e.g. 'network', 'range', etc.
|
|
payload (dict): Payload with data to send
|
|
Returns:
|
|
The object reference of the newly create object
|
|
Raises:
|
|
InfobloxException
|
|
"""
|
|
if not return_fields:
|
|
return_fields = []
|
|
|
|
self._validate_objtype_or_die(objtype)
|
|
|
|
query_params = dict()
|
|
|
|
if return_fields:
|
|
query_params['_return_fields'] = ','.join(return_fields)
|
|
|
|
url = self._construct_url(objtype, query_params)
|
|
|
|
headers = {'Content-type': 'application/json'}
|
|
|
|
r = self.session.post(url,
|
|
data=jsonutils.dumps(payload),
|
|
verify=self.sslverify,
|
|
headers=headers)
|
|
|
|
if r.status_code != requests.codes.CREATED:
|
|
raise exc.InfobloxCannotCreateObject(
|
|
response=jsonutils.loads(r.content),
|
|
objtype=objtype,
|
|
content=r.content,
|
|
args=payload,
|
|
code=r.status_code)
|
|
|
|
return jsonutils.loads(r.content)
|
|
|
|
def call_func(self, func_name, ref, payload, return_fields=None):
|
|
if not return_fields:
|
|
return_fields = []
|
|
|
|
query_params = dict()
|
|
query_params['_function'] = func_name
|
|
|
|
if return_fields:
|
|
query_params['_return_fields'] = ','.join(return_fields)
|
|
|
|
url = self._construct_url(ref, query_params)
|
|
|
|
headers = {'Content-type': 'application/json'}
|
|
r = self.session.post(url,
|
|
data=jsonutils.dumps(payload),
|
|
verify=self.sslverify,
|
|
headers=headers)
|
|
|
|
if r.status_code not in (requests.codes.CREATED,
|
|
requests.codes.ok):
|
|
raise exc.InfobloxFuncException(
|
|
response=jsonutils.loads(r.content),
|
|
ref=ref,
|
|
func_name=func_name,
|
|
content=r.content,
|
|
code=r.status_code)
|
|
|
|
return jsonutils.loads(r.content)
|
|
|
|
def update_object(self, ref, payload):
|
|
"""Update an Infoblox object
|
|
|
|
Args:
|
|
ref (str): Infoblox object reference
|
|
payload (dict): Payload with data to send
|
|
Returns:
|
|
The object reference of the updated object
|
|
Raises:
|
|
InfobloxException
|
|
"""
|
|
|
|
headers = {'Content-type': 'application/json'}
|
|
r = self.session.put(self._construct_url(ref),
|
|
data=jsonutils.dumps(payload),
|
|
verify=self.sslverify,
|
|
headers=headers)
|
|
|
|
if r.status_code != requests.codes.ok:
|
|
raise exc.InfobloxCannotUpdateObject(
|
|
response=jsonutils.loads(r.content),
|
|
ref=ref,
|
|
content=r.content,
|
|
code=r.status_code)
|
|
|
|
return jsonutils.loads(r.content)
|
|
|
|
def delete_object(self, ref):
|
|
"""Remove an Infoblox object
|
|
|
|
Args:
|
|
ref (str): Object reference
|
|
Returns:
|
|
The object reference of the removed object
|
|
Raises:
|
|
InfobloxException
|
|
"""
|
|
r = self.session.delete(self._construct_url(ref),
|
|
verify=self.sslverify)
|
|
|
|
if r.status_code != requests.codes.ok:
|
|
raise exc.InfobloxCannotDeleteObject(
|
|
response=jsonutils.loads(r.content),
|
|
ref=ref,
|
|
content=r.content,
|
|
code=r.status_code)
|
|
|
|
return jsonutils.loads(r.content)
|