Exemplo n.º 1
0
def compare_term_types(service, **kwargs):

    print("kwargs:[{}]".format(kwargs))

    years = kwargs['years']
    #termComparison = models.TermComparison('', kwargs['region'], service, years, kwargs)

    kwargs.pop('sortCriteria', '')

    scenarioArray = []
    calcKey = ''
    awsPriceListApiVersion = ""
    onDemandTotal = 0

    #Iterate through applicable combinations of term types, purchase options and years
    for t in consts.SUPPORTED_TERM_TYPES:
        i = 0
        for p in consts.EC2_SUPPORTED_PURCHASE_OPTIONS:
            addFlag = False
            kwargs['instanceHours'] = 365 * 24 * kwargs['instanceCount'] * int(
                years)
            kwargs['termType'] = t
            if t == consts.SCRIPT_TERM_TYPE_RESERVED:
                calcKey = "{}-{}-{}yr".format(t, p, years)
                kwargs['offeringType'] = p
                if p == consts.SCRIPT_EC2_PURCHASE_OPTION_ALL_UPFRONT:
                    kwargs.pop('instanceHours', 0)
                addFlag = True
            if t == consts.SCRIPT_TERM_TYPE_ON_DEMAND and i == 0:  #only calculate price once for OnDemand
                calcKey = "{}-{}yr".format(t, years)
                kwargs['offeringType'] = ''
                addFlag = True
            i += 1

            #This flag ensures there are no duplicate OnDemand entries
            if addFlag:
                priceCalc = ec2pricing.calculate(
                    models.Ec2PriceDimension(**kwargs))
                print "priceCalc: {}".format(json.dumps(priceCalc, indent=4))
                pricingScenario = models.TermPricingScenario(
                    calcKey, dict(kwargs), priceCalc['pricingRecords'],
                    priceCalc['totalCost'], onDemandTotal)
                scenarioArray.append(
                    [pricingScenario.totalCost, pricingScenario])
                if t == consts.SCRIPT_TERM_TYPE_ON_DEMAND:
                    onDemandTotal = priceCalc['totalCost']
                awsPriceListApiVersion = priceCalc['awsPriceListApiVersion']

    sortedPricingScenarios = calculate_sorted_results(scenarioArray)
    #print "calculation results:[{}]".format(json.dumps(sortedPricingScenarios, indent=4))

    pricingAnalysis = models.TermPricingAnalysis(awsPriceListApiVersion,
                                                 kwargs['region'], service,
                                                 years)
    pricingAnalysis.pricingScenarios = sortedPricingScenarios
    pricingAnalysis.get_csv_data()
    return pricingAnalysis.__dict__
Exemplo n.º 2
0
def compare(**kwargs):
    service = kwargs['service']
    sortCriteria = kwargs['sortCriteria']
    result = []
    cheapest_price = 0
    criteria_array = ()
    kwargs_key = ""
    origkwargs = kwargs  #we'll keep track of the original paramaters
    scenarioArray = []

    #Sort by AWS Region - Total Cost and To-region (for sorting by destination - find which regions are cheaper for backups)
    if sortCriteria in [
            consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
    ]:
        tableCriteriaHeader = "Sorted by total cost by region\nRegion code\tRegion name\t"
        if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
            tableCriteriaHeader = "Sorted by data transfer destination from region [" + kwargs[
                'region'] + "] to other regions\nTo-Region code\tTo-Region name\t"

        for r in consts.SUPPORTED_REGIONS:
            kwargs = dict(
                origkwargs
            )  #revert to original parameters at the beginning of each loop
            if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
                kwargs['toRegion'] = r
            else:
                kwargs['region'] = r

            #avoid a situation where source and origin destinations are the same for dataTransferOutInterRegionGb
            if kwargs.get('dataTransferOutInterRegionGb',
                          0) > 0 and kwargs['region'] == kwargs['toRegion']:
                kwargs.pop('dataTransferOutInterRegionGb', 0)

            try:
                if service == consts.SERVICE_EC2:
                    p = ec2pricing.calculate(
                        models.Ec2PriceDimension(**kwargs))
                if service == consts.SERVICE_S3:
                    p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                if service == consts.SERVICE_RDS:
                    p = rdspricing.calculate(
                        models.RdsPriceDimension(**kwargs))
                if service == consts.SERVICE_LAMBDA:
                    p = lambdapricing.calculate(
                        models.LambdaPriceDimension(**kwargs))
                if service == consts.SERVICE_DYNAMODB:
                    p = ddbpricing.calculate(
                        models.DynamoDBPriceDimension(**kwargs))
                if service == consts.SERVICE_KINESIS:
                    p = kinesispricing.calculate(
                        models.KinesisPriceDimension(**kwargs))

            except NoDataFoundError:
                continue

            log.debug("PricingResult: [{}]".format(json.dumps(p, indent=4)))
            #Only append records for those combinations that exist in the PriceList API
            if p['pricingRecords']: result.append((p['totalCost'], r, p))

