Adds Rackspace DNS Driver

Change-Id: Id49e24364ab0aa39245fc287960451e4233569bc
This commit is contained in:
Obulpathi 2014-11-07 15:15:25 -05:00
parent 5b4361bc0b
commit b893de24de
26 changed files with 647 additions and 64 deletions

View File

@ -71,13 +71,16 @@ replication_strategy = class:SimpleStrategy, replication_factor:1
[drivers:storage:mockdb]
database = poppy
[drivers:dns:rackspace]
project_id = "<operator_project_id>"
api_key = "<operator_api_key>"
use_shards = True
num_shards = 500
shard_prefix = "cdn_"
num_shards = 499
shard_prefix = "cdn"
url = "poppycdn.net"
# You email associated with DNS, for notifications
email = "your@email.com"
[drivers:provider:fastly]
apikey = "MYAPIKEY"

View File

@ -18,4 +18,4 @@ from poppy.dns.base import services
Driver = driver.DNSDriverBase
ServiceBase = services.ServicesControllerBase
ServicesBase = services.ServicesControllerBase

View File

@ -43,14 +43,29 @@ class DNSDriverBase(object):
:raises NotImplementedError
"""
raise NotImplementedError
@abc.abstractproperty
def dns_name(self):
"""Name of this provider.
:raises NotImplementedError
"""
raise NotImplementedError
@property
def client(self):
"""Client for this provider.
:raises NotImplementedError
"""
raise NotImplementedError
@abc.abstractproperty
def service_controller(self):
def services_controller(self):
"""Returns the driver's hostname controller.
:raises NotImplementedError

View File

@ -0,0 +1,62 @@
# Copyright (c) 2014 Rackspace, Inc.
#
# 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 traceback
class Responder(object):
"""Responder Class."""
def __init__(self, dns_name):
self.dns = dns_name
def failed(self, msg):
"""failed.
:param msg
:returns {error, error details}
"""
return {
'error': msg,
'error_detail': traceback.format_exc()
}
def created(self, dns_details):
"""created.
:param dns_details
:returns dns_details
"""
return dns_details
def updated(self, dns_details):
"""updated.
:param dns_details
:returns dns_details
"""
return dns_details
def deleted(self, dns_details):
"""deleted.
:param dns_details
:returns dns_details
"""
return dns_details

View File

@ -18,6 +18,7 @@ import abc
import six
from poppy.dns.base import controller
from poppy.dns.base import responder
@six.add_metaclass(abc.ABCMeta)
@ -27,3 +28,34 @@ class ServicesControllerBase(controller.DNSControllerBase):
def __init__(self, driver):
super(ServicesControllerBase, self).__init__(driver)
self.responder = responder.Responder(driver.dns_name)
def update(self, service_old, service_updates, responders):
"""update.
:param service_old: previous service state
:param service_updates: updates to service state
:param responders: responders from providers
:raises NotImplementedError
"""
raise NotImplementedError
def delete(self, provider_details):
"""delete.
:param provider_details
:raises NotImplementedError
"""
raise NotImplementedError
def create(self, responders):
"""create.
:param responders
:raises NotImplementedError
"""
raise NotImplementedError

View File

@ -24,4 +24,4 @@ Field Mappings:
from poppy.dns.default import services
ServiceController = services.ServiceController
ServicesController = services.ServicesController

View File

@ -23,17 +23,42 @@ LOG = logging.getLogger(__name__)
class DNSProvider(base.Driver):
"""Default DNS Provider."""
def __init__(self, conf):
super(DNSProvider, self).__init__(conf)
def is_alive(self):
return False
"""is_alive.
:return boolean
"""
return True
@property
def dns_name(self):
return "Default"
"""DNS provider name.
:return 'Default'
"""
return 'Default'
@property
def service_controller(self):
return controllers.ServiceController(self)
def client(self):
"""Client to this provider.
:return None
"""
return None
@property
def services_controller(self):
"""Hook for service controller.
:return service_controller
"""
return controllers.ServicesController(self)

