mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-04-08 10:52:15 +01:00
Sqlx az portal cost estimate (#1264)
* added isZoneRedundant to rp call * Pass region item everywhere * cost breakdown string added
This commit is contained in:
parent
98fb7a5fd6
commit
c534b2d74b
src/SelfServe/SqlX
@ -7,6 +7,7 @@ import SqlX from "./SqlX";
|
|||||||
import {
|
import {
|
||||||
FetchPricesResponse,
|
FetchPricesResponse,
|
||||||
PriceMapAndCurrencyCode,
|
PriceMapAndCurrencyCode,
|
||||||
|
RegionItem,
|
||||||
RegionsResponse,
|
RegionsResponse,
|
||||||
SqlxServiceResource,
|
SqlxServiceResource,
|
||||||
UpdateDedicatedGatewayRequestParameters,
|
UpdateDedicatedGatewayRequestParameters,
|
||||||
@ -139,7 +140,7 @@ const getGeneralPath = (subscriptionId: string, resourceGroup: string, name: str
|
|||||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}`;
|
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRegions = async (): Promise<Array<string>> => {
|
export const getRegions = async (): Promise<Array<RegionItem>> => {
|
||||||
const telemetryData = {
|
const telemetryData = {
|
||||||
feature: "Calculate approximate cost",
|
feature: "Calculate approximate cost",
|
||||||
function: "getRegions",
|
function: "getRegions",
|
||||||
@ -149,8 +150,6 @@ export const getRegions = async (): Promise<Array<string>> => {
|
|||||||
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
|
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const regions = new Array<string>();
|
|
||||||
|
|
||||||
const response = await armRequestWithoutPolling<RegionsResponse>({
|
const response = await armRequestWithoutPolling<RegionsResponse>({
|
||||||
host: configContext.ARM_ENDPOINT,
|
host: configContext.ARM_ENDPOINT,
|
||||||
path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name),
|
path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name),
|
||||||
@ -158,20 +157,12 @@ export const getRegions = async (): Promise<Array<string>> => {
|
|||||||
apiVersion: "2021-04-01-preview",
|
apiVersion: "2021-04-01-preview",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.result.location !== undefined) {
|
|
||||||
regions.push(response.result.location.split(" ").join("").toLowerCase());
|
|
||||||
} else {
|
|
||||||
for (const location of response.result.locations) {
|
|
||||||
regions.push(location.locationName.split(" ").join("").toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
|
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
|
||||||
return regions;
|
return response.result.properties.locations;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const failureTelemetry = { err, selfServeClassName: SqlX.name };
|
const failureTelemetry = { err, selfServeClassName: SqlX.name };
|
||||||
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
|
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
|
||||||
return new Array<string>();
|
return new Array<RegionItem>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,7 +170,7 @@ const getFetchPricesPathForRegion = (subscriptionId: string): string => {
|
|||||||
return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`;
|
return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promise<PriceMapAndCurrencyCode> => {
|
export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Promise<PriceMapAndCurrencyCode> => {
|
||||||
const telemetryData = {
|
const telemetryData = {
|
||||||
feature: "Calculate approximate cost",
|
feature: "Calculate approximate cost",
|
||||||
function: "getPriceMapAndCurrencyCode",
|
function: "getPriceMapAndCurrencyCode",
|
||||||
@ -191,7 +182,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
|
|||||||
try {
|
try {
|
||||||
const priceMap = new Map<string, Map<string, number>>();
|
const priceMap = new Map<string, Map<string, number>>();
|
||||||
let currencyCode;
|
let currencyCode;
|
||||||
for (const region of regions) {
|
for (const regionItem of regions) {
|
||||||
const regionPriceMap = new Map<string, number>();
|
const regionPriceMap = new Map<string, number>();
|
||||||
|
|
||||||
const response = await armRequestWithoutPolling<FetchPricesResponse>({
|
const response = await armRequestWithoutPolling<FetchPricesResponse>({
|
||||||
@ -202,7 +193,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
|
|||||||
queryParams: {
|
queryParams: {
|
||||||
filter:
|
filter:
|
||||||
"armRegionName eq '" +
|
"armRegionName eq '" +
|
||||||
region +
|
regionItem.locationName.split(" ").join("").toLowerCase() +
|
||||||
"' and serviceFamily eq 'Databases' and productName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'",
|
"' and serviceFamily eq 'Databases' and productName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -215,7 +206,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promis
|
|||||||
}
|
}
|
||||||
regionPriceMap.set(item.skuName, item.retailPrice);
|
regionPriceMap.set(item.skuName, item.retailPrice);
|
||||||
}
|
}
|
||||||
priceMap.set(region, regionPriceMap);
|
priceMap.set(regionItem.locationName, regionPriceMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp);
|
selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { RegionItem } from "SelfServe/SqlX/SqlxTypes";
|
||||||
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
|
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
|
||||||
import {
|
import {
|
||||||
selfServeTrace,
|
selfServeTrace,
|
||||||
@ -208,7 +209,7 @@ const ApproximateCostDropDownInfo: Info = {
|
|||||||
|
|
||||||
let priceMap: Map<string, Map<string, number>>;
|
let priceMap: Map<string, Map<string, number>>;
|
||||||
let currencyCode: string;
|
let currencyCode: string;
|
||||||
let regions: Array<string>;
|
let regions: Array<RegionItem>;
|
||||||
|
|
||||||
const calculateCost = (skuName: string, instanceCount: number): Description => {
|
const calculateCost = (skuName: string, instanceCount: number): Description => {
|
||||||
const telemetryData = {
|
const telemetryData = {
|
||||||
@ -221,27 +222,47 @@ const calculateCost = (skuName: string, instanceCount: number): Description => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let costPerHour = 0;
|
let costPerHour = 0;
|
||||||
for (const region of regions) {
|
let costBreakdown = "";
|
||||||
const incrementalCost = priceMap.get(region).get(skuName.replace("Cosmos.", ""));
|
for (const regionItem of regions) {
|
||||||
|
const incrementalCost = priceMap.get(regionItem.locationName).get(skuName.replace("Cosmos.", ""));
|
||||||
if (incrementalCost === undefined) {
|
if (incrementalCost === undefined) {
|
||||||
throw new Error("Value not found in map.");
|
throw new Error(`${regionItem.locationName} not found in price map.`);
|
||||||
|
} else if (incrementalCost === 0) {
|
||||||
|
throw new Error(`${regionItem.locationName} cost per hour = 0`);
|
||||||
}
|
}
|
||||||
costPerHour += incrementalCost;
|
|
||||||
|
let regionalInstanceCount = instanceCount;
|
||||||
|
if (regionItem.isZoneRedundant) {
|
||||||
|
regionalInstanceCount = Math.ceil(instanceCount * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionalCostPerHour = incrementalCost * regionalInstanceCount;
|
||||||
|
costBreakdown += `
|
||||||
|
${regionItem.locationName} ${regionItem.isZoneRedundant ? "(AZ)" : ""}
|
||||||
|
${regionalCostPerHour} ${currencyCode} (${regionalInstanceCount} instances * ${incrementalCost} ${currencyCode})\
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (regionalCostPerHour === 0) {
|
||||||
|
throw new Error(`${regionItem.locationName} Cost per hour = 0`);
|
||||||
|
}
|
||||||
|
|
||||||
|
costPerHour += regionalCostPerHour;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (costPerHour === 0) {
|
if (costPerHour === 0) {
|
||||||
throw new Error("Cost per hour = 0");
|
throw new Error("Cost per hour = 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
costPerHour *= instanceCount;
|
|
||||||
costPerHour = Math.round(costPerHour * 100) / 100;
|
costPerHour = Math.round(costPerHour * 100) / 100;
|
||||||
|
|
||||||
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
|
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
|
||||||
return {
|
return {
|
||||||
textTKey: `${costPerHour} ${currencyCode}`,
|
textTKey: `${costPerHour} ${currencyCode}
|
||||||
|
${costBreakdown}`,
|
||||||
type: DescriptionType.Text,
|
type: DescriptionType.Text,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
alert(err);
|
||||||
const failureTelemetry = { err, regions, priceMap, selfServeClassName: SqlX.name };
|
const failureTelemetry = { err, regions, priceMap, selfServeClassName: SqlX.name };
|
||||||
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);
|
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);
|
||||||
|
|
||||||
|
@ -48,10 +48,14 @@ export type PriceItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type RegionsResponse = {
|
export type RegionsResponse = {
|
||||||
|
properties: RegionsProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RegionsProperties = {
|
||||||
locations: Array<RegionItem>;
|
locations: Array<RegionItem>;
|
||||||
location: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RegionItem = {
|
export type RegionItem = {
|
||||||
locationName: string;
|
locationName: string;
|
||||||
|
isZoneRedundant: boolean;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user