cid-cmd 4.2.6__tar.gz → 4.3.0__tar.gz
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.
- {cid_cmd-4.2.6/cid_cmd.egg-info → cid_cmd-4.3.0}/PKG-INFO +1 -1
- cid_cmd-4.3.0/cid/_version.py +2 -0
- cid_cmd-4.3.0/cid/builtin/core/data/permissions/data_set_rls_permissions.json +8 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/cli.py +4 -1
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/common.py +70 -10
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/export.py +1 -1
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/athena.py +7 -4
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/cur.py +1 -1
- cid_cmd-4.3.0/cid/helpers/parameter_store.py +88 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/__init__.py +5 -2
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/dashboard.py +3 -1
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/dashboard_patching.py +18 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/dataset.py +29 -8
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/utils.py +1 -1
- {cid_cmd-4.2.6 → cid_cmd-4.3.0/cid_cmd.egg-info}/PKG-INFO +1 -1
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid_cmd.egg-info/SOURCES.txt +1 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid_cmd.egg-info/top_level.txt +2 -1
- cid_cmd-4.2.6/cid/_version.py +0 -2
- cid_cmd-4.2.6/cid/helpers/parameter_store.py +0 -51
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/LICENSE +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/MANIFEST.in +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/README.md +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/__init__.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/base.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/__init__.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/__init__.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cid/compute.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cid/ec2_running_cost.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cid/s3_view.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cid/summary_view.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/co/dataset.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cudos/hourly_view.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/cudos/resource_view.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/kpi/kpi_ebs_snap.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/kpi/kpi_ebs_storage_all.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/kpi/kpi_instance_all.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/kpi/kpi_s3_storage_all.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/kpi/kpi_tracker.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/shared/customer_all.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/tao/dataset.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/daily_anomaly_detection.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/monthly_anomaly_detection.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/monthly_bill_by_account.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/dashboard_permissions.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/dashboard_permissions_namespace.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/data_set_permissions.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/data_source_permissions.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/folder_permissions.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cid/compute_savings_plan_eligible_spend.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cid/ec2_running_cost.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cid/ri_sp_mapping.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cid/s3.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cid/summary_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/all_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/auto_scale.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/auto_scale_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ebs_volume.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ebs_volume_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ec2_instance.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ec2_instance_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ecs_service.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/ecs_service_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/idle.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/idle_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/lambda.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/lambda_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/license.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/license_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/rds_database.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/rds_instance_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/co/rds_storage_options.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cudos/hourly_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/cudos/resource_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/first_kpi_instance_mapping_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/kpi_ebs_snap_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/kpi_ebs_storage_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/kpi_instance_all_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/kpi_s3_storage_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/kpi/last_kpi_tracker_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/account_map.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/account_map_cur2.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/account_map_dummy.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/aws_accounts.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/aws_regions.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/aws_service_category_map.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/business_units_map.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/cur.yaml +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/customer_all_unlimited.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/payer_account_name_map.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/ta_descriptions.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/tao/glue_table.json +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/tao/ta_org_view.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/daily_anomaly_detection.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/monthly_anomaly_detection.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/monthly_bill_by_account.sql +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/resources.yaml +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/commands/__init__.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/commands/command_base.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/commands/init_qs.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/exceptions.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/__init__.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/account_map.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/cloudformation.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/csv2view.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/cur_proxy.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/diff.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/glue.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/iam.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/organizations.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/datasource.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/definition.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/resource.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/template.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/quicksight/version.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/randtime.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/s3.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/helpers/timezone.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/logger.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/plugin.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/test/python/test_csv2view.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/test/python/test_isolated_parameters.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/test/python/test_merge.py +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid_cmd.egg-info/dependency_links.txt +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid_cmd.egg-info/entry_points.txt +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid_cmd.egg-info/requires.txt +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/pyproject.toml +0 -0
- {cid_cmd-4.2.6 → cid_cmd-4.3.0}/setup.cfg +0 -0
|
@@ -94,7 +94,6 @@ def map(ctx, **kwargs):
|
|
|
94
94
|
"""
|
|
95
95
|
ctx.obj.map(**kwargs)
|
|
96
96
|
|
|
97
|
-
|
|
98
97
|
@click.option('-v', '--verbose', count=True)
|
|
99
98
|
@click.option('-y', '--yes', help='confirm all', is_flag=True, default=False)
|
|
100
99
|
@cid_command
|
|
@@ -141,6 +140,8 @@ def deploy(ctx, **kwargs):
|
|
|
141
140
|
--catalog TEXT Comma separated list of catalog files or urls (ex: foundational,advanced )
|
|
142
141
|
--theme TEXT A QuickSight Theme (CLASSIC|MIDNIGHT|SEASIDE|RAINIER)
|
|
143
142
|
--currency TEXT A currency symbol instead of default USD (USD|GBP|EUR|JPY|KRW|DKK|TWD|INR)
|
|
143
|
+
--rls TEXT Desired RLS status (CLEAR|ENABLED|DISABLED) Clear = No RLS configuration.
|
|
144
|
+
--rls-dataset-id TEXT ID of RLS Dataset that will be used
|
|
144
145
|
--share-with-account (yes|no) Make dashboard visible to other users in the same account by default.
|
|
145
146
|
"""
|
|
146
147
|
ctx.obj.deploy(**kwargs)
|
|
@@ -210,6 +211,8 @@ def update(ctx, dashboard_id, force, recursive, **kwargs):
|
|
|
210
211
|
--on-drift (show|override) Action if a drift of view and dataset is discovered. 'override' = override drift(will destroy customization) or 'show' (default) = show a diff. In Unattended mode (without terminal on-drift will have allways override behaviour)
|
|
211
212
|
--theme TEXT A QuickSight Theme (CLASSIC|MIDNIGHT|SEASIDE|RAINIER)
|
|
212
213
|
--currency TEXT A currency symbol instead of default USD (USD|GBP|EUR|JPY|KRW|DKK|TWD|INR)
|
|
214
|
+
--rls TEXT Desired RLS status (CLEAR|ENABLED|DISABLED) Clear = No RLS configuration.
|
|
215
|
+
--rls-dataset-id TEXT ID of RLS Dataset that will be used
|
|
213
216
|
|
|
214
217
|
"""
|
|
215
218
|
ctx.obj.update(dashboard_id, force=force, recursive=recursive, **kwargs)
|
|
@@ -282,16 +282,22 @@ class Cid():
|
|
|
282
282
|
export_analysis(self.qs, self.athena, glue=self.glue)
|
|
283
283
|
|
|
284
284
|
def track(self, action, dashboard_id):
|
|
285
|
-
""" Send dashboard_id and account_id to adoption tracker """
|
|
285
|
+
""" Send dashboard_id and account_id to CID adoption tracker """
|
|
286
286
|
method = {'created':'PUT', 'updated':'PATCH', 'deleted': 'DELETE'}.get(action, None)
|
|
287
287
|
if not method:
|
|
288
288
|
logger.debug(f"This will not fail the deployment. Logging action {action} is not supported. This issue will be ignored")
|
|
289
289
|
return
|
|
290
|
-
endpoint = 'https://okakvoavfg.execute-api.eu-west-1.amazonaws.com/'
|
|
290
|
+
endpoint = 'https://okakvoavfg.execute-api.eu-west-1.amazonaws.com/' # AWS Managed
|
|
291
|
+
if os.environ.get('AWS_DEPLOYMENT_TYPE'):
|
|
292
|
+
deployment_type = os.environ.get('AWS_DEPLOYMENT_TYPE')
|
|
293
|
+
elif os.environ.get('AWS_EXECUTION_ENV', '').startswith('AWS_Lambda'):
|
|
294
|
+
deployment_type = 'Lambda'
|
|
295
|
+
else:
|
|
296
|
+
deployment_type = 'CID'
|
|
291
297
|
payload = {
|
|
292
298
|
'dashboard_id': dashboard_id,
|
|
293
299
|
'account_id': self.base.account_id,
|
|
294
|
-
action + '_via':
|
|
300
|
+
action + '_via': deployment_type,
|
|
295
301
|
}
|
|
296
302
|
try:
|
|
297
303
|
res = requests.request(
|
|
@@ -470,17 +476,16 @@ class Cid():
|
|
|
470
476
|
set_defaults(defaults)
|
|
471
477
|
|
|
472
478
|
def dump_default_parameters(self):
|
|
473
|
-
|
|
474
479
|
stop_list = ['profile-name', 'region', 'aws-access-key-id', 'aws-secret-access-key', 'aws-session-token', 'athena-database', 'athena-workgroup']
|
|
475
|
-
current_defaults =
|
|
476
|
-
context=get_parameters().get('dashboard-id')
|
|
477
|
-
)
|
|
478
|
-
current_defaults = current_defaults | get_parameters()
|
|
480
|
+
current_defaults = get_parameters()
|
|
479
481
|
for key in list(current_defaults.keys()):
|
|
480
482
|
if key in stop_list:
|
|
481
483
|
del current_defaults[key]
|
|
482
484
|
logger.trace(f'dumping parameters {current_defaults}')
|
|
483
|
-
self.parameters_controller.dump_parameters(
|
|
485
|
+
self.parameters_controller.dump_parameters(
|
|
486
|
+
current_defaults,
|
|
487
|
+
context=get_parameters().get('dashboard-id')
|
|
488
|
+
)
|
|
484
489
|
|
|
485
490
|
|
|
486
491
|
def ensure_subscription(self):
|
|
@@ -1286,7 +1291,18 @@ class Cid():
|
|
|
1286
1291
|
# If there still datasets missing try automatic creation
|
|
1287
1292
|
if missing_datasets:
|
|
1288
1293
|
missing_str = ', '.join(missing_datasets)
|
|
1289
|
-
|
|
1294
|
+
cid_print(f'\nThere are still {len(missing_datasets)} datasets missing: {missing_str}')
|
|
1295
|
+
|
|
1296
|
+
# get rls status of existing datasets
|
|
1297
|
+
found_dataset_objects = [self.qs.describe_dataset(ds_id) for ds_id in found_datasets]
|
|
1298
|
+
rls_dataset_arns = [ds.rls_arn for ds in found_dataset_objects if ds.rls_arn]
|
|
1299
|
+
if rls_dataset_arns:
|
|
1300
|
+
rls_dataset_arn = max(set(rls_dataset_arns), key=rls_dataset_arns.count) #get the most frequent
|
|
1301
|
+
if not get_parameters().get('rls-dataset-id') and not get_parameters().get('rls'):
|
|
1302
|
+
rls_dataset_id = rls_dataset_arn.split('/')[-1]
|
|
1303
|
+
cid_print('Existing datasets are linked to RLS {rls_dataset_id}. We will reuse it for {missing_str} datasets.')
|
|
1304
|
+
set_parameters({'rls-dataset-id': rls_dataset_id})
|
|
1305
|
+
|
|
1290
1306
|
for dataset_name in missing_datasets[:]:
|
|
1291
1307
|
dataset_id = known_datasets.get(dataset_name)
|
|
1292
1308
|
print(f'Creating dataset: {dataset_name}')
|
|
@@ -1628,6 +1644,50 @@ class Cid():
|
|
|
1628
1644
|
compiled_dataset = Dataset.patch(dataset=compiled_dataset, custom_fields=custom_fields, athena=self.athena)
|
|
1629
1645
|
logger.trace(f"compiled_dataset = {json.dumps(compiled_dataset)}")
|
|
1630
1646
|
found_dataset = self.qs.describe_dataset(compiled_dataset.get('DataSetId'), timeout=0)
|
|
1647
|
+
|
|
1648
|
+
rls_dataset_id = get_parameters().get('rls-dataset-id')
|
|
1649
|
+
rls_dataset_status = get_parameters().get('rls')
|
|
1650
|
+
if rls_dataset_status == 'CLEAR':
|
|
1651
|
+
logger.debug('deleting rls')
|
|
1652
|
+
if "RowLevelPermissionDataSet" in compiled_dataset:
|
|
1653
|
+
del compiled_dataset["RowLevelPermissionDataSet"]
|
|
1654
|
+
if "RowLevelPermissionDataSet" in found_dataset:
|
|
1655
|
+
del found_dataset["RowLevelPermissionDataSet"]
|
|
1656
|
+
|
|
1657
|
+
if rls_dataset_id or rls_dataset_status:
|
|
1658
|
+
if not rls_dataset_id:
|
|
1659
|
+
for ds in self.qs.list_data_sets():
|
|
1660
|
+
print(ds)
|
|
1661
|
+
choices = {
|
|
1662
|
+
f"{ds.get('Name')}({ds.get('DataSetId')})":ds.get('DataSetId')
|
|
1663
|
+
for ds in self.qs.list_data_sets()
|
|
1664
|
+
if ds.get('UseAs') == 'RLS_RULES'
|
|
1665
|
+
}
|
|
1666
|
+
if not choices:
|
|
1667
|
+
raise CidCritical(f"Cannot find RLS DataSets")
|
|
1668
|
+
rls_dataset_id = get_parameter('rls-dataset-id', message='Select RLS dataset', choices=choices)
|
|
1669
|
+
rls_dataset = self.qs.describe_dataset(id=rls_dataset_id)
|
|
1670
|
+
if not rls_dataset:
|
|
1671
|
+
raise CidCritical(f"RLS DataSet {rls_dataset_id} not found")
|
|
1672
|
+
if not rls_dataset.is_rls:
|
|
1673
|
+
raise CidCritical(f"DataSet {rls_dataset_id} is not RLS")
|
|
1674
|
+
rls_permissions = {
|
|
1675
|
+
"Arn": rls_dataset.arn,
|
|
1676
|
+
"Status": (rls_dataset_status
|
|
1677
|
+
or (found_dataset and found_dataset.rls_status)
|
|
1678
|
+
or "ENABLED"
|
|
1679
|
+
),
|
|
1680
|
+
"PermissionPolicy": "GRANT_ACCESS",
|
|
1681
|
+
}
|
|
1682
|
+
cols = [c['Name'] for c in rls_dataset.columns]
|
|
1683
|
+
if 'UserName' in cols or 'GroupName' in cols:
|
|
1684
|
+
rls_permissions['FormatVersion'] = "VERSION_1"
|
|
1685
|
+
elif 'UserARN' in cols or 'GroupARN' in cols:
|
|
1686
|
+
rls_permissions['FormatVersion'] = "VERSION_2"
|
|
1687
|
+
else:
|
|
1688
|
+
raise CidCritical(f"DataSet {rls_dataset_id} must have 'UserName'/'GroupName' or 'UserARN'/'GroupARN' columns.")
|
|
1689
|
+
compiled_dataset["RowLevelPermissionDataSet"] = rls_permissions
|
|
1690
|
+
|
|
1631
1691
|
if isinstance(found_dataset, Dataset):
|
|
1632
1692
|
update_dataset = False
|
|
1633
1693
|
if update:
|
|
@@ -362,7 +362,7 @@ def export_analysis(qs, athena, glue):
|
|
|
362
362
|
# We can potentially reconsider this and use IDs at some point
|
|
363
363
|
'datasets': sorted(list(set(list(datasets.keys()) + resources_datasets)))
|
|
364
364
|
}
|
|
365
|
-
dashboard_resource['name'] = dashboard_resource.get('name')
|
|
365
|
+
dashboard_resource['name'] = dashboard_resource.get('name') or analysis['Name']
|
|
366
366
|
dashboard_resource['dashboardId'] = dashboard_id
|
|
367
367
|
dashboard_resource['category'] = get_parameters().get('category', dashboard_resource.get('category', 'Custom'))
|
|
368
368
|
|
|
@@ -601,7 +601,7 @@ class Athena(CidBase):
|
|
|
601
601
|
''')
|
|
602
602
|
|
|
603
603
|
def find_tables_with_columns(self, columns: list, database_name: str=None, catalog_name: str=None, max_items: int=10000):
|
|
604
|
-
"""
|
|
604
|
+
""" Returns an iterator that yields only tables containing all specified columns.
|
|
605
605
|
"""
|
|
606
606
|
iterator = self.client.get_paginator('list_table_metadata').paginate(
|
|
607
607
|
DatabaseName=database_name or self.DatabaseName,
|
|
@@ -610,6 +610,9 @@ class Athena(CidBase):
|
|
|
610
610
|
'MaxItems': max_items, # sometimes customers can have 1'000s of tables (due to a crawler going crazy for example)
|
|
611
611
|
},
|
|
612
612
|
)
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
613
|
+
# We cannot rely on search to find directly columns as there might be Nulls. So iterating old fashion.
|
|
614
|
+
for table in iterator.search('TableMetadataList'):
|
|
615
|
+
column_names = [c['Name'] for c in table.get('Columns', [])]
|
|
616
|
+
if all([(col in column_names) for col in columns]):
|
|
617
|
+
yield table
|
|
618
|
+
|
|
@@ -271,7 +271,7 @@ class CUR(AbstractCUR):
|
|
|
271
271
|
columns=self.cur_minimal_required_columns,
|
|
272
272
|
database_name=database,
|
|
273
273
|
)
|
|
274
|
-
all_cur_tables += [(database, table) for table in tables]
|
|
274
|
+
all_cur_tables += [(database, table['Name']) for table in tables]
|
|
275
275
|
except self.athena.client.exceptions.ClientError as exc:
|
|
276
276
|
if 'AccessDenied' in str(exc):
|
|
277
277
|
logger.info(f'Cannot read from athena database {database}')
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from cid.exceptions import CidCritical
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
class AthenaStore():
|
|
9
|
+
def __init__(self, athena, view_name='cid_parameters'):
|
|
10
|
+
self.athena = athena
|
|
11
|
+
self.view_name = view_name
|
|
12
|
+
|
|
13
|
+
def dump(self, data):
|
|
14
|
+
''' dump data to athena
|
|
15
|
+
'''
|
|
16
|
+
try:
|
|
17
|
+
# FIXME: make it multi view
|
|
18
|
+
self.athena.query(self._generate_view_query(data, self.view_name))
|
|
19
|
+
except (CidCritical, Exception) as exc:
|
|
20
|
+
logger.debug(f'failed to save parameters store: {exc}')
|
|
21
|
+
|
|
22
|
+
def load(self):
|
|
23
|
+
''' load from athena
|
|
24
|
+
'''
|
|
25
|
+
try:
|
|
26
|
+
res = self.athena.query(f'''select * from {self.view_name}''', include_header=True)
|
|
27
|
+
except CidCritical as exc:
|
|
28
|
+
if 'TABLE_NOT_FOUND' in str(exc):
|
|
29
|
+
res = []
|
|
30
|
+
else:
|
|
31
|
+
raise
|
|
32
|
+
return [{k:v for k, v in zip(res[0], row)} for row in res[1:]]
|
|
33
|
+
|
|
34
|
+
def _to_sql_str(self, val):
|
|
35
|
+
if val is None:
|
|
36
|
+
return "''"
|
|
37
|
+
return "'" + str(val).replace("'", "''") + "'"
|
|
38
|
+
|
|
39
|
+
def _generate_view_query(self, data, name):
|
|
40
|
+
all_keys = {key for dictionary in data for key in dictionary.keys()}
|
|
41
|
+
lines = ',\n '.join([f'''ROW({','.join([self._to_sql_str(line.get(k)) for k in all_keys])})''' for line in data])
|
|
42
|
+
query = f"""
|
|
43
|
+
CREATE OR REPLACE VIEW {name} AS
|
|
44
|
+
SELECT *
|
|
45
|
+
FROM (
|
|
46
|
+
VALUES
|
|
47
|
+
{lines}
|
|
48
|
+
) ignored_table_name ({','.join([key for key in all_keys])})
|
|
49
|
+
"""
|
|
50
|
+
return query
|
|
51
|
+
|
|
52
|
+
class ParametersController(AthenaStore):
|
|
53
|
+
def load_parameters(self, context):
|
|
54
|
+
data = self.load()
|
|
55
|
+
context_parameters = {}
|
|
56
|
+
any_parameters = {}
|
|
57
|
+
# get any context and then override with specific context
|
|
58
|
+
for line in sorted(data, key=lambda x: x.get('date', '')): # latest should override
|
|
59
|
+
any_parameters[line.get('parameter')] = line.get('value')
|
|
60
|
+
if line.get('context') == str(context):
|
|
61
|
+
context_parameters[line.get('parameter')] = line.get('value')
|
|
62
|
+
return any_parameters | context_parameters
|
|
63
|
+
|
|
64
|
+
def dump_parameters(self, params, context=None):
|
|
65
|
+
data = self.load()
|
|
66
|
+
logger.trace(f'loaded parameters {data}')
|
|
67
|
+
params = dict(params)
|
|
68
|
+
date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
69
|
+
|
|
70
|
+
#Update parameters if they are present already
|
|
71
|
+
for line in data:
|
|
72
|
+
for key, val in list(params.items()):
|
|
73
|
+
if line.get('context') == str(context) and line.get('parameter') == key:
|
|
74
|
+
line['value'] = val
|
|
75
|
+
line['date'] = date
|
|
76
|
+
del params[key]
|
|
77
|
+
|
|
78
|
+
#add parameters that are new
|
|
79
|
+
for key, val in list(params.items()):
|
|
80
|
+
data.append({
|
|
81
|
+
'parameter': key,
|
|
82
|
+
'value': ','.join([str(v) for v in val]) if isinstance(val, list) else str(val),
|
|
83
|
+
'context': str(context),
|
|
84
|
+
'date': date
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
logger.trace(f'dumping parameters {data}')
|
|
88
|
+
self.dump(data)
|
|
@@ -14,7 +14,7 @@ from cid.base import CidBase
|
|
|
14
14
|
from cid.helpers import diff, timezone, randtime
|
|
15
15
|
from cid.helpers.quicksight.dashboard import Dashboard
|
|
16
16
|
from cid.helpers.quicksight.dataset import Dataset
|
|
17
|
-
from cid.helpers.quicksight.dashboard_patching import add_filter_to_dashboard_definition, patch_currency, patch_group_by
|
|
17
|
+
from cid.helpers.quicksight.dashboard_patching import add_filter_to_dashboard_definition, patch_currency, patch_group_by, patch_spaces
|
|
18
18
|
from cid.helpers.quicksight.datasource import Datasource
|
|
19
19
|
from cid.helpers.quicksight.template import Template as CidQsTemplate
|
|
20
20
|
from cid.helpers.quicksight.definition import Definition as CidQsDefinition
|
|
@@ -735,6 +735,7 @@ class QuickSight(CidBase):
|
|
|
735
735
|
'AwsAccountId': self.account_id,
|
|
736
736
|
'DashboardId': dashboard_id
|
|
737
737
|
}
|
|
738
|
+
logger.debug(f'describe_dashboard_definition({dashboard_id})')
|
|
738
739
|
result = self.client.describe_dashboard_definition(**parameters)
|
|
739
740
|
self._definitions.update({f'{self.account_id}:{self.identityRegion}:{dashboard_id}': CidQsDefinition(result.get('Definition'))})
|
|
740
741
|
logger.debug(result)
|
|
@@ -1289,6 +1290,7 @@ class QuickSight(CidBase):
|
|
|
1289
1290
|
'DashboardId': definition.get('dashboardId'),
|
|
1290
1291
|
'VersionNumber': created_version
|
|
1291
1292
|
}
|
|
1293
|
+
logger.debug(f"describe_dashboard { definition.get('dashboardId')}")
|
|
1292
1294
|
dashboard = self.describe_dashboard(poll=True, **describe_parameters)
|
|
1293
1295
|
logger.debug('dashboard={dashboard}')
|
|
1294
1296
|
self.set_tags(dashboard.arn, cid_version_tag=dashboard.cid_version) # try to update version tag
|
|
@@ -1306,7 +1308,7 @@ class QuickSight(CidBase):
|
|
|
1306
1308
|
definition: cid dashboard definition. NOT QS DEFINITION.
|
|
1307
1309
|
returns: what we need tof create or update API
|
|
1308
1310
|
"""
|
|
1309
|
-
|
|
1311
|
+
cid_print('Preparing dashboard definition')
|
|
1310
1312
|
create_parameters = {
|
|
1311
1313
|
'AwsAccountId': self.account_id,
|
|
1312
1314
|
'DashboardId': definition.get('dashboardId'),
|
|
@@ -1338,6 +1340,7 @@ class QuickSight(CidBase):
|
|
|
1338
1340
|
create_parameters['Definition'],
|
|
1339
1341
|
currency_symbol=get_parameters().get('currency-symbol', 'USD')
|
|
1340
1342
|
)
|
|
1343
|
+
create_parameters['Definition'] = patch_spaces(create_parameters['Definition'])
|
|
1341
1344
|
dataset_references = []
|
|
1342
1345
|
for identifier, arn in definition.get('datasets', {}).items():
|
|
1343
1346
|
# Fetch dataset by name (preferably) OR by id
|
|
@@ -323,8 +323,10 @@ class Dashboard(CidQsResource):
|
|
|
323
323
|
if self.datasets:
|
|
324
324
|
cid_print(f" <BOLD>Datasets:<END>")
|
|
325
325
|
for dataset_name, dataset_id in sorted(self.datasets.items()):
|
|
326
|
+
ds = self.qs.describe_dataset(dataset_id)
|
|
327
|
+
rls = {'ENABLED': '🔐', 'DISABLED': '🔓', None: ' '}.get(ds.rls_status)
|
|
326
328
|
status = self.qs.get_dataset_last_ingestion(dataset_id) or '<BLUE>DIRECT<END>' #todo fix this Blue using dataset import type.
|
|
327
|
-
cid_print(f'
|
|
329
|
+
cid_print(f' {rls} {dataset_name: <36} ({dataset_id: <36}) {status}')
|
|
328
330
|
|
|
329
331
|
def display_url(self, url_template: str, launch: bool = False, **kwargs) -> None:
|
|
330
332
|
url = url_template.format(dashboard_id=self.id, **kwargs)
|
|
@@ -429,6 +429,24 @@ def detect_global_filter_fields(dashboard_definition):
|
|
|
429
429
|
cols.append(col_name)
|
|
430
430
|
|
|
431
431
|
|
|
432
|
+
def patch_spaces(definition):
|
|
433
|
+
''' put spaces in places
|
|
434
|
+
'''
|
|
435
|
+
def _patch(data):
|
|
436
|
+
"""Recursively set spaces in text fields"""
|
|
437
|
+
SPACE = '\u00A0' # special space: non-breaking space
|
|
438
|
+
if isinstance(data, dict):
|
|
439
|
+
return {k: _patch(v) if k not in ['Expression'] else v for k, v in data.items()} # skip patching code
|
|
440
|
+
elif isinstance(data, list):
|
|
441
|
+
return [_patch(item) for item in data]
|
|
442
|
+
elif isinstance(data, str):
|
|
443
|
+
text = re.sub(r'([^>\s])(\s*)(\s)(<[^/])', rf'\1{SPACE}\2\3\4', data, re.MULTILINE|re.DOTALL)
|
|
444
|
+
if text != data:
|
|
445
|
+
logger.trace(f'patch_spaces: {repr(text)} {repr(data)}')
|
|
446
|
+
return text
|
|
447
|
+
return data
|
|
448
|
+
return _patch(definition)
|
|
449
|
+
|
|
432
450
|
def patch_currency(definition, currency_symbol):
|
|
433
451
|
''' patch dashboard by adding currency symbol where the currency is configured already.
|
|
434
452
|
'''
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
import logging
|
|
2
|
-
from uuid import uuid4
|
|
3
3
|
from copy import deepcopy
|
|
4
4
|
|
|
5
5
|
import yaml
|
|
@@ -15,8 +15,14 @@ DATASET_PROPERTIES = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
def string_to_uuid(input_string):
|
|
19
|
+
"""Generate a UUID from a string using hash.
|
|
20
|
+
"""
|
|
21
|
+
return str(uuid.uuid5(uuid.NAMESPACE_DNS, input_string))
|
|
19
22
|
|
|
23
|
+
class Dataset(CidQsResource):
|
|
24
|
+
""" Dataset
|
|
25
|
+
"""
|
|
20
26
|
def __init__(self, raw: dict, qs=None, athena=None) -> None:
|
|
21
27
|
super().__init__(raw)
|
|
22
28
|
self.qs = qs
|
|
@@ -31,6 +37,19 @@ class Dataset(CidQsResource):
|
|
|
31
37
|
def id(self) -> str:
|
|
32
38
|
return self.get_property('DataSetId')
|
|
33
39
|
|
|
40
|
+
@property
|
|
41
|
+
def is_rls(self):
|
|
42
|
+
return self.get_property('UseAs') == 'RLS_RULES'
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def rls_status(self):
|
|
46
|
+
return (self.get_property('RowLevelPermissionDataSet') or {}).get('Status')
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def rls_arn(self):
|
|
50
|
+
return (self.get_property('RowLevelPermissionDataSet') or {}).get('Arn')
|
|
51
|
+
|
|
52
|
+
|
|
34
53
|
@property
|
|
35
54
|
def columns(self) -> list:
|
|
36
55
|
if not 'OutputColumns' in self.raw: self.describe()
|
|
@@ -121,11 +140,13 @@ class Dataset(CidQsResource):
|
|
|
121
140
|
renames[key] = dt["RenameColumnOperation"]['NewColumnName']
|
|
122
141
|
logger.trace(f'renames = {renames}')
|
|
123
142
|
|
|
124
|
-
projected_cols =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
projected_cols = []
|
|
144
|
+
if root_lt.get('DataTransforms'):
|
|
145
|
+
projected_cols = next( # get the first DataTransform with ProjectOperation
|
|
146
|
+
ds['ProjectOperation']["ProjectedColumns"]
|
|
147
|
+
for ds in root_lt.get('DataTransforms', [])
|
|
148
|
+
if 'ProjectOperation' in ds
|
|
149
|
+
)
|
|
129
150
|
|
|
130
151
|
# Update each PhysicalTableMap with all columns from athena views
|
|
131
152
|
all_columns = []
|
|
@@ -179,7 +200,7 @@ class Dataset(CidQsResource):
|
|
|
179
200
|
"Columns": [
|
|
180
201
|
{
|
|
181
202
|
"ColumnName": col_name,
|
|
182
|
-
"ColumnId":
|
|
203
|
+
"ColumnId": string_to_uuid(dataset['Name'] + col_name),
|
|
183
204
|
"Expression": expression
|
|
184
205
|
}
|
|
185
206
|
]
|
|
@@ -276,7 +276,7 @@ def get_parameter(param_name, message, choices=None, default=None, none_as_disab
|
|
|
276
276
|
if multi:
|
|
277
277
|
default = default or []
|
|
278
278
|
default = default if isinstance(default, list) else [default]
|
|
279
|
-
default = [c for c in
|
|
279
|
+
default = [c for c in default if c in choices]
|
|
280
280
|
if not isatty():
|
|
281
281
|
result = default
|
|
282
282
|
else:
|
|
@@ -36,6 +36,7 @@ cid/builtin/core/data/datasets/trends/monthly_bill_by_account.json
|
|
|
36
36
|
cid/builtin/core/data/permissions/dashboard_permissions.json
|
|
37
37
|
cid/builtin/core/data/permissions/dashboard_permissions_namespace.json
|
|
38
38
|
cid/builtin/core/data/permissions/data_set_permissions.json
|
|
39
|
+
cid/builtin/core/data/permissions/data_set_rls_permissions.json
|
|
39
40
|
cid/builtin/core/data/permissions/data_source_permissions.json
|
|
40
41
|
cid/builtin/core/data/permissions/folder_permissions.json
|
|
41
42
|
cid/builtin/core/data/queries/cid/compute_savings_plan_eligible_spend.sql
|
cid_cmd-4.2.6/cid/_version.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from cid.exceptions import CidCritical
|
|
2
|
-
|
|
3
|
-
class AthenaStore():
|
|
4
|
-
def __init__(self, athena, view_name='cid_parameters'):
|
|
5
|
-
self.athena = athena
|
|
6
|
-
self.view_name = view_name
|
|
7
|
-
|
|
8
|
-
def dump(self, data):
|
|
9
|
-
''' load from athena
|
|
10
|
-
'''
|
|
11
|
-
# FIXME: make it multi view
|
|
12
|
-
self.athena.query(self._generate_view_query(data, self.view_name))
|
|
13
|
-
|
|
14
|
-
def load(self):
|
|
15
|
-
''' load from athena
|
|
16
|
-
'''
|
|
17
|
-
try:
|
|
18
|
-
res = self.athena.query(f'''select * from {self.view_name}''', include_header=True)
|
|
19
|
-
except CidCritical as exc:
|
|
20
|
-
if 'TABLE_NOT_FOUND' in str(exc):
|
|
21
|
-
res = []
|
|
22
|
-
else:
|
|
23
|
-
raise
|
|
24
|
-
return [{k:v for k, v in zip(res[0], row)} for row in res[1:]]
|
|
25
|
-
|
|
26
|
-
def _to_sql_str(self, val):
|
|
27
|
-
if val is None:
|
|
28
|
-
return "''"
|
|
29
|
-
return "'" + str(val).replace("'", "''") + "'"
|
|
30
|
-
|
|
31
|
-
def _generate_view_query(self, data, name):
|
|
32
|
-
all_keys = {key for dictionary in data for key in dictionary.keys()}
|
|
33
|
-
lines = ',\n '.join([f'''ROW({','.join([self._to_sql_str(line.get(k)) for k in all_keys])})''' for line in data])
|
|
34
|
-
query = f"""
|
|
35
|
-
CREATE OR REPLACE VIEW {name} AS
|
|
36
|
-
SELECT *
|
|
37
|
-
FROM (
|
|
38
|
-
VALUES
|
|
39
|
-
{lines}
|
|
40
|
-
) ignored_table_name ({','.join([key for key in all_keys])})
|
|
41
|
-
"""
|
|
42
|
-
return query
|
|
43
|
-
|
|
44
|
-
class ParametersController(AthenaStore):
|
|
45
|
-
def load_parameters(self, context):
|
|
46
|
-
data = self.load()
|
|
47
|
-
return { line.get('parameter'):line.get('value') for line in data}
|
|
48
|
-
|
|
49
|
-
def dump_parameters(self, params, context=None):
|
|
50
|
-
data = [{'parameter': key, 'value': ','.join(val) if isinstance(val, list) else val, 'context': str(context) } for key, val in params.items()]
|
|
51
|
-
self.dump(data)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/daily_anomaly_detection.json
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/monthly_anomaly_detection.json
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/datasets/trends/monthly_bill_by_account.json
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/dashboard_permissions.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/permissions/data_source_permissions.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/aws_service_category_map.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/customer_all_unlimited.sql
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/shared/payer_account_name_map.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/daily_anomaly_detection.sql
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/monthly_anomaly_detection.sql
RENAMED
|
File without changes
|
{cid_cmd-4.2.6 → cid_cmd-4.3.0}/cid/builtin/core/data/queries/trends/monthly_bill_by_account.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|