feat: Add akamai background job admin endpoint

REQUEST:
    POST /admin/provider/akamai/background_job
    {
      "job_type": "akamai_update_papi_property_for_mod_san",
      "domain_name": "testabc.cnamecdn.com",
      "san_cert_name": "secure1.san1.test_cdn.com"
    }
RESPONSE:
    202 ACCEPTED
Note: The admin endpoints are also exposed as scripts:
    akamai-property-udpate-mod-san --domain_name <your_domain_name> --san_cert_name <your_san_cert_name>
    akamai-cert-status-check-and-update --domain_name <your_domain_name> --cert_type "san" --project_id <PROJECT_ID> --flavor_id <FLAVOR_ID>

Change-Id: I9211d9f24cbd46c8818993d3ff8493d5fba4bc28
This commit is contained in:
Sriram Madapusi Vasudevan 2015-10-15 12:15:07 -04:00 committed by tonytan4ever
parent 032af4a026
commit 7a2c4c56e4
35 changed files with 1482 additions and 18 deletions

View File

@ -0,0 +1,64 @@
# Copyright (c) 2015 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.
from oslo_config import cfg
from poppy.common import cli
from poppy.openstack.common import log
from poppy.provider.akamai.background_jobs.check_cert_status_and_update import \
check_cert_status_and_update_flow
LOG = log.getLogger(__name__)
CLI_OPT = [
cfg.StrOpt('domain_name',
required=True,
help='The domain you want to check cert status on'),
cfg.StrOpt('cert_type',
default='san',
help='Cert type of this cert'),
cfg.StrOpt('project_id',
required=True,
help='project id of this cert'),
cfg.StrOpt('flavor_id',
default='cdn',
help='flavor id of this cert'),
]
@cli.runnable
def run():
# TODO(kgriffs): For now, we have to use the global config
# to pick up common options from openstack.common.log, since
# that module uses the global CONF instance exclusively.
conf = cfg.ConfigOpts()
conf.register_cli_opts(CLI_OPT)
conf(prog='akamai-cert-check')
LOG.info('Starting to check status on domain: %s, for project_id: %s'
'flavor_id: %s, cert_type: %s' %
(
conf.domain_name,
conf.project_id,
conf.flavor_id,
conf.cert_type
))
check_cert_status_and_update_flow.run_check_cert_status_and_update_flow(
conf.domain_name,
conf.cert_type,
conf.flavor_id,
conf.project_id
)

View File

@ -0,0 +1,78 @@
# Copyright (c) 2015 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 json
from oslo_config import cfg
from poppy.common import cli
from poppy.openstack.common import log
from poppy.provider.akamai.background_jobs.update_property import \
update_property_flow
LOG = log.getLogger(__name__)
CLI_OPT = [
cfg.StrOpt('domain_name',
required=True,
help='The domain you want to add in host name (cnameFrom)'),
cfg.StrOpt('san_cert_name',
required=True,
help='Cert type of this cert'),
cfg.StrOpt('update_type',
default="hostsnames",
help='Update type for this update, available types are:'
'hostsnames, secureEdgeHost, rules'),
cfg.StrOpt('action',
default="add",
help='What kind of action, do you want "add" or "remove" '
'hostnames'),
cfg.StrOpt('property_spec',
default='akamai_https_san_config_numbers',
help='Property spec of the property to be updated'),
cfg.StrOpt('san_cert_domain_suffix',
default='edgekey.net',
help='Property spec of the property to be updated'),
]
@cli.runnable
def run():
# TODO(kgriffs): For now, we have to use the global config
# to pick up common options from openstack.common.log, since
# that module uses the global CONF instance exclusively.
conf = cfg.ConfigOpts()
conf.register_cli_opts(CLI_OPT)
conf(prog='akamai-papi-update')
LOG.info("%s: %s to %s, on property: %s" % (
conf.action,
conf.domain_name,
conf.san_cert_name,
conf.property_spec
))
update_info_list = json.dumps([
(conf.action,
{
"cnameFrom": conf.domain_name,
"cnameTo": '.'.join([conf.san_cert_name,
conf.san_cert_domain_suffix]),
"cnameType": "EDGE_HOSTNAME"
})
])
update_property_flow.run_update_property_flow(
conf.property_spec, conf.update_type, update_info_list)

View File

@ -55,9 +55,14 @@ class CreateProviderServicesTask(task.Task):
for domain in service_obj.domains: for domain in service_obj.domains:
if domain.certificate == 'san': if domain.certificate == 'san':
cert_for_domain = ( cert_for_domain = (
self.storage_controller.get_cert_by_domain( self.storage_controller.get_certs_by_domain(
domain.domain, domain.certificate, domain.domain,
service_obj.flavor_id, project_id)) project_id=project_id,
flavor_id=service_obj.flavor_id,
cert_type=domain.certificate
))
if cert_for_domain == []:
cert_for_domain = None
domain.cert_info = cert_for_domain domain.cert_info = cert_for_domain
except ValueError: except ValueError:
msg = 'Creating service {0} from Poppy failed. ' \ msg = 'Creating service {0} from Poppy failed. ' \

View File

@ -46,6 +46,8 @@ def task_controllers(program, controller=None):
return service_controller, service_controller.storage_controller return service_controller, service_controller.storage_controller
if controller == 'dns': if controller == 'dns':
return service_controller, service_controller.dns_controller return service_controller, service_controller.dns_controller
if controller == 'providers':
return service_controller, bootstrap_obj.manager.providers
if controller == 'ssl_certificate': if controller == 'ssl_certificate':
return service_controller, ( return service_controller, (
bootstrap_obj.manager.ssl_certificate_controller) bootstrap_obj.manager.ssl_certificate_controller)

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from poppy.manager.base import background_job
from poppy.manager.base import driver from poppy.manager.base import driver
from poppy.manager.base import flavors from poppy.manager.base import flavors
from poppy.manager.base import home from poppy.manager.base import home
@ -22,6 +23,7 @@ from poppy.manager.base import ssl_certificate
Driver = driver.ManagerDriverBase Driver = driver.ManagerDriverBase
BackgroundJobController = background_job.BackgroundJobControllerBase
FlavorsController = flavors.FlavorsControllerBase FlavorsController = flavors.FlavorsControllerBase
ServicesController = services.ServicesControllerBase ServicesController = services.ServicesControllerBase
HomeController = home.HomeControllerBase HomeController = home.HomeControllerBase