View File

@ -16,9 +16,64 @@
from poppy.dns import base
class ServiceController(base.ServiceBase):
class ServicesController(base.ServicesBase):
def __init__(self, driver):
super(ServiceController, self).__init__(driver)
super(ServicesController, self).__init__(driver)
self.driver = driver
def update(self, service_old, service_updates, responders):
"""Default DNS update.
:param service_old: previous service state
:param service_updates: updates to service state
:param responders: responders from providers
"""
dns_details = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
access_urls = []
for link in responder[provider_name]['links']:
access_url = {
'domain': link['domain'],
'provider_url': link['href'],
'operator_url': link['href']}
access_urls.append(access_url)
dns_details[provider_name] = {'access_urls': access_urls}
return self.responder.created(dns_details)
def delete(self, provider_details):
"""Default DNS delete.
:param provider_details
"""
dns_details = {}
for provider_name in provider_details:
dns_details[provider_name] = self.responder.deleted({})
return dns_details
def create(self, responders):
"""Default DNS create.
:param responders: responders from providers
"""
dns_details = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
access_urls = []
for link in responder[provider_name]['links']:
access_url = {
'domain': link['domain'],
'provider_url': link['href'],
'operator_url': link['href']}
access_urls.append(access_url)
dns_details[provider_name] = {'access_urls': access_urls}
return self.responder.created(dns_details)

View File

@ -24,4 +24,4 @@ Field Mappings:
from poppy.dns.designate import services
ServiceController = services.ServiceController
ServicesController = services.ServicesController

View File

@ -16,9 +16,9 @@
from poppy.dns import base
class ServiceController(base.ServiceBase):
class ServicesController(base.ServicesBase):
def __init__(self, driver):
super(ServiceController, self).__init__(driver)
super(ServicesController, self).__init__(driver)
self.driver = driver

View File

@ -24,4 +24,4 @@ Field Mappings:
from poppy.dns.rackspace import services
ServiceController = services.ServiceController
ServicesController = services.ServicesController

View File

@ -16,6 +16,7 @@
"""DNS Provider implementation."""
from oslo.config import cfg
import pyrax
from poppy.dns import base
from poppy.dns.rackspace import controllers
@ -34,6 +35,8 @@ RACKSPACE_OPTIONS = [
help='The shard prefix to use'),
cfg.StrOpt('url', default='',
help='The url for customers to CNAME to'),
cfg.StrOpt('email', help='The email to be provided to Rackspace DNS for'
'creating subdomains'),
]
RACKSPACE_GROUP = 'drivers:dns:rackspace'
@ -42,17 +45,51 @@ LOG = logging.getLogger(__name__)
class DNSProvider(base.Driver):
"""Rackspace DNS Provider."""
def __init__(self, conf):
super(DNSProvider, self).__init__(conf)
self._conf.register_opts(RACKSPACE_OPTIONS, group=RACKSPACE_GROUP)
self.rackdns_conf = self._conf[RACKSPACE_GROUP]
pyrax.set_setting("identity_type", "rackspace")
pyrax.set_credentials(self.rackdns_conf.project_id,
self.rackdns_conf.api_key)
self.rackdns_client = pyrax.cloud_dns
def is_alive(self):
"""is_alive.
:return boolean
"""
# TODO(obulpathi): Implement health check
# and add DNS to health endpoint
return True
@property
def dns_name(self):
return "Rackspace Cloud DNS"
"""DNS provider name.
:return 'Rackspace Cloud DNS'
"""
return 'Rackspace Cloud DNS'
@property
def service_controller(self):
return controllers.ServiceController(self)
def client(self):
"""Client to this provider.
:return client
"""
return self.rackdns_client
@property
def services_controller(self):
"""Hook for service controller.
:return service_controller
"""
return controllers.ServicesController(self)

View File

