cid-cmd 4.2.2__tar.gz → 4.2.4__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.
Files changed (125) hide show
  1. {cid_cmd-4.2.2/cid_cmd.egg-info → cid_cmd-4.2.4}/PKG-INFO +1 -1
  2. cid_cmd-4.2.4/cid/_version.py +2 -0
  3. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/kpi/kpi_tracker.json +6 -0
  4. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/idle.json +1 -1
  5. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/idle_options.sql +1 -1
  6. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/last_kpi_tracker_view.sql +2 -0
  7. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/common.py +13 -9
  8. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/export.py +1 -1
  9. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/account_map.py +18 -14
  10. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/athena.py +10 -8
  11. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/cur.py +7 -3
  12. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/__init__.py +2 -0
  13. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/dataset.py +36 -32
  14. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/utils.py +3 -2
  15. {cid_cmd-4.2.2 → cid_cmd-4.2.4/cid_cmd.egg-info}/PKG-INFO +1 -1
  16. cid_cmd-4.2.2/cid/_version.py +0 -2
  17. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/LICENSE +0 -0
  18. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/MANIFEST.in +0 -0
  19. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/README.md +0 -0
  20. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/__init__.py +0 -0
  21. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/base.py +0 -0
  22. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/__init__.py +0 -0
  23. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/__init__.py +0 -0
  24. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cid/compute.json +0 -0
  25. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cid/ec2_running_cost.json +0 -0
  26. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cid/s3_view.json +0 -0
  27. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cid/summary_view.json +0 -0
  28. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/co/dataset.json +0 -0
  29. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cudos/hourly_view.json +0 -0
  30. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/cudos/resource_view.json +0 -0
  31. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/kpi/kpi_ebs_snap.json +0 -0
  32. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/kpi/kpi_ebs_storage_all.json +0 -0
  33. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/kpi/kpi_instance_all.json +0 -0
  34. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/kpi/kpi_s3_storage_all.json +0 -0
  35. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/shared/customer_all.json +0 -0
  36. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/tao/dataset.json +0 -0
  37. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/trends/daily_anomaly_detection.json +0 -0
  38. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/trends/monthly_anomaly_detection.json +0 -0
  39. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/datasets/trends/monthly_bill_by_account.json +0 -0
  40. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/permissions/dashboard_permissions.json +0 -0
  41. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/permissions/dashboard_permissions_namespace.json +0 -0
  42. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/permissions/data_set_permissions.json +0 -0
  43. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/permissions/data_source_permissions.json +0 -0
  44. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/permissions/folder_permissions.json +0 -0
  45. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cid/compute_savings_plan_eligible_spend.sql +0 -0
  46. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cid/ec2_running_cost.sql +0 -0
  47. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cid/ri_sp_mapping.sql +0 -0
  48. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cid/s3.sql +0 -0
  49. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cid/summary_view.sql +0 -0
  50. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/all_options.sql +0 -0
  51. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/auto_scale.json +0 -0
  52. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/auto_scale_options.sql +0 -0
  53. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ebs_volume.json +0 -0
  54. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ebs_volume_options.sql +0 -0
  55. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ec2_instance.json +0 -0
  56. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ec2_instance_options.sql +0 -0
  57. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ecs_service.json +0 -0
  58. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/ecs_service_options.sql +0 -0
  59. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/lambda.json +0 -0
  60. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/lambda_options.sql +0 -0
  61. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/license.json +0 -0
  62. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/license_options.sql +0 -0
  63. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/rds_database.json +0 -0
  64. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/rds_instance_options.sql +0 -0
  65. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/co/rds_storage_options.sql +0 -0
  66. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cudos/hourly_view.sql +0 -0
  67. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/cudos/resource_view.sql +0 -0
  68. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/first_kpi_instance_mapping_view.sql +0 -0
  69. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/kpi_ebs_snap_view.sql +0 -0
  70. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/kpi_ebs_storage_view.sql +0 -0
  71. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/kpi_instance_all_view.sql +0 -0
  72. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/kpi/kpi_s3_storage_view.sql +0 -0
  73. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/account_map.sql +0 -0
  74. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/account_map_cur2.sql +0 -0
  75. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/account_map_dummy.sql +0 -0
  76. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/aws_accounts.sql +0 -0
  77. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/aws_regions.sql +0 -0
  78. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/aws_service_category_map.sql +0 -0
  79. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/business_units_map.sql +0 -0
  80. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/cur.yaml +0 -0
  81. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/customer_all_unlimited.sql +0 -0
  82. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/payer_account_name_map.sql +0 -0
  83. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/shared/ta_descriptions.sql +0 -0
  84. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/tao/glue_table.json +0 -0
  85. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/tao/ta_org_view.sql +0 -0
  86. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/trends/daily_anomaly_detection.sql +0 -0
  87. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/trends/monthly_anomaly_detection.sql +0 -0
  88. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/queries/trends/monthly_bill_by_account.sql +0 -0
  89. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/builtin/core/data/resources.yaml +0 -0
  90. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/cli.py +0 -0
  91. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/commands/__init__.py +0 -0
  92. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/commands/command_base.py +0 -0
  93. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/commands/init_qs.py +0 -0
  94. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/exceptions.py +0 -0
  95. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/__init__.py +0 -0
  96. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/cloudformation.py +0 -0
  97. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/csv2view.py +0 -0
  98. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/cur_proxy.py +0 -0
  99. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/diff.py +0 -0
  100. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/glue.py +0 -0
  101. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/iam.py +0 -0
  102. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/organizations.py +0 -0
  103. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/parameter_store.py +0 -0
  104. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/dashboard.py +0 -0
  105. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/dashboard_patching.py +0 -0
  106. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/datasource.py +0 -0
  107. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/definition.py +0 -0
  108. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/resource.py +0 -0
  109. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/template.py +0 -0
  110. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/quicksight/version.py +0 -0
  111. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/randtime.py +0 -0
  112. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/s3.py +0 -0
  113. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/helpers/timezone.py +0 -0
  114. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/logger.py +0 -0
  115. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/plugin.py +0 -0
  116. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/test/python/test_csv2view.py +0 -0
  117. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/test/python/test_isolated_parameters.py +0 -0
  118. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid/test/python/test_merge.py +0 -0
  119. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid_cmd.egg-info/SOURCES.txt +0 -0
  120. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid_cmd.egg-info/dependency_links.txt +0 -0
  121. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid_cmd.egg-info/entry_points.txt +0 -0
  122. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid_cmd.egg-info/requires.txt +0 -0
  123. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/cid_cmd.egg-info/top_level.txt +0 -0
  124. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/pyproject.toml +0 -0
  125. {cid_cmd-4.2.2 → cid_cmd-4.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cid-cmd
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: Cloud Intelligence Dashboards deployment helper tool
5
5
  Home-page: https://github.com/aws-samples/aws-cudos-framework-deployment
6
6
  Author: AWS CUDOS Team
@@ -0,0 +1,2 @@
1
+ __version__ = '4.2.4'
2
+
@@ -48,6 +48,11 @@
48
48
  "Type": "DECIMAL",
49
49
  "SubType": "FLOAT"
50
50
  },