View File

@ -0,0 +1,36 @@
# 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 abc
import six
from poppy.manager.base import controller
@six.add_metaclass(abc.ABCMeta)
class BackgroundJobControllerBase(controller.ManagerControllerBase):
"""Health controller base class."""
def __init__(self, manager):
super(BackgroundJobControllerBase, self).__init__(manager)
@abc.abstractmethod
def post_job(self, job_type, args):
"""Returns the health of storage and providers
:raises: NotImplementedError
"""
raise NotImplementedError

View File

@ -0,0 +1,95 @@
# 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 json
from oslo_config import cfg
from poppy.manager import base
from poppy.notification.mailgun import driver as n_driver
from poppy.openstack.common import log
from poppy.provider.akamai.background_jobs.check_cert_status_and_update import \
check_cert_status_and_update_flow
from poppy.provider.akamai.background_jobs.update_property import \
update_property_flow
from poppy.provider.akamai import driver as a_driver
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
LOG = log.getLogger(__name__)
class BackgroundJobController(base.BackgroundJobController):
def __init__(self, manager):
super(BackgroundJobController, self).__init__(manager)
self.distributed_task_controller = (
self._driver.distributed_task.services_controller)
self.akamai_san_cert_suffix = (
conf[a_driver.AKAMAI_GROUP].akamai_https_access_url_suffix)
self.notify_email_list = (
conf[n_driver.MAIL_NOTIFICATION_GROUP].recipients)
def post_job(self, job_type, kwargs):
kwargs = kwargs
if job_type == "akamai_check_and_update_cert_status":
LOG.info('Starting to check status on domain: %s,'
'for project_id: %s'
'flavor_id: %s, cert_type: %s' %
(
kwargs.get("domain_name"),
kwargs.get("project_id"),
kwargs.get("flavor_id"),
kwargs.get("cert_type")
))
self.distributed_task_controller.submit_task(
check_cert_status_and_update_flow.
check_cert_status_and_update_flow,
**kwargs)
elif job_type == "akamai_update_papi_property_for_mod_san":
LOG.info("%s: %s to %s, on property: %s" % (
kwargs.get("action", 'add'),
kwargs.get("domain_name"),
kwargs.get("san_cert_name"),
kwargs.get("property_spec", 'akamai_https_san_config_numbers')
))
t_kwargs = {}
update_info_list = json.dumps([
(kwargs.get("property_spec", 'add'),
{
"cnameFrom": kwargs.get("domain_name"),
"cnameTo": '.'.join([kwargs.get("san_cert_name"),
kwargs.get(
"san_cert_domain_suffix",
self.akamai_san_cert_suffix)]),
"cnameType": "EDGE_HOSTNAME"
})
])
t_kwargs = {
"property_spec": kwargs.get("property_spec",
'akamai_https_san_config_numbers'),
"update_type": kwargs.get("update_type", 'hostnames'),
"update_info_list": update_info_list,
"notify_email_list": self.notify_email_list
}
self.distributed_task_controller.submit_task(
update_property_flow.update_property_flow,
**t_kwargs)
else:
raise NotImplementedError('job type: %s has not been implemented')

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from poppy.manager.default import background_job
from poppy.manager.default import flavors from poppy.manager.default import flavors
from poppy.manager.default import health from poppy.manager.default import health
from poppy.manager.default import home from poppy.manager.default import home
@ -20,6 +21,7 @@ from poppy.manager.default import services
from poppy.manager.default import ssl_certificate from poppy.manager.default import ssl_certificate
BackgroundJob = background_job.BackgroundJobController
Home = home.DefaultHomeController Home = home.DefaultHomeController
Flavors = flavors.DefaultFlavorsController Flavors = flavors.DefaultFlavorsController
Health = health.DefaultHealthController Health = health.DefaultHealthController

View File

@ -44,6 +44,10 @@ class DefaultManagerDriver(base.Driver):
def health_controller(self): def health_controller(self):
return controllers.Health(self) return controllers.Health(self)
@decorators.lazy_property(write=False)
def background_job_controller(self):
return controllers.BackgroundJob(self)
@decorators.lazy_property(write=False) @decorators.lazy_property(write=False)
def ssl_certificate_controller(self): def ssl_certificate_controller(self):
return controllers.SSLCertificate(self) return controllers.SSLCertificate(self)

View File

@ -239,6 +239,18 @@ class DefaultServicesController(base.ServicesController):
existing_shared_domains[customer_domain] = domain.domain existing_shared_domains[customer_domain] = domain.domain
domain.domain = customer_domain domain.domain = customer_domain
# old domains need to bind as well
elif domain.certificate == 'san':
cert_for_domain = (
self.storage_controller.get_certs_by_domain(
domain.domain,
project_id=project_id,
flavor_id=service_old.flavor_id,
cert_type=domain.certificate))
if cert_for_domain == []:
cert_for_domain = None
domain.cert_info = cert_for_domain
service_old_json = json.loads(json.dumps(service_old.to_dict())) service_old_json = json.loads(json.dumps(service_old.to_dict()))
# remove fields that cannot be part of PATCH # remove fields that cannot be part of PATCH
@ -293,9 +305,13 @@ class DefaultServicesController(base.ServicesController):
elif domain.certificate == 'san': elif domain.certificate == 'san':
cert_for_domain = ( cert_for_domain = (
self.storage_controller.get_cert_by_domain( self.storage_controller.get_certs_by_domain(
domain.domain, domain.certificate, domain.domain,
service_new.flavor_id, project_id)) project_id=project_id,
flavor_id=service_new.flavor_id,
cert_type=domain.certificate))
if cert_for_domain == []:
cert_for_domain = None
domain.cert_info = cert_for_domain domain.cert_info = cert_for_domain
# retrofit the access url info into # retrofit the access url info into

View File

@ -0,0 +1,52 @@
# Copyright (c) 2015 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.
from oslo_config import cfg
from taskflow import engines
from taskflow.patterns import linear_flow
from poppy.openstack.common import log
from poppy.provider.akamai.background_jobs.check_cert_status_and_update import \
check_cert_status_and_update_tasks
LOG = log.getLogger(__name__)
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
def check_cert_status_and_update_flow():
flow = linear_flow.Flow('Update Akamai Property').add(
check_cert_status_and_update_tasks.GetCertInfoTask(),
check_cert_status_and_update_tasks.CheckCertStatusTask(),
check_cert_status_and_update_tasks.UpdateCertStatusTask()
)
return flow
def run_check_cert_status_and_update_flow(domain_name, cert_type, flavor_id,
project_id):
e = engines.load(
check_cert_status_and_update_flow(),
store={
'domain_name': domain_name,
'cert_type': cert_type,
'flavor_id': flavor_id,
'project_id': project_id
},
engine='serial')
e.run()

