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()
Exemple #2
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()
Exemple #3
0
 def check_login(self, username):
     try:
         method = '/users/check_login?username='******'GET', method)
     except Exception as err:
         if err.args[0]['code'] == 409:
             raise InquireRequest(err.args[0]['message'])
         else:
             raise FailRequest(err.args[0]['message'])
Exemple #4
0
 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
Exemple #5
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)
    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 _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'])
Exemple #8
0
    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 check_if_downsize(req):
     if Utils.is_downsize_request(req.asset.items):
         raise FailRequest(Message.Shared.NOT_ALLOWED_DOWNSIZE)
Exemple #10
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