#Sort by EC2 Instance Type
    if sortCriteria == consts.SORT_CRITERIA_EC2_INSTANCE_TYPE:
        tableCriteriaHeader = "Total cost sorted by EC2 Instance Type in region [" + kwargs[
            'region'] + "]\nType\t"
        instanceTypes = kwargs.get('instanceTypes', '')
        if instanceTypes: instanceTypes = instanceTypes.split(',')
        else: instanceTypes = consts.SUPPORTED_INSTANCE_TYPES
        log.info("instanceTypes: [{}]".format(instanceTypes))
        for t in instanceTypes:
            kwargs['instanceType'] = t
            try:
                p = ec2pricing.calculate(models.Ec2PriceDimension(**kwargs))
                result.append((p['totalCost'], t, p))
            except NoDataFoundError:
                pass

#Sort by EC2 Operating System
    if sortCriteria == consts.SORT_CRITERIA_OS:
        tableCriteriaHeader = "Total cost sorted by Operating System in region [" + kwargs[
            'region'] + "]\nOS\t"
        for o in consts.SUPPORTED_EC2_OPERATING_SYSTEMS:
            kwargs['operatingSystem'] = o
            try:
                p = ec2pricing.calculate(models.Ec2PriceDimension(**kwargs))
                result.append((p['totalCost'], o, p))
            except NoDataFoundError:
                pass

    #Sort by RDS DB Instance Class
    if sortCriteria == consts.SORT_CRITERIA_DB_INSTANCE_CLASS:
        tableCriteriaHeader = "Total cost sorted by DB Instance Class in region [" + kwargs[
            'region'] + "]\nDB Instance Class\t"
        for ic in consts.SUPPORTED_RDS_INSTANCE_CLASSES:
            kwargs['dbInstanceClass'] = ic
            try:
                p = rdspricing.calculate(models.RdsPriceDimension(**kwargs))
                result.append((p['totalCost'], ic, p))
            except NoDataFoundError:
                pass

    #Sort by RDS DB Engine
    if sortCriteria == consts.SORT_CRITERIA_DB_ENGINE:
        tableCriteriaHeader = "Total cost sorted by DB Engine in region [" + kwargs[
            'region'] + "]\nDB Engine - License Model\t"
        for e in consts.RDS_SUPPORTED_DB_ENGINES:
            kwargs['engine'] = e
            for lm in consts.RDS_SUPPORTED_LICENSE_MODELS:
                if 'sqlserver' in e or 'oracle' in e:
                    kwargs['licenseModel'] = lm
                else:
                    #SCRIPT_RDS_LICENSE_MODEL_PUBLIC is the only applicable license model for open source engines
                    if lm == consts.SCRIPT_RDS_LICENSE_MODEL_PUBLIC:
                        kwargs[
                            'licenseModel'] = consts.SCRIPT_RDS_LICENSE_MODEL_PUBLIC
                    else:
                        continue
                try:
                    p = rdspricing.calculate(
                        models.RdsPriceDimension(**kwargs))
                    result.append((p['totalCost'], "{} - {}".format(e, lm), p))
                except NoDataFoundError:
                    pass

    #Sort by Lambda memory
    if sortCriteria == consts.SORT_CRITERIA_LAMBDA_MEMORY:
        tableCriteriaHeader = "Total cost sorted Allocated Memory in region [" + kwargs[
            'region'] + "]\nMemory\t"
        for m in consts.LAMBDA_MEM_SIZES:
            kwargs['memoryMb'] = m
            p = lambdapricing.calculate(models.LambdaPriceDimension(**kwargs))
            if p['pricingRecords']: result.append((p['totalCost'], m, p))

    #Sort by S3 Storage Class
    if sortCriteria == consts.SORT_CRITERIA_S3_STORAGE_CLASS:
        #TODO: Use criteria_array for all sort calculations
        tableCriteriaHeader = "Tocal cost sorted by S3 Storage Class in region [" + kwargs[
            'region'] + "]\nStorage Class\t"
        criteria_array = consts.SUPPORTED_S3_STORAGE_CLASSES
        for c in criteria_array:
            kwargs['storageClass'] = c
            try:
                p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                if p['pricingRecords']: result.append((p['totalCost'], c, p))
            except NoDataFoundError:
                pass

    #Sort by S3 Storage Size (this implies that a comma-separated list of values is supplied for storage-size-gb
    if sortCriteria == consts.SORT_CRITERIA_S3_STORAGE_SIZE_GB:
        tableCriteriaHeader = "Tocal cost sorted by S3 Storage Size (GB) in region [" + kwargs[
            'region'] + "]\nStorage Size GB\t"
        criteria_array = kwargs.get('storageSizeGb', '').split(
            consts.SORT_CRITERIA_VALUE_SEPARATOR)
        for c in criteria_array:
            kwargs['storageSizeGb'] = int(c)
            try:
                p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                if p['pricingRecords']: result.append((p['totalCost'], c, p))
            except NoDataFoundError:
                pass

    #Sort by S3 Data Retrieval GB (this implies that a comma-separated list of values is supplied for data-retrieval-gb)
    #For now, it excludes data transfer out to the internet. #TODO: include a parameter for data transfer out, proportional to data retrieval
    if sortCriteria == consts.SORT_CRITERIA_S3_DATA_RETRIEVAL_GB:
        tableCriteriaHeader = "Tocal cost sorted by S3 Data Retrieval (GB) for Storage Class [{}] in region [{}]\nData Retrieval GB\t".format(
            kwargs['storageClass'], kwargs['region'])
        criteria_array = kwargs.get('dataRetrievalGb', '').split(
            consts.SORT_CRITERIA_VALUE_SEPARATOR)
        for c in criteria_array:
            kwargs['dataRetrievalGb'] = int(c)
            try:
                p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                if p['pricingRecords']: result.append((p['totalCost'], c, p))
            except NoDataFoundError:
                pass

    #Sort by S3 Data Retrieval GB AND Storage Class (this implies that a comma-separated list of values is supplied for data-retrieval-gb)
    #For now, it excludes data transfer out to the internet. #TODO: include a parameter for data transfer out, proportional to data retrieval
    if sortCriteria == consts.SORT_CRITERIA_S3_STORAGE_CLASS_DATA_RETRIEVAL_GB:
        tableCriteriaHeader = "Tocal cost sorted by S3 Data Retrieval (GB) and all Storage Classes in region [{}]\nStorage Class + Data Retrieval GB\t".format(
            kwargs['region'])
        criteria_array = kwargs.get('dataRetrievalGb', '').split(
            consts.SORT_CRITERIA_VALUE_SEPARATOR)
        for sc in consts.SUPPORTED_S3_STORAGE_CLASSES:
            for c in criteria_array:
                kwargs['storageClass'] = sc
                kwargs['dataRetrievalGb'] = int(c)
                try:
                    p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                    if p['pricingRecords']:
                        result.append((p['totalCost'], "{}_{}GB".format(sc,
                                                                        c), p))
                except NoDataFoundError:
                    pass

    sorted_result = sorted(result)
    log.debug("sorted_result: {}".format(sorted_result))
    if sorted_result: cheapest_price = sorted_result[0][0]
    result = []
    i = 0
    awsPriceListApiVersion = ''
    pricingScenarios = []
    #TODO: use a structured object (Class or dict) instead of using indexes for each field in the table
    for r in sorted_result:
        if i == 0: awsPriceListApiVersion = r[2]['awsPriceListApiVersion']
        if sorted_result[i][0] > 0:
            #Calculate the current record relative to the last record
            delta_cheapest = r[0] - cheapest_price
            delta_last = 0
            pct_to_last = 0
            pct_to_cheapest = 0
            if i >= 1:
                delta_last = sorted_result[i][0] - sorted_result[i - 1][0]
                if sorted_result[i - 1][0] > 0:
                    pct_to_last = (
                        (sorted_result[i][0] - sorted_result[i - 1][0]) /
                        sorted_result[i - 1][0]) * 100
            if cheapest_price > 0:
                pct_to_cheapest = (
                    (r[0] - cheapest_price) / cheapest_price) * 100

            result.append((r[0], r[1], pct_to_cheapest, pct_to_last,
                           delta_cheapest, delta_last))

            #TODO:populate price dimensions in PricingScenario instance
            pricingScenario = models.PricingScenario(i, r[1], {}, r[2], r[0],
                                                     sortCriteria)
            pricingScenario.deltaCheapest = delta_cheapest
            pricingScenario.deltaPrevious = delta_last
            pricingScenario.pctToCheapest = pct_to_cheapest
            pricingScenario.pctToPrevious = pct_to_last
            pricingScenarios.append(pricingScenario.__dict__)

        i = i + 1

    pricecomparison = models.PriceComparison(awsPriceListApiVersion, service,
                                             sortCriteria)
    pricecomparison.pricingScenarios = pricingScenarios

    print("Sorted cost table:")
    print(
        tableCriteriaHeader +
        "Cost(USD)\t% compared to cheapest\t% compared to previous\tdelta cheapest\tdelta previous"
    )
    for r in result:
        rowCriteriaValues = ""
        if sortCriteria in [
                consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
        ]:
            rowCriteriaValues = r[1] + "\t" + consts.REGION_MAP[r[1]] + "\t"
        else:
            rowCriteriaValues = str(r[1]) + "\t"
        print(rowCriteriaValues + str(r[0]) + "\t" + str(r[2]) + "\t" +
              str(r[3]) + "\t" + str(r[4]) + "\t" + str(r[5]))

    return pricecomparison.__dict__