View File

@ -0,0 +1,124 @@
# Copyright (c) 2015 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 json
from oslo_config import cfg
from taskflow import task
from poppy.distributed_task.utils import memoized_controllers
from poppy.openstack.common import log
from poppy.transport.pecan.models.request import ssl_certificate
LOG = log.getLogger(__name__)
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
class GetCertInfoTask(task.Task):
default_provides = "cert_obj_json"
def execute(self, domain_name, cert_type, flavor_id, project_id):
service_controller, self.storage_controller = \
memoized_controllers.task_controllers('poppy', 'storage')
res = self.storage_controller.get_certs_by_domain(
domain_name, project_id=project_id,
flavor_id=flavor_id, cert_type=cert_type)
if res is None:
return ""
return json.dumps(res.to_dict())
class CheckCertStatusTask(task.Task):
default_provides = "status_change_to"
def __init__(self):
super(CheckCertStatusTask, self).__init__()
service_controller, self.providers = \
memoized_controllers.task_controllers('poppy', 'providers')
self.akamai_driver = self.providers['akamai'].obj
def execute(self, cert_obj_json):
if cert_obj_json != "":
cert_obj = ssl_certificate.load_from_json(json.loads(cert_obj_json)
)
latest_sps_id = cert_obj.cert_details['Akamai']['extra_info'].get(
'akamai_spsId')
if latest_sps_id is None:
return ""
resp = self.akamai_driver.akamai_sps_api_client.get(
self.akamai_driver.akamai_sps_api_base_url.format(
spsId=latest_sps_id
)
)
if resp.status_code != 200:
raise RuntimeError('SPS API Request Failed'
'Exception: %s' % resp.text)
status = json.loads(resp.text)['requestList'][0]['status']
# This SAN Cert is on pending status
if status != 'SPS Request Complete':
LOG.info("SPS Not completed for %s..." %
self.cert)
return ""
else:
LOG.info("SPS completed for %s..." %
cert_obj.get_san_edge_name())
return "deployed"
class UpdateCertStatusTask(task.Task):
def __init__(self):
super(UpdateCertStatusTask, self).__init__()
service_controller, self.storage_controller = \
memoized_controllers.task_controllers('poppy', 'storage')
def execute(self, project_id, cert_obj_json, status_change_to):
if cert_obj_json != "":
cert_obj = ssl_certificate.load_from_json(json.loads(cert_obj_json)
)
cert_details = cert_obj.cert_details
if status_change_to == "deployed":
cert_details['Akamai']['extra_info']['status'] = 'deployed'
cert_details['Akamai'] = json.dumps(cert_details['Akamai'])
self.storage_controller.update_cert_info(cert_obj.domain_name,
cert_obj.cert_type,
cert_obj.flavor_id,
cert_details)
service_obj = (
self.storage_controller.
get_service_details_by_domain_name(cert_obj.domain_name)
)
# Update provider details
if service_obj is not None:
service_obj.provider_details['Akamai'].\
domains_certificate_status.\
set_domain_certificate_status(cert_obj.domain_name,
'deployed')
self.storage_controller.update_provider_details(
project_id,
service_obj.service_id,
service_obj.provider_details
)
else:
pass

View File

@ -0,0 +1,50 @@
# Copyright (c) 2015 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.
from oslo_config import cfg
from taskflow import engines
from taskflow.patterns import linear_flow
from poppy.openstack.common import log
from poppy.provider.akamai.background_jobs.update_property import (
update_property_tasks)
LOG = log.getLogger(__name__)
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
def update_property_flow():
flow = linear_flow.Flow('Update Akamai Property').add(
update_property_tasks.PropertyGetLatestVersionTask(),
update_property_tasks.PropertyUpdateTask(),
update_property_tasks.PropertyActivateTask()
)
return flow
def run_update_property_flow(property_spec, update_type, update_info_list):
e = engines.load(
update_property_flow(),
store={
"property_spec": property_spec,
"update_type": update_type,
"update_info_list": update_info_list
},
engine='serial')
e.run()

View File

