def dispatch(self, request):
        try:
            logger.info('Start request process / ID request - {}'.format(
                request.id))
            result = self.process_request(request)

            if not result:
                logger.info('Method `process_request` did not return result')
                return

            params = {}
            if isinstance(result, ActivationTileResponse):
                params = {'activation_tile': result.tile}
            elif isinstance(result, ActivationTemplateResponse):
                params = {'template_id': result.template_id}

            self.approve(request.id, params)

        except FulfillmentInquire as inquire:
            self.update_parameters(request.id, inquire.params)
            return self.inquire(request.id)

        except FulfillmentFail as fail:
            return self.fail(request.id, reason=fail.message)

        except Skip as skip:
            return skip.code
        return
Example #2
0
 def __cancel(self, req):
     logger.info("Cancelling...")
     tenant_id = req.asset.get_param_by_id("customer_tenant_id").value
     tenant_suspended = self.__toggle(False, req)
     self.service.remove(tenant_id, tenant_suspended['version'],
                         {'version': tenant_suspended['version']})
     logger.info("Cancelled")
    def _upload_spreadsheet(self, usage_file, spreadsheet):
        # type: (UsageFile, openpyxl.Workbook) -> None

        # Generate spreadsheet file
        with NamedTemporaryFile() as tmp:
            spreadsheet.save(tmp)
            tmp.seek(0)
            file_contents = tmp.read()

        # Setup request
        url = self._api.get_url(usage_file.id + '/upload/')
        headers = self._api.headers
        headers['Accept'] = 'application/json'
        del headers[
            'Content-Type']  # This must NOT be set for multipart post requests
        multipart = {'usage_file': ('usage_file.xlsx', file_contents)}
        logger.info('HTTP Request: {} - {} - {}'.format(
            url, headers, multipart))

        # Post request
        try:
            content, status = self._api.post(path=usage_file.id + '/upload/',
                                             headers=headers,
                                             files=multipart)
        except requests.RequestException as ex:
            raise FileCreationError('Error uploading file: {}'.format(ex))
        logger.info('HTTP Code: {}'.format(status))
        if status != 201:
            msg = 'Unexpected server response, returned code {}'.format(status)
            logger.error('{} -- Raw response: {}'.format(msg, content))
            raise FileCreationError(msg)
Example #4
0
 def __toggle(self, enabled, req):
     logger.info("Toggle")
     tenant_id = req.asset.get_param_by_id("customer_tenant_id").value
     version = self.service.get_tenant_info(tenant_id)['version']
     result = self.service.edit(tenant_id, {
         'enabled': enabled,
         'version': version
     })
     return result
    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()
Example #6
0
    def __purchase(self, req):
        logger.info("PURCHASE")
        setattr(self, 'tenant', None)
        login_name = req.asset.get_param_by_id('customer_admin').value
        # Check if logged user exists
        self.service.check_login(login_name)
        try:
            # Create customer account
            logger.info("Creating tenant...")
            tenant_created = self.service.provision_customer(
                req.id,
                self.get_tier_config(req.asset.tiers.tier1.id,
                                     self.config.products[0]).get_param_by_id(
                                         'tenant_id').value)
            logger.debug(tenant_created)
            setattr(self, 'tenant', tenant_created['id'])
            # Set offering based in sku
            logger.info("Setting offerings...")
            self.service.set_customer_offerings(
                tenant_created['id'],
                self.get_tier_config(req.asset.tiers.tier1.id,
                                     self.config.products[0]).get_param_by_id(
                                         'tenant_id').value,
                [
                    Utils.sku_to_offering_item(item)
                    for item in req.asset.items if item.quantity > 0
                ])
            # Create admin for user account
            logger.info("Creating admin...")
            admin_created = self.service.create_admin(tenant_created,
                                                      login_name)
            logger.debug(admin_created)
            # Create access policies for created user
            self.service.access_policies(admin_created, tenant_created)
            # Send activation email
            self.service.send_email(login_name, admin_created)
            params = [
                Param(id='customer_tenant_id',
                      value=str(tenant_created['id'])),
            ]
            self.update_parameters(req.id, params)
            logger.info("PURCHASE END")

        except Exception as err:
            if self.tenant:
                logger.error("Rollback tenant creation")
                suspended = self.service.edit(self.tenant, {
                    'enabled': False,
                    'version': 1
                })
                self.service.remove(self.tenant, suspended['version'],
                                    {'version': suspended['version']})
            raise FailRequest(err)