51
+ {
52
+ "Name": "ec2_usage_cost",
53
+ "Type": "DECIMAL",
54
+ "SubType": "FLOAT"
55
+ },
51
56
  {
52
57
  "Name": "ec2_spot_cost",
53
58
  "Type": "DECIMAL",
@@ -397,6 +402,7 @@
397
402
  "linked_account_id",
398
403
  "spend_all_cost",
399
404
  "ec2_all_cost",
405
+ "ec2_usage_cost",
400
406
  "ec2_spot_cost",
401
407
  "ec2_spot_potential_savings",
402
408
  "ec2_previous_generation_cost",
@@ -82,7 +82,7 @@
82
82
  "Type": "string"
83
83
  },
84
84
  {
85
- "Name": "utilizationetricsebsvolumewriteiopsmaximum",
85
+ "Name": "utilizationmetricsebsvolumewriteiopsmaximum",
86
86
  "Type": "string"
87
87
  },
88
88
  {
@@ -23,7 +23,7 @@ CREATE OR REPLACE VIEW compute_optimizer_idle_options AS
23
23
  utilizationmetricsnetworkinbytespersecondmaximum, ';',
24
24
  utilizationmetricsdatabaseconnectionsmaximum, ';',
25
25
  utilizationmetricsebsvolumereadiopsmaximum, ';',
26
- utilizationetricsebsvolumewriteiopsmaximum, ';',
26
+ utilizationmetricsebsvolumewriteiopsmaximum, ';',
27
27
  utilizationmetricsvolumereadopspersecondmaximum, ';',
28
28
  utilizationmetricsvolumewriteopspersecondmaximum
29
29
  ) utilizationmetrics
@@ -6,6 +6,7 @@ SELECT DISTINCT
6
6
  , spend_all.spend_all_cost