@ -0,0 +1,276 @@
# Copyright (c) 2015 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 json
from oslo_config import cfg
from taskflow import task
from poppy.distributed_task.utils import memoized_controllers
from poppy.openstack.common import log
LOG = log.getLogger(__name__)
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
class PropertyGetLatestVersionTask(task.Task):
default_provides = "new_version_number"
def __init__(self):
super(PropertyGetLatestVersionTask, self).__init__()
service_controller, self.providers = \
memoized_controllers.task_controllers('poppy', 'providers')
self.akamai_driver = self.providers['akamai'].obj
self.sc = self.akamai_driver.service_controller
self.akamai_conf = self.akamai_driver.akamai_conf
def execute(self, property_spec):
"""Get/Create a new Akamai property update version if necessary"""
self.property_id = self.akamai_driver.papi_property_id(property_spec)
LOG.info('Starting to get next version for property: %s'
% self.property_id)
resp = self.akamai_driver.akamai_papi_api_client.get(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s' % self.property_id)
)
if resp.status_code != 200:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
else:
a_property = json.loads(resp.text)['properties']['items'][0]
latestVersion = a_property['latestVersion'] or 0
production_version = a_property['productionVersion'] or -1
staging_version = a_property['stagingVersion'] or -1
max_version = max(latestVersion, production_version,
staging_version)
if production_version == -1 and staging_version == -1:
# if the max version has not been activated yet
# we just reuse the max version
LOG.info("New version for : %s is %s" % (self.property_id,
str(max_version)))
return max_version
else:
# else now we need to create a new version (bump up a version)
resp = self.akamai_driver.akamai_papi_api_client.get(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/versions/%s' % (
self.property_id, str(max_version)))
)
if resp.status_code != 200:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
etag = json.loads(resp.text)['versions']['items'][0]['etag']
# create a new version
resp = self.akamai_driver.akamai_papi_api_client.post(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/versions' % (
self.property_id)),
data=json.dumps({
'createFromVersion': max_version,
'createFromEtag': etag
}),
headers={'Content-type': 'application/json'}
)
if resp.status_code != 201:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
LOG.info("New version for : %s is %s" % (self.property_id,
str(max_version+1)))
return max_version + 1
class PropertyUpdateTask(task.Task):
default_provides = 'update_detail'
def __init__(self):
super(PropertyUpdateTask, self).__init__()
service_controller, self.providers = \
memoized_controllers.task_controllers('poppy', 'providers')
self.akamai_driver = self.providers['akamai'].obj
self.sc = self.akamai_driver.service_controller
self.akamai_conf = self.akamai_driver.akamai_conf
self.existing_hosts = []
self.existing_edgehostnames = []
def execute(self, property_spec, new_version_number, update_type,
update_info_list):
"""Update an Akamai property"""
self.property_id = self.akamai_driver.papi_property_id(property_spec)
update_info_list = json.loads(update_info_list)
update_detail = ""
# avoid loading hostnames multiple times
if update_type == 'hostnames':
if self.existing_hosts == []:
LOG.info("Getting Hostnames...")
resp = self.akamai_driver.akamai_papi_api_client.get(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/versions/%s/hostnames' %
(self.property_id,
str(new_version_number)))
)
if resp.status_code != 200:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
self.existing_hosts = json.loads(resp.text)['hostnames'][
'items']
# message should be a list assembled hosts dictionary
for action, host_info in update_info_list:
# add new hosts
if action == 'add':
cnameToEdgeHostname = host_info['cnameTo']
# avoid loading edgehostnames multiple times
if self.existing_edgehostnames == []:
LOG.info("Getting EdgeHostnames...")
resp = self.akamai_driver.akamai_papi_api_client.\
get(
self.akamai_driver.
akamai_papi_api_base_url.format(
middle_part='edgehostnames')
)
if resp.status_code != 200:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
self.existing_edgehostnames = (
json.loads(resp.text)['edgeHostnames']['items']
)
for edgehostname in self.existing_edgehostnames:
if (edgehostname['domainPrefix'] ==
cnameToEdgeHostname.replace(
edgehostname['domainSuffix'], "")[:-1]):
host_info['edgeHostnameId'] = (
edgehostname['edgeHostnameId'])
self.existing_hosts.append(host_info)
update_detail = "Add cnameFrom: %s to cnameTo %s" % (
host_info['cnameFrom'], host_info['cnameTo'])
# remove a hosts
elif action == 'remove':
for idx, existing_host_info in enumerate(
self.existing_hosts):
if existing_host_info['cnameFrom'] == (
host_info['cnameFrom']):
del self.existing_hosts[idx]
break
update_detail = ("Remove cnameFrom: %s to cnameTo %s"
% (host_info['cnameFrom'],
host_info['cnameTo']))
LOG.info('Start Updating Hostnames: %s' %
str(self.existing_hosts))
resp = self.akamai_driver.akamai_papi_api_client.put(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/versions/%s/hostnames' % (
self.property_id,
str(new_version_number))),
data=json.dumps(self.existing_hosts),
headers={'Content-type': 'application/json'}
)
if resp.status_code != 200:
LOG.info("Updating property hostnames response code: %s" %
str(resp.status_code))
LOG.info("Updating property hostnames response text: %s" %
str(resp.text))
else:
LOG.info("Update property hostnames successful...")
# Handle secureEdgeHost addition
elif update_type == 'secureEdgeHost':
# Note(tonytan4ever): This will be used when adding custom cert
pass
elif update_type == 'rules':
pass
return update_detail
class PropertyActivateTask(task.Task):
def __init__(self):
super(PropertyActivateTask, self).__init__()
service_controller, self.providers = \
memoized_controllers.task_controllers('poppy', 'providers')
self.akamai_driver = self.providers['akamai'].obj
self.akamai_conf = self.akamai_driver.akamai_conf
self.sc = self.akamai_driver.service_controller
def execute(self, property_spec, new_version_number, update_detail,
notify_email_list=[]):
"""Update an Akamai property"""
self.property_id = self.akamai_driver.papi_property_id(property_spec)
# This request needs json
LOG.info('Starting activating version: %s for property: %s' %
(new_version_number,
self.property_id))
data = {
'propertyVersion': new_version_number,
'network': 'PRODUCTION',
'note': 'Updating configuration for property %s: %s' % (
self.property_id, update_detail),
'notifyEmails': notify_email_list,
}
resp = self.akamai_driver.akamai_papi_api_client.post(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/activations' %
self.property_id),
data=json.dumps(data),
headers={'Content-type': 'application/json'}
)
# Here activation API call will return a 400,
# with all the messageIds need to handle that not as
# an exception
if resp.status_code != 201 and resp.status_code != 400:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
# else extract out all the warnings
# acknowledgementWarning
if resp.status_code == 400:
LOG.info("response text: %s" % resp.text)
warnings = [warning['messageId'] for warning in
json.loads(resp.text)['warnings']]
data['acknowledgeWarnings'] = warnings
resp = self.akamai_driver.akamai_papi_api_client.post(
self.akamai_driver.akamai_papi_api_base_url.format(
middle_part='properties/%s/activations/' %
self.property_id),
data=json.dumps(data),
headers={'Content-type': 'application/json'}
)
if resp.status_code != 201:
raise RuntimeError('PAPI API request failed.'
'Exception: %s' % resp.text)
# first get the activation id
# activation id is inside of activation link itself
activation_link = json.loads(resp.text)['activationLink']
LOG.info("Activation link: %s" % activation_link)
return {
"activation_link": activation_link
}
def revert(self, property_spec, new_version_number, **kwargs):
LOG.info('retrying task: %s ...' % self.name)

View File