Exemplo n.º 3
0
def compare_term_types(service, **kwargs):
    log.info("kwargs:[{}]".format(kwargs))
    years = kwargs['years']
    regions = []
    if kwargs.get('regions', []): regions = kwargs['regions']
    else: regions = [kwargs['region']]
    kwargs.pop('sortCriteria', '')
    scenarioArray = []
    priceCalc = {}
    calcKey = ''
    awsPriceListApiVersion = ""
    onDemandTotal = 0

    #TODO: move this logic to models.TermPricingAnalysis
    #Iterate through applicable combinations of term types, purchase options and years
    termTypes = kwargs.get('termTypes', consts.SUPPORTED_TERM_TYPES)
    purchaseOptions = kwargs.get('purchaseOptions',
                                 consts.EC2_SUPPORTED_PURCHASE_OPTIONS)
    offeringClasses = kwargs.get('offeringClasses',
                                 consts.SUPPORTED_EC2_OFFERING_CLASSES)
    for r in regions:
        kwargs['region'] = r
        for t in termTypes:
            i = 0
            for oc in offeringClasses:
                for p in purchaseOptions:
                    addFlag = False
                    kwargs['instanceHours'] = 365 * 24 * int(
                        kwargs['instanceCount']) * int(years)
                    kwargs['termType'] = t

                    if t == consts.SCRIPT_TERM_TYPE_RESERVED:
                        if len(regions) > 1:
                            calcKey = "{}-{}-{}-{}-{}yr".format(
                                r, t, oc, p, years)
                        else:
                            calcKey = "{}-{}-{}-{}yr".format(t, oc, p, years)
                        kwargs['offeringType'] = p
                        kwargs['offeringClass'] = oc
                        if p == consts.SCRIPT_EC2_PURCHASE_OPTION_ALL_UPFRONT:
                            kwargs.pop('instanceHours', 0)
                        addFlag = True
                    if t == consts.SCRIPT_TERM_TYPE_ON_DEMAND and i == 0:  #only calculate price once for OnDemand
                        if len(regions) > 1:
                            calcKey = "{}-{}-{}yr".format(r, t, years)
                        else:
                            calcKey = "{}-{}yr".format(t, years)
                        kwargs['offeringType'] = ''
                        addFlag = True
                    i += 1
                    try:
                        #This flag ensures there are no duplicate OnDemand entries
                        pdims = {}
                        priceCalc = {}
                        if addFlag:
                            if service == consts.SERVICE_EC2:
                                pdims = models.Ec2PriceDimension(**kwargs)
                                pdims.region = r
                                priceCalc = ec2pricing.calculate(pdims)
                            elif service == consts.SERVICE_RDS:
                                pdims = models.RdsPriceDimension(**kwargs)
                                pdims.region = r
                                priceCalc = rdspricing.calculate(pdims)
                            log.info("priceCalc: {}".format(
                                json.dumps(priceCalc, indent=4)))
                            #pricingScenario = models.TermPricingScenario(calcKey, dict(kwargs), priceCalc['pricingRecords'], priceCalc['totalCost'], onDemandTotal)
                            if t == consts.SCRIPT_TERM_TYPE_ON_DEMAND:
                                onDemandTotal = priceCalc['totalCost']
                            pricingScenario = models.TermPricingScenario(
                                calcKey, pdims.__dict__,
                                priceCalc['pricingRecords'],
                                priceCalc['totalCost'], onDemandTotal)
                            scenarioArray.append(
                                [pricingScenario.totalCost, pricingScenario])

                            awsPriceListApiVersion = priceCalc[
                                'awsPriceListApiVersion']

                    except NoDataFoundError as ndf:
                        log.debug("NoDataFoundError pdims:[{}]".format(pdims))

    if len(scenarioArray) == 0:
        raise NoDataFoundError(
            "NoDataFoundeError for term type comparison: [{}]".format(kwargs))
    sortedPricingScenarios = calculate_sorted_results(scenarioArray)
    #print "calculation results:[{}]".format(json.dumps(sortedPricingScenarios, indent=4))

    pricingAnalysis = models.TermPricingAnalysis(awsPriceListApiVersion,
                                                 regions, service, years)
    pricingAnalysis.pricingScenarios = sortedPricingScenarios
    #TODO: move the next 3 calls to a single method
    pricingAnalysis.calculate_months_to_recover()
    pricingAnalysis.calculate_monthly_breakdown()
    pricingAnalysis.get_csv_data()
    return pricingAnalysis.__dict__