@ -13,12 +13,334 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import random
import sets
import pyrax.exceptions as exc
from poppy.dns import base
from poppy.openstack.common import log
LOG = log.getLogger(__name__)
class ServiceController(base.ServiceBase):
class ServicesController(base.ServicesBase):
def __init__(self, driver):
super(ServiceController, self).__init__(driver)
super(ServicesController, self).__init__(driver)
self.driver = driver
self.client = driver.client
def _get_subdomain(self, subdomain_name):
"""Returns a subdomain, if it does not exist, create it
:param subdomain_name
:return subdomain
"""
try:
subdomain = self.client.find(name=subdomain_name)
except exc.NotFound:
subdomain = self.client.create(
name=subdomain_name,
emailAddress=self._driver.rackdns_conf.email,
ttl=900)
return subdomain
def _create_cname_records(self, links):
"""Creates a subdomain
:param links: Access URLS from providers
:return dns_links: Map from provider access URL to DNS access URL
"""
cdn_domain_name = self._driver.rackdns_conf.url
shard_prefix = self._driver.rackdns_conf.shard_prefix
num_shards = self._driver.rackdns_conf.num_shards
# randomly select a shard
shard_id = random.randint(0, num_shards - 1)
subdomain_name = '{0}{1}.{2}'.format(shard_prefix, shard_id,
cdn_domain_name)
subdomain = self._get_subdomain(subdomain_name)
# create CNAME record for adding
cname_records = []
dns_links = {}
for link in links:
name = '{0}.{1}'.format(link, subdomain_name)
cname_record = {'type': 'CNAME',
'name': name,
'data': links[link],
'ttl': 300}
dns_links[links[link]] = name
cname_records.append(cname_record)
# add the cname records
subdomain.add_records(cname_records)
return dns_links
def _delete_cname_record(self, access_url):
"""Delete a CNAME record
:param access_url: DNS Access URL
:return error_msg: returns error message, if any
"""
# extract shard name
shard_name = access_url.split('.')[-3]
subdomain_name = '.'.join([shard_name, self._driver.rackdns_conf.url])
# get subdomain
subdomain = self.client.find(name=subdomain_name)
# search and find the CNAME record
name = access_url
record_type = 'CNAME'
records = self.client.search_records(subdomain, record_type, name)
# delete the record
# we should get one record,
# or none if it has been deleted already
if not records:
LOG.info('DNS record already deleted: {0}'.format(access_url))
elif len(records) == 1:
LOG.info('Deleting DNS records for : {0}'.format(access_url))
records[0].delete()
elif len(records) > 1:
error_msg = 'Multiple DNS records found: {0}'.format(access_url)
return error_msg
return
def create(self, responders):
"""Create CNAME record for a service.
:param responders: responders from providers
:return dns_links: Map from provider urls to DNS urls
"""
# gather the provider urls and cname them
links = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
for link in responder[provider_name]['links']:
if link['rel'] == 'access_url':
links[link['domain']] = link['href']
# create CNAME records
try:
dns_links = self._create_cname_records(links)
except Exception as e:
error_msg = 'Rackspace DNS Exception: {0}'.format(e)
LOG.error(error_msg)
return self.responder.failed(error_msg)
# gather the CNAMED links
dns_details = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
access_urls = []
for link in responder[provider_name]['links']:
if link['rel'] == 'access_url':
access_url = {
'domain': link['domain'],
'provider_url': link['href'],
'operator_url': dns_links[link['href']]}
access_urls.append(access_url)
dns_details[provider_name] = {'access_urls': access_urls}
return self.responder.created(dns_details)
def delete(self, provider_details):
"""Delete CNAME records for a service.
:param provider_details
:return dns_details: Map from provider_name to delete errors
"""
dns_details = {}
for provider_name in provider_details:
error_msg = ''
access_urls = provider_details[provider_name].access_urls
for access_url in access_urls:
try:
msg = self._delete_cname_record(access_url['operator_url'])
if msg:
error_msg = error_msg + msg
except exc.NotFound as e:
LOG.error('Can not access the subdomain. Please make sure'
' it exists and you have permissions to CDN '
'subdomain {0}'.format(e))
error_msg = (error_msg + 'Can not access subdomain . '
'Exception: {0}'.format(e))
except Exception as e:
LOG.error('Exception: {0}'.format(e))
error_msg = error_msg + 'Exception: {0}'.format(e)
# format the error or success message for this provider
if error_msg:
dns_details[provider_name] = self.responder.failed(error_msg)
else:
dns_details[provider_name] = self.responder.deleted({})
return dns_details
def _update_added_domains(self, responders, added_domains):
"""Update added domains."""
# if no domains are added, return
dns_details = {}
if not added_domains:
for responder in responders:
for provider_name in responder:
dns_details[provider_name] = {'access_urls': {}}
return dns_details
# gather the provider links for the added domains
links = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
for link in responder[provider_name]['links']:
domain_added = (link['rel'] == 'access_url' and
link['domain'] in added_domains)
if domain_added:
links[link['domain']] = link['href']
# create CNAME records for added domains
try:
dns_links = self._create_cname_records(links)
except Exception as e:
error_msg = 'Rackspace DNS Exception: {0}'.format(e)
LOG.error(error_msg)
return self.responder.failed(error_msg)
# gather the CNAMED links for added domains
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
access_urls = {}
for link in responder[provider_name]['links']:
if link['domain'] in added_domains:
access_urls[link['href']] = dns_links[link['href']]
dns_details[provider_name] = {'access_urls': access_urls}
return dns_details
def _update_removed_domains(self, provider_details, removed_domains):
"""Update removed domains."""
# if no domains are removed, return
dns_details = {}
if not removed_domains:
for provider_name in provider_details:
dns_details[provider_name] = {'access_urls': {}}
return dns_details
# delete the records for deleted domains
for provider_name in provider_details:
error_msg = ''
provider_detail = provider_details[provider_name]
for access_url in provider_detail.access_urls:
if access_url['domain'] not in removed_domains:
continue
try:
msg = self._delete_cname_record(access_url['operator_url'])
if msg:
error_msg = error_msg + msg
except exc.NotFound as e:
LOG.error('Can not access the subdomain. Please make sure'
' it exists and you have permissions to CDN '
'subdomain {0}'.format(e))
error_msg = (error_msg + 'Can not access subdomain. '
'Exception: {0}'.format(e))
except Exception as e:
LOG.error('Exception: {0}'.format(e))
error_msg = error_msg + 'Exception: {0}'.format(e)
# format the error or success message for this provider
if error_msg:
dns_details[provider_name] = self.responder.failed(error_msg)
else:
dns_details[provider_name] = self.responder.deleted({})
return dns_details
def update(self, service_old, service_updates, responders):
"""Update CNAME records for a service.
:param service_old: previous service state
:param service_updates: updates to service state
:param responders: responders from providers
:return dns_details: Map from provider_name to update errors
"""
# get old domains
old_domains = sets.Set()
old_access_urls_map = {}
provider_details = service_old.provider_details
for provider_name in provider_details:
provider_detail = provider_details[provider_name]
access_urls = provider_detail.access_urls
old_access_urls_map[provider_name] = {'access_urls': access_urls}
for access_url in access_urls:
old_domains.add(access_url['domain'])
# get new_domains
new_domains = sets.Set()
for responder in responders:
for provider_name in responder:
links = responder[provider_name]['links']
for link in links:
new_domains.add(link['domain'])
# if domains have not been updated, return
if not service_updates.domains:
return old_access_urls_map
# if the old set of domains is the same as new set of domains, return
if old_domains == new_domains:
return old_access_urls_map
# get the list of added, removed and common domains
added_domains = new_domains.difference(old_domains)
removed_domains = old_domains.difference(new_domains)
common_domains = new_domains.intersection(old_domains)
# add new domains
dns_links = self._update_added_domains(responders, added_domains)
# remove CNAME records for deleted domains
provider_details = service_old.provider_details
self._update_removed_domains(provider_details, removed_domains)
# gather the CNAMED links and remove stale links
dns_details = {}
for responder in responders:
for provider_name in responder:
if 'error' in responder[provider_name]:
continue
provider_detail = service_old.provider_details[provider_name]
old_access_urls = provider_detail.access_urls
operator_urls = dns_links[provider_name]['access_urls']
access_urls = []
for link in responder[provider_name]['links']:
if link['domain'] in removed_domains:
continue
elif link['domain'] in added_domains:
operator_url = operator_urls[link['href']]
access_url = {
'domain': link['domain'],
'provider_url': link['href'],
'operator_url': operator_url}
access_urls.append(access_url)
elif link['domain'] in common_domains:
# iterate through old access urls and get access url
operator_url = None
for old_access_url in old_access_urls:
if old_access_url['domain'] == link['domain']:
operator_url = old_access_url['operator_url']
break
access_url = {
'domain': link['domain'],
'provider_url': link['href'],
'operator_url': operator_url}
access_urls.append(access_url)
dns_details[provider_name] = {'access_urls': access_urls}
return self.responder.updated(dns_details)

