Remove rsdns directory
The rsdns service is no longer needed since trove now supports designate. It is not being tested in the gate and is currently unsupported. Remove other rsdns related files and references. Change-Id: I44009dace44afb5467c51def33c794641ffa33c0 Closes-Bug: #1454028
This commit is contained in:
parent
25e629315e
commit
8ad786e9be
@ -31,8 +31,5 @@
|
||||
"root_removed_from_instance_api": true,
|
||||
"root_timestamp_disabled": false,
|
||||
"openvz_disabled": false,
|
||||
"management_api_disabled": true,
|
||||
|
||||
"dns_instance_entry_factory":"trove.dns.rsdns.driver.RsDnsInstanceEntryFactory",
|
||||
"sentinel": null
|
||||
"management_api_disabled": true
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Dns Driver that uses Rackspace DNSaaS.
|
||||
"""
|
||||
|
||||
__all__ = ["client"]
|
@ -1,23 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
dnsclient module.
|
||||
"""
|
||||
|
||||
from rsdns.client.dns_client import DNSaas
|
||||
from rsdns.client.dns_client import DNSaasClient
|
||||
from rsdns.client.domains import DomainsManager
|
||||
from rsdns.client.records import RecordsManager
|
@ -1,125 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
DNS Client interface. Child of OpenStack client to handle auth issues.
|
||||
We have to duplicate a lot of code from the OpenStack client since so much
|
||||
is different here.
|
||||
"""
|
||||
|
||||
from trove.openstack.common import log as logging
|
||||
|
||||
import exceptions
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
|
||||
from novaclient.client import HTTPClient
|
||||
from novaclient.v2.client import Client
|
||||
|
||||
LOG = logging.getLogger('rsdns.client.dns_client')
|
||||
|
||||
|
||||
class DNSaasClient(HTTPClient):
|
||||
|
||||
def __init__(self, accountId, user, apikey, auth_url, management_base_url):
|
||||
tenant = "dbaas"
|
||||
super(DNSaasClient, self).__init__(user, apikey, tenant, auth_url)
|
||||
self.accountId = accountId
|
||||
self.management_base_url = management_base_url
|
||||
self.api_key = apikey
|
||||
self.disable_ssl_certificate_validation = True
|
||||
self.service = "cloudDNS"
|
||||
|
||||
def authenticate(self):
|
||||
"""Set the management url and auth token"""
|
||||
req_body = {'credentials': {'username': self.user,
|
||||
'key': self.api_key}}
|
||||
resp, body = self.request(self.auth_url, "POST", body=req_body)
|
||||
if 'access' in body:
|
||||
if not self.management_url:
|
||||
# Assume the new Keystone lite:
|
||||
catalog = body['access']['serviceCatalog']
|
||||
for service in catalog:
|
||||
if service['name'] == self.service:
|
||||
self.management_url = service['adminURL']
|
||||
self.auth_token = body['access']['token']['id']
|
||||
else:
|
||||
# Assume pre-Keystone Light:
|
||||
try:
|
||||
if not self.management_url:
|
||||
keys = ['auth',
|
||||
'serviceCatalog',
|
||||
self.service,
|
||||
0,
|
||||
'publicURL']
|
||||
url = body
|
||||
for key in keys:
|
||||
url = url[key]
|
||||
self.management_url = url
|
||||
self.auth_token = body['auth']['token']['id']
|
||||
except KeyError:
|
||||
raise NotImplementedError("Service: %s is not available"
|
||||
% self.service)
|
||||
|
||||
def request(self, *args, **kwargs):
|
||||
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||
kwargs['headers']['User-Agent'] = self.USER_AGENT
|
||||
kwargs['headers']['Accept'] = 'application/json'
|
||||
if 'body' in kwargs:
|
||||
kwargs['headers']['Content-Type'] = 'application/json'
|
||||
kwargs['body'] = json.dumps(kwargs['body'])
|
||||
LOG.debug("REQ HEADERS:" + str(kwargs['headers']))
|
||||
LOG.debug("REQ BODY:" + str(kwargs['body']))
|
||||
|
||||
resp, body = super(HTTPClient, self).request(*args, **kwargs)
|
||||
|
||||
self.http_log(args, kwargs, resp, body)
|
||||
|
||||
if body:
|
||||
try:
|
||||
body = json.loads(body)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
body = None
|
||||
|
||||
if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
|
||||
raise exceptions.from_response(resp, body)
|
||||
|
||||
return resp, body
|
||||
|
||||
|
||||
class DNSaas(Client):
|
||||
"""
|
||||
Top-level object to access the DNSaas service
|
||||
"""
|
||||
|
||||
def __init__(self, accountId, username, apikey,
|
||||
auth_url='https://auth.api.rackspacecloud.com/v1.0',
|
||||
management_base_url=None):
|
||||
from rsdns.client.dns_client import DNSaasClient
|
||||
from rsdns.client.domains import DomainsManager
|
||||
from rsdns.client.records import RecordsManager
|
||||
|
||||
super(DNSaas, self).__init__(self, accountId, username, apikey,
|
||||
auth_url, management_base_url)
|
||||
self.client = DNSaasClient(accountId, username, apikey, auth_url,
|
||||
management_base_url)
|
||||
self.domains = DomainsManager(self)
|
||||
self.records = RecordsManager(self)
|
@ -1,90 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Domains interface.
|
||||
"""
|
||||
|
||||
from novaclient import base
|
||||
|
||||
import os
|
||||
from rsdns.client.future import FutureResource
|
||||
|
||||
|
||||
class Domain(base.Resource):
|
||||
"""
|
||||
A Domain has a name and stores records. In the API they are id'd by ints.
|
||||
"""
|
||||
|
||||
def response_list_name(self):
|
||||
return "domains"
|
||||
|
||||
|
||||
class FutureDomain(FutureResource):
|
||||
|
||||
def convert_callback(self, resp, body):
|
||||
return Domain(self.manager, body)
|
||||
|
||||
def response_list_name(self):
|
||||
return "domains"
|
||||
|
||||
|
||||
class DomainsManager(base.ManagerWithFind):
|
||||
"""
|
||||
Manage :class:`Domain` resources.
|
||||
"""
|
||||
resource_class = Domain
|
||||
|
||||
def create(self, name):
|
||||
"""Not implemented / needed yet."""
|
||||
if os.environ.get("ADD_DOMAINS", "False") == 'True':
|
||||
accountId = self.api.client.accountId
|
||||
data = {"domains":
|
||||
[
|
||||
{"name": name,
|
||||
"ttl":"5600",
|
||||
"emailAddress":"dbaas_dns@rackspace.com",
|
||||
}
|
||||
]
|
||||
}
|
||||
resp, body = self.api.client.post("/domains", body=data)
|
||||
if resp.status == 202:
|
||||
return FutureDomain(self, **body)
|
||||
raise RuntimeError("Did not expect response " + str(resp.status))
|
||||
else:
|
||||
raise NotImplementedError("No need for create.")
|
||||
|
||||
def create_from_list(self, list):
|
||||
return [self.resource_class(self, res) for res in list]
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""Not implemented / needed yet."""
|
||||
raise NotImplementedError("No need for create.")
|
||||
|
||||
def list(self, name=None):
|
||||
"""
|
||||
Get a list of all domains.
|
||||
|
||||
:rtype: list of :class:`Domain`
|
||||
"""
|
||||
url = "/domains"
|
||||
if name:
|
||||
url += "?name=" + name
|
||||
resp, body = self.api.client.get(url)
|
||||
try:
|
||||
list = body['domains']
|
||||
except KeyError:
|
||||
raise RuntimeError('Body was missing "domains" key.')
|
||||
return self.create_from_list(list)
|
@ -1,53 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
from novaclient import exceptions
|
||||
|
||||
|
||||
class UnprocessableEntity(exceptions.ClientException):
|
||||
"""
|
||||
HTTP 422 - Unprocessable Entity: The request cannot be processed.
|
||||
"""
|
||||
http_status = 422
|
||||
message = "Unprocessable Entity"
|
||||
|
||||
|
||||
_code_map = {c.http_status: c for c in [UnprocessableEntity]}
|
||||
|
||||
|
||||
def from_response(response, body):
|
||||
"""
|
||||
Return an instance of an ClientException or subclass
|
||||
based on an httplib2 response.
|
||||
|
||||
Usage::
|
||||
|
||||
resp, body = http.request(...)
|
||||
if resp.status != 200:
|
||||
raise exception_from_response(resp, body)
|
||||
"""
|
||||
cls = _code_map.get(response.status, None)
|
||||
if not cls:
|
||||
cls = exceptions._code_map.get(response.status,
|
||||
exceptions.ClientException)
|
||||
if body:
|
||||
message = "n/a"
|
||||
details = "n/a"
|
||||
if hasattr(body, 'keys'):
|
||||
error = body[body.keys()[0]]
|
||||
message = error.get('message', None)
|
||||
details = error.get('details', None)
|
||||
return cls(code=response.status, message=message, details=details)
|
||||
else:
|
||||
return cls(code=response.status)
|
@ -1,54 +0,0 @@
|
||||
|
||||
|
||||
class RsDnsError(RuntimeError):
|
||||
|
||||
def __init__(self, error):
|
||||
self.error_msg = ""
|
||||
try:
|
||||
for message in error['validationErrors']['messages']:
|
||||
self.error_msg += message
|
||||
except KeyError:
|
||||
self.error_msg += "... (did not understand the RsDNS response)."
|
||||
super(RsDnsError, self).__init__(self.error_msg)
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
class FutureResource(object):
|
||||
"""Polls a callback url to return a resource."""
|
||||
|
||||
def __init__(self, manager, jobId, callbackUrl, status, **kwargs):
|
||||
self.manager = manager
|
||||
self.jobId = jobId
|
||||
self.callbackUrl = unicode(callbackUrl)
|
||||
self.result = None
|
||||
management_url = unicode(self.manager.api.client.management_url)
|
||||
if self.callbackUrl.startswith(management_url):
|
||||
self.callbackUrl = self.callbackUrl[len(management_url):]
|
||||
|
||||
def call_callback(self):
|
||||
return self.manager.api.client.get(self.callbackUrl +
|
||||
"?showDetails=true")
|
||||
|
||||
def poll(self):
|
||||
if not self.result:
|
||||
resp, body = self.call_callback()
|
||||
if resp.status == 202:
|
||||
return None
|
||||
if resp.status == 200:
|
||||
if body['status'] == 'ERROR':
|
||||
raise RsDnsError(body['error'])
|
||||
elif body['status'] != 'COMPLETED':
|
||||
return None
|
||||
resp_list = body['response'][self.response_list_name()]
|
||||
self.result = self.manager.create_from_list(resp_list)
|
||||
return self.result
|
||||
|
||||
@property
|
||||
def ready(self):
|
||||
return (self.result or self.poll()) is not None
|
||||
|
||||
@property
|
||||
def resource(self):
|
||||
return self.result or self.poll()
|
@ -1,146 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Records interface.
|
||||
"""
|
||||
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from novaclient import base
|
||||
from rsdns.client.future import FutureResource
|
||||
|
||||
|
||||
class FutureRecord(FutureResource):
|
||||
|
||||
def convert_callback(self, resp, body):
|
||||
try:
|
||||
list = body['records']
|
||||
except NameError:
|
||||
raise RuntimeError('Body was missing "records" or "record" key.')
|
||||
if len(list) != 1:
|
||||
raise RuntimeError('Return result had ' + str(len(list)) +
|
||||
'records, not 1.')
|
||||
return Record(self, list[0])
|
||||
|
||||
def response_list_name(self):
|
||||
return "records"
|
||||
|
||||
|
||||
class Record(base.Resource):
|
||||
"""
|
||||
A Record is a individual dns record (Cname, A, MX, etc..)
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RecordsManager(base.ManagerWithFind):
|
||||
"""
|
||||
Manage :class:`Record` resources.
|
||||
"""
|
||||
resource_class = Record
|
||||
|
||||
def create(self, domain, record_name, record_data, record_type,
|
||||
record_ttl):
|
||||
"""
|
||||
Create a new Record on the given domain
|
||||
|
||||
:param domain: The ID of the :class:`Domain` to get.
|
||||
:param record: The ID of the :class:`Record` to get.
|
||||
:rtype: :class:`Record`
|
||||
"""
|
||||
data = {"records": [{"type": record_type, "name": record_name,
|
||||
"data": record_data, "ttl": record_ttl}]}
|
||||
resp, body = self.api.client.post("/domains/%s/records" % \
|
||||
base.getid(domain), body=data)
|
||||
if resp.status == 202:
|
||||
return FutureRecord(self, **body)
|
||||
raise RuntimeError("Did not expect response when creating a DNS "
|
||||
"record %s" % str(resp.status))
|
||||
|
||||
def create_from_list(self, list):
|
||||
return [self.resource_class(self, res) for res in list]
|
||||
|
||||
def delete(self, domain_id, record_id):
|
||||
self._delete("/domains/%s/records/%s" % (domain_id, record_id))
|
||||
|
||||
def match_record(self, record, name=None, address=None, type=None):
|
||||
assert(isinstance(record, Record))
|
||||
return (not name or record.name == name) and \
|
||||
(not address or record.data == address) and \
|
||||
(not type or record.type == type)
|
||||
|
||||
def get(self, domain_id, record_id):
|
||||
"""
|
||||
Get a single record by id.
|
||||
|
||||
:rtype: Single instance of :class:`Record`
|
||||
"""
|
||||
url = "/domains/%s/records" % domain_id
|
||||
if record_id:
|
||||
url += ("/%s" % record_id)
|
||||
resp, body = self.api.client.get(url)
|
||||
try:
|
||||
item = body
|
||||
except IndexError:
|
||||
raise RuntimeError('Body was missing record element.')
|
||||
return self.resource_class(self, item)
|
||||
|
||||
def list(self, domain_id, record_id=None, record_name=None,
|
||||
record_address=None, record_type=None):
|
||||
"""
|
||||
Get a list of all records under a domain.
|
||||
|
||||
:rtype: list of :class:`Record`
|
||||
"""
|
||||
url = "/domains/%s/records" % domain_id
|
||||
if record_id:
|
||||
url += ("/%s" % record_id)
|
||||
offset = 0
|
||||
list = []
|
||||
while offset is not None:
|
||||
next_url = "%s?offset=%d" % (url, offset)
|
||||
partial_list, offset = self.page_list(next_url)
|
||||
list += partial_list
|
||||
all_records = self.create_from_list(list)
|
||||
return [record for record in all_records
|
||||
if self.match_record(record, record_name, record_address,
|
||||
record_type)]
|
||||
|
||||
def page_list(self, url):
|
||||
"""
|
||||
Given a URL and an offset, returns a tuple containing a list and the
|
||||
next URL.
|
||||
"""
|
||||
resp, body = self.api.client.get(url)
|
||||
try:
|
||||
list = body['records']
|
||||
except NameError:
|
||||
raise RuntimeError('Body was missing "records" or "record" key.')
|
||||
next_offset = None
|
||||
links = body.get('links', [])
|
||||
for link in links:
|
||||
if link['rel'] == 'next':
|
||||
next = link['href']
|
||||
params = urlparse.parse_qs(urlparse.urlparse(next).query)
|
||||
offset_list = params.get('offset', [])
|
||||
if len(offset_list) == 1:
|
||||
next_offset = int(offset_list[0])
|
||||
elif len(offset_list) == 0:
|
||||
next_offset = None
|
||||
else:
|
||||
raise RuntimeError("Next href had multiple offset params!")
|
||||
return (list, next_offset)
|
2
tox.ini
2
tox.ini
@ -45,7 +45,7 @@ show-source = True
|
||||
# The rest of the ignores are TODOs.
|
||||
ignore = F821,H237,H238,H301,H305,H307,H402,H404,H405,H407,H501,H904
|
||||
builtins = _
|
||||
exclude=.venv,.tox,dist,doc,openstack,*egg,rsdns,tools,etc,build,*.po,*.pot
|
||||
exclude=.venv,.tox,dist,doc,openstack,*egg,tools,etc,build,*.po,*.pot
|
||||
filename=*.py,trove-*
|
||||
|
||||
[testenv:checklinks]
|
||||
|
@ -1,225 +0,0 @@
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Dns Driver that uses Rackspace DNSaaS.
|
||||
"""
|
||||
|
||||
__version__ = '2.4'
|
||||
|
||||
import hashlib
|
||||
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common.exception import NotFound
|
||||
from trove.dns.models import DnsRecord
|
||||
from rsdns.client import DNSaas
|
||||
from rsdns.client.future import RsDnsError
|
||||
|
||||
from trove.dns.driver import DnsEntry
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
DNS_ACCOUNT_ID = CONF.dns_account_id
|
||||
DNS_AUTH_URL = CONF.dns_auth_url
|
||||
DNS_DOMAIN_NAME = CONF.dns_domain_name
|
||||
DNS_USERNAME = CONF.dns_username
|
||||
DNS_PASSKEY = CONF.dns_passkey
|
||||
DNS_MANAGEMENT_BASE_URL = CONF.dns_management_base_url
|
||||
DNS_TTL = CONF.dns_ttl
|
||||
DNS_DOMAIN_ID = CONF.dns_domain_id
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EntryToRecordConverter(object):
|
||||
|
||||
def __init__(self, default_dns_zone):
|
||||
self.default_dns_zone = default_dns_zone
|
||||
|
||||
def domain_to_dns_zone(self, domain):
|
||||
return RsDnsZone(id=domain.id, name=domain.name)
|
||||
|
||||
def name_to_long_name(self, name, dns_zone=None):
|
||||
dns_zone = dns_zone or self.default_dns_zone
|
||||
if name:
|
||||
long_name = name + "." + dns_zone.name
|
||||
else:
|
||||
long_name = ""
|
||||
return long_name
|
||||
|
||||
def record_to_entry(self, record, dns_zone):
|
||||
entry_name = record.name
|
||||
return DnsEntry(name=entry_name, content=record.data,
|
||||
type=record.type, ttl=record.ttl, dns_zone=dns_zone)
|
||||
|
||||
|
||||
def create_client_with_flag_values():
|
||||
"""Creates a RS DNSaaS client using the Flag values."""
|
||||
if DNS_MANAGEMENT_BASE_URL is None:
|
||||
raise RuntimeError("Missing flag value for dns_management_base_url.")
|
||||
return DNSaas(DNS_ACCOUNT_ID, DNS_USERNAME, DNS_PASSKEY,
|
||||
auth_url=DNS_AUTH_URL,
|
||||
management_base_url=DNS_MANAGEMENT_BASE_URL)
|
||||
|
||||
|
||||
def find_default_zone(dns_client, raise_if_zone_missing=True):
|
||||
"""Using the domain_name from the FLAG values, creates a zone.
|
||||
|
||||
Because RS DNSaaS needs the ID, we need to find this value before we start.
|
||||
In testing it's difficult to keep up with it because the database keeps
|
||||
getting wiped... maybe later we could go back to storing it as a FLAG value
|
||||
|
||||
"""
|
||||
domain_name = DNS_DOMAIN_NAME
|
||||
try:
|
||||
domains = dns_client.domains.list(name=domain_name)
|
||||
for domain in domains:
|
||||
if domain.name == domain_name:
|
||||
return RsDnsZone(id=domain.id, name=domain_name)
|
||||
except NotFound:
|
||||
pass
|
||||
if not raise_if_zone_missing:
|
||||
return RsDnsZone(id=None, name=domain_name)
|
||||
msg = ("The dns_domain_name from the FLAG values (%s) "
|
||||
"does not exist! account_id=%s, username=%s, LIST=%s")
|
||||
params = (domain_name, DNS_ACCOUNT_ID, DNS_USERNAME, domains)
|
||||
raise RuntimeError(msg % params)
|
||||
|
||||
|
||||
class RsDnsDriver(object):
|
||||
"""Uses RS DNSaaS"""
|
||||
|
||||
def __init__(self, raise_if_zone_missing=True):
|
||||
self.dns_client = create_client_with_flag_values()
|
||||
self.dns_client.authenticate()
|
||||
self.default_dns_zone = RsDnsZone(id=DNS_DOMAIN_ID,
|
||||
name=DNS_DOMAIN_NAME)
|
||||
self.converter = EntryToRecordConverter(self.default_dns_zone)
|
||||
if DNS_TTL < 300:
|
||||
msg = "TTL value '--dns_ttl=%s' should be greater than 300"
|
||||
raise Exception(msg % DNS_TTL)
|
||||
|
||||
def create_entry(self, entry, content):
|
||||
dns_zone = entry.dns_zone or self.default_dns_zone
|
||||
if dns_zone.id is None:
|
||||
raise TypeError("The entry's dns_zone must have an ID specified.")
|
||||
name = entry.name # + "." + dns_zone.name
|
||||
LOG.debug("Going to create RSDNS entry %s." % name)
|
||||
try:
|
||||
future = self.dns_client.records.create(
|
||||
domain=dns_zone.id,
|
||||
record_name=name,
|
||||
record_data=content,
|
||||
record_type=entry.type,
|
||||
record_ttl=entry.ttl)
|
||||
try:
|
||||
#TODO: Bring back our good friend poll_until.
|
||||
while(future.ready is False):
|
||||
import time
|
||||
time.sleep(2)
|
||||
LOG.info("Waiting for the dns record_id.. ")
|
||||
|
||||
if len(future.resource) < 1:
|
||||
raise RsDnsError("No DNS records were created.")
|
||||
elif len(future.resource) > 1:
|
||||
LOG.error("More than one DNS record created. Ignoring.")
|
||||
actual_record = future.resource[0]
|
||||
DnsRecord.create(name=name, record_id=actual_record.id)
|
||||
LOG.debug("Added RS DNS entry.")
|
||||
except RsDnsError as rde:
|
||||
LOG.error("An error occurred creating DNS entry!")
|
||||
raise
|
||||
except Exception as ex:
|
||||
LOG.error("Error when creating a DNS record!")
|
||||
raise
|
||||
|
||||
def delete_entry(self, name, type, dns_zone=None):
|
||||
dns_zone = dns_zone or self.default_dns_zone
|
||||
long_name = name
|
||||
db_record = DnsRecord.find_by(name=name)
|
||||
record = self.dns_client.records.get(
|
||||
domain_id=dns_zone.id,
|
||||
record_id=db_record.record_id)
|
||||
if record.name != name or record.type != 'A':
|
||||
LOG.error("Tried to delete DNS record with name=%s, id=%s, but the"
|
||||
" database returned a DNS record with the name %s and "
|
||||
"type %s." % (name, db_record.id, record.name,
|
||||
record.type))
|
||||
raise exception.DnsRecordNotFound(name)
|
||||
self.dns_client.records.delete(
|
||||
domain_id=dns_zone.id,
|
||||
record_id=record.id)
|
||||
db_record.delete()
|
||||
|
||||
def get_entries(self, name=None, content=None, dns_zone=None):
|
||||
dns_zone = dns_zone or self.defaucreate_entrylt_dns_zone
|
||||
long_name = name # self.converter.name_to_long_name(name)
|
||||
records = self.dns_client.records.list(
|
||||
domain_id=dns_zone.id,
|
||||
record_name=long_name,
|
||||
record_address=content)
|
||||
return [self.converter.record_to_entry(record, dns_zone)
|
||||
for record in records]
|
||||
|
||||
def get_entries_by_content(self, content, dns_zone=None):
|
||||
return self.get_entries(content=content)
|
||||
|
||||
def get_entries_by_name(self, name, dns_zone=None):
|
||||
return self.get_entries(name=name, dns_zone=dns_zone)
|
||||
|
||||
def get_dns_zones(self, name=None):
|
||||
domains = self.dns_client.domains.list(name=name)
|
||||
return [self.converter.domain_to_dns_zone(domain)
|
||||
for domain in domains]
|
||||
|
||||
def modify_content(self, *args, **kwargs):
|
||||
raise NotImplementedError("Not implemented for RS DNS.")
|
||||
|
||||
def rename_entry(self, *args, **kwargs):
|
||||
raise NotImplementedError("Not implemented for RS DNS.")
|
||||
|
||||
|
||||
class RsDnsInstanceEntryFactory(object):
|
||||
"""Defines how instance DNS entries are created for instances."""
|
||||
|
||||
def __init__(self, dns_domain_id=None):
|
||||
dns_domain_id = dns_domain_id or DNS_DOMAIN_ID
|
||||
self.default_dns_zone = RsDnsZone(id=dns_domain_id,
|
||||
name=DNS_DOMAIN_NAME)
|
||||
|
||||
def create_entry(self, instance_id):
|
||||
id = instance_id
|
||||
hostname = ("%s.%s" % (hashlib.sha1(id).hexdigest(),
|
||||
self.default_dns_zone.name))
|
||||
return DnsEntry(name=hostname, content=None, type="A", ttl=DNS_TTL,
|
||||
dns_zone=self.default_dns_zone)
|
||||
|
||||
|
||||
class RsDnsZone(object):
|
||||
|
||||
def __init__(self, id, name):
|
||||
self.name = name
|
||||
self.id = id
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, RsDnsZone) and
|
||||
self.name == other.name and
|
||||
self.id == other.id)
|
||||
|
||||
def __str__(self):
|
||||
return "%s:%s" % (self.id, self.name)
|
Loading…
x
Reference in New Issue
Block a user