Exemplo n.º 4
0
def compare(**kwargs):
    service = kwargs['service']
    sortCriteria = kwargs['sortCriteria']
    result = []
    cheapest_price = 0
    criteria_array = ()
    kwargs_key = ""

    #Sort by AWS Region - Total Cost and To-region (for sorting by destination - find which regions are cheaper for backups)
    if sortCriteria in [
            consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
    ]:
        tableCriteriaHeader = "Sorted by total cost by region\nRegion code\tRegion name\t"
        if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
            tableCriteriaHeader = "Sorted by data transfer destination from region [" + kwargs[
                'region'] + "] to other regions\nTo-Region code\tTo-Region name\t"

        for r in consts.SUPPORTED_REGIONS:
            if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
                kwargs['toRegion'] = r
            else:
                kwargs['region'] = r
            if service == consts.SERVICE_EC2:
                p = ec2pricing.calculate(models.Ec2PriceDimension(**kwargs))
            if service == consts.SERVICE_S3:
                p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
            if service == consts.SERVICE_RDS:
                p = rdspricing.calculate(models.RdsPriceDimension(**kwargs))
            if service == consts.SERVICE_LAMBDA:
                p = lambdapricing.calculate(
                    models.LambdaPriceDimension(**kwargs))
            if service == consts.SERVICE_DYNAMODB:
                p = ddbpricing.calculate(
                    models.DynamoDBPriceDimension(**kwargs))
            if service == consts.SERVICE_KINESIS:
                p = kinesispricing.calculate(
                    models.KinesisPriceDimension(**kwargs))

            print(json.dumps(p, indent=True))
            #Only append records for those combinations that exist in the PriceList API
            if p['pricingRecords']: result.append((p['totalCost'], r))