7
7
  , spend_all.tags_json
8
8
  , instance_all.ec2_all_cost
9
+ , instance_all.ec2_usage_cost
9
10
  , instance_all.ec2_spot_cost
10
11
  , instance_all.ec2_spot_potential_savings
11
12
  , instance_all.ec2_previous_generation_cost
@@ -94,6 +95,7 @@ LEFT JOIN (
94
95
  , linked_account_id
95
96
  , tags_json
96
97
  , "sum"("ec2_all_cost") "ec2_all_cost"
98
+ , "sum"("ec2_usage_cost") "ec2_usage_cost"
97
99
  , "sum"("ec2_spot_cost") "ec2_spot_cost"
98
100
  , "sum"("ec2_spot_potential_savings") "ec2_spot_potential_savings"
99
101
  , "sum"("ec2_previous_generation_cost") "ec2_previous_generation_cost"
@@ -131,10 +131,10 @@ class Cid():
131
131
  """ get/create parameters_controller """
132
132
  return ParametersController(self.athena)
133
133
 
134
- def get_cur(self, target_cur_version):
134
+ def get_cur(self, target_cur_version=None):
135
135
  """ get a cur """
136
136
  cur_version = self.cur.version
137
- if cur_version != target_cur_version or get_parameters().get('use-cur-proxy'):
137
+ if (target_cur_version and cur_version != target_cur_version) or get_parameters().get('use-cur-proxy'):
138
138
  return ProxyCUR(self.cur, target_cur_version=target_cur_version)
139
139
  return self.cur
140
140
 
@@ -170,7 +170,7 @@ class Cid():
170
170
  account_map = AccountMap(
171
171
  self.base.session,
172
172
  self.athena,
173
- self.cur, # can be any CUR. But it is only needed for trends and dummy
173
+ self.get_cur, # can be any CUR. But it is only needed for trends and dummy
174
174
  )
175
175
  return account_map.create_or_update(name)
176
176
 
@@ -409,9 +409,7 @@ class Cid():
409
409
  res_list = self.athena.query(query)
410
410
  except (self.athena.client.exceptions.ClientError, CidError, CidCritical) as exc:
411
411
  raise CidCritical(f'Failed fetching parameter {prefix}{key}: {exc}.') from exc
412
- if not res_list:
413
- raise CidCritical(f'Failed fetching parameter {prefix}{key}, {value}. Athena returns empty results. {value.get("error")}')
414
- options = ['-'.join(res) for res in res_list]
412
+ options = ['-'.join(res) for res in (res_list or [])]
415
413
  params[key] = self.generic_tags_json(
416
414
  param_name=key,
417
415
  options=options,
@@ -1605,9 +1603,15 @@ class Cid():
1605
1603
  # patch dataset for tags
1606
1604
  cur_tags_json_required = False
1607
1605
  for dep_view_name in dataset_definition.get('dependsOn', {}).get('views', []):
1608
- cur_tags_json_required = 'tags_json' in str()
1609
- if self.resources['views'].get(dep_view_name, {}).get('dependsOn',{}).get('tags') == 'json' \
1610
- or self.resources['views'].get(dep_view_name, {}).get('parameters',{}).get('resource-tags'):
1606
+ try:
1607
+ tags_type = self.resources['views'][dep_view_name]['dependsOn']['tags']
1608
+ except (KeyError, TypeError, AttributeError):
1609
+ tags_type = None
1610
+ try:
1611
+ param_res_tag = self.resources['views'][dep_view_name]['parameters']['resource-tags']
1612
+ except (KeyError, TypeError, AttributeError):
1613
+ param_res_tag = None
1614
+ if tags_type == 'json' or param_res_tag:
1611
1615
  cur_tags_json_required = True
1612
1616
  break
1613
1617
  custom_fields = {}
@@ -179,7 +179,7 @@ def export_analysis(qs, athena, glue):
179
179
  elif 'CustomSql' in value and 'DataSourceArn' in value['CustomSql']:
180
180
  logger.debug(f"Dataset {dataset.raw['DataSetId']} looks like CustomSql athena dataset")
181
181
  value['CustomSql']['DataSourceArn'] = '${athena_datasource_arn}'
182
- databases = [db_['Name'] for db_ in athena.list_databases()]
182
+ databases = athena.list_databases()
183
183
  for database in databases:
184
184
  if f'{database}.' in value['CustomSql']['SqlQuery'] or f'"{database}".' in value['CustomSql']['SqlQuery']:
185
185
  logger.debug(f"Replacing {database} in text")
@@ -65,8 +65,8 @@ class AccountMap(CidBase):
65
65
  }
66
66
  }