Example #7
0
 def __change(self, req):
     logger.info("Changing...")
     changed = [
         Utils.sku_to_offering_item(item) for item in req.asset.items
         if item.quantity > 1 and item.quantity != 0
     ]
     logger.debug(changed)
     self.service.set_customer_offerings(
         req.asset.get_param_by_id("customer_tenant_id").value,
         self.get_tier_config(
             req.asset.tiers.tier1.id,
             self.config.products[0]).get_param_by_id('tenant_id').value,
         changed, True)
Example #8
0
 def process_request(self, request):
     if (request.type == 'purchase'):
         for param in request.asset.params:
             if (param.name == 'tenantId'):
                 tenant_param_id = param.value
         url = VENDOR_API_URL + 'tenant/' + request.id
         response = requests.get(url, data='').json()
         if (tenant_param_id != '' and response['status'] == 'ready'):
             self.approve_request(request)
         else:
             logger.info('Skip process')
     else:
         logger.info('This processor not handle this type of request')
     return False
Example #9
0
    def create_request(self, request):
        for item in request.asset.items:
            mpn = item.mpn
            quantity = item.quantity
            break

        url = VENDOR_API_URL + "tenant?externalId=" + mpn
        response = requests.get(url, data='').json()
        first_name = request.asset.tiers.customer.contact_info.contact.first_name
        last_name = request.asset.tiers.customer.contact_info.contact.last_name
        address = request.asset.tiers.customer.contact_info.address_line1
        postal_code = request.asset.tiers.customer.contact_info.postal_code
        country = request.asset.tiers.customer.contact_info.country
        email = request.asset.tiers.customer.contact_info.contact.email
        account_phone = request.asset.tiers.customer.contact_info.contact.phone_number.phone_number
        if response['externalId'] != request.asset.id:
            url = VENDOR_API_URL + 'tenant'
            payload = {
                'Attributes': {
                    'product': {
                        'item': mpn,
                        'quantity': quantity
                        },
                    'account': {
                        'accountFirstName': first_name,
                        'accountLastName': last_name,
                        'accountCompany': request.asset.tiers.customer.name,
                        'accountAddress': address,
                        'accountCity': request.asset.tiers.customer.contact_info.city,
                        'accountState': request.asset.tiers.customer.contact_info.state,
                        'accountPostalCode': postal_code,
                        'accountCountry': country,
                        'accountEmail': email,
                        'accountPhone': account_phone
                        }
                    }
                }
            response = requests.post(url, data=payload).json()
            if (response['tenantId'] != ''):
                data = {
                    "params": [{
                        "id": "tenantId",
                        "value": response['tenantId']
                    }]
                }
                self._fulfillment.update_param_asset_request(request.id, data, 'vendor system Id')
                return response
            else:
                logger.info('Error in Vendor System')
                return False
Example #10
0
    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()
Example #11
0
    def dispatch(self, request):
        # type: (TierConfigRequest) -> str
        try:
            if self.config.products \
                    and request.configuration.product.id not in self.config.products:
                return 'Invalid product'

            logger.info(
                'Start tier config request process / ID request - {}'.format(
                    request.id))
            result = self.process_request(request)

            if not result:
                logger.info('Method `process_request` did not return result')
                return ''

            params = {}
            if isinstance(result, ActivationTileResponse):
                params = {'template': {'representation': result.tile}}
            elif isinstance(result, ActivationTemplateResponse):
                params = {'template': {'id': result.template_id}}

            self.approve(request.id, params)

        except InquireRequest as inquire:
            self.update_parameters(request.id, inquire.params)
            return self.inquire(request.id)

        except FailRequest as fail:
            return self.fail(request.id, reason=str(fail))

        except SkipRequest as skip:
            return skip.code

        except NotImplementedError:
            raise

        except Exception as ex:
            logger.warning(
                'Skipping request {} because an exception was raised: {}'.
                format(request.id, ex))
            return ''

        return ''
    def dispatch(self, request):
        # type: (UsageListing) -> str

        # TODO Shouldn't this raise an exception on ALL automation classes?
        if self.config.products \
                and request.product.id not in self.config.products:
            return 'Listing not handled by this processor'

        logger.info('Processing Usage for Product {} ({}) '.format(
            request.product.id, request.product.name) +
                    'on Contract {} '.format(request.contract.id) +
                    'and provider {}({})'.format(request.provider.id,
                                                 request.provider.name))

        try:
            result = self.process_request(request)
        except FileCreationError:
            logger.info('Error processing Usage for Product {} ({}) '.format(
                request.product.id, request.product.name) +
                        'on Contract {} '.format(request.contract.id) +
                        'and provider {}({})'.format(request.provider.id,
                                                     request.provider.name))
            return 'failure'

        logger.info('Processing result for usage on listing {}: {}'.format(
            request.product.id, result))
        return 'success'