View File

@ -29,29 +29,34 @@ def service_create_worker(providers_list, service_controller,
service_obj)
responders.append(responder)
# create dns mapping
dns = service_controller.dns_controller
dns_responder = dns.create(responders)
provider_details_dict = {}
for responder in responders:
for provider_name in responder:
if 'error' not in responder[provider_name]:
if 'error' in responder[provider_name]:
error_info = responder[provider_name]['error_detail']
provider_details_dict[provider_name] = (
provider_details.ProviderDetail(error_info=error_info))
provider_details_dict[provider_name].status = 'failed'
elif 'error' in dns_responder[provider_name]:
error_info = dns_responder[provider_name]['error_detail']
provider_details_dict[provider_name] = (
provider_details.ProviderDetail(error_info=error_info))
provider_details_dict[provider_name].status = 'failed'
else:
access_urls = dns_responder[provider_name]['access_urls']
provider_details_dict[provider_name] = (
provider_details.ProviderDetail(
provider_service_id=responder[provider_name]['id'],
access_urls=[link['href'] for link in
responder[provider_name]['links']])
)
access_urls=access_urls))
if 'status' in responder[provider_name]:
provider_details_dict[provider_name].status = (
responder[provider_name]['status'])
else:
provider_details_dict[provider_name].status = (
'deployed')
else:
provider_details_dict[provider_name] = (
provider_details.ProviderDetail(
error_info=responder[provider_name]['error_detail']
)
)
provider_details_dict[provider_name].status = 'failed'
provider_details_dict[provider_name].status = 'deployed'
service_controller.storage_controller.update_provider_details(
project_id,

View File

@ -31,6 +31,9 @@ def service_delete_worker(provider_details, service_controller,
responders.append(responder)
LOG.info('Deleting service from %s complete...' % provider)
# delete associated cname records from DNS
dns_responder = service_controller.dns_controller.delete(provider_details)
for responder in responders:
# this is the item of responder, if there's "error"
# key in it, it means the deletion for this provider failed.
@ -43,13 +46,19 @@ def service_delete_worker(provider_details, service_controller,
(provider_name, service_name))
# stores the error info for debugging purposes.
provider_details[provider_name].error_info = (
responder[provider_name].get('error_info')
)
responder[provider_name].get('error_info'))
elif 'error' in dns_responder[provider_name]:
LOG.info('Delete service from DNS failed')
LOG.info('Updating provider detail status of %s for %s'.foramt(
(provider_name, service_name)))
# stores the error info for debugging purposes.
provider_details[provider_name].error_info = (
dns_responder[provider_name].get('error_info'))
else:
# delete service successful, remove this provider detail record
del provider_details[provider_name]
service_controller.storage_controller._driver.connect()
service_controller.storage_controller._driver.connect()
if provider_details == {}:
# Only if all provider successfully deleted we can delete
# the poppy service.
@ -65,4 +74,4 @@ def service_delete_worker(provider_details, service_controller,
service_controller.storage_controller.update_provider_details(
project_id,
service_name,
provider_details)
provider_details)

View File

@ -32,17 +32,20 @@ def update_worker(service_controller, project_id, service_name,
responders.append(responder)
LOG.info(u'Updating service from {0} complete'.format(provider))
# create dns mapping
dns = service_controller.dns_controller
dns_responder = dns.update(service_old, service_updates, responders)
# gather links and status for service from providers
provider_details_dict = {}
for responder in responders:
for provider_name in responder:
if 'error' not in responder[provider_name]:
access_urls = dns_responder[provider_name]['access_urls']
provider_details_dict[provider_name] = (
provider_details.ProviderDetail(
provider_service_id=responder[provider_name]['id'],
access_urls=[link['href'] for link in
responder[provider_name]['links']])
)
access_urls=access_urls))
if 'status' in responder[provider_name]:
provider_details_dict[provider_name].status = (
responder[provider_name]['status'])

