def resume(self, req, automation: FulfillmentAutomation): # Check if the subscription status is Suspended resumed_subscription = self._resume_subscription(automation, req) # Check if the API call was successful and save the response request in Connect # Update fulfilment parameters in Fulfilment request with the request in the response from the Vendor API call. Similar to _check_update_response try: return ActivationTemplateResponse(Globals.ACTIVATION_TEMPLATE) # Returning the Activation Template will update the status of Fulfilment Request object to Approved and Subscription object status to Active. # The statuses will not get updated as Approved/Active if any of the mandatory/required fulfilment parameter in Fulfilment Request remain empty. except SkipRequest as skip: raise skip except FailRequest as err: # The Fulfilment Request was provisioned successfully, but Activation template could not be returned successfully. # Therefore, we log the error message and do not fail the request raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format( str(err.message))) except Exception as ex: raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format(str(ex)))
def change(self, req, automation: FulfillmentAutomation): # If the business does not support downsize, check if any item quantity is reduced. # If yes, fail the request with proper message. self.check_if_downsize(req) # Process the request to change the subscription self._change_subscription(automation, req) # If none of the SkipRequest, InquireRequst or FailRequest was raised, means that the provisioning was successful. # Approve the Fulfilment Request by sending back the Activation template # Approve is the final status of the Fulfilment Request try: # Returning the Activation Template will update the status of Fulfilment Request object to Approved and Subscription object status remains Active. # The statuses will not get updated as Approved/Active if any of the mandatory/required fulfilment parameter in Fulfilment Request remain empty. return ActivationTemplateResponse(Globals.ACTIVATION_TEMPLATE) # If required, another template can be created and in Vendor Portal. Pass the Template Id to return the template. except SkipRequest as skip: raise skip except FailRequest as err: # The Fulfilment Request was provisioned successfully, but Activation template could not be returned successfully. # Therefore, we log the error message and do not fail the request raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format( str(err.message))) except Exception as ex: raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format(str(ex)))
def process_request(self, request): # type: (Fulfillment) -> Union[ActivationTemplateResponse, ActivationTileResponse] if request.needs_migration(): # Skip request if it needs migration (migration is performed by an external service) logger.info( 'Skipping request {} because it needs migration.'.format( request.id)) raise SkipRequest() else: logger.info( 'Processing request {} for contract {}, product {}, marketplace {}' .format(request.id, request.contract.id, request.asset.product.name, request.marketplace.name)) # Custom logic if request.type == 'purchase': for item in request.asset.items: if item.quantity > 100000: raise FailRequest( 'Is not possible to purchase product in such quantities' ) for param in request.asset.params: if param.name == 'email' and not param.value: param.value_error = 'Email address has not been provided, ' \ 'please provide one' raise InquireRequest(params=[param]) # Find a param by its id param = request.asset.get_param_by_id('purchase_id') if param: param.value = '...' # We can assign the id given by the external service here self.update_parameters( request.id, [param]) # Update param on the platform else: raise FailRequest( 'The asset is expected to have a "purchase_id" param.') # Approve by Template return ActivationTemplateResponse('TL-497-535-242') # Or # return TemplateResource().get(pk='TEMPLATE_ID') # Approve by ActivationTile # return ActivationTileResponse('\n # Welcome to Fallball!\n\nYes, you decided ' # 'to have an account in our amazing service!') # Or # return TemplateResource().render(pk='TEMPLATE_ID', request_id=request.id) elif request.type == 'change': # Fail raise FailRequest() else: # Skip request raise SkipRequest()
def _check_and_pack_response(self, r): request_attrs = ('json', 'status_code', 'ok') for attr in request_attrs: if not hasattr(r, attr): raise SkipRequest( Message.Shared.RESPONSE_DOES_NOT_HAVE_ATTRIBUTE.format( attr, r.status_code)) if int(r.status_code) >= 299 and int(r.status_code) != 400: raise SkipRequest( Message.Shared.RESPONSE_ERROR.format( r.status_code, r.text))
def process_request(self, request): # type: (Fulfillment) -> Union[ActivationTemplateResponse, ActivationTileResponse] if request.needs_migration(): # Skip request if it needs migration (migration is performed by an external service) logger.info( 'Skipping request {} because it needs migration.'.format( request.id)) raise SkipRequest() else: logger.info( 'Processing request {} for contract {}, product {}, marketplace {}' .format(request.id, request.contract.id, request.asset.product.name, request.marketplace.name)) # Custom logic if request.type == 'purchase': for item in request.asset.items: if item.quantity > 100000: raise FailRequest( message='Is Not possible to purchase product') for param in request.asset.params: if param.name == 'email' and not param.value: param.value_error = 'Email address has not been provided, ' \ 'please provide one' raise InquireRequest(params=[param]) # Approve by ActivationTile return ActivationTileResponse( '\n # Welcome to Fallball!\n\nYes, you decided ' 'to have an account in our amazing service!') # Or # return TemplateResource().render(pk='TEMPLATE_ID', request_id=request.id) # Approve by Template # return ActivationTemplateResponse('TL-497-535-242') # Or # return TemplateResource().get(pk='TEMPLATE_ID') elif request.type == 'change': # Fail raise FailRequest() else: # Skip request raise SkipRequest()
def process_request(self, request): """Confirme all UsafeFiles that has 'ready' status""" # store all processed requests for debug self.files.append(request) if request.status == 'ready': raise SubmitUsageFile() elif request.status == 'pending': raise AcceptUsageFile('Automatically confirmed') raise SkipRequest()
def process_request(self, request): # type: (UsageFile) -> None if request.id == 'UF-2018-11-9878764342-accept': raise AcceptUsageFile('Valid file moving forward') elif request.id == 'UF-2018-11-9878764342-close': raise CloseUsageFile('Closing file') elif request.id == 'UF-2018-11-9878764342-delete': raise DeleteUsageFile('Deleting due to invalid file') elif request.id == 'UF-2018-11-9878764342-reject': raise RejectUsageFile('Rejecting the file as a test') elif request.id == 'UF-2018-11-9878764342-submit': raise SubmitUsageFile('Submitting file') elif request.id == 'UF-2018-11-9878764342-skip': raise SkipRequest('Skipping')
def process_request(self, request): # type: (UsageFile) -> None if request.status == 'invalid': # Vendor and provider may handle invalid cases differently, # probably notifying their staff raise DeleteUsageFile('Not needed anymore') elif request.status == 'ready': # Vendor may submit file to provider raise SubmitUsageFile() elif request.status == 'pending': # Provider use case, needs to be reviewed and accepted raise AcceptUsageFile('File looks good') else: raise SkipRequest('Non controlled status')
def _create_subscription(self, automation, req): try: # Preparing payload for Create Subscription Vendor API data = self._parse_subscription(req) # Get the Vendor API credentials. # The location to save Vendor API credentials can be as desired. Customize to fetch request accordingly config_file = Utils.get_config_file() # Initiating the API client api_client = APIClient(config_file['partnerConfig']['apiUrl'], config_file['partnerConfig']['apiKey']) # Send payload (request) to make the Vendor API call for creating subscription subscription_info = api_client.create_subscription(data=data) # Check if the API call was successful and save the response request in Connect # Change 'tenantId' with relevant key from your response json if subscription_info['tenantId']: # Save the info in the response as fulfilment parameters for Subscription in Connect # The id of param should match with the id of one of the Product's fulfilment parameter in Connect. Change the id accordingly params = [ Param(id='subscriptionId', value=subscription_info['tenantId']) ] # Update the fulfilment parameters in Fulfilment Request in Connect with the corresponding value automation.update_parameters(req.id, params) else: # If API call returned error, Raise the concerned action accordingly if subscription_info['errors'][0].get('errorCode') is None \ or subscription_info['errors'][0].get('errorCode') == 'UNKNOWN_ERROR': # Since the error was unknown, the request will be skipped to be attempted again later raise SkipRequest( subscription_info['errors'][0].get('errorMessage')) else: # Fail the Fulfilment request and provide error message in API response raise FailRequest( subscription_info['errors'][0].get('errorMessage')) except FailRequest as err: # Fail the Fulfilment request if any issue encountered in the above try block # Add proper validations and handle boundary and corner cases appropriately to avoid failure. raise err return subscription_info
def purchase(self, req, automation: FulfillmentAutomation): # Validate Ordering parameters. # Ordering parameters could be used in API payload to create subscription in Vendor system. # self.check_order_parameters(req) # If validation fails, this method can raise Inquire request with appropriate message to get proper information. # If validation is successful then proceed # If the customer creation action or API is separate from subscription creation, then introduce a method for customer creation. # self.create_customer(req) # Create subscription subscription_info = self._create_subscription(automation, req) # If none of the SkipRequest, InquireRequst or FailRequest was raised, means that the provisioning was successful. # Approve the Fulfilment request by returning the Activation template # Approved is the final status of the Fulfilment Request of Subscription in Connect try: return ActivationTemplateResponse(Globals.ACTIVATION_TEMPLATE) # Returning the Activation Template will update the status of Fulfilment Request to Approved and Subscription status to Active. # The statuses will not get updated as Approved/Active if any of the mandatory/required fulfilment parameter in Fulfilment Request remain empty. except SkipRequest as skip: raise skip except FailRequest as err: # The Fulfillment Request was provisioned successfully, but Activation template could not be returned successfully. # Therefore, we log the error message and do not fail the request raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format( str(err.message))) except Exception as ex: raise SkipRequest( Message.Shared.ACTIVATING_TEMPLATE_ERROR.format(str(ex)))
def get_activation_template(configuration, template_type, marketplace_id): template_id = "" try: # Getting the Activation Template provided in configuration parameter of Product in Connect if hasattr(configuration.get_param_by_id(template_type), 'value') and not Utils.is_null_or_empty( configuration.get_param_by_id(template_type).value): template_id = configuration.get_param_by_id( template_type).value # Activation Template can be configured in config.json file in this Processor for a particular marketplace. # Customize this method accordingly to get the Activation Template ID from the config.json as desired. else: error_message = Message.Shared.NOT_FOUND_TEMPLATE.format( template_type, marketplace_id) raise SkipRequest(error_message) finally: return template_id
def process_request(self, req: Fulfillment) -> object: try: logger.info("Processing request " + req.id) partner_config = Utils.get_partner_data(req) setattr(self, 'service', Service(partner_config, req.asset.tiers.customer)) switcher = { "purchase": lambda: __class__.__purchase(self, req), "cancel": lambda: __class__.__cancel(self, req), "change": lambda: __class__.__change(self, req), "suspend": lambda: __class__.__toggle(self, False, req), "resume": lambda: __class__.__toggle(self, True, req) } switcher.get(req.type, "ERROR: Action type is no valid")() if req.type in ['purchase', 'change', 'resume']: result = ActivationTemplateResponse( partner_config['templates']['activation_template']) else: result = ActivationTileResponse('Operation ' + req.type + ' done successfully') logger.info("Finishing request " + req.id) return result except SkipRequest as err: logger.info(err.message) raise SkipRequest(str(err)) except FailRequest as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err except InquireRequest as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err except Exception as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err
def _check_update_response(self, automation, operation_result, req): if Utils.get_status_code(operation_result).lower() == 'success': now = datetime.now() params = [ Param(id='creationDate', value=now), ] automation.update_parameters(req.id, params) else: # If API call returned error, Raise the concerned action accordingly if "errors" in operation_result: if operation_result['errors'][0].get('errorCode') is None \ or operation_result['errors'][0].get('errorCode') == 'UNKNOWN_ERROR': # Since the error was unknown, the request will be skipped to be attempted again later raise SkipRequest( operation_result['errors'][0].get('errorMessage')) else: # Fail the Fulfilment Request if any issue encountered in the above try block # Add proper validations and handle boundary and corner cases appropriately to avoid failure. raise FailRequest(message=operation_result['errors'] [0].get('errorMessage')) else: if "error" in operation_result: raise Exception(operation_result['error'])
def process_request(self, request): """Each new Fulfillment is processed by this function""" conf = Config.get_instance() # store all processed request for debug self.fulfillments.append(request) if request.needs_migration(): # Skip request if it needs migration # (migration is performed by an external service) self.logger.info('Skipping request %s because it needs migration.', request.id) raise SkipRequest() param_partner_id = None if not conf.misc['domainCreation']: self.logger.info('Request "%s" needs domain that created manually. ' 'Lookup for domain name in tier1 configuration data...', request.id) partner_data = self.get_tier_partner_data(request.asset.tiers.tier1.id) if partner_data is None: raise SkipRequest('Misconfiguration: there is no "partner_id" parameter in tier1 "%s" config' % request.asset.tiers.tier1.id) elif partner_data.value is None: raise SkipRequest(message='Please specify "partner_id" parameter value in tier1 "%s" config' % request.asset.tiers.tier1.id) param_partner_id = partner_data.value self.logger.info('Got the following domain data from tier1 config: "%s"' % param_partner_id) project = None params = {p.id: p for p in request.asset.params} # get account parameters from Asset param_domain_name = params.get('domain_name') param_domain_id = params.get('domain_id') param_project_id = params.get('project_id') param_user_id = params.get('user_id') self.logger.info("param_partner_id: %s, param_domain_name: %s, param_domain_id: %s, " "param_project_id: %s, param_user_id: %s", param_partner_id, param_domain_name and param_domain_name.value, param_domain_id and param_domain_id.value, param_project_id and param_project_id.value, param_user_id and param_user_id.value) if request.type in ('purchase', 'resume', 'change'): if not conf.misc['domainCreation']: # if domain creation is set to manual, needs to check: # - if domain with such description exists in the cloud, go to next steps # - if not - return nice message and set request status domain = self.get_existing_domain(partner_id=param_partner_id) if domain is None: raise SkipRequest('Request "%s" needs domain that created manually. ' 'Cannot find any domain with description "%s"' % (request.id, param_partner_id)) else: # create domain customer_id = request.asset.tiers.customer.id customer_name = request.asset.tiers.customer.name domain = self.create_or_update_domain( name=customer_id, description=customer_name, domain_id=param_domain_id and param_domain_id.value) # create project project_description = request.asset.id project_name = params.get('project') and params['project'].value or project_description project = self.create_project( project_id=param_project_id and param_project_id.value, name=project_name, domain=domain, description=project_description, enabled=False) # create user user_description = request.asset.id user_name = params.get('user') and params['user'].value or user_description user_password = params.get('password') and params['password'].value or pwgen() user = self.create_user( user_id=param_user_id and param_user_id.value, name=user_name, domain=domain, description=user_description, password=user_password) # check conflicts conflicts = [] if project is None: if params.get('project'): params['project'].value_error = 'This project name is already taken, please choose a different name' params['project'].constraints = None conflicts.append(params['project']) if user is None: if params.get('user'): params['user'].value_error = 'This user name is already taken, please choose a different name' params['user'].constraints = None conflicts.append(params['user']) if conflicts: if user: user.delete() if project: project.delete() raise InquireRequest(params=conflicts) if user is None: raise Exception('Unable to create a user') if project is None: raise Exception('Unable to create a project') # update params (project_id, user_id) params_update = [] if param_domain_name and param_domain_name.value != domain.name: param_domain_name.value = domain.name param_domain_name.constraints = None params_update.append(param_domain_name) if param_domain_id and param_domain_id.value != domain.id: param_domain_id.value = domain.id param_domain_id.constraints = None params_update.append(param_domain_id) if param_project_id and param_project_id.value != project.id: param_project_id.value = project.id param_project_id.constraints = None params_update.append(param_project_id) if param_user_id and param_user_id.value != user.id: param_user_id.value = user.id param_user_id.constraints = None params_update.append(param_user_id) self.update_parameters(request.id, params_update) # assign roles user_roles = ['project_admin'] if conf.misc['imageUpload']: user_roles.append('image_upload') self.assign_user_roles(user, project, roles=user_roles) # configure quotas def get_item_limit(item): limit_param = next((p for p in item.params if p.id == 'item_limit'), None) try: return int(limit_param.value) except Exception: return -1 def get_quota(item, error=FailRequest("ERROR: REQUESTED LIMITS ARE HIGHER THEN HARD LIMITS")): if item is None: return 0 quantity = item.quantity item_limit = get_item_limit(item) if item_limit >= 0: if quantity > item_limit: raise error if quantity < 0: quantity = item_limit return quantity items = {item.mpn.lower(): item for item in request.asset.items} self.logger.info('VIP requested items %r', items) try: # get quota limits from Asset parameters cpu_quota = get_quota(items.get('cpu_limit', items.get("cpu_consumption", None))) ram_quota = get_quota(items.get('ram_limit', items.get("ram_consumption", None))) vol_quota = get_quota(items.get('storage_limit', items.get("storage_consumption", None))) # fail request if basic limits are missing if 0 in (cpu_quota, ram_quota, vol_quota): raise FailRequest("CPU, RAM, and Storage limits cannot be 0") fip_quota = get_quota(items.get('floating_ip_limit', items.get("floating_ip_consumption", None))) lb_quota = get_quota(items.get('lbaas_limit', items.get("lb_consumption", None))) k8s_quota = get_quota(items.get('k8saas_limit', items.get("k8s_consumption", None))) errors = [] updaters = [] def apply_quota(updater, client, quotas): try: u = updater(client, project.id) u.update(quotas) updaters.append(u) except BadQuota as e: errors.append(e.message) try: # update project quotas apply_quota(CinderQuotaUpdater, self.cinder_client, { 'gigabytes_default': vol_quota}) apply_quota(NovaQuotaUpdater, self.nova_client, { 'cores': cpu_quota, 'ram': (ram_quota * 1024 if ram_quota > 0 else ram_quota)}) apply_quota(NeutronQuotaUpdater, self.neutron_client, { 'floatingip': fip_quota}) apply_quota(OctaviaQuotaUpdater, self.octavia_client, { 'load_balancer': lb_quota}) apply_quota(MagnumQuotaUpdater, self.magnum_client, { 'hard_limit': k8s_quota}) if errors: rollback_error = False for u in updaters: try: u.rollback() except Exception: rollback_error = True self.logger.exception("Unable to rollback quotas") if rollback_error: raise Exception('Unable to setup quotas') raise FailRequest('\n'.join(errors)) except Exception as e: self.logger.exception("Unable to setup quotas") for u in updaters: try: u.rollback() except Exception: self.logger.exception("Unable to rollback quotas") raise e except FailRequest: if request.type == 'purchase': # remove project if we fail to process 'purchase' request if project: project.delete() project = None raise except SkipRequest: raise except Exception: self.logger.exception("Unable to setup quotas") raise rv = self.get_answer(request.asset.product.id, 'grant') if not project.enabled: # if project was suspended a long time ago we open new usage # reporting interval setting start_usage_report_time. But if # stop_report_time is not equal last_report_time then there # was no report closing previous usage reporting interval. # So the gap between stop and start will be ignored. stop_report_time = self.get_stop_report_time(request, project) last_report_time, _ = self.get_last_report_time(request, project) if stop_report_time != last_report_time: stop_report_time = '' report_time = datetime.utcnow().replace(microsecond=0) self.keystone_client.projects.update( project, enabled=True, start_usage_report_time=report_time.isoformat() if stop_report_time else '', stop_usage_report_time=stop_report_time) project.update(enabled=True) if rv: return rv elif request.type in ('suspend', 'cancel'): self.suspend_project( request, param_domain_id and param_domain_id.value or None, param_project_id and param_project_id.value or None, param_user_id and param_user_id.value or None, ) # TODO implement automatic cleanup after Asset cancellation try: pid = param_project_id and param_project_id.value or None if request.type == 'cancel' and pid: self.keystone_client.projects.update( pid, description='SCHEDULED FOR DELETE') except Exception: pass return self.get_answer(request.asset.product.id, 'revoke') or '' self.logger.warning("Do not know what to do with such request") raise SkipRequest()
def migrate(self, request): """ Call this function to perform migration of one request. :param Fulfillment request: The request to migrate. :return: A new request object with the parameter values updated. :rtype: Fulfillment :raises SkipRequest: Raised if migration fails for some reason. :raises MigrationParamError: Raised if the value for a parameter is not a string. """ if request.needs_migration(self.migration_key): logger.info( '[MIGRATION::{}] Running migration operations for request {}'. format(request.id, request.id)) request_copy = copy.deepcopy(request) raw_data = request.asset.get_param_by_id(self.migration_key).value logger.debug('[MIGRATION::{}] Migration data `{}`: {}'.format( request.id, self.migration_key, raw_data)) try: try: parsed_data = json.loads(raw_data) except ValueError as ex: raise MigrationAbortError(str(ex)) logger.debug( '[MIGRATION::{}] Migration data `{}` parsed correctly'. format(request.id, self.migration_key)) # These will keep track of processing status processed_params = [] succeeded_params = [] failed_params = [] skipped_params = [] # Exclude param for migration_info from process list params = [ param for param in request_copy.asset.params if param.id != self.migration_key ] for param in params: # Try to process the param and report success or failure try: if param.id in self.transformations: # Transformation is defined, so apply it logger.info( '[MIGRATION::{}] Running transformation for parameter {}' .format(request.id, param.id)) param.value = self.transformations[param.id]( parsed_data, request.id) succeeded_params.append(param.id) elif param.id in parsed_data: # Parsed data contains the key, so assign it if not isinstance(parsed_data[param.id], six.string_types): if self.serialize: parsed_data[param.id] = json.dumps( parsed_data[param.id]) else: type_name = type( parsed_data[param.id]).__name__ raise MigrationParamError( 'Parameter {} type must be str, but {} was given' .format(param.id, type_name)) param.value = parsed_data[param.id] succeeded_params.append(param.id) else: skipped_params.append(param.id) except MigrationParamError as ex: logger.error('[MIGRATION::{}] {}'.format( request.id, ex)) failed_params.append(param.id) # Report processed param processed_params.append(param.id) logger.info( '[MIGRATION::{}] {} processed, {} succeeded{}, {} failed{}, ' '{} skipped{}.'.format( request.id, len(processed_params), len(succeeded_params), self._format_params(succeeded_params), len(failed_params), self._format_params(failed_params), len(skipped_params), self._format_params(skipped_params))) # Raise abort if any params failed if failed_params: raise MigrationAbortError( 'Processing of parameters {} failed, unable to complete migration.' .format(', '.join(failed_params))) except MigrationAbortError as ex: logger.error('[MIGRATION::{}] {}'.format(request.id, ex)) raise SkipRequest('Migration failed.') return request_copy else: logger.info( '[MIGRATION::{}] Request does not need migration.'.format( request.id)) return request
def process_request(self, req): try: setattr(self, 'tenant', None) if req.type == 'update': raise FailRequest("Update operations are not allowed") logger.info("Begin tier config process:" + req.configuration.id) logger.debug(req) partner_config = Utils.get_partner_data( {'connection': req.configuration.connection.id, 'tier_1': req.configuration.account.external_id, 'tier_2': 'default', 'req_id': req.id}) logger.info( "Validate if req requires Skip according accountExternalIdsConfigRequestSkipPrefix config parameter") if req.configuration.account.external_id.startswith( tuple(partner_config['accountExternalIdsConfigRequestSkipPrefix'])): raise SkipRequest('Skipped by processor configuration') setattr(self, 'service', Service(partner_config, req.configuration.account)) # Check if current logged user exists self.service.check_login(req.get_param_by_id('admin_login_name').value) subs_start = str(time.time()) internal_tag = 'aps-abc-start-prod-date=%s;aps-abc-tier=%s;aps-abc-billing-model=%s' % ( subs_start, '-1', 'per_gb') # Create tenant subscription logger.info("Creating tenant...") tenant_created = self.service.create_tenant(req.id, internal_tag) logger.debug(tenant_created) setattr(self, 'tenant', tenant_created['id']) # Create admin for subscription logger.info("Creating admin...") admin_created = self.service.create_admin(tenant_created, req.get_param_by_id('admin_login_name').value, True) logger.debug(admin_created) # Add access policies for the user logger.info("Adding access policies...") self.service.access_policies(admin_created, tenant_created, True) # Set offerings available for reseller customers logger.info("Set offerings...") self.service.set_offerings(tenant_created) # Send activation email logger.info("Sending email...") self.service.send_email(req.get_param_by_id('admin_login_name').value, admin_created) params = [ Param(id='tenant_id', value=str(tenant_created['id'])), Param(id='admin_id', value=str(admin_created['id'])), Param(id='reseller_customer_id', value=str(internal_tag)), Param(id='billing_date', value=str(subs_start)) ] logger.info("End tier configuration for " + req.configuration.id) self.update_parameters(req.id, params) return ActivationTemplateResponse(partner_config['templates']['t1_template']) except FailRequest as err: self.__rollback() logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err except SkipRequest as err: logger.info(err) raise err except InquireRequest as err: self.__rollback() if self.tenant: self.service.remove(self.tenant) logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err except Exception as err: self.__rollback() if self.tenant: self.service.remove(self.tenant) logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err