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:
parent
032af4a026
commit
7a2c4c56e4
|
@ -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
|
||||
)
|
|
@ -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)
|
|
@ -55,9 +55,14 @@ class CreateProviderServicesTask(task.Task):
|
|||
for domain in service_obj.domains:
|
||||
if domain.certificate == 'san':
|
||||
cert_for_domain = (
|
||||
self.storage_controller.get_cert_by_domain(
|
||||
domain.domain, domain.certificate,
|
||||
service_obj.flavor_id, project_id))
|
||||
self.storage_controller.get_certs_by_domain(
|
||||
domain.domain,
|
||||
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
|
||||
except ValueError:
|
||||
msg = 'Creating service {0} from Poppy failed. ' \
|
||||
|
|
|
@ -46,6 +46,8 @@ def task_controllers(program, controller=None):
|
|||
return service_controller, service_controller.storage_controller
|
||||
if controller == 'dns':
|
||||
return service_controller, service_controller.dns_controller
|
||||
if controller == 'providers':
|
||||
return service_controller, bootstrap_obj.manager.providers
|
||||
if controller == 'ssl_certificate':
|
||||
return service_controller, (
|
||||
bootstrap_obj.manager.ssl_certificate_controller)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from poppy.manager.base import background_job
|
||||
from poppy.manager.base import driver
|
||||
from poppy.manager.base import flavors
|
||||
from poppy.manager.base import home
|
||||
|
@ -22,6 +23,7 @@ from poppy.manager.base import ssl_certificate
|
|||
|
||||
Driver = driver.ManagerDriverBase
|
||||
|
||||
BackgroundJobController = background_job.BackgroundJobControllerBase
|
||||
FlavorsController = flavors.FlavorsControllerBase
|
||||
ServicesController = services.ServicesControllerBase
|
||||
HomeController = home.HomeControllerBase
|
||||
|
|
|
@ -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
|
|
@ -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')
|
|
@ -13,6 +13,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from poppy.manager.default import background_job
|
||||
from poppy.manager.default import flavors
|
||||
from poppy.manager.default import health
|
||||
from poppy.manager.default import home
|
||||
|
@ -20,6 +21,7 @@ from poppy.manager.default import services
|
|||
from poppy.manager.default import ssl_certificate
|
||||
|
||||
|
||||
BackgroundJob = background_job.BackgroundJobController
|
||||
Home = home.DefaultHomeController
|
||||
Flavors = flavors.DefaultFlavorsController
|
||||
Health = health.DefaultHealthController
|
||||
|
|
|
@ -44,6 +44,10 @@ class DefaultManagerDriver(base.Driver):
|
|||
def health_controller(self):
|
||||
return controllers.Health(self)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def background_job_controller(self):
|
||||
return controllers.BackgroundJob(self)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def ssl_certificate_controller(self):
|
||||
return controllers.SSLCertificate(self)
|
||||
|
|
|
@ -239,6 +239,18 @@ class DefaultServicesController(base.ServicesController):
|
|||
existing_shared_domains[customer_domain] = domain.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()))
|
||||
|
||||
# remove fields that cannot be part of PATCH
|
||||
|
@ -293,9 +305,13 @@ class DefaultServicesController(base.ServicesController):
|
|||
|
||||
elif domain.certificate == 'san':
|
||||
cert_for_domain = (
|
||||
self.storage_controller.get_cert_by_domain(
|
||||
domain.domain, domain.certificate,
|
||||
service_new.flavor_id, project_id))
|
||||
self.storage_controller.get_certs_by_domain(
|
||||
domain.domain,
|
||||
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
|
||||
|
||||
# retrofit the access url info into
|
||||
|
|
|
@ -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()
|
|
@ -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
|
|
@ -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()
|
|
@ -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)
|
|
@ -102,14 +102,18 @@ AKAMAI_OPTIONS = [
|
|||
cfg.StrOpt(
|
||||
'group_id',
|
||||
help='Operator groupID'),
|
||||
cfg.StrOpt(
|
||||
'property_id',
|
||||
help='Operator propertyID')
|
||||
]
|
||||
|
||||
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):
|
||||
|
||||
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_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 = (
|
||||
zookeeper_queue.ZookeeperModSanQueue(self._conf))
|
||||
|
||||
|
@ -229,6 +242,16 @@ class CDNProvider(base.Driver):
|
|||
def papi_api_client(self):
|
||||
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
|
||||
def service_controller(self):
|
||||
"""Returns the driver's hostname controller."""
|
||||
|
|
|
@ -892,6 +892,9 @@ class ServicesController(base.ServicesController):
|
|||
CQL_SEARCH_BY_DOMAIN,
|
||||
consistency_level=self._driver.consistency_level)
|
||||
results = self.session.execute(stmt, args)
|
||||
# If there is not service with this domain
|
||||
# return None
|
||||
details = None
|
||||
for r in results:
|
||||
proj_id = r.get('project_id')
|
||||
service = r.get('service_id')
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import random
|
||||
|
||||
from poppy.model.helpers import domain
|
||||
from poppy.model.helpers import origin
|
||||
from poppy.model.helpers import provider_details
|
||||
from poppy.model import service
|
||||
from poppy.model import ssl_certificate
|
||||
from poppy.storage import base
|
||||
|
||||
|
||||
|
@ -166,6 +168,9 @@ class ServicesController(base.ServicesController):
|
|||
if key in self.certs:
|
||||
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):
|
||||
key = (cert_obj.flavor_id, cert_obj.domain_name, cert_obj.cert_type)
|
||||
if key not in self.certs:
|
||||
|
@ -173,12 +178,33 @@ class ServicesController(base.ServicesController):
|
|||
else:
|
||||
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 = []
|
||||
for cert in self.certs:
|
||||
if domain_name in cert:
|
||||
certs.append(self.certs[cert])
|
||||
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]
|
||||
else:
|
||||
return certs
|
||||
|
|
|
@ -22,6 +22,7 @@ from poppy.transport.pecan.controllers import base
|
|||
from poppy.transport.pecan import hooks as poppy_hooks
|
||||
from poppy.transport.pecan.models.response import service as resp_service_model
|
||||
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 service_action
|
||||
from poppy.transport.validators.schemas import service_limit
|
||||
|
@ -73,10 +74,40 @@ class DomainMigrationController(base.Controller, hooks.HookController):
|
|||
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):
|
||||
def __init__(self, driver):
|
||||
super(AkamaiController, self).__init__(driver)
|
||||
self.__class__.service = DomainMigrationController(driver)
|
||||
self.__class__.background_job = BackgroundJobController(driver)
|
||||
|
||||
|
||||
class ProviderController(base.Controller, hooks.HookController):
|
||||
|
|
|
@ -20,5 +20,8 @@ def load_from_json(json_data):
|
|||
flavor_id = json_data.get("flavor_id")
|
||||
domain_name = json_data.get("domain_name")
|
||||
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)
|
||||
|
|
|
@ -108,6 +108,7 @@ class Model(collections.OrderedDict):
|
|||
continue
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
if 'operator_url' in access_url:
|
||||
self['links'].append(link.Model(
|
||||
access_url['operator_url'],
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ upload-dir = doc/build/html
|
|||
console_scripts =
|
||||
poppy-server = poppy.cmd.server: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 =
|
||||
pecan = poppy.transport.pecan:Driver
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[drivers]
|
||||
providers = mock,maxcdn,cloudfront,fastly
|
||||
providers = mock,maxcdn,akamai,fastly
|
||||
transport = pecan
|
||||
manager = default
|
||||
storage = mockdb
|
||||
dns = default
|
||||
notifications = mailgun
|
||||
|
||||
[drivers:storage:cassandra]
|
||||
cluster = "192.168.59.103"
|
||||
|
@ -18,9 +19,15 @@ alias = "MYALIAS"
|
|||
consumer_secret = "MYCONSUMER_SECRET"
|
||||
consumer_key = "MYCONSUMERKEY"
|
||||
|
||||
[drivers:provider:akamai]
|
||||
akamai_https_access_url_suffix = "my_https_url_suffix"
|
||||
|
||||
[drivers:provider:cloudfront]
|
||||
aws_access_key_id = "MY_AWS_ACCESS_KEY_ID"
|
||||
aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY"
|
||||
|
||||
[drivers:notification:mailgun]
|
||||
recipients = "myrecipients@abc.com"
|
||||
|
||||
[drivers:transport:limits]
|
||||
max_services_per_page = 20
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2014 Rackspace, Inc.
|
||||
# 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.
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -180,10 +180,10 @@ class CassandraStorageServiceTests(base.TestCase):
|
|||
self.assertTrue("CloudFront" 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(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 the response from cassandra
|
||||
mock_execute.execute.return_value = cert_details_json[0]
|
||||
|
|
Loading…
Reference in New Issue