Example #13
0
    def dispatch(self, request):
        # type: (UsageFile) -> str
        try:
            # Validate product
            if self.config.products \
                    and request.product.id not in self.config.products:
                return 'Invalid product'

            # Process request
            logger.info(
                'Start usage file request process / ID request - {}'.format(
                    request.id))
            result = self.process_request(request)

            # Report that expected exception was not raised
            processing_result = 'UsageFileAutomation.process_request returned {} while ' \
                                'is expected to raise UsageFileAction or SkipRequest exception' \
                .format(str(result))
            logger.warning(processing_result)
            raise UserWarning(processing_result)

        # Catch action
        except UsageFileAction as usage:
            self._api.post(
                path='{}/{}'.format(request.id, usage.code),
                json=usage.obj.json if isinstance(usage.obj, BaseModel) else
                getattr(usage.obj, '__dict__', str(usage.obj)))
            processing_result = usage.code

        # Catch skip
        except SkipRequest:
            processing_result = 'skip'

        logger.info(
            'Finished processing of usage file with ID {} with result {}'.
            format(request.id, processing_result))
        return processing_result
Example #14
0
 def process_request(self, request):
     if (request.type == 'purchase'):
         if (len(request.asset.items) == 1):
             tenant_param_id = ''
             for param in request.asset.params:
                 if (param.name == 'tenantId'):
                     tenant_param_id = param.value
             if (tenant_param_id == ''):
                 self.create_request(request)
             else:
                 logger.info('Skip process')
         else:
             logger.info('Request malformed, too many items')
     else:
         logger.info('This processor not handle this type of request')
     return False
Example #15
0
    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
Example #16
0
 def list(self, filters=None):
     # type: (Dict[str, Any]) -> List[Any]
     filters = filters or self.filters()
     logger.info('Get list request with filters - {}'.format(filters))
     response, _ = self._api.get(params=filters)
     return self.model_class.deserialize(response)
Example #17
0
    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
Example #18
0
    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
Example #19
0
 def list(self):
     filters = self.build_filter()
     logger.info('Get list request by filter - {}'.format(filters))
     response = self.api.get(url=self._list_url, params=filters)
     return self.__loads_scheme(response)
    def dispatch(self, request):
        # type: (Fulfillment) -> str

        conversation = request.get_conversation(self.config)

        try:
            if self.config.products \
                    and request.asset.product.id not in self.config.products:
                return 'Invalid product'

            logger.info('Start request process / ID request - {}'.format(
                request.id))
            process_result = self.process_request(request)

            if not process_result:
                logger.info('Method `process_request` did not return result')
                return ''

            if isinstance(process_result, ActivationTileResponse):
                message = 'Activated using custom activation tile.'
                approved = self.approve(
                    request.id, {'activation_tile': process_result.tile})
            elif isinstance(process_result, ActivationTemplateResponse):
                message = 'Activated using template {}.'.format(
                    process_result.template_id)
                approved = self.approve(
                    request.id, {'template_id': process_result.template_id})
            else:
                # We should not get here
                message = ''
                approved = ''

            self._update_conversation_if_exists(conversation, request.id,
                                                message)
            return approved

        except InquireRequest as inquire:
            self.update_parameters(request.id, inquire.params)
            inquired = self.inquire(request.id)
            self._update_conversation_if_exists(conversation, request.id,
                                                inquire)
            return inquired

        except FailRequest as fail:
            # PyCharm incorrectly detects unreachable code here, so disable
            # noinspection PyUnreachableCode
            failed = self.fail(request.id, reason=str(fail))
            self._update_conversation_if_exists(conversation, request.id, fail)
            return failed

        except SkipRequest as skip:
            self._update_conversation_if_exists(conversation, request.id, skip)
            return skip.code

        except NotImplementedError:
            raise

        except Exception as ex:
            logger.warning(
                'Skipping request {} because an exception was raised: {}'.
                format(request.id, ex))
            return ''