View File

@ -32,6 +32,7 @@ class DefaultServicesController(base.ServicesController):
self.storage_controller = self._driver.storage.services_controller
self.flavor_controller = self._driver.storage.flavors_controller
self.dns_controller = self._driver.dns.services_controller
def _get_provider_details(self, project_id, service_name):
try:
@ -86,6 +87,7 @@ class DefaultServicesController(base.ServicesController):
raise e
self.storage_controller._driver.close_connection()
p = multiprocessing.Process(
name='Process: create poppy service %s for'
' project id: %s' %
@ -141,6 +143,7 @@ class DefaultServicesController(base.ServicesController):
provider_details)
self.storage_controller._driver.close_connection()
p = multiprocessing.Process(
name=('Process: update poppy service {0} for project id: {1}'
.format(service_name, project_id)),
@ -172,6 +175,7 @@ class DefaultServicesController(base.ServicesController):
provider_details)
self.storage_controller._driver.close_connection()
p = multiprocessing.Process(
name='Process: delete poppy service %s for'
' project id: %s' %
@ -184,6 +188,7 @@ class DefaultServicesController(base.ServicesController):
project_id,
service_name))
p.start()
return
def purge(self, project_id, service_name, purge_url=None):

View File

@ -26,7 +26,7 @@ class ProviderDetail(object):
"""ProviderDetail object for each provider."""
def __init__(self, provider_service_id=None, access_urls=[],
def __init__(self, provider_service_id=None, access_urls={},
status=u"deploy_in_progress", name=None, error_info=None):
self._provider_service_id = provider_service_id
self._id = provider_service_id
@ -44,14 +44,14 @@ class ProviderDetail(object):
def provider_service_id(self, value):
self._provider_service_id = value
@property
def access_urls(self):
return self._access_urls
@property
def name(self):
return self._name
@property
def access_urls(self):
return self._access_urls
@access_urls.setter
def access_urls(self, value):
self._access_urls = value

View File

@ -98,7 +98,8 @@ class ServiceController(base.ServiceBase):
LOG.info('Creating policy %s on domain %s complete' %
(dp, ','.join(classified_domain)))
links.append({'href': self.driver.akamai_access_url_link,
"rel": 'access_url'
'rel': 'access_url',
'domain': service_obj.name
})
except Exception:
return self.responder.failed("failed to create service")
@ -279,7 +280,8 @@ class ServiceController(base.ServiceBase):
LOG.info('Creating/Updateing policy %s on domain %s '
'complete' % (dp, ','.join(classified_domain)))
links.append({'href': self.driver.akamai_access_url_link,
'rel': 'access_url'
'rel': 'access_url',
'domain': service_obj.name
})
except Exception:
return self.responder.failed("failed to update service")
@ -348,7 +350,9 @@ class ServiceController(base.ServiceBase):
return self.responder.failed("failed to update service")
ids = policies
links.append({'href': self.driver.akamai_access_url_link,
'rel': 'access_url'})
'rel': 'access_url',
'domain': service_obj.name
})
return self.responder.updated(json.dumps(ids), links)
def delete(self, provider_service_id):

