aws-cost-calculator-cli 1.11.1__py3-none-any.whl → 2.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aws-cost-calculator-cli might be problematic. Click here for more details.

@@ -0,0 +1,141 @@
1
+ """
2
+ Dimension mapping between CLI, Cost Explorer, and Athena CUR.
3
+
4
+ This module provides utilities for translating dimension names across different backends.
5
+ """
6
+
7
+ # Dimension mapping: CLI dimension -> (CE dimension, Athena column)
8
+ DIMENSION_MAP = {
9
+ 'service': ('SERVICE', 'line_item_product_code'),
10
+ 'account': ('LINKED_ACCOUNT', 'line_item_usage_account_id'),
11
+ 'region': ('REGION', 'product_region'),
12
+ 'usage_type': ('USAGE_TYPE', 'line_item_usage_type'),
13
+ 'resource': (None, 'line_item_resource_id'), # Athena only
14
+ 'instance_type': ('INSTANCE_TYPE', 'product_instance_type'),
15
+ 'operation': ('OPERATION', 'line_item_operation'),
16
+ 'availability_zone': ('AVAILABILITY_ZONE', 'product_availability_zone'),
17
+ }
18
+
19
+
20
+ def get_ce_dimension(cli_dimension):
21
+ """
22
+ Get Cost Explorer dimension key for a CLI dimension.
23
+
24
+ Args:
25
+ cli_dimension: CLI dimension name (e.g., 'service', 'account')
26
+
27
+ Returns:
28
+ str: Cost Explorer dimension key (e.g., 'SERVICE', 'LINKED_ACCOUNT')
29
+ None: If dimension is not available in Cost Explorer
30
+
31
+ Raises:
32
+ ValueError: If dimension is unknown
33
+ """
34
+ if cli_dimension not in DIMENSION_MAP:
35
+ raise ValueError(f"Unknown dimension: {cli_dimension}")
36
+
37
+ ce_dim, _ = DIMENSION_MAP[cli_dimension]
38
+ return ce_dim
39
+
40
+
41
+ def get_athena_column(cli_dimension):
42
+ """
43
+ Get Athena CUR column name for a CLI dimension.
44
+
45
+ Args:
46
+ cli_dimension: CLI dimension name (e.g., 'service', 'account')
47
+
48
+ Returns:
49
+ str: Athena column name (e.g., 'line_item_product_code', 'line_item_usage_account_id')
50
+
51
+ Raises:
52
+ ValueError: If dimension is unknown
53
+ """
54
+ if cli_dimension not in DIMENSION_MAP:
55
+ raise ValueError(f"Unknown dimension: {cli_dimension}")
56
+
57
+ _, athena_col = DIMENSION_MAP[cli_dimension]
58
+ return athena_col
59
+
60
+
61
+ def is_athena_only(cli_dimension):
62
+ """
63
+ Check if a dimension is only available in Athena (not in Cost Explorer).
64
+
65
+ Args:
66
+ cli_dimension: CLI dimension name
67
+
68
+ Returns:
69
+ bool: True if dimension requires Athena, False otherwise
70
+ """
71
+ if cli_dimension not in DIMENSION_MAP:
72
+ raise ValueError(f"Unknown dimension: {cli_dimension}")
73
+
74
+ ce_dim, _ = DIMENSION_MAP[cli_dimension]
75
+ return ce_dim is None
76
+
77
+
78
+ def get_available_dimensions(backend='auto'):
79
+ """
80
+ Get list of available dimensions for a given backend.
81
+
82
+ Args:
83
+ backend: 'auto', 'ce', or 'athena'
84
+
85
+ Returns:
86
+ list: List of available dimension names
87
+ """
88
+ if backend == 'athena':
89
+ # All dimensions available in Athena
90
+ return list(DIMENSION_MAP.keys())
91
+ elif backend == 'ce':
92
+ # Only dimensions with CE mapping
93
+ return [dim for dim, (ce_dim, _) in DIMENSION_MAP.items() if ce_dim is not None]
94
+ else: # auto
95
+ # All dimensions (backend will be auto-selected)
96
+ return list(DIMENSION_MAP.keys())
97
+
98
+
99
+ def validate_dimension_backend(dimension, backend):
100
+ """
101
+ Validate that a dimension is available for the specified backend.
102
+
103
+ Args:
104
+ dimension: CLI dimension name
105
+ backend: 'auto', 'ce', or 'athena'
106
+
107
+ Returns:
108
+ tuple: (is_valid, error_message)
109
+
110
+ Examples:
111
+ >>> validate_dimension_backend('resource', 'ce')
112
+ (False, "Dimension 'resource' requires Athena backend (not available in Cost Explorer)")
113
+
114
+ >>> validate_dimension_backend('service', 'ce')
115
+ (True, None)
116
+ """
117
+ if dimension not in DIMENSION_MAP:
118
+ return False, f"Unknown dimension: {dimension}"
119
+
120
+ if backend == 'ce' and is_athena_only(dimension):
121
+ return False, f"Dimension '{dimension}' requires Athena backend (not available in Cost Explorer)"
122
+
123
+ return True, None
124
+
125
+
126
+ # Human-readable dimension descriptions
127
+ DIMENSION_DESCRIPTIONS = {
128
+ 'service': 'AWS Service (e.g., EC2, S3, RDS)',
129
+ 'account': 'AWS Account ID',
130
+ 'region': 'AWS Region (e.g., us-east-1, eu-west-1)',
131
+ 'usage_type': 'Usage Type (e.g., BoxUsage:t3.micro)',
132
+ 'resource': 'Resource ID/ARN (Athena only)',
133
+ 'instance_type': 'Instance Type (e.g., t3.micro, m5.large)',
134
+ 'operation': 'Operation (e.g., RunInstances, CreateBucket)',
135
+ 'availability_zone': 'Availability Zone (e.g., us-east-1a)',
136
+ }
137
+
138
+
139
+ def get_dimension_description(dimension):
140
+ """Get human-readable description for a dimension."""
141
+ return DIMENSION_DESCRIPTIONS.get(dimension, dimension)
@@ -92,12 +92,16 @@ def get_credentials_dict(config):
92
92
 