67
67
 
68
- def __init__(self, session: Session, athena: Athena, cur=None) -> None:
69
- self.cur = cur # Only for trends and dummy
68
+ def __init__(self, session: Session, athena: Athena, get_cur=None) -> None:
69
+ self.get_cur = get_cur # get_cur callback Only for trends and dummy
70
70
  self.athena = athena
71
71
  super().__init__(session)
72
72
 
@@ -121,7 +121,7 @@ class AccountMap(CidBase):
121
121
  template = Template(
122
122
  resource_string(
123
123
  view_definition.get('providedBy'),
124
- f'data/queries/{view_file}'
124
+ f'data/{view_file}'
125
125
  ).decode('utf-8')
126
126
  )
127
127
  elif view_definition.get('data'):
@@ -133,8 +133,8 @@ class AccountMap(CidBase):
133
133
  vars = {
134
134
  'metadata_table_name': metadata_table['Name'],
135
135
  'metadata_database_name': metadata_table['Database'],
136
- 'cur_database': self.cur.database, # only for trends in metadata
137
- 'cur_table_name': self.cur.table_name, # only for trends in metadata
136
+ 'cur_database': self.get_cur().database if name == 'aws_accounts' and self.get_cur else None, # only for trends in metadata
137
+ 'cur_table_name': self.get_cur().table_name if name == 'aws_accounts' and self.get_cur else None, # only for trends in metadata
138
138
  }
139
139
  for key, val in self.mappings.get(name).get(metadata_table['Name']).items():
140
140
  logger.debug(f'Mapping field {key} to {val}')
@@ -151,7 +151,8 @@ class AccountMap(CidBase):
151
151
  def get_dummy_account_mapping_sql(self, name) -> list:
152
152
  """Create dummy account mapping"""
153
153
  logger.info(f'Creating dummy account mapping for {name}')
154
- if self.cur.version.startswith('2'):
154
+ cur = self.get_cur()
155
+ if cur.version.startswith('2'):
155
156
  sql_file = 'data/queries/shared/account_map_cur2.sql'
156
157
  else:
157
158
  sql_file = 'data/queries/shared/account_map_dummy.sql'
@@ -161,8 +162,8 @@ class AccountMap(CidBase):
161
162
  ).decode('utf-8'))
162
163
  columns_tpl = {
163
164
  'athena_view_name': name,
164
- 'cur_table_name': self.cur.table_name,
165
- 'cur_database': self.cur.database,
165
+ 'cur_table_name': cur.table_name,
166
+ 'cur_database': cur.database,
166
167
  }
167
168
  compiled_query = template.safe_substitute(columns_tpl)
168
169
  return compiled_query
@@ -278,20 +279,23 @@ class AccountMap(CidBase):
278
279
  except CidError as exc:
279
280
  logger.debug(exc)
280
281
 
281
- if self.cur.version.startswith('2'):
282
+ if self.get_cur and self.get_cur().version.startswith('2'):
282
283
  cid_print('Looks like CUR2 is used. Will use it for account map.')
283
284
  set_parameters({'account-map-source': 'dummy'})
284
285
 
285
286
  for attempt in range(3):
286
287
  try:
288
+ choices = {
289
+ 'AWS Organizations (one time account listing)': 'organization',
290
+ 'CSV file (relative path required)': 'csv',
291
+ }
292
+ if self.get_cur:
293
+ choices['Dummy (CUR account data, no names for CUR1)'] = 'dummy'
294
+
287
295
  metadata_source = get_parameter(
288
296
  param_name='account-map-source',
289
297
  message="Please select account metadata collection method",
290
- choices={
291
- 'Dummy (CUR account data, no names for CUR1)': 'dummy',
292
- 'AWS Organizations (one time account listing)': 'organization',
293
- 'CSV file (relative path required)': 'csv',
294
- },
298
+ choices=choices,
295
299
  )
296
300
  logger.info(f'Attempt {attempt + 2}' )
297
301
  # Collect account list from different sources of user choice
@@ -282,16 +282,18 @@ class Athena(CidBase):
282
282
  logger.debug(f'WorkGroups: {result.get("WorkGroups")}')
283
283
  return result.get('WorkGroups')
284
284
 