View File

@ -50,7 +50,8 @@ class ServiceController(base.ServiceBase):
service_version.number)
links = [{"href": '.'.join([domain_check.domain.name,
"global.prod.fastly.net"]),
"rel": 'access_url'}
"rel": 'access_url',
"domain": domain_check.domain.name}
for domain_check in domain_checks]
for origin in service_obj.origins:

View File

@ -37,6 +37,7 @@ class ServiceController(base.ServiceBase):
service_id = uuid.uuid1()
return self.responder.created(str(service_id), [{
"href": "www.mysite.com",
"domain": "www.mydomain.com",
'rel': "access_url"}])
def delete(self, provider_service_id):

View File

@ -293,7 +293,7 @@ class ServicesController(base.ServicesController):
provider_detail_dict = json.loads(
provider_details_result[provider_name])
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get("access_urls", None)
access_urls = provider_detail_dict.get("access_urls", [])
status = provider_detail_dict.get("status", u'creating')
error_info = provider_detail_dict.get("error_info", None)
provider_detail_obj = provider_details.ProviderDetail(

View File

@ -37,16 +37,17 @@ class ServicesController(base.ServicesController):
provider_details = {
'MaxCDN': json.dumps(
{'id': 11942,
'access_urls': ['mypullzone.netdata.com']}),
'access_urls': [{'operator_url': 'mypullzone.netdata.com'}]}),
'Mock': json.dumps(
{'id': 73242,
'access_urls': ['mycdn.mock.com']}),
'access_urls': [{'operator_url': 'mycdn.mock.com'}]}),
'CloudFront': json.dumps(
{'id': '5ABC892',
'access_urls': ['cf123.cloudcf.com']}),
'access_urls': [{'operator_url': 'cf123.cloudcf.com'}]}),
'Fastly': json.dumps(
{'id': 3488,
'access_urls': ['mockcf123.fastly.prod.com']})}
'access_urls':
[{'operator_url': 'mockcf123.fastly.prod.com'}]})}
services = [{'name': 'mockdb1_service_name',
'domains': [json.dumps({'domain': 'www.mywebsite.com'})],
@ -88,16 +89,17 @@ class ServicesController(base.ServicesController):
provider_details = {
'MaxCDN': json.dumps(
{'id': 11942,
'access_urls': ['mypullzone.netdata.com']}),
'access_urls': [{'operator_url': 'mypullzone.netdata.com'}]}),
'Mock': json.dumps(
{'id': 73242,
'access_urls': ['mycdn.mock.com']}),
'access_urls': [{'operator_url': 'mycdn.mock.com'}]}),
'CloudFront': json.dumps(
{'id': '5ABC892',
'access_urls': ['cf123.cloudcf.com']}),
'access_urls': [{'operator_url': 'cf123.cloudcf.com'}]}),
'Fastly': json.dumps(
{'id': 3488,
'access_urls': ['mockcf123.fastly.prod.com']})}
'access_urls':
[{'operator_url': 'mockcf123.fastly.prod.com'}]})}
service_dict = {'name': service_name,
'domains': [domain_json],

View File

@ -49,10 +49,10 @@ class Model(collections.OrderedDict):
request.host_url,
self['name']))),
'self')]
for provider_name in service_obj.provider_details:
for access_url in (
service_obj.provider_details[provider_name].access_urls):
provider_detail = service_obj.provider_details[provider_name]
access_urls = provider_detail.access_urls
for access_url in access_urls:
self["links"].append(link.Model(
access_url,
access_url['operator_url'],
'access_url'))

View File

@ -0,0 +1 @@
pyrax

View File

@ -1,8 +1,9 @@
[drivers]
providers = mock,cloudfront,fastly
providers = mock,maxcdn,cloudfront,fastly
transport = pecan
manager = default
storage = mockdb
dns = default
[drivers:storage:cassandra]
cluster = "192.168.59.103"