93
93
  def execute_trends(config, weeks):
94
94
  """
95
- Execute trends analysis via API or locally.
95
+ Execute trends analysis via API.
96
+
97
+ Args:
98
+ config: Profile configuration
99
+ weeks: Number of weeks to analyze
96
100
 
97
101
  Returns:
98
102
  dict: trends data
99
103
  """
100
- accounts = config['accounts']
104
+ profile_name = config.get('profile_name', config.get('name'))
101
105
 
102
106
  if not is_api_configured():
103
107
  raise Exception(
@@ -110,7 +114,7 @@ def execute_trends(config, weeks):
110
114
  credentials = get_credentials_dict(config)
111
115
  if not credentials:
112
116
  raise Exception("Failed to get AWS credentials. Check your AWS SSO session.")
113
- return call_lambda_api('trends', credentials, accounts, weeks=weeks)
117
+ return call_lambda_api('trends', credentials, profile=profile_name, weeks=weeks)
114
118
 
115
119
 
116
120
  def execute_monthly(config, months):
@@ -120,7 +124,7 @@ def execute_monthly(config, months):
120
124
  Returns:
121
125
  dict: monthly data
122
126
  """
123
- accounts = config['accounts']
127
+ profile_name = config.get('profile_name', config.get('name'))
124
128
 
125
129
  if not is_api_configured():
126
130
  raise Exception(
@@ -133,10 +137,10 @@ def execute_monthly(config, months):
133
137
  credentials = get_credentials_dict(config)
134
138
  if not credentials:
135
139
  raise Exception("Failed to get AWS credentials. Check your AWS SSO session.")
136
- return call_lambda_api('monthly', credentials, accounts, months=months)
140
+ return call_lambda_api('monthly', credentials, profile=profile_name, months=months)
137
141
 
138
142
 
139
- def execute_drill(config, weeks, service_filter=None, account_filter=None, usage_type_filter=None, resources=False):
143
+ def execute_drill(config, weeks, service_filter=None, account_filter=None, usage_type_filter=None, resources=False, dimension=None, backend='auto'):
140
144
  """
141
145
  Execute drill-down analysis via API.
142
146
 
@@ -147,11 +151,13 @@ def execute_drill(config, weeks, service_filter=None, account_filter=None, usage
147
151
  account_filter: Optional account ID filter
148
152
  usage_type_filter: Optional usage type filter
149
153
  resources: If True, query CUR for resource-level details
154
+ dimension: Dimension to analyze by (service, account, region, usage_type, resource, etc.)
155
+ backend: Backend to use ('auto', 'ce', 'athena')
150
156
 
151
157
  Returns:
152
158
  dict: drill data or resource data
153
159
  """
154
- accounts = config['accounts']
160
+ profile_name = config.get('profile_name', config.get('name'))
155
161
 
156
162
  if not is_api_configured():
157
163
  raise Exception(
@@ -172,12 +178,16 @@ def execute_drill(config, weeks, service_filter=None, account_filter=None, usage
172
178
  kwargs['account'] = account_filter
173
179
  if usage_type_filter:
174
180
  kwargs['usage_type'] = usage_type_filter
181
+ if dimension:
182
+ kwargs['dimension'] = dimension
183
+ if backend and backend != 'auto':
184
+ kwargs['backend'] = backend
175
185
  if resources:
176
- if not service_filter:
177
- raise click.ClickException("--service is required when using --resources flag")
186
+ if not service_filter and not dimension:
187
+ raise click.ClickException("--service or --dimension is required when using --resources flag")
178
188
  kwargs['resources'] = True
179
189
 
180
- return call_lambda_api('drill', credentials, accounts, **kwargs)
190
+ return call_lambda_api('drill', credentials, profile=profile_name, **kwargs)
181
191
 
182
192
 
183
193
  def execute_analyze(config, weeks, analysis_type, pattern=None, min_cost=None):
@@ -227,8 +237,11 @@ def execute_profile_operation(operation, profile_name=None, accounts=None, descr
227
237
 
228
238
  api_secret = os.environ.get('COST_API_SECRET', '')
229
239
 
230
- # Use profiles endpoint (hardcoded URL)
231
- url = 'https://64g7jq7sjygec2zmll5lsghrpi0txrzo.lambda-url.us-east-1.on.aws/'
240
+ # Use profiles endpoint (can be overridden via environment variable)
241
+ url = os.environ.get(
242
+ 'COST_CALCULATOR_PROFILES_URL',
243
+ 'https://64g7jq7sjygec2zmll5lsghrpi0txrzo.lambda-url.us-east-1.on.aws/'
244
+ )
232
245
 
233
246
  payload = {'operation': operation}
234
247
  if profile_name:
@@ -1,15 +0,0 @@
1
- aws_cost_calculator_cli-1.11.1.dist-info/licenses/LICENSE,sha256=cYtmQZHNGGTXOtg3T7LHDRneleaH0dHXHfxFV3WR50Y,1079
2
- cost_calculator/__init__.py,sha256=PJeIqvWh5AYJVrJxPPkI4pJnAt37rIjasrNS0I87kaM,52
3
- cost_calculator/api_client.py,sha256=4ZI2XcGIN3FBeQqb7xOxQ91kCoeM43-rExiOELXoKBQ,2485
4
- cost_calculator/cli.py,sha256=OudMcAitmJfWgZKxjlHtfXnx2SlKrYh6FzPTkHabJlI,77975
5
- cost_calculator/cur.py,sha256=QaZ_nyDSw5_cti-h5Ho6eYLbqzY5TWoub24DpyzIiSs,9502
6
- cost_calculator/drill.py,sha256=hGi-prLgZDvNMMICQc4fl3LenM7YaZ3To_Ei4LKwrdc,10543
7
- cost_calculator/executor.py,sha256=yZTCUgJc1OpB892O3mq9ZA0Yekc7N-HvaW8xLFyrXjo,8681
8
- cost_calculator/forensics.py,sha256=uhRo3I_zOeMEaBENHfgq65URga31W0Z4vzS2UN6VmTY,12819
9
- cost_calculator/monthly.py,sha256=6k9F8S7djhX1wGV3-T1MZP7CvWbbfhSTEaddwCfVu5M,7932
10
- cost_calculator/trends.py,sha256=k_s4ylBX50sqoiM_fwepi58HW01zz767FMJhQUPDznk,12246
11
- aws_cost_calculator_cli-1.11.1.dist-info/METADATA,sha256=d-AHFprwq0-lD-D3ilWa7IGBxOszOdMqP7nJl5J0gyA,11979
12
- aws_cost_calculator_cli-1.11.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
13
- aws_cost_calculator_cli-1.11.1.dist-info/entry_points.txt,sha256=_5Qy4EcHbYVYrdgOu1E48faMHb9fLUl5VJ3djDHuJBo,47
14
- aws_cost_calculator_cli-1.11.1.dist-info/top_level.txt,sha256=PRwGPPlNqASfyhGHDjSfyl4SXeE7GF3OVTu1tY1Uqyc,16
15
- aws_cost_calculator_cli-1.11.1.dist-info/RECORD,,