#Sort by EC2 Operating System
    if sortCriteria == consts.SORT_CRITERIA_OS:
        tableCriteriaHeader = "Sorted by total cost by Operating System in region [" + kwargs[
            'region'] + "]\nOS\t"
        for o in consts.SUPPORTED_EC2_OPERATING_SYSTEMS:
            kwargs['operatingSystem'] = o
            if service == consts.SERVICE_EC2:
                p = ec2pricing.calculate(models.Ec2PriceDimension(**kwargs))

            result.append((p['totalCost'], o))

    #Sort by Lambda memory
    if sortCriteria == consts.SORT_CRITERIA_LAMBDA_MEMORY:
        tableCriteriaHeader = "Sorted by total Allocated Memory in region [" + kwargs[
            'region'] + "]\nMemory\t"
        for m in consts.LAMBDA_MEM_SIZES:
            kwargs['memoryMb'] = m
            p = lambdapricing.calculate(models.LambdaPriceDimension(**kwargs))
            if p['pricingRecords']: result.append((p['totalCost'], m))

    #Sort by S3 Storage Class
    if sortCriteria == consts.SORT_CRITERIA_S3_STORAGE_CLASS:
        #TODO: Use criteria_array for all sort calculations
        tableCriteriaHeader = "Sorted by S3 Storage Class in region [" + kwargs[
            'region'] + "]\nStorage Class\t"
        criteria_array = consts.SUPPORTED_S3_STORAGE_CLASSES
        for c in criteria_array:
            kwargs['storageClass'] = c
            p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
            if p['pricingRecords']: result.append((p['totalCost'], c))

    sorted_result = sorted(result)
    print("sorted_result: {}".format(sorted_result))
    if sorted_result:
        cheapest_price = sorted_result[0][0]
    result = []
    i = 0
    #TODO: use a structured object (Class or dict) instead of using indexes for each field in the table
    for r in sorted_result:
        #Calculate the current record relative to the last record
        delta_last = 0
        pct_to_last = 0
        pct_to_cheapest = 0
        #TODO:handle cases where cheapest is 0
        if i >= 1:
            delta_last = sorted_result[i][0] - sorted_result[i - 1][0]
            if sorted_result[i - 1][0] > 0:
                pct_to_last = (
                    (sorted_result[i][0] - sorted_result[i - 1][0]) /
                    sorted_result[i - 1][0]) * 100
        if cheapest_price > 0:
            pct_to_cheapest = ((r[0] - cheapest_price) / cheapest_price) * 100

        result.append((r[0], r[1], pct_to_cheapest, pct_to_last,
                       (r[0] - cheapest_price), delta_last))

        i = i + 1

    print("Sorted cost table:")
    print(
        tableCriteriaHeader +
        "Cost(USD)\t% compared to cheapest\t% compared to previous\tdelta cheapest\tdelta previous"
    )
    for r in result:
        rowCriteriaValues = ""
        if sortCriteria in [
                consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
        ]:
            rowCriteriaValues = r[1] + "\t" + consts.REGION_MAP[r[1]] + "\t"
        if sortCriteria in [
                consts.SORT_CRITERIA_OS, consts.SORT_CRITERIA_LAMBDA_MEMORY,
                consts.SORT_CRITERIA_S3_STORAGE_CLASS
        ]:
            rowCriteriaValues = str(r[1]) + "\t"
        print(rowCriteriaValues + str(r[0]) + "\t" + str(r[2]) + "\t" +
              str(r[3]) + "\t" + str(r[4]) + "\t" + str(r[5]))

    return result
