def execute(self, providers_list, domain_name, cert_type, project_id, flavor_id): service_controller, self.ssl_certificate_manager = \ memoized_controllers.task_controllers('poppy', 'ssl_certificate') self.storage_controller = self.ssl_certificate_manager.storage try: cert_obj = self.storage_controller.get_certs_by_domain(domain_name) except ValueError: cert_obj = ssl_certificate.SSLCertificate(flavor_id, domain_name, cert_type, project_id) responders = [] # try to delete all certificates from each provider for provider in providers_list: LOG.info( 'Starting to delete ssl certificate: {0} from {1}.'.format( cert_obj.to_dict(), provider)) responder = service_controller.provider_wrapper.delete_certificate( service_controller._driver.providers[provider.lower()], cert_obj, ) if responder: if 'error' in responder[provider]: msg = "Failed to delete ssl certificate: {0} : due to {1}:" \ "The delete operation will be retried".format( cert_obj.to_dict(), responder[provider]['error']) LOG.info(msg) raise Exception(msg) responders.append(responder) return responders
def test_create_with_multiple_domains(self, service_json): service_obj = service.load_from_json(service_json) self.controller.subcustomer_api_client.get.return_value = \ mock.Mock(status_code=200, ok=True, content=json.dumps({"geo": "US"})) self.controller.policy_api_client.put.return_value = mock.Mock( status_code=200, text='Put successful') for curr_domain in service_obj.domains: if (curr_domain.certificate in ['san', 'sni'] and curr_domain.protocol == 'https'): cert_key = ('san cert' if curr_domain.certificate == 'san' else 'sni_cert') curr_domain.cert_info = ssl_certificate.SSLCertificate( 'flavor_id', curr_domain.domain, curr_domain.certificate, service_obj.project_id, cert_details={ 'Akamai': dict(cert_domain='1', extra_info={ 'status': 'deployed', cert_key: '1', 'created_at': str(datetime.datetime.now()) }) }) provider_responses = self.controller.create(service_obj) for provider_name in provider_responses: provider_response = provider_responses[provider_name] num_of_domains = len(service_obj.domains) num_of_links = len(provider_response['links']) # make sure we have same number of domains and links self.assertEqual(num_of_domains, num_of_links) self.assertIn('id', provider_responses[provider_name])
def test_recreate_ssl_certificate(self): providers = ['cdn_provider'] cert_obj_json = ssl_certificate.SSLCertificate('cdn', 'mytestsite.com', 'san') kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': json.dumps(str(uuid.uuid4())), 'domain_name': 'mytestsite.com', 'cert_type': 'san', 'cert_obj_json': json.dumps(cert_obj_json.to_dict()), 'context_dict': context_utils.RequestContext().to_dict() } (service_controller, storage_controller, dns_controller, ssl_cert_controller) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, ssl_cert_controller, memoized_controllers.task_controllers): self.patch_recreate_ssl_certificate_flow(service_controller, storage_controller, dns_controller) engines.run(recreate_ssl_certificate.recreate_ssl_certificate(), store=kwargs)
def test_delete_ssl_certificate_normal(self): providers = ['cdn_provider'] cert_obj = ssl_certificate.SSLCertificate( 'cdn', 'mytestsite.com', 'san', ) kwargs = { 'cert_type': "san", 'project_id': json.dumps(str(uuid.uuid4())), 'domain_name': "mytestsite.com", 'cert_obj': json.dumps(cert_obj.to_dict()), 'providers_list': json.dumps(providers), 'flavor_id': "premium", 'context_dict': context_utils.RequestContext().to_dict() } (service_controller, storage_controller, dns_controller, ssl_cert_controller) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, ssl_cert_controller, memoized_controllers.task_controllers): self.patch_delete_ssl_certificate_flow(service_controller, storage_controller, dns_controller, ssl_cert_controller) engines.run(delete_ssl_certificate.delete_ssl_certificate(), store=kwargs)
def synchronize_cert_status(session): session.execute('USE poppy') # TODO(obulpathi): Need a better way to select results, results = session.execute(CQL_GET_ALL_CERTIFICATE_INFO) certificates = [] for result in results: if result.cert_details is not None: r_cert_details = {} # in case cert_details is None cert_details = result.cert_details or {} for key in cert_details: r_cert_details[key] = json.loads(cert_details[key]) ssl_cert = ssl_certificate.SSLCertificate( domain_name=str(result.domain_name), flavor_id=str(result.flavor_id), cert_details=r_cert_details, cert_type=str(result.cert_type), project_id=str(result.project_id)) certificates.append(ssl_cert) print("Found the following certificates:\n") for certificate in certificates: print("{0} with status {1}".format(certificate.domain_name, certificate.get_cert_status())) for certificate in certificates: cert_args = { 'domain_name': certificate.domain_name, 'status': certificate.get_cert_status() } stmt = query.SimpleStatement(CQL_INSERT_CERT_STATUS) session.execute(stmt, cert_args)
def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None, cert_type=None, status=u'create_in_progress'): 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': status} } } ) return [cert for cert in certs if cert.project_id == project_id] else: return certs
def test_ssl_certificate(self): project_id = '12345' cert_details = { 'mock': { 'extra_info': 'nope' } } flavor_id = 'myflavor' domain_name = 'www.mydomain.com' cert_type = 'san' ssl_cert = ssl_certificate.SSLCertificate(project_id=project_id, flavor_id=flavor_id, domain_name=domain_name, cert_type=cert_type, cert_details=cert_details) # test all properties # project_id self.assertEqual(ssl_cert.project_id, project_id) ssl_cert.project_id = '123456' # flavor_id self.assertEqual(ssl_cert.flavor_id, flavor_id) ssl_cert.flavor_id = 'yourflavor' # domain_name self.assertEqual(ssl_cert.domain_name, domain_name) ssl_cert.domain_name = 'www.yourdomain.com' # cert_type self.assertEqual(ssl_cert.cert_type, cert_type) ssl_cert.cert_type = 'custom' self.assertRaises(ValueError, setattr, ssl_cert, 'cert_type', 'whatever') # cert_details self.assertEqual(ssl_cert.cert_details, cert_details) cert_details_two = cert_details.copy() cert_details_two['mock']['extra_info'] = 'maybe' ssl_cert.cert_details = cert_details_two self.assertEqual(ssl_cert.cert_details, cert_details_two) # check cert type here, the model was incorrectly modifying # cert_type in the cert_details setter self.assertEqual('custom', ssl_cert.cert_type) # get cert status cert_details['mock']['extra_info'] = { 'status': 'deployed' } ssl_cert.cert_details = cert_details self.assertEqual(ssl_cert.get_cert_status(), 'deployed') # check san edge on cert_type custom self.assertEqual(ssl_cert.get_edge_host_name(), None) cert_details['mock']['extra_info'] = { 'status': 'whatever' } self.assertRaises(ValueError, ssl_cert.get_cert_status)
def test_create_ssl_certificate_exception_validation(self): cert_obj = ssl_cert_model.SSLCertificate('flavor_id', 'invalid_domain', 'san', project_id='project_id') with testtools.ExpectedException(ValueError): self.scc.create_ssl_certificate('project_id', cert_obj=cert_obj)
def test_create_ssl_certificate_invalid_domain(self): cert_obj = ssl_cert_model.SSLCertificate('premium', 'www.krusty.happyclowns', 'san', project_id='000') validators.is_valid_tld = mock.Mock(return_value=False) with testtools.ExpectedException(ValueError): self.scc.create_ssl_certificate('project_id', cert_obj=cert_obj)
def test_create_cert_already_exists(self, cert_details_json): # mock the response from cassandra self.mock_session.execute.return_value = cert_details_json[0] ssl_cert_obj = ssl_certificate.SSLCertificate('flavor1', 'www.mydomain.com', 'san') self.assertRaises(ValueError, self.cc.create_certificate, '12345', ssl_cert_obj)
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, project_id, cert_details)
def test_create_ssl_certificate_exception_storage_create_cert(self): cert_obj = ssl_cert_model.SSLCertificate('flavor_id', 'www.valid-domain.com', 'san', project_id='project_id') self.scc.storage.create_certificate.side_effect = ValueError( 'Mock -- Cert already exists!') with testtools.ExpectedException(ValueError): self.scc.create_ssl_certificate('project_id', cert_obj=cert_obj)
def load_from_json(json_data): domain_name = json_data.get('domain') protocol = json_data.get('protocol', 'http') certification_option = json_data.get('certificate', None) res_d = domain.Domain(domain_name, protocol, certification_option) # Note(tonytan4ever): # if the domain is in binding status, set the cert_info object if json_data.get('cert_info') is not None: cert_info = ssl_certificate.SSLCertificate( json_data.get('cert_info').get('flavor_id'), domain_name, json_data.get('cert_info').get('cert_type'), json_data.get('cert_info').get('cert_details', {})) setattr(res_d, 'cert_info', cert_info) return res_d
def test_cert_details_is_none(self): project_id = '12345' cert_details = None flavor_id = 'myflavor' domain_name = 'www.mydomain.com' cert_type = 'san' ssl_cert = ssl_certificate.SSLCertificate(project_id=project_id, flavor_id=flavor_id, domain_name=domain_name, cert_type=cert_type, cert_details=cert_details) self.assertEqual(ssl_cert.get_cert_status(), 'create_in_progress') self.assertEqual(ssl_cert.get_edge_host_name(), None)
def test_create_certificate_with_cert_status_in_details(self): ssl_cert_obj = ssl_certificate.SSLCertificate( 'flavor1', 'www.mydomain.com', 'san', project_id='12345', cert_details={ "provider": "{\"cert_domain\": \"abc\", \"extra_info\": " "{ \"status\": \"deployed\", \"san_cert\": \"" "awesome_san\", \"action\": \"Ready\"}}" }) try: self.cc.create_certificate('12345', ssl_cert_obj) except Exception as e: self.fail(e)
def test_ssl_certificate(self): project_id = '12345' cert_details = {'mock': {'extra_info': 'nope'}} flavor_id = 'myflavor' domain_name = 'www.mydomain.com' cert_type = 'san' ssl_cert = ssl_certificate.SSLCertificate(project_id=project_id, flavor_id=flavor_id, domain_name=domain_name, cert_type=cert_type, cert_details=cert_details) # test all properties # project_id self.assertEqual(ssl_cert.project_id, project_id) ssl_cert.project_id = '123456' # flavor_id self.assertEqual(ssl_cert.flavor_id, flavor_id) ssl_cert.flavor_id = 'yourflavor' # domain_name self.assertEqual(ssl_cert.domain_name, domain_name) ssl_cert.domain_name = 'www.yourdomain.com' # cert_type self.assertEqual(ssl_cert.cert_type, cert_type) ssl_cert.cert_type = 'custom' self.assertRaises(ValueError, setattr, ssl_cert, 'cert_type', 'whatever') # cert_details self.assertEqual(ssl_cert.cert_details, cert_details) cert_details_two = cert_details.copy() cert_details_two['mock']['extra_info'] = 'maybe' ssl_cert.cert_details = cert_details_two # get cert status cert_details['mock']['extra_info'] = {'status': 'deployed'} ssl_cert.cert_details = cert_details self.assertEqual(ssl_cert.get_cert_status(), 'deployed') cert_details['mock']['extra_info'] = {'status': 'whatever'} self.assertRaises(ValueError, ssl_cert.get_cert_status)
def test_check_cert_status_and_update_flow(self): cert_obj_json = json.dumps( ssl_certificate.SSLCertificate( 'cdn', 'website.com', 'san', cert_details={ 'Akamai': { 'extra_info': { 'san cert': 'secure1.san1.testcdn.com' } } }).to_dict()) kwargs = {'cert_obj_json': cert_obj_json, 'project_id': "000"} engines.run(check_cert_status_and_update_flow. check_cert_status_and_update_flow(), store=kwargs)
def execute(self, providers_list, domain_name, cert_type, project_id, flavor_id): service_controller = memoized_controllers.task_controllers('poppy') cert_obj = ssl_certificate.SSLCertificate(flavor_id, domain_name, cert_type, project_id) responders = [] # try to delete all certificates from each provider for provider in providers_list: LOG.info( 'Starting to delete ssl certificate: {0} from {1}.'.format( cert_obj.to_dict(), provider)) responder = service_controller.provider_wrapper.delete_certificate( service_controller._driver.providers[provider.lower()], cert_obj, ) responders.append(responder) return responders
def test_get_san_edge_positive(self): project_id = '12345' cert_details = { 'mock': { 'extra_info': { 'san cert': 'secureX.sanX.content.com' } } } flavor_id = 'flavor' domain_name = 'www.domain.com' cert_type = 'san' ssl_cert = ssl_certificate.SSLCertificate(project_id=project_id, flavor_id=flavor_id, domain_name=domain_name, cert_type=cert_type, cert_details=cert_details) self.assertEqual( ssl_cert.get_edge_host_name(), 'secureX.sanX.content.com')
def test_create_ssl_certificate_normal(self, mock_creds, mock_dns_client): providers = ['cdn_provider'] cert_obj_json = ssl_certificate.SSLCertificate('cdn', 'mytestsite.com', 'san') kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': json.dumps(str(uuid.uuid4())), 'cert_obj_json': json.dumps(cert_obj_json.to_dict()), } service_controller, storage_controller, dns_controller = \ self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, memoized_controllers.task_controllers): self.patch_create_ssl_certificate_flow(service_controller, storage_controller, dns_controller) engines.run(create_ssl_certificate.create_ssl_certificate(), store=kwargs)
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_cert_by_domain(self, domain_name, cert_type, flavor_id, project_id): LOG.info(("Search for cert on '{0}', type: {1}, flavor_id: {2}, " "project_id: {3}").format(domain_name, cert_type, flavor_id, project_id)) args = { 'domain_name': domain_name.lower() } stmt = query.SimpleStatement( CQL_SEARCH_CERT_BY_DOMAIN, consistency_level=self._driver.consistency_level) results = self.session.execute(stmt, args) if results: for r in results: r_project_id = str(r.get('project_id')) r_flavor_id = str(r.get('flavor_id')) r_cert_type = str(r.get('cert_type')) r_cert_details = {} cert_details = r.get('cert_details', {}) # Need to convert cassandra dict into real dict # And the value of cert_details is a string dict for key in cert_details: r_cert_details[key] = json.loads(cert_details[key]) if r_project_id == str(project_id) and \ r_flavor_id == str(flavor_id) and \ r_cert_type == str(cert_type): res = ssl_certificate.SSLCertificate(r_flavor_id, domain_name, r_cert_type, r_cert_details) return res else: return None else: return None
def update(self, project_id, service_id, auth_token, service_updates): """update. :param project_id :param service_id :param service_updates :raises LookupError, ValueError """ # get the current service object try: service_old = self.storage_controller.get(project_id, service_id) except ValueError: raise errors.ServiceNotFound("Service not found") if service_old.operator_status == u'disabled': raise errors.ServiceStatusDisabled( u'Service {0} is disabled'.format(service_id)) if service_old.status not in [u'deployed', u'failed']: raise errors.ServiceStatusNeitherDeployedNorFailed( u'Service {0} neither deployed nor failed'.format(service_id)) # Fixing the operator_url domain for ssl # for schema validation existing_shared_domains = {} for domain in service_old.domains: if domain.protocol == 'https' and domain.certificate == 'shared': customer_domain = domain.domain.split('.')[0] 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 del service_old_json['service_id'] del service_old_json['status'] del service_old_json['operator_status'] del service_old_json['provider_details'] for domain in service_old_json['domains']: if 'cert_info' in domain: del domain['cert_info'] service_new_json = jsonpatch.apply_patch(service_old_json, service_updates) # add any default rules so its explicitly defined self._append_defaults(service_new_json, operation='update') # validate the updates schema = service_schema.ServiceSchema.get_schema("service", "POST") validators.is_valid_service_configuration(service_new_json, schema) try: self.flavor_controller.get(service_new_json['flavor_id']) # raise a lookup error if the flavor is not found except LookupError as e: raise e # must be valid, carry on service_new_json['service_id'] = service_old.service_id service_new = service.Service.init_from_dict(project_id, service_new_json) store = str(uuid.uuid4()).replace('-', '_') service_new.provider_details = service_old.provider_details # fixing the old and new shared ssl domains in service_new for domain in service_new.domains: if domain.protocol == 'https': if domain.certificate == 'shared': customer_domain = domain.domain.split('.')[0] # if this domain is from service_old if customer_domain in existing_shared_domains: domain.domain = existing_shared_domains[ customer_domain] else: domain.domain = self._pick_shared_ssl_domain( customer_domain, service_new.service_id, store) elif domain.certificate == 'san': cert_for_domain = ( 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 # certificate_info table # Note(tonytan4ever): this is for backward # compatibility if domain.cert_info is None and \ service_new.provider_details is not None: # Note(tonytan4ever): right now we assume # only one provider per flavor, that's # why we use values()[0] access_url_for_domain = ( service_new.provider_details.values() [0].get_domain_access_url(domain.domain)) if access_url_for_domain is not None: providers = (self.flavor_controller.get( service_new.flavor_id).providers) san_cert_url = access_url_for_domain.get( 'provider_url') # Note(tonytan4ever): stored san_cert_url # for two times, that's intentional # a little extra info does not hurt new_cert_detail = { providers[0].provider_id.title(): json.dumps( dict(cert_domain=san_cert_url, extra_info={ 'status': 'deployed', 'san cert': san_cert_url, 'created_at': str(datetime.datetime.now()) })) } new_cert_obj = ssl_certificate.SSLCertificate( service_new.flavor_id, domain.domain, 'san', project_id, new_cert_detail) self.storage_controller.create_cert( project_id, new_cert_obj) # deserialize cert_details dict new_cert_obj.cert_details[ providers[0].provider_id.title()] = json.loads( new_cert_obj.cert_details[ providers[0].provider_id.title()]) domain.cert_info = new_cert_obj if hasattr(self, store): delattr(self, store) # check if the service domain names already exist # existing ones does not count! for d in service_new.domains: if self.storage_controller.domain_exists_elsewhere( d.domain, service_id) is True and \ d.domain not in existing_shared_domains.values(): raise ValueError("Domain {0} has already been taken".format( d.domain)) # set status in provider details to u'update_in_progress' provider_details = service_old.provider_details for provider in provider_details: provider_details[provider].status = u'update_in_progress' service_new.provider_details = provider_details self.storage_controller.update(project_id, service_id, service_new) kwargs = { 'project_id': project_id, 'service_id': service_id, 'auth_token': auth_token, 'service_old': json.dumps(service_old.to_dict()), 'service_obj': json.dumps(service_new.to_dict()), 'time_seconds': self.determine_sleep_times(), 'context_dict': context_utils.get_current().to_dict() } self.distributed_task_controller.submit_task( update_service.update_service, **kwargs) return
def post_job(self, job_type, kwargs): """Submit a job to the Job board. Iterate over each certificate in san mapping queue. For each job type, build ``run_list`` and ``ignore_list`` of certificates based on some logic while submitting jobs for those certificates belonging to the run_list. San mapping queue may not be intact depending on the job type. Return both run_list and ignore_list. Valid job_type includes. - akamai_check_and_update_cert_status - akamai_update_papi_property_for_mod_san - akamai_update_papi_property_for_mod_sni Example return: .. code-block:: python ([{"domain_name": "one.example.com", "project_id": "000", "flavor_id": "cdn", "cert_type": "sni"}],[]) :param str job_type: Type of the job :param dict kwargs: Additional arguments :return: Tuple of run_list and ignore_list :rtype: tuple(list, list) :raises NotImplementedError: if the job_type is not supported """ queue_data = [] run_list = [] ignore_list = [] if job_type == "akamai_check_and_update_cert_status": # this task consumes the san mapping queue # items marked as having an updated property are processed # for the this job type, all other items are returned to the # queue until they are ready for processing if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj queue_data += akamai_driver.san_mapping_queue.traverse_queue( consume=True) for cert in queue_data: cert_dict = dict() try: cert_dict = json.loads(cert) LOG.info('Starting to check status on domain: {0},' 'for project_id: {1}' 'flavor_id: {2}, cert_type: {3}'.format( cert_dict.get("domain_name"), cert_dict.get("project_id"), cert_dict.get("flavor_id"), cert_dict.get("cert_type"))) t_kwargs = { "cert_obj_json": json.dumps(cert_dict), "project_id": cert_dict.get("project_id") } if cert_dict.get('property_activated', False) is True: self.distributed_task_controller.submit_task( check_cert_status_and_update_flow. check_cert_status_and_update_flow, **t_kwargs) run_list.append(cert_dict) else: akamai_driver.san_mapping_queue.\ enqueue_san_mapping(json.dumps(cert_dict)) ignore_list.append(cert_dict) LOG.info("Queue item for {0} was sent back to the " "queue because it wasn't marked as " "activated.".format( cert_dict.get("domain_name"))) except Exception as exc: try: akamai_driver.san_mapping_queue.\ enqueue_san_mapping(json.dumps(cert_dict)) except Exception as e: LOG.exception(e) akamai_driver.san_mapping_queue.\ enqueue_san_mapping(cert_dict) cert_dict['error_message'] = str(exc) ignore_list.append(cert_dict) LOG.exception(exc) return run_list, ignore_list elif job_type == "akamai_update_papi_property_for_mod_san": # this task leaves the san mapping queue intact, # once items are successfully processed they are marked # ready for the next job type execution if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj queue_data += akamai_driver.san_mapping_queue.traverse_queue() cname_host_info_list = [] for cert in queue_data: cert_dict = dict() try: cert_dict = json.loads(cert) if cert_dict['cert_type'] == 'san': # add validation that the domain still exists on a # service and that it has a type of SAN cert_obj = ssl_certificate.SSLCertificate( cert_dict['flavor_id'], cert_dict['domain_name'], cert_dict['cert_type'], project_id=cert_dict['project_id']) try: self.cert_storage.get_certs_by_domain( cert_obj.domain_name, project_id=cert_obj.project_id, flavor_id=cert_obj.flavor_id, cert_type=cert_obj.cert_type) except ValueError: ignore_list.append(cert_dict) LOG.info( "Ignored property update because " "certificate for {0} does not exist.".format( cert_obj.domain_name)) continue if cert_dict.get('property_activated', False) is True: ignore_list.append(cert_dict) LOG.info("{0} ignored for property update because " "hostname has already been activated. " "Set property_activated = False to " "activation.".format( cert_obj.domain_name)) continue service_obj = self.service_storage.\ get_service_details_by_domain_name( cert_obj.domain_name, cert_obj.project_id ) if service_obj is None: ignore_list.append(cert_dict) LOG.info("Ignored property update because " "Service not found for domain {0}".format( cert_obj.domain_name)) continue found = False for domain in service_obj.domains: if (domain.domain == cert_obj.domain_name and domain.protocol == 'https' and domain.certificate == 'san'): found = True if found is False: # skip the task for current cert obj if the # domain doesn't exist on a service with the # same protocol and certificate. ignore_list.append(cert_dict) LOG.info( "Ignored update property for domain " "'{0}' that no longer exists on a service " "with the same protocol 'https' and " "certificate type '{1}'".format( cert_obj.domain_name, cert_obj.cert_type)) continue domain_name = cert_dict["domain_name"] san_cert = (cert_dict["cert_details"]["Akamai"] ["extra_info"]["san cert"]) LOG.info("{0}: {1} to {2}, on property: {3}".format( kwargs.get("action", 'add'), domain_name, san_cert, kwargs.get("property_spec", 'akamai_https_san_config_numbers'))) # Note(tonytan4ever): Put this check here so erroneous # san cert params will not pass. Support occasionally # put in the ending "edgekey.net" # (e.g: securexxx.san1.abc.com.edgekey.net), this check # will effectively error that out if san_cert not in self.akamai_san_cert_cname_list: raise ValueError( "Not A valid san cert cname: {0}, " "valid san cert cnames are: {1}".format( san_cert, self.akamai_san_cert_cname_list)) cname_host_info_list.append({ "cnameFrom": domain_name, "cnameTo": '.'.join([san_cert, self.akamai_san_cert_suffix]), "cnameType": "EDGE_HOSTNAME" }) run_list.append(cert_dict) except Exception as e: cert_dict['error_message'] = str(e) ignore_list.append(cert_dict) LOG.exception(e) update_info_list = json.dumps([(kwargs.get("action", 'add'), cname_host_info_list)]) 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 } # check to see if there are changes to be made before submitting # the task, avoids creating a new property version when there are # no changes to be made. if len(cname_host_info_list) > 0: self.distributed_task_controller.submit_task( update_property_flow.update_property_flow, **t_kwargs) else: LOG.info( "No tasks submitted to update_property_flow" "update_info_list was empty: {0}".format(update_info_list)) return run_list, ignore_list elif job_type == 'akamai_update_papi_property_for_mod_sni': # this task leaves the san mapping queue intact, # once items are successfully processed they are marked as # ready for the next job type execution if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj queue_data += akamai_driver.san_mapping_queue.traverse_queue() cname_host_info_list = [] for cert in queue_data: cert_dict = dict() try: cert_dict = json.loads(cert) if cert_dict['cert_type'] == 'sni': # validate that the domain still exists on a # service and that it has a type of SAN cert_obj = ssl_certificate.SSLCertificate( cert_dict['flavor_id'], cert_dict['domain_name'], cert_dict['cert_type'], project_id=cert_dict['project_id']) try: self.cert_storage.get_certs_by_domain( cert_obj.domain_name, project_id=cert_obj.project_id, flavor_id=cert_obj.flavor_id, cert_type=cert_obj.cert_type) except ValueError: ignore_list.append(cert_dict) LOG.info( "Ignored property update because " "certificate for {0} does not exist.".format( cert_obj.domain_name)) continue service_obj = self.service_storage.\ get_service_details_by_domain_name( cert_obj.domain_name, cert_obj.project_id ) if service_obj is None: ignore_list.append(cert_dict) LOG.info("Ignored property update because " "Service not found for domain {0}".format( cert_obj.domain_name)) continue if cert_dict.get('property_activated', False) is True: ignore_list.append(cert_dict) LOG.info("{0} ignored for property update because " "hostname has already been activated. " "Set property_activated = False to " "activation.".format( cert_obj.domain_name)) continue found = False for domain in service_obj.domains: if (domain.domain == cert_obj.domain_name and domain.protocol == 'https' and domain.certificate == cert_obj.cert_type): found = True if found is False: # skip the task for current cert obj is the # domain doesn't exist on a service with the # same protocol and certificate. ignore_list.append(cert_dict) LOG.info( "Ignored update property for domain " "'{0}' that no longer exists on a service " "with the same protocol 'https' and " "certificate type '{1}'".format( cert_obj.domain_name, cert_obj.cert_type)) continue domain_name = cert_dict["domain_name"] sni_cert = (cert_dict["cert_details"]["Akamai"] ["extra_info"]["sni_cert"]) LOG.info("{0}: {1} to {2}, on property: {3}".format( kwargs.get("action", 'add'), domain_name, sni_cert, kwargs.get("property_spec", 'akamai_https_sni_config_numbers'))) if sni_cert not in self.akamai_sni_cert_cname_list: raise ValueError( "Not a valid sni cert cname: {0}, " "valid sni cert cnames are: {1}".format( sni_cert, self.akamai_sni_cert_cname_list)) cname_host_info_list.append({ "cnameFrom": domain_name, "cnameTo": '.'.join([sni_cert, self.akamai_san_cert_suffix]), "cnameType": "EDGE_HOSTNAME" }) run_list.append(cert_dict) except Exception as e: cert_dict['error_message'] = str(e) ignore_list.append(cert_dict) LOG.exception(e) update_info_list = json.dumps([(kwargs.get("action", 'add'), cname_host_info_list)]) t_kwargs = { "property_spec": kwargs.get("property_spec", 'akamai_https_sni_config_numbers'), "update_type": kwargs.get("update_type", 'hostnames'), "update_info_list": update_info_list, "notify_email_list": self.notify_email_list } # check to see if there are changes to be made before submitting # the task, avoids creating a new property version when there are # no changes to be made. if len(cname_host_info_list) > 0: self.distributed_task_controller.submit_task( update_property_flow.update_property_flow, **t_kwargs) else: LOG.info( "No tasks submitted to update_property_flow" "update_info_list was empty: {0}".format(update_info_list)) return run_list, ignore_list else: raise NotImplementedError( 'job type: {0} has not been implemented'.format(job_type))
def rerun_san_retry_list(self): run_list = [] ignore_list = [] if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj retry_list = [] while len(akamai_driver.mod_san_queue.mod_san_queue_backend) > 0: res = akamai_driver.mod_san_queue.dequeue_mod_san_request() retry_list.append(json.loads(res.decode('utf-8'))) # remove duplicates # see http://bit.ly/1mX2Vcb for details def remove_duplicates(data): """Remove duplicates from the data (normally a list). The data must be sortable and have an equality operator """ data = sorted(data) return [k for k, _ in itertools.groupby(data)] retry_list = remove_duplicates(retry_list) # double check in POST. This check should really be first done in # PUT for r in retry_list: err_state = False service_obj = self.service_storage\ .get_service_details_by_domain_name(r['domain_name']) if service_obj is None and r.get('validate_service', True): err_state = True LOG.error( u'Domain {0} does not exist on any service, are you ' 'sure you want to proceed request, {1}? You can set ' 'validate_service to False to retry this san-retry ' 'request forcefully'.format(r['domain_name'], r)) cert_for_domain = self.storage.get_certs_by_domain( r['domain_name']) if cert_for_domain != []: if cert_for_domain.get_cert_status() == "deployed": err_state = True LOG.error( u'Certificate on {0} has already been provisioned ' 'successfully.'.format(r['domain_name'])) if err_state is False: run_list.append(r) else: ignore_list.append(r) akamai_driver.mod_san_queue.enqueue_mod_san_request( json.dumps(r)) LOG.warn( "{0} was skipped because it failed validation.".format( r['domain_name'])) for cert_obj_dict in run_list: try: cert_obj = ssl_certificate.SSLCertificate( cert_obj_dict['flavor_id'], cert_obj_dict['domain_name'], 'san', project_id=cert_obj_dict['project_id']) cert_for_domain = (self.storage.get_certs_by_domain( cert_obj.domain_name, project_id=cert_obj.project_id, flavor_id=cert_obj.flavor_id, cert_type=cert_obj.cert_type)) if cert_for_domain == []: pass else: # If this cert has been deployed through manual # process we ignore the rerun process for this entry if cert_for_domain.get_cert_status() == 'deployed': continue # rerun the san process try: flavor = self.flavor_controller.get(cert_obj.flavor_id) # raise a lookup error if the flavor is not found except LookupError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'project_id': cert_obj.project_id, 'domain_name': cert_obj.domain_name, 'cert_type': 'san', 'providers_list_json': json.dumps(providers), 'cert_obj_json': json.dumps(cert_obj.to_dict()), 'enqueue': False, } self.distributed_task_controller.submit_task( recreate_ssl_certificate.recreate_ssl_certificate, **kwargs) except Exception as e: # When exception happens we log it and re-queue this # request LOG.exception(e) run_list.remove(cert_obj_dict) ignore_list.append(cert_obj_dict) akamai_driver.mod_san_queue.enqueue_mod_san_request( json.dumps(cert_obj_dict)) # For other providers post san_retry_list implementation goes here else: # if not using akamai driver just return summary of run list and # ignore list pass return run_list, ignore_list
def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None, cert_type=None): LOG.info("Check if cert on '{0}' exists".format(domain_name)) args = { 'domain_name': domain_name.lower() } stmt = query.SimpleStatement( CQL_SEARCH_CERT_BY_DOMAIN, consistency_level=self._driver.consistency_level) resultset = self.session.execute(stmt, args) complete_results = list(resultset) certs = [] if complete_results: for r in complete_results: r_project_id = str(r.get('project_id')) r_flavor_id = str(r.get('flavor_id')) r_cert_type = str(r.get('cert_type')) r_cert_details = {} # in case cert_details is None cert_details = r.get('cert_details', {}) or {} # Need to convert cassandra dict into real dict # And the value of cert_details is a string dict for key in cert_details: r_cert_details[key] = json.loads(cert_details[key]) LOG.info( "Certificate for domain: {0} with flavor_id: {1}, " "cert_details : {2} and cert_type: {3} present " "on project_id: {4}".format( domain_name, r_flavor_id, r_cert_details, r_cert_type, r_project_id ) ) ssl_cert = ssl_certificate.SSLCertificate( domain_name=domain_name, flavor_id=r_flavor_id, cert_details=r_cert_details, cert_type=r_cert_type, project_id=r_project_id ) certs.append(ssl_cert) non_none_attrs_gen = filterfalse( lambda x: list(x.values())[0] is None, [{'project_id': project_id}, {'flavor_id': flavor_id}, {'cert_type': cert_type}]) non_none_attrs_list = list(non_none_attrs_gen) non_none_attrs_dict = {} if non_none_attrs_list: for attr in non_none_attrs_list: non_none_attrs_dict.update(attr) def argfilter(certificate): all_conditions = True if non_none_attrs_dict: for k, v in non_none_attrs_dict.items(): if getattr(certificate, k) != v: all_conditions = False return all_conditions total_certs = [cert for cert in certs if argfilter(cert)] if len(total_certs) == 1: return total_certs[0] else: return total_certs
def test_update_https_san_domains_with_cert_info(self, service_json): provider_service_id = [] for domain_obj in service_json.get('domains', []): provider_service_id.append( { 'policy_name': domain_obj['domain'], 'protocol': domain_obj.get('protocol', 'http'), 'certificate': domain_obj.get('certificate', None) } ) if len(provider_service_id) == 0: provider_service_id = [{'policy_name': str(uuid.uuid1()), 'protocol': 'http', 'certificate': None}] provider_service_id = json.dumps(provider_service_id) controller = services.ServiceController(self.driver) controller.subcustomer_api_client.get.side_effect = [ mock.Mock(status_code=400, ok=False, text='Error retrieving sub customer!'), mock.Mock(status_code=200, ok=True, content=json.dumps({"geo": "US"})), mock.Mock(status_code=200, ok=True, content=json.dumps({"geo": "US"})), mock.Mock(status_code=200, ok=True, content=json.dumps({"geo": "US"})) ] controller.subcustomer_api_client.delete.return_value = \ mock.Mock(status_code=200, ok=True) controller.policy_api_client.get.return_value = mock.Mock( status_code=200, text=json.dumps(dict(rules=[])) ) controller.policy_api_client.put.return_value = mock.Mock( status_code=200, text='Put successful' ) controller.policy_api_client.delete.return_value = mock.Mock( status_code=200, text='Delete successful' ) service_obj = service.load_from_json(service_json) san_domains = [] for curr_domain in service_obj.domains: if ( curr_domain.certificate in ['san', 'sni'] and curr_domain.protocol == 'https' ): cert_key = ( 'san cert' if curr_domain.certificate == 'san' else 'sni_cert' ) curr_domain.cert_info = ssl_certificate.SSLCertificate( 'flavor_id', curr_domain.domain, curr_domain.certificate, service_obj.project_id, cert_details={ 'Akamai': dict( cert_domain='1', extra_info={ 'status': 'deployed', cert_key: '1', 'created_at': str(datetime.datetime.now()) } ) } ) san_domains.append(curr_domain) resp = controller.update(provider_service_id, service_obj) self.assertIn('id', resp[self.driver.provider_name])
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") return ssl_certificate.SSLCertificate(flavor_id, domain_name, cert_type)
def rerun_san_retry_list(self): run_list = [] ignore_list = [] if 'akamai' in self._driver.providers: akamai_driver = self._driver.providers['akamai'].obj retry_list = [] while len(akamai_driver.mod_san_queue.mod_san_queue_backend) > 0: res = akamai_driver.mod_san_queue.dequeue_mod_san_request() retry_list.append(json.loads(res.decode('utf-8'))) retry_list = util.remove_duplicates(retry_list) # double check in POST. This check should really be first done in # PUT for r in retry_list: err_state = False service_obj = self.service_storage\ .get_service_details_by_domain_name(r['domain_name']) if service_obj is None and r.get('validate_service', True): err_state = True LOG.error( u'Domain {0} does not exist on any service, are you ' 'sure you want to proceed request, {1}? You can set ' 'validate_service to False to retry this san-retry ' 'request forcefully'.format(r['domain_name'], r) ) elif ( service_obj is not None and service_obj.operator_status.lower() == 'disabled' ): err_state = True LOG.error( u'The service for domain {0} is disabled.' 'No certificates will be created for ' 'service {1} while it remains in {2} operator_status' 'request forcefully'.format( r['domain_name'], service_obj.service_id, service_obj.operator_status ) ) try: cert_for_domain = self.storage.get_certs_by_domain( r['domain_name']) if cert_for_domain.get_cert_status() == "deployed": err_state = True LOG.error( u'Certificate on {0} has already been provisioned ' 'successfully.'.format(r['domain_name'])) except ValueError: LOG.info("No matching certificates found for " "the domain {}".format(r['domain_name'])) if err_state is False: run_list.append(r) else: ignore_list.append(r) if not r.get('validate_service', True): # validation is False, send ignored retry_list # object back to queue akamai_driver.mod_san_queue.enqueue_mod_san_request( json.dumps(r) ) LOG.warn( "{0} was skipped because it failed validation.".format( r['domain_name'] ) ) for cert_obj_dict in run_list: try: cert_obj = ssl_certificate.SSLCertificate( cert_obj_dict['flavor_id'], cert_obj_dict['domain_name'], cert_obj_dict['cert_type'], project_id=cert_obj_dict['project_id'] ) try: cert_for_domain = ( self.storage.get_certs_by_domain( cert_obj.domain_name, project_id=cert_obj.project_id, flavor_id=cert_obj.flavor_id, cert_type=cert_obj.cert_type)) # If this cert has been deployed through manual # process we ignore the rerun process for this entry if cert_for_domain.get_cert_status() == 'deployed': run_list.remove(cert_obj_dict) ignore_list.append(cert_obj_dict) continue except ValueError: LOG.info("No matching certificates found for " "the domain {}".format(cert_obj.domain_name)) # rerun the san process try: flavor = self.flavor_controller.get(cert_obj.flavor_id) # raise a lookup error if the flavor is not found except LookupError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'project_id': cert_obj.project_id, 'domain_name': cert_obj.domain_name, 'cert_type': cert_obj.cert_type, 'providers_list_json': json.dumps(providers), 'cert_obj_json': json.dumps(cert_obj.to_dict()), 'enqueue': False, 'context_dict': context_utils.RequestContext( tenant=cert_obj.project_id ).to_dict() } self.distributed_task_controller.submit_task( recreate_ssl_certificate.recreate_ssl_certificate, **kwargs) except Exception as e: # When exception happens we log it and re-queue this # request LOG.exception(e) run_list.remove(cert_obj_dict) ignore_list.append(cert_obj_dict) akamai_driver.mod_san_queue.enqueue_mod_san_request( json.dumps(cert_obj_dict) ) # For other providers post san_retry_list implementation goes here else: # if not using akamai driver just return summary of run list and # ignore list pass return run_list, ignore_list