def get_metrics_by_domain(self, project_id, domain_name, **extras): storage_controller = self.storage_controller try: result = storage_controller.get_service_details_by_domain_name( domain_name=domain_name, project_id=project_id) except ValueError: msg = "Domain: {0} was not found for project_id: {1}".format( domain_name, project_id) LOG.warning(msg) raise errors.ServiceNotFound(msg) if not result: msg = "Domain: {0} was not found for project_id: {1}".format( domain_name, project_id) LOG.warning(msg) raise errors.ServiceNotFound(msg) if not result.provider_details: msg = "Provider Details were None " \ "for the service_id: {0} " \ "corresponding to project_id: {1}".format(result.service_id, project_id) LOG.warning(msg) raise errors.ServiceProviderDetailsNotFound(msg) provider_details_dict = result.provider_details provider_for_domain = None for provider, provider_details in provider_details_dict.items(): if provider_details.get_domain_access_url(domain=domain_name): provider_for_domain = provider if not provider_for_domain: msg = "Provider not found for Domain : {0}".format(domain_name) LOG.warning(msg) raise errors.ProviderNotFound(msg) provider_obj = self.providers[provider_for_domain.lower()].obj provider_service_controller = provider_obj.service_controller extras['metrics_controller'] = self.metrics_controller metrics = provider_service_controller.get_metrics_by_domain( project_id, domain_name, provider_obj.regions, **extras) metrics['provider'] = provider_for_domain.lower() metrics['flavor'] = result.flavor_id return metrics
def test_migrate_domain_service_not_found(self): payload = { "project_id": "12345", "service_id": "abcdef", "domain_name": "www.mywebsite.com", "new_cert": "scdn1.secure6.raxcdn.com.edgekey.net", "cert_status": "create_in_progress" } self.request.body = bytearray(json.dumps(payload), encoding='utf-8') self.manager.services_controller.migrate_domain.side_effect = ( errors.ServiceNotFound("Mock -- Couldn't find service!") ) self.assertRaises(exc.HTTPNotFound, self.controller.post, payload)
def migrate_domain(self, project_id, service_id, domain_name, new_cert, cert_status='deployed'): dns_controller = self.dns_controller storage_controller = self.storage_controller try: # Update CNAME records and provider_details in cassandra provider_details = storage_controller.get_provider_details( project_id, service_id) except ValueError as e: # If service is not found LOG.warning('Migrating domain failed: Service {0} could not ' 'be found.. Error message: {1}'.format(service_id, e)) raise errors.ServiceNotFound(e) for provider in provider_details: for url in provider_details[provider].access_urls: if url.get('domain') == domain_name: if 'operator_url' in url: access_url = url['operator_url'] dns_controller.modify_cname(access_url, new_cert) url['provider_url'] = new_cert storage_controller.update_provider_details( project_id, service_id, provider_details) break else: links = {} link_key_tuple = (domain_name, 'san') links[link_key_tuple] = new_cert created_dns_link = dns_controller._create_cname_records(links) new_url = { 'domain': domain_name, 'operator_url': (created_dns_link[link_key_tuple]['operator_url']), 'provider_url': new_cert } provider_details[provider].access_urls.append(new_url) provider_details[provider].domains_certificate_status.\ set_domain_certificate_status(domain_name, cert_status) storage_controller.update_provider_details( project_id, service_id, provider_details)
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 migrate_domain(self, project_id, service_id, domain_name, new_cert, cert_status='deployed'): dns_controller = self.dns_controller storage_controller = self.storage_controller try: # Update CNAME records and provider_details in cassandra provider_details = storage_controller.get_provider_details( project_id, service_id) except ValueError as e: # If service is not found LOG.warning('Migrating domain failed: Service {0} could not ' 'be found. Error message: {1}'.format(service_id, e)) raise errors.ServiceNotFound(e) for provider in provider_details: provider_details[provider].domains_certificate_status.\ set_domain_certificate_status(domain_name, cert_status) # Currently there's only one flavor, and thus expect one result # from the query below. Once additional flavors are added, a # query for the service object rather than provider details only # should provide the flavor id to use in the query below cert_obj = self.ssl_certificate_storage.get_certs_by_domain( domain_name, project_id=project_id, cert_type='san') if cert_obj != []: # cert was found, update the cert status cert_details = cert_obj.cert_details cert_details[provider]['extra_info']['status'] = cert_status cert_details[provider] = json.dumps(cert_details[provider]) storage_controller.update_cert_info(cert_obj.domain_name, cert_obj.cert_type, cert_obj.flavor_id, cert_details) for url in provider_details[provider].access_urls: if url.get('domain') == domain_name: if 'operator_url' in url: access_url = url['operator_url'] dns_controller.modify_cname(access_url, new_cert) url['provider_url'] = new_cert break else: links = {} link_key_tuple = (domain_name, 'san') links[link_key_tuple] = new_cert created_dns_link = dns_controller._create_cname_records(links) new_url = { 'domain': domain_name, 'operator_url': (created_dns_link[link_key_tuple]['operator_url']), 'provider_url': new_cert } provider_details[provider].access_urls.append(new_url) storage_controller.update_provider_details(project_id, service_id, provider_details)
def update_access_url_service(self, project_id, service_id, access_url_changes): try: service_old = self.storage_controller.get_service( project_id, service_id) except ValueError as e: # If service is not found LOG.warning('Get service {0} failed. ' 'Error message: {1}'.format(service_id, e)) raise errors.ServiceNotFound(e) updated_details = False provider_details = service_old.provider_details domain_name = access_url_changes.get('domain_name') for provider in provider_details: for access_url in provider_details[provider].access_urls: if access_url.get('domain') == domain_name: if ('operator_url' in access_url and 'provider_url' in access_url): new_access_url = access_url_changes['operator_url'] new_provider_url = access_url_changes['provider_url'] if access_url.get('shared_ssl_flag', False) is True: raise errors.InvalidOperation( 'Changing access urls for shared ssl domains ' 'is not supported.') if not new_access_url.startswith(domain_name): LOG.info('Invalid access_url/domain_name.') raise errors.InvalidResourceName( 'Invalid access_url/domain_name.') if new_access_url == access_url['operator_url']: LOG.info( "No changes made, both old and new access " "urls are the same. " "Domain '{0}'.".format(domain_name)) return False if new_provider_url != access_url['provider_url']: raise errors.InvalidOperation( 'Please use the migrate domain functionality ' 'to migrate the domain to a new cert.') certificate = ("shared" if access_url.get( 'shared_ssl_flag', False) is True else None) self.dns_controller._create_preferred_cname_record( domain_name, certificate, new_access_url, new_provider_url) self.dns_controller._delete_cname_record( access_url['operator_url'], access_url.get('shared_ssl_flag', False)) access_url['provider_url'] = new_provider_url access_url['operator_url'] = new_access_url updated_details = True break if updated_details is True: self.storage_controller.update_provider_details( project_id, service_id, provider_details) else: err_msg = 'Domain {0} could not be found on service {1}.'.format( domain_name, service_id) LOG.error(err_msg) raise ValueError(err_msg) return updated_details
def migrate_domain(self, project_id, service_id, domain_name, new_cert, cert_status='deployed'): """Migrate domain. Domain migration includes - Set cert_status to all the provider's domain_certification - Update the all the certificates with cert_status - Update the access_url of all the providers with new_cert As of now, below are the supported statuses. - create_in_progress - deployed - failed - cancelled :param unicode project_id: The project id :param unicode service_id: The service id :param unicode domain_name: The domain name :param unicode new_cert: The new certificate :param unicode cert_status: (Default 'deployed')Certificate status :raises ServiceNotFound: if the service not found """ dns_controller = self.dns_controller storage_controller = self.storage_controller try: # Update CNAME records and provider_details in cassandra provider_details = storage_controller.get_provider_details( project_id, service_id) except ValueError as e: # If service is not found LOG.warning('Migrating domain failed: Service {0} could not ' 'be found. Error message: {1}'.format(service_id, e)) raise errors.ServiceNotFound(e) for provider in provider_details: provider_details[provider].domains_certificate_status.\ set_domain_certificate_status(domain_name, cert_status) # Currently there's only one flavor, and thus expect one result # from the query below. Once additional flavors are added, a # query for the service object rather than provider details only # should provide the flavor id to use in the query below try: cert_obj = self.ssl_certificate_storage.get_certs_by_domain( domain_name, project_id=project_id, cert_type='san') # cert was found, update the cert status cert_details = cert_obj.cert_details cert_details[provider]['extra_info']['status'] = cert_status cert_details[provider] = json.dumps(cert_details[provider]) self.ssl_certificate_storage.update_certificate( cert_obj.domain_name, cert_obj.cert_type, cert_obj.flavor_id, cert_details) except ValueError: LOG.info("No matching certificate found for the " "combination of {0} and {1} and {2} " "".format(domain_name, project_id, 'san')) for url in provider_details[provider].access_urls: if url.get('domain') == domain_name: if 'operator_url' in url: access_url = url['operator_url'] dns_controller.modify_cname(access_url, new_cert) url['provider_url'] = new_cert break else: links = {} link_key_tuple = (domain_name, 'san', None) links[link_key_tuple] = new_cert created_dns_link = dns_controller._create_cname_records(links) new_url = { 'domain': domain_name, 'operator_url': (created_dns_link[link_key_tuple]['operator_url']), 'provider_url': new_cert } provider_details[provider].access_urls.append(new_url) storage_controller.update_provider_details(project_id, service_id, provider_details)
def get_metrics_by_domain(self, project_id, domain_name, **extras): """Gets metrics information for a domain. The below keys are expected in ``extras`` along with their values. - metricType - startTime - endTime - metrics_controller Example return: ``{'provider': '', 'flavor':'', 'domain':'', 'metricType': {}}`` :param unicode project_id: The project id :param unicode domain_name: The domain name :param dict extras: Additional arguments to supply :return: A dictionary containing domain name and its metrics :rtype: dict :raises ServiceNotFound: if domain not present under project id :raises ServiceProviderDetailsNotFound: if no provider details exists for the service id and project id combo :raises ProviderDetailsIncomplete: if provider is not found for the domain """ storage_controller = self.storage_controller try: result = storage_controller.get_service_details_by_domain_name( domain_name=domain_name, project_id=project_id) except ValueError as ve: msg = ("Error retrieving details for domain {0} " "project_id {1} : {2}".format(domain_name, project_id, ve)) LOG.error(msg) raise errors.ServiceNotFound(msg) if not result: msg = "Domain: {0} was not found for project_id: {1}".format( domain_name, project_id) LOG.warning(msg) raise errors.ServiceNotFound(msg) if not result.provider_details: msg = "Provider Details were None " \ "for the service_id: {0} " \ "corresponding to project_id: {1}".format(result.service_id, project_id) LOG.warning(msg) raise errors.ServiceProviderDetailsNotFound(msg) provider_details_dict = result.provider_details provider_for_domain = None for provider, provider_details in provider_details_dict.items(): if provider_details.get_domain_access_url(domain=domain_name): provider_for_domain = provider if not provider_for_domain: msg = "Provider not found for Domain {0}".format(domain_name) LOG.error(msg) raise errors.ProviderDetailsIncomplete(msg) provider_obj = self.providers[provider_for_domain.lower()].obj provider_service_controller = provider_obj.service_controller extras['metrics_controller'] = self.metrics_controller metrics = provider_service_controller.get_metrics_by_domain( project_id, domain_name, provider_obj.regions, **extras) metrics['provider'] = provider_for_domain.lower() metrics['flavor'] = result.flavor_id return metrics