Exemplo n.º 5
0
def compare(**kwargs):
    service = kwargs['service']
    sortCriteria = kwargs['sortCriteria']
    result = []
    cheapest_price = 0
    criteria_array = ()
    kwargs_key = ""
    origkwargs = kwargs  #we'll keep track of the original paramaters

    #Sort by AWS Region - Total Cost and To-region (for sorting by destination - find which regions are cheaper for backups)
    if sortCriteria in [
            consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
    ]:
        tableCriteriaHeader = "Sorted by total cost by region\nRegion code\tRegion name\t"
        if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
            tableCriteriaHeader = "Sorted by data transfer destination from region [" + kwargs[
                'region'] + "] to other regions\nTo-Region code\tTo-Region name\t"

        for r in consts.SUPPORTED_REGIONS:
            kwargs = dict(
                origkwargs
            )  #revert to original parameters at the beginning of each loop
            if sortCriteria == consts.SORT_CRITERIA_TO_REGION:
                kwargs['toRegion'] = r
            else:
                kwargs['region'] = r

            #avoid a situation where source and origin destinations are the same for dataTransferOutInterRegionGb
            if kwargs.get('dataTransferOutInterRegionGb',
                          0) > 0 and kwargs['region'] == kwargs['toRegion']:
                kwargs.pop('dataTransferOutInterRegionGb', 0)

            try:
                if service == consts.SERVICE_EC2:
                    p = ec2pricing.calculate(
                        models.Ec2PriceDimension(**kwargs))
                if service == consts.SERVICE_S3:
                    p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
                if service == consts.SERVICE_RDS:
                    p = rdspricing.calculate(
                        models.RdsPriceDimension(**kwargs))
                if service == consts.SERVICE_LAMBDA:
                    p = lambdapricing.calculate(
                        models.LambdaPriceDimension(**kwargs))
                if service == consts.SERVICE_DYNAMODB:
                    p = ddbpricing.calculate(
                        models.DynamoDBPriceDimension(**kwargs))
                if service == consts.SERVICE_KINESIS:
                    p = kinesispricing.calculate(
                        models.KinesisPriceDimension(**kwargs))

            except NoDataFoundError:
                pass

            print(json.dumps(p, indent=True))
            #Only append records for those combinations that exist in the PriceList API
            if p['pricingRecords']: result.append((p['totalCost'], r))