@ -102,14 +102,18 @@ AKAMAI_OPTIONS = [
cfg.StrOpt( cfg.StrOpt(
'group_id', 'group_id',
help='Operator groupID'), help='Operator groupID'),
cfg.StrOpt(
'property_id',
help='Operator propertyID')
] ]
AKAMAI_GROUP = 'drivers:provider:akamai' AKAMAI_GROUP = 'drivers:provider:akamai'
VALID_PROPERTY_SPEC = [
"akamai_http_config_number",
"akamai_https_shared_config_number",
"akamai_https_san_config_numbers",
"akamai_https_custom_config_numbers"]
class CDNProvider(base.Driver): class CDNProvider(base.Driver):
def __init__(self, conf): def __init__(self, conf):
@ -164,11 +168,20 @@ class CDNProvider(base.Driver):
) )
]) ])
self.akamai_sps_api_client = self.akamai_policy_api_client self.akamai_papi_api_base_url = ''.join([
str(self.akamai_conf.policy_api_base_url),
'papi/v0/{middle_part}/'
'?contractId=ctr_%s&groupId=grp_%s' % (
self.akamai_conf.contract_id,
self.akamai_conf.group_id)
])
self.san_cert_cnames = self.akamai_conf.san_cert_cnames self.san_cert_cnames = self.akamai_conf.san_cert_cnames
self.san_cert_hostname_limit = self.akamai_conf.san_cert_hostname_limit self.san_cert_hostname_limit = self.akamai_conf.san_cert_hostname_limit
self.akamai_sps_api_client = self.akamai_policy_api_client
self.akamai_papi_api_client = self.akamai_policy_api_client
self.mod_san_queue = ( self.mod_san_queue = (
zookeeper_queue.ZookeeperModSanQueue(self._conf)) zookeeper_queue.ZookeeperModSanQueue(self._conf))
@ -229,6 +242,16 @@ class CDNProvider(base.Driver):
def papi_api_client(self): def papi_api_client(self):
return self.akamai_papi_api_client return self.akamai_papi_api_client
def papi_property_id(self, property_spec):
if property_spec not in VALID_PROPERTY_SPEC:
raise ValueError('No a valid property spec: %s'
', valid property specs are: %s'
% (property_spec, VALID_PROPERTY_SPEC))
prp_number = self.akamai_conf[property_spec]
if isinstance(prp_number, list):
prp_number = prp_number[0]
return 'prp_%s' % self.akamai_conf[property_spec][0]
@property @property
def service_controller(self): def service_controller(self):
"""Returns the driver's hostname controller.""" """Returns the driver's hostname controller."""

View File

@ -892,10 +892,13 @@ class ServicesController(base.ServicesController):
CQL_SEARCH_BY_DOMAIN, CQL_SEARCH_BY_DOMAIN,
consistency_level=self._driver.consistency_level) consistency_level=self._driver.consistency_level)
results = self.session.execute(stmt, args) results = self.session.execute(stmt, args)
# If there is not service with this domain
# return None
details = None
for r in results: for r in results:
proj_id = r.get('project_id') proj_id = r.get('project_id')
service = r.get('service_id') service = r.get('service_id')
details = self.get(proj_id, service) details = self.get(proj_id, service)
return details return details
def update_provider_details(self, project_id, service_id, def update_provider_details(self, project_id, service_id,

View File

@ -14,11 +14,13 @@
# limitations under the License. # limitations under the License.
import json import json
import random
from poppy.model.helpers import domain from poppy.model.helpers import domain
from poppy.model.helpers import origin from poppy.model.helpers import origin
from poppy.model.helpers import provider_details from poppy.model.helpers import provider_details
from poppy.model import service from poppy.model import service
from poppy.model import ssl_certificate
from poppy.storage import base from poppy.storage import base
@ -166,6 +168,9 @@ class ServicesController(base.ServicesController):
if key in self.certs: if key in self.certs:
self.certs[key].cert_details = cert_details self.certs[key].cert_details = cert_details
def get_service_details_by_domain_name(self, domain_name):
pass
def create_cert(self, project_id, cert_obj): def create_cert(self, project_id, cert_obj):
key = (cert_obj.flavor_id, cert_obj.domain_name, cert_obj.cert_type) key = (cert_obj.flavor_id, cert_obj.domain_name, cert_obj.cert_type)
if key not in self.certs: if key not in self.certs:
@ -173,12 +178,33 @@ class ServicesController(base.ServicesController):
else: else:
raise ValueError raise ValueError
def get_certs_by_domain(self, domain_name, project_id=None): def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None,
cert_type=None):
certs = [] certs = []
for cert in self.certs: for cert in self.certs:
if domain_name in cert: if domain_name in cert:
certs.append(self.certs[cert]) certs.append(self.certs[cert])
if project_id: if project_id:
if flavor_id is not None and cert_type is not None:
return ssl_certificate.SSLCertificate(
"premium",
"blog.testabcd.com",
"san",
project_id=project_id,
cert_details={
'Akamai': {
u'cert_domain': u'secure2.san1.test_123.com',
u'extra_info': {
u'action': u'Waiting for customer domain '
'validation for blog.testabc.com',
u'akamai_spsId': str(random.randint(1, 100000)
),
u'create_at': u'2015-09-29 16:09:12.429147',
u'san cert': u'secure2.san1.test_123.com',
u'status': u'create_in_progress'}
}
}
)
return [cert for cert in certs if cert.project_id == project_id] return [cert for cert in certs if cert.project_id == project_id]
else: else:
return certs return certs

View File

@ -22,6 +22,7 @@ from poppy.transport.pecan.controllers import base
from poppy.transport.pecan import hooks as poppy_hooks from poppy.transport.pecan import hooks as poppy_hooks
from poppy.transport.pecan.models.response import service as resp_service_model from poppy.transport.pecan.models.response import service as resp_service_model
from poppy.transport.validators import helpers from poppy.transport.validators import helpers
from poppy.transport.validators.schemas import background_jobs
from poppy.transport.validators.schemas import domain_migration from poppy.transport.validators.schemas import domain_migration
from poppy.transport.validators.schemas import service_action from poppy.transport.validators.schemas import service_action
from poppy.transport.validators.schemas import service_limit from poppy.transport.validators.schemas import service_limit
@ -73,10 +74,40 @@ class DomainMigrationController(base.Controller, hooks.HookController):
return pecan.Response(None, 202) return pecan.Response(None, 202)
class BackgroundJobController(base.Controller, hooks.HookController):
__hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()]
def __init__(self, driver):
super(BackgroundJobController, self).__init__(driver)
@pecan.expose('json')
@decorators.validate(
request=rule.Rule(
helpers.json_matches_service_schema(
background_jobs.BackgroundJobSchema.get_schema(
"background_jobs", "POST")),
helpers.abort_with_message,
stoplight_helpers.pecan_getter))
def post(self):
request_json = json.loads(pecan.request.body.decode('utf-8'))
job_type = request_json.pop('job_type')
try:
self._driver.manager.background_job_controller.post_job(
job_type,
request_json
)
except NotImplementedError as e:
pecan.abort(404, str(e))
return pecan.Response(None, 202)
class AkamaiController(base.Controller, hooks.HookController): class AkamaiController(base.Controller, hooks.HookController):
def __init__(self, driver): def __init__(self, driver):
super(AkamaiController, self).__init__(driver) super(AkamaiController, self).__init__(driver)
self.__class__.service = DomainMigrationController(driver) self.__class__.service = DomainMigrationController(driver)
self.__class__.background_job = BackgroundJobController(driver)
class ProviderController(base.Controller, hooks.HookController): class ProviderController(base.Controller, hooks.HookController):

