def create(self, project_id, auth_token, service_json): """create. :param project_id :param service_obj :raises LookupError, ValueError """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) # raise a lookup error if the flavor is not found except LookupError as e: raise e # add any default rules so its explicitly defined self._append_defaults(service_json) # convert to an object service_obj = service.Service.init_from_dict(service_json) service_id = service_obj.service_id # validate the service service_json = service_obj.to_dict() schema = service_schema.ServiceSchema.get_schema("service", "POST") validators.is_valid_service_configuration(service_json, schema) # deal with shared ssl domains for domain in service_obj.domains: if domain.protocol == 'https' and domain.certificate == 'shared': domain.domain = self._generate_shared_ssl_domain( domain.domain ) try: self.storage_controller.create( project_id, service_obj) # ValueError will be raised if the service has already existed except ValueError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': project_id, 'auth_token': auth_token, 'service_id': service_id, 'time_seconds': self.determine_sleep_times() } self.distributed_task_controller.submit_task( create_service.create_service, **kwargs) return service_obj
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 create(self, project_id, auth_token, service_json): """create. :param project_id :param service_obj :raises LookupError, ValueError """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) # raise a lookup error if the flavor is not found except LookupError as e: raise e # add any default rules so its explicitly defined self._append_defaults(service_json, operation='create') # convert to an object service_obj = service.Service.init_from_dict(project_id, service_json) service_id = service_obj.service_id # validate the service service_json = service_obj.to_dict() schema = service_schema.ServiceSchema.get_schema("service", "POST") validators.is_valid_service_configuration(service_json, schema) service_limit = self.storage_controller.get_service_limit(project_id) service_count = self.storage_controller.get_service_count(project_id) if service_count >= service_limit: raise errors.ServicesOverLimit('Maximum Services ' 'Limit of {0} ' 'reached!'.format(service_limit)) if any([ domain for domain in service_obj.domains if domain.certificate == "shared" ]): try: store = str(uuid.uuid4()).replace('-', '_') service_obj = self._shard_retry(project_id, service_obj, store=store) except errors.SharedShardsExhausted as e: raise e except ValueError as e: raise e try: self.storage_controller.create(project_id, service_obj) except ValueError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': project_id, 'auth_token': auth_token, 'service_id': service_id, 'time_seconds': self.determine_sleep_times(), 'context_dict': context_utils.get_current().to_dict() } self.distributed_task_controller.submit_task( create_service.create_service, **kwargs) return service_obj
def update(self, project_id, service_id, auth_token, service_updates, force_update=False): """update. :param project_id :param service_id :param auth_token :param service_updates :param force_update :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'] and force_update is False ): 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 create(self, project_id, auth_token, service_json): """create. :param project_id :param auth_token :param service_json :raises LookupError, ValueError """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) # raise a lookup error if the flavor is not found except LookupError as e: raise e # add any default rules so its explicitly defined self._append_defaults(service_json, operation='create') # convert to an object service_obj = service.Service.init_from_dict(project_id, service_json) service_id = service_obj.service_id # validate the service service_json = service_obj.to_dict() schema = service_schema.ServiceSchema.get_schema("service", "POST") validators.is_valid_service_configuration(service_json, schema) service_limit = self.storage_controller.get_service_limit(project_id) service_count = self.storage_controller.get_service_count(project_id) if service_count >= service_limit: raise errors.ServicesOverLimit('Maximum Services ' 'Limit of {0} ' 'reached!'.format(service_limit)) if any([domain for domain in service_obj.domains if domain.certificate == "shared"]): try: store = str(uuid.uuid4()).replace('-', '_') service_obj = self._shard_retry(project_id, service_obj, store=store) except errors.SharedShardsExhausted as e: raise e except ValueError as e: raise e try: self.storage_controller.create(project_id, service_obj) except ValueError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': project_id, 'auth_token': auth_token, 'service_id': service_id, 'time_seconds': self.determine_sleep_times(), 'context_dict': context_utils.get_current().to_dict() } self.distributed_task_controller.submit_task( create_service.create_service, **kwargs) return service_obj
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.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 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['provider_details'] 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) # 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(service_new_json) # check if the service domain names already exist for d in service_new.domains: if self.storage_controller.domain_exists_elsewhere( d.domain, service_id) is True: raise ValueError( "Domain {0} has already been taken".format(d.domain)) # fixing the old and new shared ssl domains in service_new for domain in service_new.domains: if domain.protocol == 'https' and 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._generate_shared_ssl_domain( domain.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' self.storage_controller.update(project_id, service_id, service_old) 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() } self.distributed_task_controller.submit_task( update_service.update_service, **kwargs) return
def create_service(self, project_id, auth_token, service_json): """Create a new service. :param unicode project_id: The project id :param unicode auth_token: Token for authorization :param dict service_json: The service details to create :return: The new service object created :rtype: poppy.model.service.Service :raises LookupError: if the flavor does not exists :raises ValueError: if shard domain retry operation has problem """ try: flavor = self.flavor_controller.get(service_json.get('flavor_id')) # raise a lookup error if the flavor is not found except LookupError as e: raise e # add any default rules so its explicitly defined self._append_defaults(service_json, operation='create') # convert to an object service_obj = service.Service.init_from_dict(project_id, service_json) service_id = service_obj.service_id # validate the service service_json = service_obj.to_dict() schema = service_schema.ServiceSchema.get_schema("service", "POST") validators.is_valid_service_configuration(service_json, schema) service_limit = self.storage_controller.get_service_limit(project_id) service_count = self.storage_controller.get_service_count(project_id) services_delete_in_progress = self.storage_controller.\ get_services_by_status('delete_in_progress') services_delete_count = len(services_delete_in_progress) # Check that the number of deleted services is less # than the total number of existing services for the project. # Adjust the service count removing delete_in_progress # services. service_count -= (services_delete_count if 0 < services_delete_count < service_count else 0) # service_count should always be a >= 0. if service_count >= service_limit: raise errors.ServicesOverLimit('Maximum Services ' 'Limit of {0} ' 'reached!'.format(service_limit)) if any([ domain for domain in service_obj.domains if domain.certificate == "shared" ]): try: store = str(uuid.uuid4()).replace('-', '_') service_obj = self._shard_retry(project_id, service_obj, store=store) except errors.SharedShardsExhausted as e: raise e except ValueError as e: raise e try: self.storage_controller.create_service(project_id, service_obj) except ValueError as e: raise e providers = [p.provider_id for p in flavor.providers] kwargs = { 'providers_list_json': json.dumps(providers), 'project_id': project_id, 'auth_token': auth_token, 'service_id': service_id, 'time_seconds': self.determine_sleep_times(), 'context_dict': context_utils.get_current().to_dict() } self.distributed_task_controller.submit_task( create_service.create_service, **kwargs) return service_obj