#Sort by EC2 Operating System
    if sortCriteria == consts.SORT_CRITERIA_OS:
        tableCriteriaHeader = "Total cost sorted by Operating System in region [" + kwargs[
            'region'] + "]\nOS\t"
        for o in consts.SUPPORTED_EC2_OPERATING_SYSTEMS:
            kwargs['operatingSystem'] = o
            try:
                p = ec2pricing.calculate(models.Ec2PriceDimension(**kwargs))
                result.append((p['totalCost'], o))
            except NoDataFoundError:
                pass

    #Sort by RDS DB Instance Class
    if sortCriteria == consts.SORT_CRITERIA_DB_INSTANCE_CLASS:
        tableCriteriaHeader = "Total cost sorted by DB Instance Class in region [" + kwargs[
            'region'] + "]\nDB Instance Class\t"
        for ic in consts.SUPPORTED_RDS_INSTANCE_CLASSES:
            kwargs['dbInstanceClass'] = ic
            try:
                p = rdspricing.calculate(models.RdsPriceDimension(**kwargs))
                result.append((p['totalCost'], ic))
            except NoDataFoundError:
                pass

    #Sort by RDS DB Engine
    if sortCriteria == consts.SORT_CRITERIA_DB_ENGINE:
        tableCriteriaHeader = "Total cost sorted by DB Engine in region [" + kwargs[
            'region'] + "]\nDB Engine - License Model\t"
        for e in consts.RDS_SUPPORTED_DB_ENGINES:
            kwargs['engine'] = e
            for lm in consts.RDS_SUPPORTED_LICENSE_MODELS:
                if 'sqlserver' in e or 'oracle' in e:
                    kwargs['licenseModel'] = lm
                else:
                    #SCRIPT_RDS_LICENSE_MODEL_PUBLIC is the only applicable license model for open source engines
                    if lm == consts.SCRIPT_RDS_LICENSE_MODEL_PUBLIC:
                        kwargs[
                            'licenseModel'] = consts.SCRIPT_RDS_LICENSE_MODEL_PUBLIC
                    else:
                        continue
                try:
                    p = rdspricing.calculate(
                        models.RdsPriceDimension(**kwargs))
                    result.append((p['totalCost'], "{} - {}".format(e, lm)))
                except NoDataFoundError:
                    pass

    #Sort by Lambda memory
    if sortCriteria == consts.SORT_CRITERIA_LAMBDA_MEMORY:
        tableCriteriaHeader = "Total cost sorted Allocated Memory in region [" + kwargs[
            'region'] + "]\nMemory\t"
        for m in consts.LAMBDA_MEM_SIZES:
            kwargs['memoryMb'] = m
            p = lambdapricing.calculate(models.LambdaPriceDimension(**kwargs))
            if p['pricingRecords']: result.append((p['totalCost'], m))

    #Sort by S3 Storage Class
    if sortCriteria == consts.SORT_CRITERIA_S3_STORAGE_CLASS:
        #TODO: Use criteria_array for all sort calculations
        tableCriteriaHeader = "Tocal cost sorted by S3 Storage Class in region [" + kwargs[
            'region'] + "]\nStorage Class\t"
        criteria_array = consts.SUPPORTED_S3_STORAGE_CLASSES
        for c in criteria_array:
            kwargs['storageClass'] = c
            p = s3pricing.calculate(models.S3PriceDimension(**kwargs))
            if p['pricingRecords']: result.append((p['totalCost'], c))

    sorted_result = sorted(result)
    log.debug("sorted_result: {}".format(sorted_result))
    if sorted_result: cheapest_price = sorted_result[0][0]
    result = []
    i = 0
    #TODO: use a structured object (Class or dict) instead of using indexes for each field in the table
    for r in sorted_result:
        if sorted_result[i][0] > 0:
            #Calculate the current record relative to the last record
            delta_last = 0
            pct_to_last = 0
            pct_to_cheapest = 0
            if i >= 1:
                delta_last = sorted_result[i][0] - sorted_result[i - 1][0]
                if sorted_result[i - 1][0] > 0:
                    pct_to_last = (
                        (sorted_result[i][0] - sorted_result[i - 1][0]) /
                        sorted_result[i - 1][0]) * 100
            if cheapest_price > 0:
                pct_to_cheapest = (
                    (r[0] - cheapest_price) / cheapest_price) * 100

            result.append((r[0], r[1], pct_to_cheapest, pct_to_last,
                           (r[0] - cheapest_price), delta_last))

        i = i + 1

    print("Sorted cost table:")
    print(
        tableCriteriaHeader +
        "Cost(USD)\t% compared to cheapest\t% compared to previous\tdelta cheapest\tdelta previous"
    )
    for r in result:
        rowCriteriaValues = ""
        if sortCriteria in [
                consts.SORT_CRITERIA_REGION, consts.SORT_CRITERIA_TO_REGION
        ]:
            rowCriteriaValues = r[1] + "\t" + consts.REGION_MAP[r[1]] + "\t"
        else:
            rowCriteriaValues = str(r[1]) + "\t"
        print(rowCriteriaValues + str(r[0]) + "\t" + str(r[2]) + "\t" +
              str(r[3]) + "\t" + str(r[4]) + "\t" + str(r[5]))

    return result