View File

@ -20,5 +20,8 @@ def load_from_json(json_data):
flavor_id = json_data.get("flavor_id") flavor_id = json_data.get("flavor_id")
domain_name = json_data.get("domain_name") domain_name = json_data.get("domain_name")
cert_type = json_data.get("cert_type") cert_type = json_data.get("cert_type")
project_id = json_data.get("project_id")
cert_details = json_data.get("cert_details", {})
return ssl_certificate.SSLCertificate(flavor_id, domain_name, cert_type) return ssl_certificate.SSLCertificate(flavor_id, domain_name,
cert_type, project_id, cert_details)

View File

@ -108,6 +108,7 @@ class Model(collections.OrderedDict):
continue continue
except StopIteration: except StopIteration:
pass pass
if 'operator_url' in access_url: if 'operator_url' in access_url:
self['links'].append(link.Model( self['links'].append(link.Model(
access_url['operator_url'], access_url['operator_url'],

View File

@ -0,0 +1,88 @@
# Copyright (c) 2015 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.
from poppy.transport.validators import schema_base
class BackgroundJobSchema(schema_base.SchemaBase):
'''JSON Schmema validation for /admin/provider/akamai/background_jobs'''
schema = {
'background_jobs': {
'POST': {
'type': [{
'additionalProperties': False,
'properties': {
'job_type': {
'type': 'string',
'required': True,
'enum': ['akamai_check_and_update_cert_status']
},
'domain_name': {
'type': 'string',
'required': True
},
'project_id': {
'type': 'string',
'required': True
},
'cert_type': {
'type': 'string',
'required': True,
'enum': ['san']
},
'flavor_id': {
'type': 'string',
'required': True
}
}
},
{
'additionalProperties': False,
'properties': {
'job_type': {
'type': 'string',
'required': True,
'enum': ['akamai_update_papi_property_for_mod_san']
},
'domain_name': {
'type': 'string',
'required': True
},
'san_cert_name': {
'type': 'string',
'required': True
},
'update_type': {
'type': 'string',
'enum': ['hostsnames']
},
'action': {
'type': 'string',
'enum': ['add', 'remove']
},
'property_spec': {
'type': 'string',
'enum': ['akamai_https_san_config_numbers']
},
'san_cert_domain_suffix': {
'type': 'string'
}
}
}]
}
}
}

View File

@ -34,6 +34,8 @@ upload-dir = doc/build/html
console_scripts = console_scripts =
poppy-server = poppy.cmd.server:run poppy-server = poppy.cmd.server:run
poppy-worker = poppy.cmd.task_flow_worker:run poppy-worker = poppy.cmd.task_flow_worker:run
akamai-cert-status-check-and-update = poppy.cmd.akamai_check_and_update_cert_status:run
akamai-property-udpate-mod-san = poppy.cmd.akamai_update_papi_property_for_mod_san:run
poppy.transport = poppy.transport =
pecan = poppy.transport.pecan:Driver pecan = poppy.transport.pecan:Driver

View File

@ -1,9 +1,10 @@
[drivers] [drivers]
providers = mock,maxcdn,cloudfront,fastly providers = mock,maxcdn,akamai,fastly
transport = pecan transport = pecan
manager = default manager = default
storage = mockdb storage = mockdb
dns = default dns = default
notifications = mailgun
[drivers:storage:cassandra] [drivers:storage:cassandra]
cluster = "192.168.59.103" cluster = "192.168.59.103"
@ -18,9 +19,15 @@ alias = "MYALIAS"
consumer_secret = "MYCONSUMER_SECRET" consumer_secret = "MYCONSUMER_SECRET"
consumer_key = "MYCONSUMERKEY" consumer_key = "MYCONSUMERKEY"
[drivers:provider:akamai]
akamai_https_access_url_suffix = "my_https_url_suffix"
[drivers:provider:cloudfront] [drivers:provider:cloudfront]
aws_access_key_id = "MY_AWS_ACCESS_KEY_ID" aws_access_key_id = "MY_AWS_ACCESS_KEY_ID"
aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY" aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY"
[drivers:notification:mailgun]
recipients = "myrecipients@abc.com"
[drivers:transport:limits] [drivers:transport:limits]
max_services_per_page = 20 max_services_per_page = 20

View File

@ -0,0 +1,14 @@
{
"akamai_check_and_update_cert_status": {
"job_type": "akamai_check_and_update_cert_status",
"domain_name": "www.abc.com",
"flavor_id": "mock",
"cert_type": "san",
"project_id": "000"
},
"akamai_update_papi_property_for_mod_san": {
"job_type": "akamai_update_papi_property_for_mod_san",
"domain_name": "www.abc.com",
"san_cert_name": "secure1.test_san.com"
}
}

View File

@ -0,0 +1,22 @@
{
"invalid_job_type_name": {
"job_type": "nonsense",
"flavor_id": "mock"
},
"missing_cert_type": {
"job_type": "akamai_check_and_update_cert_status",
"domain_name": "www.abc.com",
"flavor_id": "mock",
"project_id": "000"
},
"missing_domain_name": {
"job_type": "akamai_check_and_update_cert_status",
"cert_type": "san",
"flavor_id": "mock",
"project_id": "000"
},
"missing_san_cert_name": {
"job_type": "akamai_check_and_update_cert_status",
"domain_name": "www.abc.com"
}
}

View File

@ -0,0 +1,51 @@
# Copyright (c) 2015 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 json
import uuid
import ddt
from tests.functional.transport.pecan import base
@ddt.ddt
class BackgroundJobControllerTest(base.FunctionalTest):
def setUp(self):
super(BackgroundJobControllerTest, self).setUp()
self.project_id = str(uuid.uuid1())
self.service_name = str(uuid.uuid1())
self.flavor_id = str(uuid.uuid1())
@ddt.file_data("data_post_background_jobs_bad_input.json")
def test_post_background_job_negative(self, background_job_json):
response = self.app.post('/v1.0/admin/provider/akamai/background_job',
headers={'Content-Type': 'application/json',
'X-Project-ID': self.project_id},
params=json.dumps(background_job_json),
expect_errors=True)
self.assertEqual(400, response.status_code)
@ddt.file_data("data_post_background_jobs.json")
def test_post_background_job_positive(self, background_job_json):
response = self.app.post('/v1.0/admin/provider/akamai/background_job',
headers={'Content-Type': 'application/json',
'X-Project-ID': self.project_id},
params=json.dumps(background_job_json))
self.assertEqual(202, response.status_code)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Rackspace, Inc. # Copyright (c) 2015 Rackspace, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -0,0 +1,321 @@
# Copyright (c) 2015 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 random
import uuid
import json
import mock
from poppy.model.helpers import domain
from poppy.model.helpers import origin
from poppy.model import service
from poppy.model import ssl_certificate
class MockBootStrap(mock.Mock):
def __init__(self, conf):
super(MockBootStrap, self).__init__()
@property
def manager(self):
return MockManager()
class MockProviderDetails(mock.Mock):
def __init__(self, service_id):
super(MockProviderDetails, self).__init__()
self.service_id = service_id
def __getitem__(self, provider_name):
akamai_provider_detail_mock = mock.Mock()
akamai_provider_detail_mock.provider_service_id = ''.join([
'[{"protocol":',
' "https", "certificate": ',
'"san", "policy_name": "blog.testabc.com"}]'])
akamai_provider_detail_mock.status = 'deployed'
return akamai_provider_detail_mock
class MockManager(mock.Mock):
def __init__(self):
super(MockManager, self).__init__()
@property
def providers(self):
akamai_mock_provider = mock.Mock()
akamai_mock_provider_obj = mock.Mock()
akamai_mock_provider_obj.service_controller = mock.Mock()
akamai_mock_provider_obj.akamai_conf = {
'property_id': 'prp_12345',
'contract_id': "B-ABCDE",
'group_id': 12345
}
akamai_mock_provider_obj.akamai_sps_api_client = MockSPSAPIClient()
akamai_mock_provider_obj.akamai_papi_api_client = MockPapiAPIClient()
akamai_mock_provider_obj.akamai_sps_api_base_url = (
'https://mybaseurl.net/config-secure-provisioning-service/'
'v1/sps-requests/{spsId}?'
'contractId=None&groupId=None')
akamai_mock_provider_obj.akamai_papi_api_base_url = (
'https://mybaseurl.net/papi/v0/{middle_part}/'
'?contractId=ctr_None&groupId=grp_None')
akamai_mock_provider.obj = akamai_mock_provider_obj
providers = {
'akamai': akamai_mock_provider,
}
return providers
@property
def services_controller(self):
sc = mock.Mock()
sc.storage_controller = MockStorageController()
return sc
class MockStorageController(mock.Mock):
def get_certs_by_domain(self, domain_name, project_id=None,
flavor_id=None,
cert_type=None):
return ssl_certificate.SSLCertificate(
"premium",
"blog.testabcd.com",
"san",
project_id=project_id,
cert_details={
'Akamai': {
u'cert_domain': u'secure2.san1.test_123.com',
u'extra_info': {
u'action': u'Waiting for customer domain '
'validation for blog.testabc.com',
u'akamai_spsId': str(random.randint(1, 100000)),
u'create_at': u'2015-09-29 16:09:12.429147',
u'san cert': u'secure2.san1.test_123.com',
u'status': u'create_in_progress'}
}
}
)
def get_service_details_by_domain_name(self, domain_name):
r = service.Service(
str(uuid.uuid4()),
str(uuid.uuid4()),
[domain.Domain('wiki.cc', 'https', 'shared')],
[origin.Origin('mysite.com')],
"strawberry")
r.provider_details = MockProviderDetails(r.service_id)
return r
class MockPapiAPIClient(mock.Mock):
def __init__(self):
super(MockPapiAPIClient, self).__init__()
self.response_200 = mock.Mock(status_code=200)
def get(self, url):
if 'hostnames' in url:
self.response_200.text = json.dumps({
"accountId": "act_1-ABCDE",
"contractId": "B-ABCDE",
"groupId": "grp_12345",
"propertyId": "prp_12345",
"propertyName": "ssl.san.test_123.com_pm",
"propertyVersion": 2,
"etag": str(uuid.uuid4()),
"hostnames": {
"items": [{
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1052022",
"cnameFrom": "www.testxxx.com",
"cnameTo": "ssl.test_123.com.edge_host_test.net"
}, {
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1126816",
"cnameFrom": "secure.san2.test_789.com",
"cnameTo": "secure.test_456.com.edge_host_test.net"
}]
}
})
if 'edgehostnames' in url:
self.response_200.text = json.dumps({
"accountId": "act_1-ABCDE",
"contractId": "B-ABCDE",
"groupId": "grp_12345",
"edgeHostnames": {
"items": [{
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1052022",
"domainPrefix": "secure1.san1.test_123.com",
"domainSuffix": "edge_host_test.net",
"ipVersionBehavior": "IPV4",
"secure": True,
"edgeHostnameDomain": "secure1.san1.test_123.com"
".edge_host_test.net"
}, {
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1159587",
"domainPrefix": "secure2.san1.test_123.com",
"domainSuffix": "edge_host_test.net",
"ipVersionBehavior": "IPV4",
"secure": True,
"edgeHostnameDomain": "secure2.san1.test_123.com"
".edge_host_test.net"
}]
}
})
if 'activations' in url:
self.response_200.text = json.dumps({
"activationId": "atv_2511473",
"status": "SUCCESS"
})
if 'versions' in url:
self.response_200.text = json.dumps({
"propertyId": "prp_12345",
"propertyName": "secure.test_123.com_pm",
"accountId": "act_1-ABCDE",
"contractId": "B-ABCDE",
"groupId": "grp_12345",
"versions": {
"items": [{
"propertyVersion": 1,
"etag": str(uuid.uuid4())
}]
}
})
else:
self.response_200.text = json.dumps({
"properties": {
"items": [{
"accountId": "act_1-ABCDE",
"contractId": "B-ABCDE",
"groupId": "grp_12345",
"propertyId": "prp_12345",
"propertyName": "secure.test_123.com_pm",
"latestVersion": 2,
"stagingVersion": 2,
"productionVersion": 1
}]
}
})
self.response_200.status_code = 200
return self.response_200
def post(self, url, data=None, headers=None):
if 'activations' in url:
self.response_200.status_code = 201
self.response_200.text = json.dumps({
"activationLink": "/papi/v0/properties/prp_227429/"
"activations/atv_2511473?contractId"
"=ctr_C-2M6JYA&groupId=grp_12345",
'warnings': []
})
if 'versions' in url:
self.response_200.status_code = 201
return self.response_200
def put(self, url, data=None, headers=None):
if 'hostnames' in url:
self.response_200.text = json.dumps({
"accountId": "act_1-ABCDE",
"contractId": "B-ABCDE",
"groupId": "grp_12345",
"propertyId": "prp_12345",
"propertyName": "ssl.san.test_123.com_pm",
"propertyVersion": 2,
"etag": str(uuid.uuid4()),
"hostnames": {
"items": [{
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1052022",
"cnameFrom": "secure.san1.test_789.com",
"cnameTo": "ssl.test_123.com.edge_host_test.net"
}, {
"cnameType": "EDGE_HOSTNAME",
"edgeHostnameId": "ehn_1126816",
"cnameFrom": "secure.san2.test_789.com",
"cnameTo": "secure.test_456.com.edge_host_test.net"
}, {
'cnameTo': 'secure.test_7891.com.edge_host_test.net',
'cnameFrom': 'www.blogyyy.com',
'edgeHostnameId': 'ehn_1126816',
'cnameType': u'EDGE_HOSTNAME'
}, {
'cnameTo': u'secure.test_7891.com.edge_host_test.net',
'cnameFrom': u'www.testxxx.com',
'edgeHostnameId': u'ehn_1126816',
'cnameType': u'EDGE_HOSTNAME'
}]
}
})
return self.response_200
class MockSPSAPIClient(mock.Mock):
def __init__(self):
super(MockSPSAPIClient, self).__init__()
self.response_200 = mock.Mock(status_code=200)
def get(self, url):
self.response_200.text = json.dumps({
"requestList":
[{"resourceUrl": "/config-secure-provisioning-service/"
"v1/sps-requests/1849",
"parameters": [{
"name": "cnameHostname",
"value": "secure.san3.test_123.com"
}, {"name": "createType", "value": "san"},
{"name": "csr.cn",
"value": "secure.san3.test_123.com"},
{"name": "csr.c", "value": "US"},
{"name": "csr.st", "value": "TX"},
{"name": "csr.l", "value": "San Antonio"},
{"name": "csr.o", "value": "Rackspace US Inc."},
{"name": "csr.ou", "value": "IT"},
{"name": "csr.sans",
"value": "secure.san3.test_123.com"},
{"name": "organization-information.organization-name",
"value": "Rackspace US Inc."},
{"name": "organization-information.address-line-one",
"value": "1 Fanatical Place"},
{"name": "organization-information.city",
"value": "San Antonio"}],
"lastStatusChange": "2015-03-19T21:47:10Z",
"spsId": random.randint(1, 10000),
"status": "SPS Request Complete",
"jobId": random.randint(1, 100000)}]})
self.response_200.status_code = 200
return self.response_200
def post(self, url, data=None, headers=None):
self.response_200.status_code = 202
self.response_200.text = json.dumps({
"spsId": 1789,
"resourceLocation":
"/config-secure-provisioning-service/v1/sps-requests/1856",
"Results": {
"size": 1,
"data": [{
"text": None,
"results": {
"type": "SUCCESS",
"jobID": 44434}
}]}})
return self.response_200
def put(self, url, data=None, headers=None):
return self.response_200

View File

@ -0,0 +1,66 @@
# Copyright (c) 2015 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 json
import mock
from taskflow import engines
from poppy.provider.akamai.background_jobs.check_cert_status_and_update \
import check_cert_status_and_update_flow
from poppy.provider.akamai.background_jobs.update_property import (
update_property_flow)
from tests.unit import base
from tests.unit.provider.akamai.background_jobs import akamai_mocks
class TestAkamaiBJFlowRuns(base.TestCase):
def setUp(self):
super(TestAkamaiBJFlowRuns, self).setUp()
bootstrap_patcher = mock.patch(
'poppy.bootstrap.Bootstrap',
new=akamai_mocks.MockBootStrap
)
bootstrap_patcher.start()
self.addCleanup(bootstrap_patcher.stop)
def test_check_cert_status_and_update_flow(self):
kwargs = {
'domain_name': "blog.testabc.com",
'cert_type': "san",
'flavor_id': "premium",
'project_id': "000"
}
engines.run(check_cert_status_and_update_flow.
check_cert_status_and_update_flow(),
store=kwargs)
def test_update_papi_flow(self):
kwargs = {
"property_spec": "akamai_https_san_config_numbers",
"update_type": "hostnames",
"update_info_list": json.dumps([
(
"add",
{
"cnameFrome": "blog.testabc.com",
"cnameTo": 'secure2.san1.test_cdn.com',
"cnameType": "EDGE_HOSTNAME"
}
)])
}
engines.run(update_property_flow.update_property_flow(),
store=kwargs)

View File

@ -180,11 +180,11 @@ class CassandraStorageServiceTests(base.TestCase):
self.assertTrue("CloudFront" in actual_response) self.assertTrue("CloudFront" in actual_response)
self.assertTrue("Fastly" in actual_response) self.assertTrue("Fastly" in actual_response)
@ddt.file_data('data_get_cert_by_domain.json') @ddt.file_data('data_get_certs_by_domain.json')
@mock.patch.object(services.ServicesController, 'session') @mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute') @mock.patch.object(cassandra.cluster.Session, 'execute')
def test_get_cert_by_domain(self, cert_details_json, def test_get_certs_by_domain(self, cert_details_json,
mock_session, mock_execute): mock_session, mock_execute):
# mock the response from cassandra # mock the response from cassandra
mock_execute.execute.return_value = cert_details_json[0] mock_execute.execute.return_value = cert_details_json[0]
actual_response = self.sc.get_certs_by_domain( actual_response = self.sc.get_certs_by_domain(