diff --git a/etc/tests/core.test.conf b/etc/tests/core.test.conf index 94c162f8c6..2c18c73477 100644 --- a/etc/tests/core.test.conf +++ b/etc/tests/core.test.conf @@ -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 } diff --git a/rsdns/__init__.py b/rsdns/__init__.py deleted file mode 100644 index 96445cd0ae..0000000000 --- a/rsdns/__init__.py +++ /dev/null @@ -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"] diff --git a/rsdns/client/__init__.py b/rsdns/client/__init__.py deleted file mode 100644 index 63a5e2364e..0000000000 --- a/rsdns/client/__init__.py +++ /dev/null @@ -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 diff --git a/rsdns/client/dns_client.py b/rsdns/client/dns_client.py deleted file mode 100644 index 2042744893..0000000000 --- a/rsdns/client/dns_client.py +++ /dev/null @@ -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) diff --git a/rsdns/client/domains.py b/rsdns/client/domains.py deleted file mode 100644 index c69c44ebd4..0000000000 --- a/rsdns/client/domains.py +++ /dev/null @@ -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) diff --git a/rsdns/client/exceptions.py b/rsdns/client/exceptions.py deleted file mode 100644 index 138af9f32a..0000000000 --- a/rsdns/client/exceptions.py +++ /dev/null @@ -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) diff --git a/rsdns/client/future.py b/rsdns/client/future.py deleted file mode 100644 index 170b89bf5b..0000000000 --- a/rsdns/client/future.py +++ /dev/null @@ -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() diff --git a/rsdns/client/records.py b/rsdns/client/records.py deleted file mode 100644 index b5f4178e90..0000000000 --- a/rsdns/client/records.py +++ /dev/null @@ -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) diff --git a/tox.ini b/tox.ini index 4ff380ccb7..6b0234a7cb 100644 --- a/tox.ini +++ b/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] diff --git a/trove/dns/rsdns/__init__.py b/trove/dns/rsdns/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/trove/dns/rsdns/driver.py b/trove/dns/rsdns/driver.py deleted file mode 100644 index ae6a10cbc7..0000000000 --- a/trove/dns/rsdns/driver.py +++ /dev/null @@ -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)