285
- def get_table_metadata(self, table_name: str, database_name: str=None) -> dict:
286
- table_metadata = self._metadata.get(table_name)
287
- params = {
288
- 'CatalogName': self.CatalogName,
289
- 'DatabaseName': database_name or self.DatabaseName,
290
- 'TableName': table_name
291
- }
285
+ def get_table_metadata(self, table_name: str, database_name: str=None, no_cache: bool=False) -> dict:
286
+ table_metadata = None
287
+ if not no_cache:
288
+ table_metadata = self._metadata.get(table_name)
292
289
  if not table_metadata:
290
+ params = {
291
+ 'CatalogName': self.CatalogName,
292
+ 'DatabaseName': database_name or self.DatabaseName,
293
+ 'TableName': table_name,
294
+ }
293
295
  table_metadata = self.client.get_table_metadata(**params).get('TableMetadata')
294
- self._metadata.update({table_name: table_metadata})
296
+ self._metadata[table_name] = table_metadata
295
297
 
296
298
  return table_metadata
297
299
 
@@ -184,8 +184,10 @@ class AbstractCUR(CidBase):
184
184
  return self._tag_and_cost_category
185
185
 
186
186
  self._tag_and_cost_category = []
187
- number_of_rows_scanned = 100000 # empiric value
188
- for tag_type in ["resource_tags", 'cost_category']:
187
+ number_of_rows_scanned = 500000 # empiric value
188
+ for tag_type in ['resource_tags', 'cost_category']:
189
+ if tag_type not in self.fields:
190
+ logging.debug(f'skipping {tag_type} scan')
189
191
  cid_print(f'Scanning {tag_type} in {self.table_name}.')
190
192
  try:
191
193
  res = self.athena.query(
@@ -211,7 +213,9 @@ class AbstractCUR(CidBase):
211
213
  cid_print(f' <BOLD>{tag_type:<{max_width}} | Distinct Values <END> ')
212
214
  for line in res:
213
215
  if int(line[1]) > 10:
214
- cid_print(f' <BOLD>{line[0]:<{max_width}}<END> | {line[1]} ')
216
+ name = line[0]
217
+ name = name.replace('user_', '')
218
+ cid_print(f' <BOLD>{name:<{max_width}}<END> | {line[1]} ')
215
219
  self._tag_and_cost_category += sorted([f"{tag_type}['{line[0]}']" for line in res])
216
220
  except (self.athena.client.exceptions.ClientError, CidCritical, ValueError) as exc:
217
221
  logger.error(f'Failed to read {tag_type} from {self.table_name}: "{exc}". Will continue without.')
@@ -1205,6 +1205,8 @@ class QuickSight(CidBase):
1205
1205
  schedule["RefreshType"] = schedule.get("RefreshType", "FULL_REFRESH")
1206
1206
  if "providedBy" in schedule:
1207
1207
  del schedule["providedBy"]
1208
+ if "source" in schedule:
1209
+ del schedule["source"]
1208
1210
 
1209
1211
  if not existing_schedule:
1210
1212
  # Avoid adding a new schedule when customer already has put a schedule manually as this can lead to additional charges.
@@ -68,33 +68,12 @@ class Dataset(CidQsResource):
68
68
  '''
69
69
  def _get_athena_columns(table, database=None):
70
70
  '''returns athena columns'''
71
+ metadata = athena.get_table_metadata(table, database_name=database, no_cache=True)
71
72
  return [
72
- line[0].split(None, 1)
73
- for line in athena.query(
74
- f'SHOW COLUMNS FROM {table}',
75
- include_header=True, database=database
76
- )
73
+ (col['Name'], col['Type'])
74
+ for col in metadata.get('Columns', [])
77
75
  ]
78
76
 
79
- def _replace_columns(existing_columns, new_columns):
80
- '''replace columns but keep the order, ignore case changes
81
- assert _replace_columns(
82
- [{'Name': 'a'}, {'Name': 'B'}, {'Name': 'c'}],
83
- [{'Name': 'A'}, {'Name': 'b'}, {'Name': 'd'}]) \
84
- == [{'Name': 'a'}, {'Name': 'B'}, {'Name': 'd'}]
85
- Different types will be replaced
86
- '''
87
- existing_columns = [
88
- existing_col
89
- for existing_col in existing_columns
90
- if str(existing_col).lower() in [str(c).lower() for c in new_columns]
91
- ] # filter out old
92
- for col in new_columns: # add new
93
- if col['Name'].lower() not in [c['Name'].lower() for c in existing_columns]:
94
- existing_columns.append(col)
95
- #REFACTOR: what if col is there but another type?
96
- return existing_columns
97
-
98
77
  def _athena_to_qs_type(col, athena_type):
99
78
  '''map athena type to QS type
100
79
  The following data types are supported in SPICE: Date, Decimal-fixed, Decimal-float, Integer, and String.
@@ -125,8 +104,14 @@ class Dataset(CidQsResource):
125
104
  root_lt = dataset['LogicalTableMap'][lt]
126
105
  break
127
106
  else:
128
- raise ValueError(f'Unable to find a root logical table in the dataset {dataset}')
129
- projected_cols = next(ds['ProjectOperation']["ProjectedColumns"] for ds in root_lt['DataTransforms'] if 'ProjectOperation' in ds)
107
+ # take the first one and let's hope it is fine
108
+ root_lt = next(iter(dataset['LogicalTableMap'].values()))
109
+
110
+ projected_cols = next( # get the first DataTrasform with ProjectOperation
111
+ ds['ProjectOperation']["ProjectedColumns"]
112
+ for ds in root_lt['DataTransforms']
113
+ if 'ProjectOperation' in ds
114
+ )
130
115
 
131
116
  # Update each PhysicalTableMap with all columns from athena views
132
117
  all_columns = []
@@ -135,12 +120,31 @@ class Dataset(CidQsResource):
135
120
  database = pt['RelationalTable']['Schema']
136
121
  columns = _get_athena_columns(table_name, database)
137
122
  logger.trace(f'columns = {columns}')
138
-
139
- new_columns = [_athena_to_qs_type(name, athena_type) for name, athena_type in columns]
140
- #for col in new_columns:
141
- # if col['Name'] in [existing_col['Name'] for existing_col in all_columns]: #FIXME not all_columns so far but must be all cols before modification
142
- # col['Name'] = f'{col["Name"]}[{table_name}]'
143
- pt['RelationalTable']['InputColumns'] = _replace_columns(pt['RelationalTable']['InputColumns'], new_columns)
123
+ athena_columns = [
124
+ _athena_to_qs_type(name, athena_type.lower())
125
+ for name, athena_type in columns
126
+ ]
127
+ logger.trace(f'athena_columns = {columns}')
128
+ athena_columns_names = [c['Name'].lower() for c in athena_columns]
129
+ dataset_columns = pt['RelationalTable']['InputColumns']
130
+ dataset_columns_names = [col['Name'].lower() for col in dataset_columns]
131
+ dataset_columns_to_keep = [
132
+ col for col in dataset_columns
133
+ if col['Name'].lower() in athena_columns_names
134
+ ]
135
+ new_columns = [
136
+ col for col in athena_columns
137
+ if col['Name'].lower() not in dataset_columns_names
138
+ ] # BTW what if col is there but another type?
139
+
140
+ for col in new_columns: # alter names for columns that already exist
141
+ if col['Name'].lower() in projected_cols:
142
+ col['Name'] = f"{col['Name']}[{table_name}]" # What if it is alrady there?
143
+
144
+ logger.trace(f'dataset_columns_to_keep = {dataset_columns_to_keep}')
145
+ if new_columns:
146
+ logger.trace(f'new_columns = {new_columns}')
147
+ pt['RelationalTable']['InputColumns'] = dataset_columns_to_keep + new_columns
144
148
  all_columns += [col['Name'] for col in pt['RelationalTable']['InputColumns']]
145
149
 
146
150
  # Add all needed calc fields
@@ -190,7 +190,7 @@ def set_defaults(data: dict) -> None:
190
190
  if data:
191
191
  defaults.update(data)
192
192
 
193
- def get_defaults() -> None:
193
+ def get_defaults() -> dict:
194
194
  global defaults
195
195
  return dict(defaults)
196
196
 
@@ -250,7 +250,7 @@ def get_parameter(param_name, message, choices=None, default=None, none_as_disab
250
250
  param_name = param_name.replace('_', '-')
251
251
 
252
252
  # override defaults from code with outside defaults
253
- if param_name in defaults:
253
+ if param_name in get_defaults():
254
254
  default = defaults.get(param_name)
255
255
  if multi and isinstance(default, str):
256
256
  default = split_respecting_quotes(default)
@@ -276,6 +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 defaults if c in choices]
279
280
  if not isatty():
280
281
  result = default
281
282
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cid-cmd
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: Cloud Intelligence Dashboards deployment helper tool
5
5
  Home-page: https://github.com/aws-samples/aws-cudos-framework-deployment
6
6
  Author: AWS CUDOS Team
@@ -1,2 +0,0 @@
1
- __version__ = '4.2.2'
2
-
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