├── dev ├── __init__.py ├── scripts │ ├── __init__.py │ ├── cost_models │ │ ├── aws_cost_model.json │ │ ├── gcp_cost_model.json │ │ ├── azure_cost_model.json │ │ └── openshift_on_aws_cost_model.json │ ├── check_postgres_running.sh │ ├── common │ │ ├── utils.sh │ │ └── logging.sh │ ├── delete_cost_models.py │ ├── delete_test_sources.py │ ├── create_test_db_user.sh │ ├── nise_ymls │ │ └── gcp │ │ │ └── gcp_static_data.yml │ ├── migrate_schemas.sql │ ├── delete_gh_action_cache.py │ ├── migrate_tenant_schema_names.sql │ └── validate_dashboards.py ├── containers │ ├── trino │ │ ├── etc │ │ │ ├── log.properties │ │ │ ├── config.properties │ │ │ ├── catalog │ │ │ │ ├── postgres.properties │ │ │ │ ├── hive.properties │ │ │ │ └── glue.properties │ │ │ └── jvm.config │ │ ├── data │ │ │ └── .gitignore │ │ └── logs │ │ │ └── .gitignore │ ├── unleash │ │ └── flags.json │ ├── minio │ │ └── .gitignore │ └── postgresql │ │ └── Dockerfile ├── credentials │ ├── .gitignore │ └── gcp.example └── config.yaml.github-example ├── koku ├── common │ ├── __init__.py │ └── test │ │ └── __init__.py ├── key_metrics │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ └── apps.py ├── koku │ ├── test │ │ └── __init__.py │ ├── __init__.py │ ├── metrics.py │ ├── wsgi.py │ ├── env.py │ ├── urls.py │ └── sentry.py ├── api │ ├── migrations │ │ ├── __init__.py │ │ ├── 0034_remove_sources_endpoint_id.py │ │ ├── 0064_delete_dataexportrequest.py │ │ ├── 0030_auto_20201007_1403.py │ │ ├── 0045_update_django_migration_sequences.py │ │ ├── 0055_install_pg_stat_statements.py │ │ ├── 0061_alter_providerinfrastructuremap_unique_together.py │ │ ├── 0049_auto_20210818_2208.py │ │ ├── 0044_auto_20210505_1747.py │ │ ├── 0060_provider_polling_timestamp.py │ │ ├── 0036_reapply_check_migrations_func.py │ │ ├── 0031_clone_schema.py │ │ ├── 0032_presto_delete_log_trigger_func.py │ │ ├── 0042_reapply_clone_func.py │ │ ├── 0038_drop_app_needs_migrations_func.py │ │ ├── 0043_apply_turbo_schema_clone_func.py │ │ ├── 0053_additional_context.py │ │ ├── 0063_remove_infra_map_account_region.py │ │ ├── 0037_auto_20210223_2136.py │ │ ├── 0041_array_subtract_dbfunc.py │ │ ├── 0046_jsonb_sha256_text.py │ │ ├── 0056_reapply_clone_schema_func.py │ │ ├── 0057_add_org_ids.py │ │ ├── 0052_sources_provider.py │ │ ├── 0033_sources_name_text.py │ │ ├── 0062_add_infra_map_account_region.py │ │ ├── 0048_new_partition_manager_func.py │ │ ├── 0058_exchangeratedictionary.py │ │ ├── 0035_reapply_partition_and_clone_func.py │ │ └── 0051_reapply_partition_trigger_func.py │ ├── iam │ │ ├── __init__.py │ │ └── test │ │ │ └── __init__.py │ ├── report │ │ ├── __init__.py │ │ ├── test │ │ │ ├── ocp │ │ │ │ ├── __init__.py │ │ │ │ └── view │ │ │ │ │ └── __init__.py │ │ │ ├── __init__.py │ │ │ ├── all │ │ │ │ ├── __init__.py │ │ │ │ └── openshift │ │ │ │ │ └── __init__.py │ │ │ ├── aws │ │ │ │ ├── __init__.py │ │ │ │ └── openshift │ │ │ │ │ └── __init__.py │ │ │ ├── gcp │ │ │ │ ├── __init__.py │ │ │ │ └── openshift │ │ │ │ │ └── __init__.py │ │ │ ├── util │ │ │ │ └── __init__.py │ │ │ ├── azure │ │ │ │ ├── __init__.py │ │ │ │ └── openshift │ │ │ │ │ └── __init__.py │ │ │ └── gcp_static_data.yml │ │ ├── all │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ ├── aws │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ ├── gcp │ │ │ ├── __init__.py │ │ │ ├── openshift │ │ │ │ └── __init__.py │ │ │ └── view.py │ │ ├── ocp │ │ │ ├── __init__.py │ │ │ ├── capacity │ │ │ │ └── __init__.py │ │ │ └── network │ │ │ │ └── __init__.py │ │ └── azure │ │ │ ├── __init__.py │ │ │ ├── openshift │ │ │ └── __init__.py │ │ │ └── view.py │ ├── resource_types │ │ ├── __init__.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ ├── aws_category │ │ │ │ └── __init__.py │ │ │ ├── gcp_regions │ │ │ │ └── __init__.py │ │ │ ├── gcp_accounts │ │ │ │ └── __init__.py │ │ │ ├── gcp_projects │ │ │ │ └── __init__.py │ │ │ ├── gcp_services │ │ │ │ └── __init__.py │ │ │ ├── openshift_nodes │ │ │ │ └── __init__.py │ │ │ ├── openshift_clusters │ │ │ │ └── __init__.py │ │ │ ├── openshift_projects │ │ │ │ └── __init__.py │ │ │ └── openshift_virtual_machines │ │ │ │ └── __init__.py │ │ ├── aws_accounts │ │ │ └── __init__.py │ │ ├── aws_category │ │ │ ├── __init__.py │ │ │ └── serializers.py │ │ ├── aws_org_unit │ │ │ └── __init__.py │ │ ├── cost_models │ │ │ └── __init__.py │ │ ├── openshift_gpus │ │ │ ├── __init__.py │ │ │ └── view.py │ │ ├── openshift_nodes │ │ │ └── __init__.py │ │ ├── aws_ec2_compute_os │ │ │ └── __init__.py │ │ ├── aws_regions │ │ │ └── __init__.py │ │ ├── gcp_regions │ │ │ └── __init__.py │ │ ├── openshift_clusters │ │ │ └── __init__.py │ │ ├── openshift_projects │ │ │ └── __init__.py │ │ ├── aws_ec2_compute_instances │ │ │ └── __init__.py │ │ ├── aws_services │ │ │ └── __init__.py │ │ ├── azure_regions │ │ │ └── __init__.py │ │ ├── azure_services │ │ │ └── __init__.py │ │ ├── azure_subscription_guid │ │ │ └── __init__.py │ │ ├── gcp_accounts │ │ │ └── __init__.py │ │ ├── gcp_projects │ │ │ └── __init__.py │ │ ├── gcp_services │ │ │ └── __init__.py │ │ ├── openshift_virtual_machines │ │ │ └── __init__.py │ │ └── serializers.py │ ├── settings │ │ ├── tags │ │ │ ├── __init__.py │ │ │ └── mapping │ │ │ │ ├── __init__.py │ │ │ │ └── query_handler.py │ │ ├── __init__.py │ │ ├── cost_groups │ │ │ └── __init__.py │ │ ├── test │ │ │ ├── tags │ │ │ │ ├── __init__.py │ │ │ │ ├── mappings │ │ │ │ │ └── __init__.py │ │ │ │ └── test_view.py │ │ │ ├── __init__.py │ │ │ ├── cost_groups │ │ │ │ └── __init__.py │ │ │ └── aws_category_keys │ │ │ │ └── __init__.py │ │ ├── aws_category_keys │ │ │ ├── __init__.py │ │ │ └── serializers.py │ │ └── serializers.py │ ├── status │ │ ├── __init__.py │ │ └── views.py │ ├── currency │ │ ├── __init__.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ └── test_views.py │ │ ├── models.py │ │ └── utils.py │ ├── forecast │ │ ├── __init__.py │ │ └── test │ │ │ └── __init__.py │ ├── ingress │ │ ├── __init__.py │ │ ├── reports │ │ │ └── __init__.py │ │ └── test │ │ │ ├── __init__.py │ │ │ └── reports │ │ │ └── __init__.py │ ├── metrics │ │ ├── __init__.py │ │ ├── test │ │ │ └── __init__.py │ │ └── serializers.py │ ├── openapi │ │ └── __init__.py │ ├── provider │ │ └── __init__.py │ ├── tags │ │ ├── all │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ ├── __init__.py │ │ │ │ └── view.py │ │ ├── aws │ │ │ ├── __init__.py │ │ │ ├── openshift │ │ │ │ ├── __init__.py │ │ │ │ └── view.py │ │ │ └── view.py │ │ ├── azure │ │ │ ├── __init__.py │ │ │ ├── openshift │ │ │ │ ├── __init__.py │ │ │ │ └── view.py │ │ │ └── view.py │ │ ├── gcp │ │ │ ├── __init__.py │ │ │ ├── openshift │ │ │ │ ├── __init__.py │ │ │ │ └── view.py │ │ │ └── view.py │ │ ├── ocp │ │ │ ├── __init__.py │ │ │ └── view.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ ├── aws │ │ │ │ ├── __init__.py │ │ │ │ └── openshift │ │ │ │ │ └── __init__.py │ │ │ ├── azure │ │ │ │ └── __init__.py │ │ │ ├── gcp │ │ │ │ └── __init__.py │ │ │ └── ocp │ │ │ │ └── __init__.py │ │ └── __init__.py │ ├── user_access │ │ ├── __init__.py │ │ └── test │ │ │ └── __init__.py │ ├── organizations │ │ ├── __init__.py │ │ ├── aws │ │ │ ├── __init__.py │ │ │ ├── view.py │ │ │ └── provider_map.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ └── aws │ │ │ │ └── __init__.py │ │ └── view.py │ ├── cloud_accounts │ │ ├── test │ │ │ └── __init__.py │ │ └── __init__.py │ ├── common │ │ ├── permissions │ │ │ ├── test │ │ │ │ └── __init__.py │ │ │ ├── settings_access.py │ │ │ └── azure_access.py │ │ ├── csv.py │ │ ├── filters.py │ │ └── __init__.py │ ├── __init__.py │ ├── apps.py │ ├── functions.py │ ├── models.py │ └── model_utils.py ├── masu │ ├── __init__.py │ ├── test │ │ ├── util │ │ │ ├── gcp │ │ │ │ └── __init__.py │ │ │ ├── __init__.py │ │ │ ├── aws │ │ │ │ └── __init__.py │ │ │ ├── azure │ │ │ │ └── __init__.py │ │ │ └── ocp │ │ │ │ └── __init__.py │ │ ├── api │ │ │ └── __init__.py │ │ ├── database │ │ │ └── __init__.py │ │ ├── external │ │ │ ├── __init__.py │ │ │ ├── accounts │ │ │ │ ├── __init__.py │ │ │ │ └── hierarchy │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── aws │ │ │ │ │ └── __init__.py │ │ │ └── downloader │ │ │ │ ├── __init__.py │ │ │ │ ├── azure │ │ │ │ ├── __init__.py │ │ │ │ └── fixtures │ │ │ │ │ └── manifest.json │ │ │ │ ├── gcp │ │ │ │ └── __init__.py │ │ │ │ ├── aws_local │ │ │ │ └── __init__.py │ │ │ │ ├── gcp_local │ │ │ │ └── __init__.py │ │ │ │ └── azure_local │ │ │ │ └── __init__.py │ │ ├── processor │ │ │ ├── __init__.py │ │ │ ├── aws │ │ │ │ └── __init__.py │ │ │ ├── azure │ │ │ │ └── __init__.py │ │ │ ├── gcp │ │ │ │ └── __init__.py │ │ │ ├── ocp │ │ │ │ └── __init__.py │ │ │ └── parquet │ │ │ │ └── __init__.py │ │ └── data │ │ │ ├── ocp │ │ │ ├── empty-file-payload │ │ │ │ ├── empty-file.csv │ │ │ │ ├── manifest.json │ │ │ │ └── not-empty-file.csv │ │ │ ├── valid-csv.csv │ │ │ ├── tokenizing-error.csv │ │ │ ├── payload.tar.gz │ │ │ ├── payload2.tar.gz │ │ │ ├── bad_payload.tar.gz │ │ │ ├── no_manifest.tar.gz │ │ │ ├── ros_payload.tar.gz │ │ │ └── outside_retention_payload.tar.gz │ │ │ ├── test_cur.csv.gz │ │ │ ├── parquet_input.csv │ │ │ ├── test_local_bucket.tar.gz │ │ │ ├── test_local_bucket_prefix.tar.gz │ │ │ └── azure │ │ │ └── empty_frame.csv │ ├── celery │ │ └── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── check_migrations.py │ │ │ └── migrate.py │ ├── util │ │ ├── azure │ │ │ └── __init__.py │ │ ├── gcp │ │ │ └── __init__.py │ │ ├── ocp │ │ │ └── __init__.py │ │ └── __init__.py │ ├── api │ │ ├── manifest │ │ │ ├── __init__.py │ │ │ └── test │ │ │ │ └── __init__.py │ │ ├── sources │ │ │ ├── __init__.py │ │ │ └── test │ │ │ │ └── __init__.py │ │ ├── db_performance │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ │ └── __init__.py │ │ └── __init__.py │ ├── processor │ │ ├── aws │ │ │ └── __init__.py │ │ ├── azure │ │ │ └── __init__.py │ │ ├── ocp │ │ │ └── __init__.py │ │ ├── _tasks │ │ │ └── __init__.py │ │ └── parquet │ │ │ └── __init__.py │ ├── external │ │ ├── accounts │ │ │ ├── __init__.py │ │ │ └── hierarchy │ │ │ │ ├── __init__.py │ │ │ │ ├── aws │ │ │ │ └── __init__.py │ │ │ │ └── account_crawler.py │ │ ├── downloader │ │ │ ├── __init__.py │ │ │ ├── aws │ │ │ │ └── __init__.py │ │ │ ├── azure │ │ │ │ └── __init__.py │ │ │ ├── gcp │ │ │ │ └── __init__.py │ │ │ ├── aws_local │ │ │ │ └── __init__.py │ │ │ ├── gcp_local │ │ │ │ └── __init__.py │ │ │ └── azure_local │ │ │ │ └── __init__.py │ │ └── __init__.py │ ├── apps.py │ ├── database │ │ └── sql │ │ │ ├── openshift │ │ │ ├── cost_model │ │ │ │ ├── delete_monthly_cost_model_rate_type.sql │ │ │ │ └── delete_monthly_cost.sql │ │ │ └── create_virtualization_tmp_table.sql │ │ │ ├── azure │ │ │ ├── ui_summary │ │ │ │ └── reporting_azure_cost_summary_p.sql │ │ │ └── openshift │ │ │ │ └── reporting_ocpazure_matched_tags.sql │ │ │ └── gcp │ │ │ └── openshift │ │ │ └── reporting_ocpgcp_matched_tags.sql │ ├── exceptions.py │ └── urls.py ├── subs │ ├── __init__.py │ ├── trino_sql │ │ ├── aws │ │ │ ├── determine_ids_for_provider.sql │ │ │ ├── determine_resource_ids_for_usage_account.sql │ │ │ └── subs_row_count.sql │ │ └── azure │ │ │ ├── determine_ids_for_provider.sql │ │ │ └── subs_row_count.sql │ └── test │ │ └── __init__.py ├── hcs │ ├── test │ │ ├── database │ │ │ └── __init__.py │ │ └── test_exceptions.py │ ├── database │ │ ├── __init__.py │ │ └── sql │ │ │ ├── reporting_gcp_hcs_daily_summary.sql │ │ │ └── reporting_azure_hcs_daily_summary.sql │ ├── __init__.py │ └── exceptions.py ├── providers │ ├── __init__.py │ ├── aws │ │ └── __init__.py │ ├── azure │ │ └── __init__.py │ ├── gcp │ │ └── __init__.py │ ├── ocp │ │ └── __init__.py │ ├── test │ │ ├── __init__.py │ │ ├── aws │ │ │ └── __init__.py │ │ ├── azure │ │ │ └── __init__.py │ │ ├── gcp │ │ │ └── __init__.py │ │ ├── ocp │ │ │ └── __init__.py │ │ ├── aws_local │ │ │ └── __init__.py │ │ ├── gcp_local │ │ │ └── __init__.py │ │ └── azure_local │ │ │ └── __init__.py │ ├── aws_local │ │ ├── __init__.py │ │ └── provider.py │ └── gcp_local │ │ ├── __init__.py │ │ └── provider.py ├── reporting │ ├── __init__.py │ ├── partition │ │ └── __init__.py │ ├── currency │ │ ├── __init__.py │ │ └── models.py │ ├── ingress │ │ └── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0310_delete_ocppodsummarybynodep.py │ │ ├── 0311_actually_delete_ocppodsummarybynodep.py │ │ ├── 0301_create_ocppod_by_node_p.py │ │ ├── 0326_delete_awscostentrylineitemsummarybyec2compute.py │ │ ├── 0316_remove_openshiftcostcategory_namespace.py │ │ ├── 0338_ocpnode_architecture.py │ │ ├── 0324_ocppvc_csi_volume_handle.py │ │ ├── 0334_subslastprocessed_event_sent.py │ │ ├── 0339_ocpusagelineitemdailysummary_cost_model_gpu_cost.py │ │ ├── 0322_alter_awscostentrylineitemsummarybyec2compute_instance_name.py │ │ ├── 0337_remove_ocpcostsummarybyprojectp_infrastructure_project_markup_cost_and_more.py │ │ ├── 0309_auto_20231004_1009.py │ │ ├── 0323_remove_awscostentrylineitemsummarybyec2compute_availability_zone_and_org_unit.py │ │ ├── 0307_ingressreports_customer.py │ │ ├── 0313_remove_deprecated_tags.py │ │ ├── 0333_ocpcostsummarybyprojectp_infrastructure_markup_cost_and_more.py │ │ ├── 0328_alter_ocpusagelineitemdailysummary_monthly_cost_type.py │ │ ├── 0330_alter_ocpusagelineitemdailysummary_monthly_cost_type.py │ │ ├── 0336_alter_enabledtagkeys_provider_type.py │ │ ├── 0335_alter_ocpusagelineitemdailysummary_monthly_cost_type.py │ │ ├── 0306_subsidmap.py │ │ └── 0305_pvc.py │ ├── provider │ │ ├── all │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ ├── aws │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ ├── azure │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ ├── gcp │ │ │ ├── __init__.py │ │ │ └── openshift │ │ │ │ └── __init__.py │ │ └── ocp │ │ │ ├── __init__.py │ │ │ └── costs │ │ │ └── __init__.py │ ├── user_settings │ │ ├── __init__.py │ │ └── models.py │ └── apps.py ├── sources │ ├── __init__.py │ ├── test │ │ ├── __init__.py │ │ └── api │ │ │ └── __init__.py │ ├── api │ │ ├── views.py │ │ ├── urls.py │ │ └── __init__.py │ ├── apps.py │ └── urls.py ├── cost_models │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0004_auto_20210903_1559.py │ │ ├── 0007_net_stor_distribute_default.py │ │ ├── 0003_auto_20210615_2011.py │ │ └── 0006_add_distribution_info.py │ ├── test │ │ ├── __init__.py │ │ └── test_sql_parameters.py │ ├── views.py │ ├── apps.py │ └── urls.py ├── forecast │ ├── test │ │ └── __init__.py │ └── __init__.py ├── kafka_utils │ ├── __init__.py │ └── test │ │ └── __init__.py ├── reporting_common │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0032_costusagereportmanifest_last_reports.py │ │ ├── 0030_costusagereportmanifest_cluster_id.py │ │ ├── 0028_costusagereportmanifest_operator_version.py │ │ ├── 0031_costusagereportmanifest_export_time.py │ │ ├── 0042_alter_costusagereportmanifest_s3_parquet_cleared.py │ │ ├── 0033_costusagereportmanifest_reports_tracker.py │ │ ├── 0027_auto_20210412_1731.py │ │ ├── 0035_alter_costusagereportmanifest_s3_parquet_cleared.py │ │ ├── 0034_costusagereportmanifest_operator_daily_files.py │ │ ├── 0044_alter_diskcapacity_provider_type.py │ │ ├── 0043_alter_diskcapacity_provider_type.py │ │ └── 0029_costusagereportmanifest_operator_info.py │ ├── apps.py │ └── states.py ├── static │ └── README ├── templates │ ├── welcome.txt │ └── welcome.html └── manage.py ├── testing ├── data │ ├── .gitkeep │ └── insights_local │ │ └── .gitkeep ├── parquet_data │ └── .gitkeep ├── local_providers │ ├── aws_local │ │ └── .gitkeep │ ├── gcp_local │ │ └── .gitkeep │ ├── aws_local_0 │ │ └── .gitkeep │ ├── aws_local_1 │ │ └── .gitkeep │ ├── aws_local_2 │ │ └── .gitkeep │ ├── aws_local_3 │ │ └── .gitkeep │ ├── aws_local_4 │ │ └── .gitkeep │ ├── aws_local_5 │ │ └── .gitkeep │ ├── azure_local │ │ └── .gitkeep │ ├── azure_local_0 │ │ └── .gitkeep │ ├── azure_local_1 │ │ └── .gitkeep │ ├── gcp_local_0 │ │ └── .gitkeep │ ├── gcp_local_1 │ │ └── .gitkeep │ ├── gcp_local_2 │ │ └── .gitkeep │ ├── gcp_local_3 │ │ └── .gitkeep │ └── insights_local │ │ └── .gitkeep ├── run_api_tests.sh ├── run_vortex_api_tests.sh └── run_smoke_tests.sh ├── .shellcheckrc ├── .github ├── CODEOWNERS ├── scripts │ ├── get_description.sh │ ├── check_clowdapp.sh │ ├── files_require_smokes.sh │ └── check_migrations.sh ├── dependabot.yml ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── Feature_request.md │ └── Bug_report.md └── postgres │ └── docker-compose.yaml ├── docs ├── index.md ├── images │ └── generating-release-notes │ │ ├── run-workflow.png │ │ └── auto-generate-notes.png ├── sources.md └── generating-release-notes.md ├── mkdocs.yml ├── scripts ├── entrypoint.sh └── README ├── .baseimagedigest ├── .gitleaks.toml ├── grafana └── Dockerfile-grafana ├── pgadmin_servers.json.example ├── db_functions ├── partitioned_manage_trigger.sql ├── partitioned_tables_active_trigger.sql ├── partitioned_tables_manage_trigger.sql ├── array_subtract_func.sql ├── presto_delete_trigger_func.sql └── jsonb_sha256_text.sql ├── renovate.json ├── run_server.sh ├── .dockerignore ├── codecov.yml ├── .sourcery.yaml ├── .snyk ├── .coveragerc └── licenses └── MIT.txt /dev/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/common/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/key_metrics/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/koku/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/masu/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/subs/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /testing/parquet_data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/iam/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/tags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/status/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/hcs/test/database/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/masu/test/util/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/providers/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/partition/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/sources/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /testing/data/insights_local/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/currency/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/forecast/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/iam/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/ingress/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/openapi/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/provider/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/all/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/ocp/capacity/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/report/ocp/network/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/report/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/ocp/view/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/settings/cost_groups/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/tags/mapping/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/test/tags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/tags/all/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/gcp/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/tags/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/user_access/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/cost_models/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/cost_models/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/forecast/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/hcs/database/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/kafka_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/key_metrics/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/masu/celery/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/management/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/api/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/util/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/util/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/util/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/util/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/sources/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/gcp_local/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC1090,SC1091,SC2164 2 | -------------------------------------------------------------------------------- /koku/api/currency/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/forecast/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/ingress/reports/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/ingress/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/metrics/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/organizations/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/all/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/gcp/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/report/test/util/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_category/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_org_unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/cost_models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/aws_category_keys/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/settings/test/cost_groups/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/test/tags/mappings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/tags/test/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/test/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/test/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/test/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/user_access/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/cost_models/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/kafka_utils/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/api/manifest/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/api/sources/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/processor/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/processor/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/processor/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/database/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/util/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/util/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/util/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/aws_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/gcp_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/currency/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/ingress/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting_common/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/sources/test/api/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_0/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_2/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_3/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_4/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/aws_local_5/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/azure_local/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/azure_local_0/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/azure_local_1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/gcp_local_0/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/gcp_local_1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/gcp_local_2/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/gcp_local_3/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testing/local_providers/insights_local/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/cloud_accounts/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/ingress/test/reports/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/organizations/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/organizations/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/all/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/aws/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/gcp/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_gpus/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_nodes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/aws_category/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/settings/test/aws_category_keys/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/tags/all/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/aws/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/tags/azure/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/api/manifest/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/api/sources/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/processor/_tasks/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/processor/parquet/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/aws_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/gcp_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/all/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/ocp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/user_settings/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/log.properties: -------------------------------------------------------------------------------- 1 | io.trino=INFO 2 | -------------------------------------------------------------------------------- /koku/api/common/permissions/test/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/organizations/test/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/azure/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/all/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/report/test/aws/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_ec2_compute_os/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_regions/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/gcp_regions/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_clusters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_projects/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/tags/test/aws/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/empty-file-payload/empty-file.csv: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/masu/test/external/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/processor/parquet/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/providers/test/azure_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/ocp/costs/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/static/README: -------------------------------------------------------------------------------- 1 | Root directory for static files 2 | -------------------------------------------------------------------------------- /koku/api/report/test/azure/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_ec2_compute_instances/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_services/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/azure_regions/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/azure_services/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/azure_subscription_guid/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/gcp_accounts/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/gcp_projects/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/gcp_services/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_virtual_machines/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/gcp_regions/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/accounts/hierarchy/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/aws_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/gcp_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/azure/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/all/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/aws/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/azure/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/reporting/provider/gcp/openshift/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/gcp_accounts/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/gcp_projects/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/gcp_services/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/openshift_nodes/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/accounts/hierarchy/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/external/downloader/azure_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/accounts/hierarchy/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/aws_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/gcp_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/openshift_clusters/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/openshift_projects/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/accounts/hierarchy/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/azure_local/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/api/resource_types/test/openshift_virtual_machines/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/valid-csv.csv: -------------------------------------------------------------------------------- 1 | h1,h2 2 | col1,col2 3 | col1,col2 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @project-koku/development @project-koku/quality-engineers 2 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/tokenizing-error.csv: -------------------------------------------------------------------------------- 1 | h1,h2 2 | col1,col2 3 | col1,col2,col3 4 | -------------------------------------------------------------------------------- /dev/containers/unleash/flags.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "features": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Koku Documentation Index # 2 | 3 | Welcome to the Koku documentation! 4 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | docs_dir: docs/ 2 | site_name: Koku Documentation 3 | plugins: 4 | - techdocs-core 5 | -------------------------------------------------------------------------------- /dev/credentials/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /koku/masu/test/data/test_cur.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/test_cur.csv.gz -------------------------------------------------------------------------------- /dev/containers/minio/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files in this dir... 2 | * 3 | 4 | # ... except for this one. 5 | !.gitignore 6 | -------------------------------------------------------------------------------- /dev/containers/trino/data/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files in this dir... 2 | * 3 | 4 | # ... except for this one. 5 | !.gitignore 6 | -------------------------------------------------------------------------------- /dev/containers/trino/logs/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files in this dir... 2 | * 3 | 4 | # ... except for this one. 5 | !.gitignore 6 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/payload.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/payload.tar.gz -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/payload2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/payload2.tar.gz -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/bad_payload.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/bad_payload.tar.gz -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/no_manifest.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/no_manifest.tar.gz -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/ros_payload.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/ros_payload.tar.gz -------------------------------------------------------------------------------- /koku/masu/test/data/parquet_input.csv: -------------------------------------------------------------------------------- 1 | date1,date2,numeric1,numeric2,other 2 | 08/27/20 02:15 PM,08/26/20 02:15 PM,334.55,332.35,abc 3 | -------------------------------------------------------------------------------- /koku/masu/api/db_performance/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2022 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # noqa 6 | -------------------------------------------------------------------------------- /koku/masu/test/data/test_local_bucket.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/test_local_bucket.tar.gz -------------------------------------------------------------------------------- /.github/scripts/get_description.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > release_body.md << EOF 4 | ### Summary: 5 | 6 | TODO: insert summary 7 | 8 | EOF 9 | -------------------------------------------------------------------------------- /koku/masu/util/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Masu utility methods.""" 6 | -------------------------------------------------------------------------------- /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | cd $APP_HOME 5 | gunicorn koku.wsgi --access-logfile=- --config gunicorn_conf.py --preload 6 | -------------------------------------------------------------------------------- /koku/masu/api/db_performance/templates/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2022 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # noqa 6 | -------------------------------------------------------------------------------- /koku/masu/test/data/test_local_bucket_prefix.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/test_local_bucket_prefix.tar.gz -------------------------------------------------------------------------------- /docs/images/generating-release-notes/run-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/docs/images/generating-release-notes/run-workflow.png -------------------------------------------------------------------------------- /koku/masu/api/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Masu API.""" 6 | 7 | API_VERSION = "1" 8 | -------------------------------------------------------------------------------- /.baseimagedigest: -------------------------------------------------------------------------------- 1 | sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392 2 | bc29672cb3545f80138112096a9c50c791164859021acc0393377ddcbf71aa7d - 3 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/outside_retention_payload.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/koku/masu/test/data/ocp/outside_retention_payload.tar.gz -------------------------------------------------------------------------------- /docs/images/generating-release-notes/auto-generate-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-koku/koku/HEAD/docs/images/generating-release-notes/auto-generate-notes.png -------------------------------------------------------------------------------- /koku/hcs/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | # 3 | # Copyright 2021 Red Hat Inc. 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | """Hybrid Cloud Spend (HCS) report processing""" 7 | -------------------------------------------------------------------------------- /koku/key_metrics/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class KeyMetricsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "key_metrics" 7 | -------------------------------------------------------------------------------- /koku/cost_models/views.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """API views for import organization""" 6 | from cost_models.view import CostModelViewSet 7 | -------------------------------------------------------------------------------- /testing/run_api_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 3 | echo "Running API Tests at $SCRIPTPATH" 4 | 5 | $SCRIPTPATH/run_test.sh 'iqe tests plugin cost_management -k test_api' 6 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [allowlist] 2 | paths = [ 3 | '''grafana\/grafana.db.sql''', 4 | '''.env.example''', 5 | '''docs\/devtools.md''', 6 | '''dev\/scripts\/nise_ymls\/ocp_on_aws\/aws_static_data.yml''', 7 | ] 8 | -------------------------------------------------------------------------------- /koku/api/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Configuration for the api app.""" 6 | 7 | API_VERSION = 1 8 | default_app_config = "api.apps.ApiConfig" 9 | -------------------------------------------------------------------------------- /scripts/README: -------------------------------------------------------------------------------- 1 | This directory holds simple scripts used for stage or production. 2 | 3 | entrypoint - contains default command used by the Dockerfile 4 | run_migrations - contains commands to run postgres db migrations for koku 5 | -------------------------------------------------------------------------------- /testing/run_vortex_api_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 3 | echo "Running API Tests at $SCRIPTPATH" 4 | 5 | $SCRIPTPATH/run_test.sh 'iqe tests plugin cost_management -k test_api -m qa' 6 | -------------------------------------------------------------------------------- /testing/run_smoke_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 3 | echo "Running Smoke Tests" 4 | 5 | ENV_FOR_DYNACONF=local $SCRIPTPATH/run_test.sh 'iqe tests plugin cost_management -k test_api -m cost_smoke' 6 | -------------------------------------------------------------------------------- /koku/reporting_common/apps.py: -------------------------------------------------------------------------------- 1 | """App declaration for reporting_common.""" 2 | from django.apps import AppConfig 3 | 4 | 5 | class ReportingCommonConfig(AppConfig): 6 | """Reporting Common app config.""" 7 | 8 | name = "reporting_common" 9 | -------------------------------------------------------------------------------- /dev/containers/postgresql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:16 2 | 3 | ARG USER_ID=999 4 | ARG GROUP_ID=999 5 | 6 | COPY 99_postgresql_conf.sh docker-entrypoint-initdb.d/ 7 | 8 | RUN groupmod -o -g ${GROUP_ID} postgres 9 | RUN usermod -o -u ${USER_ID} -g ${GROUP_ID} postgres 10 | -------------------------------------------------------------------------------- /koku/koku/__init__.py: -------------------------------------------------------------------------------- 1 | """Configurations for the Koku Project.""" 2 | from .celery import app as celery_app 3 | from .celery import CELERY_INSPECT 4 | from .celery import is_task_currently_running 5 | 6 | __all__ = ("celery_app", "CELERY_INSPECT", "is_task_currently_running") 7 | -------------------------------------------------------------------------------- /koku/sources/api/views.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Sources views for import organization""" 6 | # flake8: noqa 7 | from sources.api.source_status import source_status 8 | from sources.api.view import SourcesViewSet 9 | -------------------------------------------------------------------------------- /grafana/Dockerfile-grafana: -------------------------------------------------------------------------------- 1 | FROM grafana/grafana:latest 2 | USER root 3 | RUN apk add sqlite 4 | USER grafana 5 | COPY --chown=grafana:root grafana.db.sql /var/lib/grafana/grafana.db.sql 6 | RUN cat /var/lib/grafana/grafana.db.sql | sqlite3 /var/lib/grafana/grafana.db && rm /var/lib/grafana/grafana.db.sql 7 | -------------------------------------------------------------------------------- /koku/api/apps.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """API application configuration module.""" 6 | from django.apps import AppConfig 7 | 8 | 9 | class ApiConfig(AppConfig): 10 | """API application configuration.""" 11 | 12 | name = "api" 13 | -------------------------------------------------------------------------------- /koku/masu/apps.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Masu application configuration module.""" 6 | from django.apps import AppConfig 7 | 8 | 9 | class MasuConfig(AppConfig): 10 | """Masu application configuration.""" 11 | 12 | name = "masu" 13 | -------------------------------------------------------------------------------- /dev/credentials/gcp.example: -------------------------------------------------------------------------------- 1 | { 2 | "type": "", 3 | "project_id": "", 4 | "private_key_id": "", 5 | "private_key": "", 6 | "client_email": "", 7 | "client_id": "", 8 | "auth_uri": "", 9 | "token_uri": "", 10 | "auth_provider_x509_cert_url": "", 11 | "client_x509_cert_url": "" 12 | } 13 | -------------------------------------------------------------------------------- /koku/api/organizations/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for organizations.""" 6 | from api.report.view import ReportView 7 | 8 | 9 | class OrganizationView(ReportView): 10 | """Base Organizations View.""" 11 | 12 | report = "organizations" 13 | -------------------------------------------------------------------------------- /koku/sources/apps.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Sources application configuration module.""" 6 | from django.apps import AppConfig 7 | 8 | 9 | class SourcesConfig(AppConfig): 10 | """Sources application configuration.""" 11 | 12 | name = "sources" 13 | -------------------------------------------------------------------------------- /koku/templates/welcome.txt: -------------------------------------------------------------------------------- 1 | Welcome to Hybrid Cloud Cost Management. 2 | 3 | Start gaining insights on your costs today. 4 | 5 | You user has been created with username {{username}}. 6 | A separate email will follow with the associated password. 7 | Point your browser to the following URL to login to the service. {{login_link}} 8 | -------------------------------------------------------------------------------- /koku/cost_models/apps.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Rates application configuration module.""" 6 | from django.apps import AppConfig 7 | 8 | 9 | class CostModelsConfig(AppConfig): 10 | """Rates application configuration.""" 11 | 12 | name = "cost_models" 13 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/config.properties: -------------------------------------------------------------------------------- 1 | coordinator=true 2 | node-scheduler.include-coordinator=true 3 | http-server.http.port=8080 4 | discovery.uri=http://trino:8080 5 | 6 | jmx.rmiserver.port=10000 7 | jmx.rmiregistry.port=10000 8 | query.max-length=10000000 9 | 10 | web-ui.authentication.type=fixed 11 | web-ui.user=trino 12 | -------------------------------------------------------------------------------- /koku/masu/test/data/azure/empty_frame.csv: -------------------------------------------------------------------------------- 1 | SubscriptionGuid,ResourceGroup,ResourceLocation,UsageDateTime,MeterCategory,MeterSubcategory,MeterId,MeterName,MeterRegion,UsageQuantity,ResourceRate,PreTaxCost,ConsumedService,ResourceType,InstanceId,Tags,OfferId,AdditionalInfo,ServiceInfo1,ServiceInfo2,ServiceName,ServiceTier,Currency,UnitOfMeasure 2 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/empty-file-payload/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "empty-file.csv", 4 | "not-empty-file.csv" 5 | ], 6 | "date": "2119-02-05 16:18:40.706683", 7 | "uuid": "f6b3701e-1e91-433b-b238-a31e49937558", 8 | "cluster_id": "my-ocp-cluster-8", 9 | "daily_reports": true 10 | } 11 | -------------------------------------------------------------------------------- /koku/reporting/apps.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Reporting application configuration module.""" 6 | from django.apps import AppConfig 7 | 8 | 9 | class ReportingConfig(AppConfig): 10 | """Reporting application configuration.""" 11 | 12 | name = "reporting" 13 | -------------------------------------------------------------------------------- /koku/api/migrations/0034_remove_sources_endpoint_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2021-01-15 15:32 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [("api", "0033_sources_name_text")] 8 | 9 | operations = [migrations.RemoveField(model_name="sources", name="endpoint_id")] 10 | -------------------------------------------------------------------------------- /pgadmin_servers.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "Servers": { 3 | "1": { 4 | "Name": "Koku", 5 | "Group": "Servers", 6 | "Port": 5432, 7 | "Username": "postgres", 8 | "Host": "koku_db", 9 | "SSLMode": "prefer", 10 | "MaintenanceDB": "postgres" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /koku/api/cloud_accounts/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | CLOUD_ACCOUNTS = [ 7 | { 8 | "name": "AWS", 9 | "value": "589173575009", 10 | "description": "Cost Management's AWS account ID", 11 | "updated_timestamp": "2020-03-06T14:41:11.808752Z", 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /dev/scripts/cost_models/aws_cost_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cost Management AWS Cost Model", 3 | "description": "A cost model for markup on AWS costs.", 4 | "source_type": "AWS", 5 | "source_uuids": [ 6 | "PROVIDER_UUID" 7 | ], 8 | "rates": [], 9 | "markup": { 10 | "value": 10, 11 | "unit": "percent" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dev/scripts/cost_models/gcp_cost_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cost Management GCP Cost Model", 3 | "description": "A cost model for markup on GCP costs.", 4 | "source_type": "GCP", 5 | "source_uuids": [ 6 | "PROVIDER_UUID" 7 | ], 8 | "rates": [], 9 | "markup": { 10 | "value": 30, 11 | "unit": "percent" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0310_delete_ocppodsummarybynodep.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.22 on 2023-10-16 18:34 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0309_auto_20231004_1009"), 9 | ] 10 | 11 | # This migration is replaced by 0312 12 | operations = [] 13 | -------------------------------------------------------------------------------- /dev/scripts/cost_models/azure_cost_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cost Management Azure Cost Model", 3 | "description": "A cost model for markup on Azure costs.", 4 | "source_type": "Azure", 5 | "source_uuids": [ 6 | "PROVIDER_UUID" 7 | ], 8 | "rates": [], 9 | "markup": { 10 | "value": 20, 11 | "unit": "percent" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /koku/masu/database/sql/openshift/cost_model/delete_monthly_cost_model_rate_type.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM {{schema | sqlsafe}}.reporting_ocpusagelineitem_daily_summary AS lids 2 | WHERE lids.usage_start >= {{start_date}}::date 3 | AND lids.usage_start <= {{end_date}}::date 4 | AND lids.report_period_id = {{report_period_id}} 5 | AND lids.cost_model_rate_type = {{cost_model_rate_type}} 6 | ; 7 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0311_actually_delete_ocppodsummarybynodep.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.22 on 2023-10-16 18:52 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0310_delete_ocppodsummarybynodep"), 9 | ] 10 | 11 | # This migration is replaced by 0312 12 | operations = [] 13 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/catalog/postgres.properties: -------------------------------------------------------------------------------- 1 | connector.name=postgresql 2 | connection-url=jdbc:postgresql://${ENV:POSTGRES_SQL_SERVICE_HOST}:${ENV:POSTGRES_SQL_SERVICE_PORT}/${ENV:DATABASE_NAME} 3 | connection-user=${ENV:DATABASE_USER} 4 | connection-password=${ENV:DATABASE_PASSWORD} 5 | postgresql.array-mapping=AS_ARRAY 6 | insert.non-transactional-insert.enabled=true 7 | write.batch-size=10000 8 | -------------------------------------------------------------------------------- /koku/reporting/user_settings/models.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Red Hat Inc. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | from django.db import models 5 | 6 | 7 | class UserSettings(models.Model): 8 | """A table that maps between account and settings: settings has many accounts.""" 9 | 10 | class Meta: 11 | db_table = "user_settings" 12 | 13 | settings = models.JSONField() 14 | -------------------------------------------------------------------------------- /db_functions/partitioned_manage_trigger.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | 6 | DROP TRIGGER IF EXISTS tr_partition_manager ON partitioned_tables; 7 | CREATE TRIGGER tr_partition_manager 8 | AFTER INSERT 9 | OR DELETE 10 | OR UPDATE 11 | ON partitioned_tables 12 | FOR EACH ROW EXECUTE FUNCTION public.trfn_partition_manager(); 13 | -------------------------------------------------------------------------------- /koku/reporting/currency/models.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Red Hat Inc. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | from django.db import models 5 | 6 | 7 | class CurrencySettings(models.Model): 8 | """A table that maps between account and currency: currency has many accounts.""" 9 | 10 | class Meta: 11 | db_table = "currency_settings" 12 | 13 | currency = models.TextField() 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | groups: 12 | ci-dependencies: 13 | patterns: 14 | - "*" # update all github-actions in single PR 15 | -------------------------------------------------------------------------------- /dev/scripts/cost_models/openshift_on_aws_cost_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cost Management OpenShift on AWS Cost Model", 3 | "description": "A cost model for markup on OpenShift on AWS costs.", 4 | "source_type": "OCP", 5 | "source_uuids": [ 6 | "PROVIDER_UUID" 7 | ], 8 | "rates": [], 9 | "markup": { 10 | "value": 10, 11 | "unit": "percent" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /db_functions/partitioned_tables_active_trigger.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | 6 | DROP TRIGGER IF EXISTS tr_attach_date_range_partition ON partitioned_tables; 7 | CREATE TRIGGER tr_attach_date_range_partition 8 | AFTER UPDATE OF active 9 | ON partitioned_tables 10 | FOR EACH ROW EXECUTE FUNCTION public.trfn_attach_date_range_partition(); 11 | -------------------------------------------------------------------------------- /koku/api/functions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """PostgreSQL DB functions for use by Django ORM.""" 6 | from django.db.models.aggregates import Func 7 | 8 | 9 | class JSONBObjectKeys(Func): 10 | """Helper to get json keys.""" 11 | 12 | function = "jsonb_object_keys" 13 | template = "%(function)s(%(expressions)s)" 14 | arity = 1 15 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>konflux-ci/mintmaker//config/renovate/renovate.json" 4 | ], 5 | "updateNotScheduled": false, 6 | "schedule": [ 7 | "on Tuesday after 3am and before 10am" 8 | ], 9 | "ignorePaths": [ 10 | "dev/containers/**", 11 | ".pre-commit-config.yaml", 12 | "Pipfile", 13 | "Pipfile.lock" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sleep 5 3 | python koku/manage.py migrate_schemas 4 | if [[ -z "$RUN_GUNICORN" ]]; then 5 | DJANGO_READ_DOT_ENV_FILE=True python koku/manage.py runserver 0.0.0.0:8000 6 | else 7 | cd ./koku 8 | PYTHONPATH="$(pwd):${PYTHONPATH}" 9 | DJANGO_READ_DOT_ENV_FILE=True gunicorn koku.wsgi --bind=0.0.0.0:8000 --access-logfile=- --error-logfile=- --config "gunicorn_conf.py" 10 | fi 11 | -------------------------------------------------------------------------------- /koku/api/migrations/0064_delete_dataexportrequest.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.15 on 2024-10-31 18:48 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("api", "0063_remove_infra_map_account_region"), 9 | ] 10 | 11 | operations = [ 12 | migrations.DeleteModel( 13 | name="DataExportRequest", 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /koku/api/migrations/0030_auto_20201007_1403.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2020-10-07 14:03 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("api", "0001_initial")] 9 | 10 | operations = [ 11 | migrations.AlterField(model_name="user", name="is_active", field=models.BooleanField(default=True, null=True)) 12 | ] 13 | -------------------------------------------------------------------------------- /koku/api/migrations/0045_update_django_migration_sequences.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.10 on 2021-05-13 21:31 2 | import logging 3 | 4 | from django.db import migrations 5 | 6 | 7 | LOG = logging.getLogger(__name__) 8 | 9 | 10 | def no_op(apps, schema_editor): 11 | pass 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0044_auto_20210505_1747")] 17 | 18 | operations = [migrations.RunPython(no_op)] 19 | -------------------------------------------------------------------------------- /db_functions/partitioned_tables_manage_trigger.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | 6 | DROP TRIGGER IF EXISTS tr_manage_date_range_partition ON partitioned_tables; 7 | CREATE TRIGGER tr_manage_date_range_partition 8 | AFTER INSERT 9 | OR DELETE 10 | OR UPDATE OF partition_parameters 11 | ON partitioned_tables 12 | FOR EACH ROW EXECUTE FUNCTION public.trfn_manage_date_range_partition(); 13 | -------------------------------------------------------------------------------- /koku/koku/metrics.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Prometheus metrics.""" 6 | from prometheus_client import CollectorRegistry 7 | from prometheus_client import Counter 8 | from prometheus_client import multiprocess 9 | 10 | REGISTRY = CollectorRegistry() 11 | multiprocess.MultiProcessCollector(REGISTRY) 12 | DB_CONNECTION_ERRORS_COUNTER = Counter("db_connection_errors", "Number of DB connection errors") 13 | -------------------------------------------------------------------------------- /koku/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Welcome to Hybrid Cloud Cost Management. 5 | Start gaining insights on your costs today. 6 |
7 |
8 | You user has been created with username {{username}}. 9 | A separate email will follow with the associated password. 10 |
11 | Point your browser to the following URL to login to the service. 12 | {{login_link}} 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/scripts/check_clowdapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | kustomize build ./deploy/kustomize > ./deploy/clowdapp.yaml 6 | 7 | changed=`git diff --name-only HEAD` 8 | 9 | if [[ $changed == *"clowdapp"* ]]; then 10 | echo "clowdapp.yaml cannot be directly modified. Update the base.yaml or the patch files and run 'make clowdapp'." 11 | exit_code=1 12 | else 13 | echo "clowdapp.yaml is up to date." 14 | exit_code=0 15 | fi 16 | 17 | exit $exit_code 18 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0301_create_ocppod_by_node_p.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2023-07-13 16:56 2 | import django.contrib.postgres.fields 3 | import django.db.models.deletion 4 | from django.db import migrations 5 | from django.db import models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ("reporting", "0300_squash"), 12 | ] 13 | 14 | # This migration is replaced by 0312 15 | operations = [] 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | ** 3 | 4 | # ... except for dependencies and source 5 | !Pipfile 6 | !Pipfile.lock 7 | !koku/ 8 | !docs/specs/ 9 | !db_functions/ 10 | !licenses/ 11 | !scripts/ 12 | 13 | # keep the test files out of the final image 14 | # WARNING - This will also remove any file that contains test in the name like latest. 15 | **/*test* 16 | **/__pycache__ 17 | **/*.log 18 | 19 | # remove any static files 20 | koku/static/** 21 | !koku/static/README 22 | -------------------------------------------------------------------------------- /docs/sources.md: -------------------------------------------------------------------------------- 1 | # Source Management 2 | 3 | This section covers managing sources. See the relevant document below for information on adding a specific source. 4 | 5 | [AWS](sources/aws.md) 6 | 7 | [Azure](sources/azure.md) 8 | 9 | [GCP](sources/gcp.md) 10 | 11 | [Local](sources/local.md) 12 | 13 | [OCP](sources/ocp.md) 14 | 15 | [OpenShift on AWS](sources/openshift_on_aws.md) 16 | 17 | 18 | ## Removing a Source 19 | 20 | A source can currently be removed by any user. 21 | -------------------------------------------------------------------------------- /.github/scripts/files_require_smokes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # this script will print all files that are copied into the docker image. 4 | 5 | cat < Dockerfile.build-context 6 | FROM busybox 7 | COPY . /build-context 8 | WORKDIR /build-context 9 | CMD find . -type f -mtime -14 | sed 's|^./||' 10 | EOF 11 | 12 | docker build -q -f Dockerfile.build-context -t build-context . 13 | docker run --rm build-context 14 | 15 | # cleanup the build-context file 16 | rm Dockerfile.build-context 17 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0326_delete_awscostentrylineitemsummarybyec2compute.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.14 on 2024-09-24 09:11 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0325_awscostentrylineitemsummarybyec2computep"), 9 | ] 10 | 11 | operations = [ 12 | migrations.DeleteModel( 13 | name="AWSCostEntryLineItemSummaryByEC2Compute", 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0316_remove_openshiftcostcategory_namespace.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.22 on 2023-12-04 20:26 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0315_openshiftcostcategorynamespace"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="openshiftcostcategory", 14 | name="namespace", 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Jira Ticket 2 | 3 | [COST-####](https://issues.redhat.com/browse/COST-####) 4 | 5 | ## Description 6 | 7 | This change will ... 8 | 9 | ## Testing 10 | 11 | 1. Checkout Branch 12 | 2. Restart Koku 13 | 3. Hit endpoint or launch shell 14 | 1. You should see ... 15 | 4. Do more things... 16 | 17 | ## Release Notes 18 | - [ ] proposed release note 19 | 20 | ```markdown 21 | * [COST-####](https://issues.redhat.com/browse/COST-####) Fix some things 22 | ``` 23 | -------------------------------------------------------------------------------- /db_functions/array_subtract_func.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | create or replace function public.array_subtract( 6 | minuend anyarray, subtrahend anyarray, out difference anyarray 7 | ) 8 | returns anyarray as 9 | $$ 10 | begin 11 | execute 'select array(select unnest($1) except select unnest($2))' 12 | using minuend, subtrahend 13 | into difference; 14 | end; 15 | $$ language plpgsql returns null on null input; 16 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0032_costusagereportmanifest_last_reports.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | from django.db import models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [("reporting_common", "0031_costusagereportmanifest_export_time")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="costusagereportmanifest", name="last_reports", field=models.JSONField(default=dict, null=True) 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0030_costusagereportmanifest_cluster_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-08-04 19:24 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("reporting_common", "0029_costusagereportmanifest_operator_info")] 9 | 10 | operations = [ 11 | migrations.AddField(model_name="costusagereportmanifest", name="cluster_id", field=models.TextField(null=True)) 12 | ] 13 | -------------------------------------------------------------------------------- /koku/api/migrations/0055_install_pg_stat_statements.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-22 14:33 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [("api", "0054_adding_oci_provider")] 8 | 9 | operations = [ 10 | migrations.RunSQL( 11 | sql="create extension if not exists pg_stat_statements schema public;", 12 | reverse_sql="drop extension if exists pg_stat_statements", 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /koku/masu/exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Masu Processor Exceptions.""" 6 | 7 | 8 | class MasuProcessingError(Exception): 9 | """Masu Processing Error.""" 10 | 11 | 12 | class MasuProviderError(Exception): 13 | """Masu Provider Error.""" 14 | 15 | 16 | class MasuConfigurationError(Exception): 17 | """Masu Configuration Error.""" 18 | 19 | 20 | class HasherError(Exception): 21 | """Hasher Utility class error.""" 22 | -------------------------------------------------------------------------------- /dev/scripts/check_postgres_running.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Check that postgres is available for Koku use. 3 | # 4 | # Assumes environment variables have been set: 5 | # - POSTGRES_SQL_SERVICE_PORT 6 | # - POSTGRES_SQL_SERVICE_HOST 7 | 8 | export PGPORT="${POSTGRES_SQL_SERVICE_PORT}" 9 | export PGHOST="${POSTGRES_SQL_SERVICE_HOST}" 10 | 11 | pg_isready || { 12 | PG_ISREADY_CODE=$? 13 | echo "CRITICAL: postgres is not running or environment is misconfigured" 14 | exit ${PG_ISREADY_CODE} 15 | } 16 | -------------------------------------------------------------------------------- /koku/hcs/exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """HCS Processor Exceptions.""" 6 | 7 | 8 | class HCSTableNotFoundError(Exception): 9 | """HCS table not found error.""" 10 | 11 | def __init__(self, table_name, message="table not found"): 12 | self.table_name = table_name 13 | self.message = message 14 | super().__init__(self.message) 15 | 16 | def __str__(self): 17 | return f"{self.table_name} {self.message}" 18 | -------------------------------------------------------------------------------- /koku/masu/test/external/downloader/azure/fixtures/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "2021-01-01", 3 | "dataFormat": "csv", 4 | "blobCount": 1, 5 | "byteCount": 423869, 6 | "dataRowCount": 490, 7 | "blobs": [ 8 | { 9 | "blobName": "cost/partitioned/20230101-20230131/202301041442/73746dc5-5dcc-4b83-b340-bb1bd2b1ca30/000001.csv", 10 | "byteCount": 423869, 11 | "dataRowCount": 490, 12 | "headerRowCount": 1 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /dev/scripts/common/utils.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | check_vars() { 4 | # Variable validation. 5 | # 6 | # Args: ($@) - 1 or many variable(s) to check 7 | # 8 | local _var_names=("$@") 9 | 10 | for var_name in "${_var_names[@]}"; do 11 | if [ -z "${!var_name}" ];then 12 | log-err "Environment variable $var_name is not set! Unable to continue." 13 | exit 1 14 | else 15 | log-debug "${var_name}=`echo ${!var_name}`" 16 | fi 17 | done 18 | } 19 | -------------------------------------------------------------------------------- /koku/hcs/database/sql/reporting_gcp_hcs_daily_summary.sql: -------------------------------------------------------------------------------- 1 | SELECT *, {{ebs_acct_num}} as ebs_account_id, {{org_id}} as org_id 2 | FROM hive.{{schema | sqlsafe}}.{{table | sqlsafe}} 3 | WHERE ( sku_description LIKE '%RedHat%' 4 | OR sku_description LIKE '%Red Hat%' 5 | OR service_description LIKE '%Red Hat%') 6 | AND source = {{provider_uuid | string}} 7 | AND year = {{year}} 8 | AND month = {{month}} 9 | AND usage_start_time >= {{date}} 10 | AND usage_start_time < date_add('day', 1, {{date}}) 11 | -------------------------------------------------------------------------------- /koku/cost_models/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Describes the urls and patterns for the API application.""" 6 | from django.conf.urls import include 7 | from django.urls import path 8 | from rest_framework.routers import DefaultRouter 9 | 10 | from cost_models.views import CostModelViewSet 11 | 12 | ROUTER = DefaultRouter() 13 | ROUTER.register(r"cost-models", CostModelViewSet, basename="cost-models") 14 | 15 | urlpatterns = [path("", include(ROUTER.urls))] 16 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0028_costusagereportmanifest_operator_version.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-06-30 14:05 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("reporting_common", "0027_auto_20210412_1731")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="costusagereportmanifest", name="operator_version", field=models.TextField(null=True) 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /koku/api/models.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """API models for import organization.""" 6 | # flake8: noqa 7 | from api.currency.models import ExchangeRates 8 | from api.iam.models import Customer 9 | from api.iam.models import Tenant 10 | from api.iam.models import User 11 | from api.provider.models import Provider 12 | from api.provider.models import ProviderAuthentication 13 | from api.provider.models import ProviderBillingSource 14 | from api.status.models import Status 15 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0031_costusagereportmanifest_export_time.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2021-09-22 20:38 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("reporting_common", "0030_costusagereportmanifest_cluster_id")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="costusagereportmanifest", name="export_time", field=models.DateTimeField(null=True) 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /koku/api/migrations/0061_alter_providerinfrastructuremap_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.22 on 2023-12-05 14:38 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("api", "0060_provider_polling_timestamp"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterUniqueTogether( 13 | name="providerinfrastructuremap", 14 | unique_together={("infrastructure_type", "infrastructure_provider")}, 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /koku/api/tags/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Tags module.""" 6 | from .all.openshift.queries import OCPAllTagQueryHandler # noqa: F401 7 | from .aws.openshift.queries import OCPAWSTagQueryHandler # noqa: F401 8 | from .aws.queries import AWSTagQueryHandler # noqa: F401 9 | from .azure.openshift.queries import OCPAzureTagQueryHandler # noqa: F401 10 | from .azure.queries import AzureTagQueryHandler # noqa: F401 11 | from .ocp.queries import OCPTagQueryHandler # noqa: F401 12 | -------------------------------------------------------------------------------- /koku/masu/database/sql/openshift/cost_model/delete_monthly_cost.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM {{schema | sqlsafe}}.reporting_ocpusagelineitem_daily_summary AS lids 2 | WHERE lids.report_period_id = {{report_period_id}} 3 | AND lids.usage_start >= {{start_date}}::date 4 | AND lids.usage_start <= {{end_date}}::date 5 | {%- if rate_type is defined %} 6 | AND lids.cost_model_rate_type = {{rate_type}} 7 | {%- else %} 8 | AND lids.cost_model_rate_type IS NOT NULL 9 | {%- endif %} 10 | AND lids.monthly_cost_type = {{cost_type}} 11 | ; 12 | -------------------------------------------------------------------------------- /koku/api/migrations/0049_auto_20210818_2208.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2021-08-18 22:08 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("api", "0048_new_partition_manager_func")] 9 | 10 | operations = [ 11 | migrations.AddField(model_name="provider", name="paused", field=models.BooleanField(default=False)), 12 | migrations.AddField(model_name="sources", name="paused", field=models.BooleanField(default=False)), 13 | ] 14 | -------------------------------------------------------------------------------- /koku/koku/wsgi.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """ 6 | WSGI config for koku project. 7 | 8 | It exposes the WSGI callable as a module-level variable named ``application``. 9 | 10 | For more information on this file, see 11 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 12 | """ 13 | import os 14 | 15 | from django.core.wsgi import get_wsgi_application 16 | 17 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "koku.settings") 18 | 19 | application = get_wsgi_application() 20 | -------------------------------------------------------------------------------- /koku/subs/trino_sql/aws/determine_ids_for_provider.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | DISTINCT lineitem_usageaccountid 3 | FROM hive.{{schema | sqlsafe}}.aws_line_items 4 | WHERE source={{source_uuid | string}} 5 | AND year={{year}} 6 | AND month={{month}} 7 | AND lineitem_productcode = 'AmazonEC2' 8 | AND strpos(lower(resourcetags), 'com_redhat_rhel') > 0 9 | AND lineitem_usageaccountid NOT IN ( 10 | SELECT 11 | DISTINCT usage_id 12 | FROM postgres.{{schema | sqlsafe}}.reporting_subs_id_map 13 | WHERE source_uuid!={{source_uuid}} 14 | ) 15 | -------------------------------------------------------------------------------- /koku/api/migrations/0044_auto_20210505_1747.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-05-05 17:47 2 | from django.db import migrations 3 | 4 | from koku.database import install_migrations_dbfunc 5 | 6 | 7 | def reapply_app_needs_migrations_func(apps, schema_editor): 8 | conn = schema_editor.connection 9 | install_migrations_dbfunc(conn) 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [("api", "0043_apply_turbo_schema_clone_func")] 15 | 16 | operations = [migrations.RunPython(code=reapply_app_needs_migrations_func)] 17 | -------------------------------------------------------------------------------- /koku/api/migrations/0060_provider_polling_timestamp.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-07-06 08:30 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("api", "0059_alter_tenant_schema"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="provider", 15 | name="polling_timestamp", 16 | field=models.DateTimeField(blank=True, default=None, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/cost_models/migrations/0004_auto_20210903_1559.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2021-09-03 15:59 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("cost_models", "0003_auto_20210615_2011")] 9 | 10 | operations = [ 11 | migrations.AddField(model_name="costmodel", name="currency", field=models.TextField(default="USD")), 12 | migrations.AddField(model_name="costmodelaudit", name="currency", field=models.TextField(default="USD")), 13 | ] 14 | -------------------------------------------------------------------------------- /db_functions/presto_delete_trigger_func.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | create or replace function public.tr_presto_delete_wrapper_log_action() returns trigger as $$ 6 | begin 7 | if NEW.result_rows is null 8 | then 9 | execute 'delete from ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(NEW.table_name) || ' ' || 10 | NEW.where_clause; 11 | get diagnostics NEW.result_rows = row_count; 12 | end if; 13 | 14 | return NEW; 15 | end; 16 | $$ language plpgsql; 17 | -------------------------------------------------------------------------------- /koku/api/migrations/0036_reapply_check_migrations_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-02-18 22:34 2 | from django.db import migrations 3 | 4 | from koku.database import install_migrations_dbfunc 5 | 6 | 7 | def reapply_app_needs_migrations_func(apps, schema_editor): 8 | conn = schema_editor.connection 9 | install_migrations_dbfunc(conn) 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [("api", "0035_reapply_partition_and_clone_func")] 15 | 16 | operations = [migrations.RunPython(code=reapply_app_needs_migrations_func)] 17 | -------------------------------------------------------------------------------- /koku/api/common/csv.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """API views for CSV output.""" 6 | from rest_framework_csv.renderers import PaginatedCSVRenderer 7 | 8 | 9 | class PaginatedCSVRenderer(PaginatedCSVRenderer): 10 | """ 11 | A Paginated CSV Renderer. 12 | 13 | To be used with views that paginate data. 14 | """ 15 | 16 | results_field = "data" 17 | 18 | def render(self, data, *args, **kwargs): 19 | """Render a paginated CSV.""" 20 | 21 | return super().render(data, *args, **kwargs) 22 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0338_ocpnode_architecture.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.24 on 2025-09-29 19:34 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0337_remove_ocpcostsummarybyprojectp_infrastructure_project_markup_cost_and_more"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ocpnode", 15 | name="architecture", 16 | field=models.TextField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0324_ocppvc_csi_volume_handle.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-05-16 18:41 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0323_remove_awscostentrylineitemsummarybyec2compute_availability_zone_and_org_unit"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ocppvc", 15 | name="csi_volume_handle", 16 | field=models.TextField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0031_clone_schema.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2020-10-14 12:24 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_clone_schema(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | msh.apply_sql_file(schema_editor, os.path.join(path, "clone_schema.sql"), literal_placeholder=True) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0030_auto_20201007_1403")] 17 | 18 | operations = [migrations.RunPython(code=apply_clone_schema)] 19 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0334_subslastprocessed_event_sent.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-05-15 14:37 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0333_ocpcostsummarybyprojectp_infrastructure_markup_cost_and_more"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="subslastprocessed", 15 | name="latest_event_sent", 16 | field=models.DateTimeField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0042_alter_costusagereportmanifest_s3_parquet_cleared.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-06-28 12:00 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0041_diskcapacity"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="costusagereportmanifest", 15 | name="s3_parquet_cleared", 16 | field=models.BooleanField(default=False, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | ## User Story 8 | As X, I want Y so that Z. 9 | 10 | ## Impacts 11 | Ex. API, UI, Docs 12 | 13 | ## Role 14 | 15 | 16 | ## Assumptions 17 | - 18 | - 19 | 20 | ## API Details 21 | 22 | | API | Detail| 23 | | --- | --- | 24 | | **Method** | GET/POST/PUT/PATCH/DELETE | 25 | | **Path** | /api/v1/?/ | 26 | | **Body** | | 27 | | **Response** | | 28 | 29 | ## UI Details 30 | 31 | 32 | ## Acceptance Criteria 33 | 34 | - [ ] 35 | - [ ] 36 | -------------------------------------------------------------------------------- /db_functions/jsonb_sha256_text.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2021 Red Hat Inc. 3 | -- SPDX-License-Identifier: Apache-2.0 4 | -- 5 | 6 | create or replace function public.jsonb_sha256_text(j_param jsonb, out hash_val text) 7 | returns text 8 | as $$ 9 | begin 10 | select encode(sha256(decode(string_agg(key || ':' || value, '|'), 'escape')), 'hex') 11 | from ( 12 | select * 13 | from jsonb_each_text(j_param) 14 | order by key, value 15 | ) as ordered_jsonb 16 | into hash_val; 17 | end; 18 | $$ language plpgsql returns null on null input 19 | immutable; 20 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0033_costusagereportmanifest_reports_tracker.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-13 19:15 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0032_costusagereportmanifest_last_reports"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="costusagereportmanifest", 15 | name="report_tracker", 16 | field=models.JSONField(default=dict, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/forecast/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Forecasting Module.""" 6 | from .forecast import AWSForecast # noqa: F401 7 | from .forecast import AzureForecast # noqa: F401 8 | from .forecast import Forecast # noqa: F401 9 | from .forecast import GCPForecast # noqa: F401 10 | from .forecast import OCPAllForecast # noqa: F401 11 | from .forecast import OCPAWSForecast # noqa: F401 12 | from .forecast import OCPAzureForecast # noqa: F401 13 | from .forecast import OCPForecast # noqa: F401 14 | from .forecast import OCPGCPForecast # noqa: F401 15 | -------------------------------------------------------------------------------- /.github/scripts/check_migrations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | OUTPUT=$(pipenv run make make-migrations 2>&1 > /dev/null) 5 | if echo "$OUTPUT" | grep -q "ImportError: Couldn't import Django"; then 6 | echo "Django is not installed or the virtual environment isn't activated. Maybe clear github-action cache?" 7 | exit 2 8 | fi 9 | 10 | changed=`git add -N . && git diff --name-only HEAD` 11 | 12 | if [[ $changed == *"migration"* ]]; then 13 | echo "Migrations are out of sync with the models. Run 'make make-migrations' to update." 14 | exit 1 15 | else 16 | echo "Migrations are in sync." 17 | exit 0 18 | fi 19 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/catalog/hive.properties: -------------------------------------------------------------------------------- 1 | connector.name=hive 2 | hive.auto-purge=true 3 | hive.collect-column-statistics-on-write=true 4 | hive.compression-codec=SNAPPY 5 | hive.metastore.authentication.type=NONE 6 | hive.metastore.uri=thrift://hive-metastore:8000 7 | hive.parquet.use-column-names=true 8 | hive.recursive-directories=true 9 | hive.storage-format=Parquet 10 | 11 | fs.native-s3.enabled=true 12 | 13 | s3.aws-access-key=${ENV:S3_ACCESS_KEY} 14 | s3.aws-secret-key=${ENV:S3_SECRET} 15 | s3.endpoint=${ENV:MINIO_ENDPOINT} 16 | s3.path-style-access=true 17 | s3.region=${ENV:AWS_REGION} 18 | s3.sse.type=None 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0032_presto_delete_log_trigger_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2020-10-30 19:22 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_presto_del_trigger_func(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | msh.apply_sql_file(schema_editor, os.path.join(path, "presto_delete_trigger_func.sql")) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0031_clone_schema")] 17 | 18 | operations = [migrations.RunPython(code=apply_presto_del_trigger_func)] 19 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0339_ocpusagelineitemdailysummary_cost_model_gpu_cost.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.25 on 2025-11-10 19:54 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0338_ocpnode_architecture"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ocpusagelineitemdailysummary", 15 | name="cost_model_gpu_cost", 16 | field=models.DecimalField(decimal_places=15, max_digits=33, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0042_reapply_clone_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-12-16 21:27 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_public_clone_func_update(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | msh.apply_sql_file(schema_editor, os.path.join(path, "clone_schema.sql"), literal_placeholder=True) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0041_array_subtract_dbfunc")] 17 | 18 | operations = [migrations.RunPython(code=apply_public_clone_func_update)] 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0038_drop_app_needs_migrations_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-04 17:19 2 | from django.db import migrations 3 | 4 | from koku.database import install_migrations_dbfunc 5 | 6 | 7 | def drop_old_app_needs_migrations_func(apps, schema_editor): 8 | conn = schema_editor.connection 9 | cur = conn.cursor() 10 | cur.execute("""drop function if exists app_needs_migrations(jsonb, boolean);""") 11 | 12 | 13 | class Migration(migrations.Migration): 14 | 15 | dependencies = [("api", "0037_auto_20210223_2136")] 16 | 17 | operations = [migrations.RunPython(code=drop_old_app_needs_migrations_func)] 18 | -------------------------------------------------------------------------------- /koku/api/migrations/0043_apply_turbo_schema_clone_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-04-24 00:08 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_public_clone_func_update(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | msh.apply_sql_file(schema_editor, os.path.join(path, "clone_schema.sql"), literal_placeholder=True) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0042_reapply_clone_func")] 17 | 18 | operations = [migrations.RunPython(code=apply_public_clone_func_update)] 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0053_additional_context.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.14 on 2022-02-07 19:18 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("api", "0052_sources_provider")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="provider", name="additional_context", field=models.JSONField(default=dict, null=True) 13 | ), 14 | migrations.AddField( 15 | model_name="sources", name="additional_context", field=models.JSONField(default=dict, null=True) 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /koku/api/migrations/0063_remove_infra_map_account_region.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-04-30 14:10 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("api", "0062_add_infra_map_account_region"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="providerinfrastructuremap", 14 | name="infrastructure_account", 15 | ), 16 | migrations.RemoveField( 17 | model_name="providerinfrastructuremap", 18 | name="infrastructure_region", 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0322_alter_awscostentrylineitemsummarybyec2compute_instance_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-05-21 16:05 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0321_awscostentrylineitemsummarybyec2compute"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="awscostentrylineitemsummarybyec2compute", 15 | name="instance_name", 16 | field=models.CharField(max_length=256, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /koku/api/migrations/0037_auto_20210223_2136.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-02-23 21:36 2 | import django.core.validators 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("api", "0036_reapply_check_migrations_func")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="sources", 14 | name="name", 15 | field=models.TextField( 16 | max_length=256, null=True, validators=[django.core.validators.MaxLengthValidator(256)] 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | strict_yaml_branch: main # only use the latest copy on main branch 5 | 6 | coverage: 7 | precision: 1 8 | round: nearest 9 | range: "80...100" 10 | 11 | status: 12 | project: 13 | default: 14 | target: 90 15 | patch: 16 | default: 17 | target: 90 18 | threshold: 5 19 | changes: no 20 | 21 | parsers: 22 | gcov: 23 | branch_detection: 24 | conditional: yes 25 | loop: yes 26 | method: no 27 | macro: no 28 | 29 | comment: 30 | layout: "header, diff" 31 | behavior: default 32 | require_changes: no 33 | -------------------------------------------------------------------------------- /koku/masu/management/commands/check_migrations.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | from django.core.management.base import BaseCommand 6 | 7 | from koku.database import check_migrations 8 | 9 | 10 | class Command(BaseCommand): 11 | help = "Check if migrations need to be run" 12 | 13 | def handle(self, *args, **options): 14 | """Run our database check_migratons function.""" 15 | 16 | self.stdout.write(str(check_migrations())) 17 | 18 | # if check_migrations(): 19 | # self.stdout.write("True") 20 | # else: 21 | # self.stdout.write("False") 22 | -------------------------------------------------------------------------------- /koku/api/migrations/0041_array_subtract_dbfunc.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-29 15:57 2 | import os 3 | import sys 4 | 5 | from django.db import migrations 6 | 7 | from koku import migration_sql_helpers as msh 8 | 9 | 10 | def apply_public_function_updates(apps, schema_editor): 11 | path = msh.find_db_functions_dir() 12 | for funcfile in ("array_subtract_func.sql",): 13 | msh.apply_sql_file(schema_editor, os.path.join(path, funcfile)) 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [("api", "0040_auto_20210318_1514")] 19 | 20 | operations = [migrations.RunPython(code=apply_public_function_updates)] 21 | -------------------------------------------------------------------------------- /koku/masu/database/sql/openshift/create_virtualization_tmp_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS {{schema | sqlsafe}}.tmp_virt_{{uuid | sqlsafe}} ( 2 | vm_name TEXT, 3 | node TEXT, 4 | pvc_name TEXT, 5 | cpu_request FLOAT, 6 | mem_request FLOAT 7 | ); 8 | 9 | CREATE INDEX IF NOT EXISTS idx_tmp_virt_vm_name_{{uuid | sqlsafe}} 10 | ON {{schema | sqlsafe}}.tmp_virt_{{uuid | sqlsafe}} (vm_name); 11 | CREATE INDEX IF NOT EXISTS idx_tmp_virt_pvc_name_{{uuid | sqlsafe}} 12 | ON {{schema | sqlsafe}}.tmp_virt_{{uuid | sqlsafe}} (pvc_name); 13 | CREATE INDEX IF NOT EXISTS idx_tmp_virt_node_{{uuid | sqlsafe}} 14 | ON {{schema | sqlsafe}}.tmp_virt_{{uuid | sqlsafe}} (node); 15 | -------------------------------------------------------------------------------- /koku/api/migrations/0046_jsonb_sha256_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.10 on 2021-05-14 17:53 2 | import os 3 | import sys 4 | 5 | from django.db import migrations 6 | 7 | from koku import migration_sql_helpers as msh 8 | 9 | 10 | def apply_public_function_updates(apps, schema_editor): 11 | path = msh.find_db_functions_dir() 12 | for funcfile in ("jsonb_sha256_text.sql",): 13 | msh.apply_sql_file(schema_editor, os.path.join(path, funcfile)) 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [("api", "0045_update_django_migration_sequences")] 19 | 20 | operations = [migrations.RunPython(code=apply_public_function_updates)] 21 | -------------------------------------------------------------------------------- /koku/api/resource_types/aws_category/serializers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2023 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Serializer for aws category resource types.""" 6 | from rest_framework import serializers 7 | 8 | 9 | class AWSCategorySerializer(serializers.Serializer): 10 | """Serializer for resource-specific resource-type APIs.""" 11 | 12 | key = serializers.CharField() 13 | values = serializers.ListField() 14 | enabled = serializers.CharField() 15 | 16 | 17 | class AWSCategoryKeyOnlySerializer(serializers.BaseSerializer): 18 | child = serializers.CharField() 19 | 20 | def to_representation(self, data): 21 | return data 22 | -------------------------------------------------------------------------------- /koku/api/migrations/0056_reapply_clone_schema_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-05 18:05 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_public_clone_func_update(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | msh.apply_sql_file(schema_editor, os.path.join(path, "clone_schema.sql"), literal_placeholder=True) 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [("api", "0055_install_pg_stat_statements")] 17 | 18 | operations = [migrations.RunPython(code=apply_public_clone_func_update, reverse_code=migrations.RunPython.noop)] 19 | -------------------------------------------------------------------------------- /koku/subs/trino_sql/azure/determine_ids_for_provider.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | DISTINCT COALESCE(NULLIF(subscriptionid, ''), subscriptionguid) 3 | FROM hive.{{schema | sqlsafe}}.azure_line_items 4 | WHERE source={{source_uuid | string}} 5 | AND year={{year}} 6 | AND month={{month}} 7 | AND metercategory = 'Virtual Machines' 8 | AND json_extract_scalar(lower(additionalinfo), '$.vcpus') IS NOT NULL 9 | AND json_extract_scalar(lower(tags), '$.com_redhat_rhel') IS NOT NULL 10 | AND COALESCE(NULLIF(subscriptionid, ''), subscriptionguid) NOT IN ( 11 | SELECT 12 | DISTINCT usage_id 13 | FROM postgres.{{schema | sqlsafe}}.reporting_subs_id_map 14 | WHERE source_uuid!={{source_uuid}} 15 | ) 16 | -------------------------------------------------------------------------------- /koku/api/tags/aws/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for AWS tags.""" 6 | from api.common.permissions.aws_access import AwsAccessPermission 7 | from api.provider.models import Provider 8 | from api.tags.aws.queries import AWSTagQueryHandler 9 | from api.tags.aws.serializers import AWSTagsQueryParamSerializer 10 | from api.tags.view import TagView 11 | 12 | 13 | class AWSTagView(TagView): 14 | """Get AWS tags.""" 15 | 16 | provider = "aws" 17 | serializer = AWSTagsQueryParamSerializer 18 | query_handler = AWSTagQueryHandler 19 | tag_providers = [Provider.PROVIDER_AWS] 20 | permission_classes = [AwsAccessPermission] 21 | -------------------------------------------------------------------------------- /koku/api/tags/gcp/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for GCP tags.""" 6 | from api.common.permissions.gcp_access import GcpAccessPermission 7 | from api.provider.models import Provider 8 | from api.tags.gcp.queries import GCPTagQueryHandler 9 | from api.tags.gcp.serializers import GCPTagsQueryParamSerializer 10 | from api.tags.view import TagView 11 | 12 | 13 | class GCPTagView(TagView): 14 | """Get GCP tags.""" 15 | 16 | provider = "gcp" 17 | serializer = GCPTagsQueryParamSerializer 18 | query_handler = GCPTagQueryHandler 19 | tag_providers = [Provider.PROVIDER_GCP] 20 | permission_classes = [GcpAccessPermission] 21 | -------------------------------------------------------------------------------- /koku/api/migrations/0057_add_org_ids.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-06-01 14:06 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("api", "0056_reapply_clone_schema_func"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="customer", 15 | name="org_id", 16 | field=models.CharField(max_length=36, null=True, unique=True), 17 | ), 18 | migrations.AddField( 19 | model_name="sources", 20 | name="org_id", 21 | field=models.TextField(null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0337_remove_ocpcostsummarybyprojectp_infrastructure_project_markup_cost_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.23 on 2025-09-12 13:08 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0336_alter_enabledtagkeys_provider_type"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="ocpcostsummarybyprojectp", 14 | name="infrastructure_project_markup_cost", 15 | ), 16 | migrations.RemoveField( 17 | model_name="ocpcostsummarybyprojectp", 18 | name="infrastructure_project_raw_cost", 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /koku/sources/api/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Describes the urls and patterns for the API application.""" 6 | from django.conf.urls import include 7 | from django.urls import path 8 | from rest_framework.routers import DefaultRouter 9 | 10 | from sources.api.status import get_status 11 | from sources.api.views import source_status 12 | from sources.api.views import SourcesViewSet 13 | 14 | ROUTER = DefaultRouter() 15 | ROUTER.register(r"sources", SourcesViewSet) 16 | 17 | urlpatterns = [ 18 | path("status/", get_status, name="server-status"), 19 | path("source-status/", source_status, name="source-status"), 20 | path("", include(ROUTER.urls)), 21 | ] 22 | -------------------------------------------------------------------------------- /koku/api/resource_types/serializers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Serializer for Resource Types.""" 6 | from rest_framework import serializers 7 | 8 | 9 | class ResourceTypeSerializer(serializers.Serializer): 10 | """Serializer for resource-specific resource-type APIs.""" 11 | 12 | extra_kwargs = {"test": {"error_messages": {"required": "Give yourself a username"}}} 13 | cluster_alias = serializers.CharField(source="ocp_cluster_alias", required=False) 14 | account_alias = serializers.CharField(source="alias", required=False) 15 | instance_name = serializers.CharField(source="ec2_instance_name", required=False) 16 | value = serializers.CharField() 17 | -------------------------------------------------------------------------------- /koku/api/tags/azure/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for AWS tags.""" 6 | from api.common.permissions.azure_access import AzureAccessPermission 7 | from api.provider.models import Provider 8 | from api.tags.azure.queries import AzureTagQueryHandler 9 | from api.tags.azure.serializers import AzureTagsQueryParamSerializer 10 | from api.tags.view import TagView 11 | 12 | 13 | class AzureTagView(TagView): 14 | """Get Azure tags.""" 15 | 16 | provider = "azure" 17 | serializer = AzureTagsQueryParamSerializer 18 | query_handler = AzureTagQueryHandler 19 | tag_providers = [Provider.PROVIDER_AZURE] 20 | permission_classes = [AzureAccessPermission] 21 | -------------------------------------------------------------------------------- /koku/api/tags/ocp/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for OpenShift tags.""" 6 | from api.common.permissions.openshift_access import OpenShiftAccessPermission 7 | from api.provider.models import Provider 8 | from api.tags.ocp.queries import OCPTagQueryHandler 9 | from api.tags.ocp.serializers import OCPTagsQueryParamSerializer 10 | from api.tags.view import TagView 11 | 12 | 13 | class OCPTagView(TagView): 14 | """Get OpenShift tags.""" 15 | 16 | provider = "ocp" 17 | serializer = OCPTagsQueryParamSerializer 18 | query_handler = OCPTagQueryHandler 19 | tag_providers = [Provider.PROVIDER_OCP] 20 | permission_classes = [OpenShiftAccessPermission] 21 | -------------------------------------------------------------------------------- /koku/masu/test/data/ocp/empty-file-payload/not-empty-file.csv: -------------------------------------------------------------------------------- 1 | report_period_start,report_period_end,pod,namespace,node,resource_id,interval_start,interval_end,pod_usage_cpu_core_seconds,pod_request_cpu_core_seconds,pod_limit_cpu_core_seconds,pod_usage_memory_byte_seconds,pod_request_memory_byte_seconds,pod_limit_memory_byte_seconds,node_capacity_cpu_cores,node_capacity_cpu_core_seconds,node_capacity_memory_bytes,node_capacity_memory_byte_seconds,pod_labels 2 | 2019-02-01 00:00:00 +0000 UTC,2019-03-01 00:00:00 +0000 UTC,pod_name1,namespace_ci,alpha,myresourceid,2019-02-01 00:00:00 +0000 UTC,2019-02-01 01:00:00 +0000 UTC,1172.8799999999999,18000,18000,482946476940.0,7200,7200,2,7200,4194304,15099494400,label_key1:label_value1|label_key2:label_value2 3 | -------------------------------------------------------------------------------- /.sourcery.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - .git 3 | - venv 4 | - .venv 5 | - env 6 | - .env 7 | - .tox 8 | - node_modules 9 | - vendor 10 | 11 | rule_settings: 12 | enable: [default] 13 | disable: 14 | - no-loop-in-tests 15 | rule_types: 16 | - refactoring 17 | - suggestion 18 | - comment 19 | python_version: '3.11' 20 | 21 | rules: [] 22 | 23 | metrics: 24 | quality_threshold: 25.0 25 | 26 | github: 27 | labels: [] 28 | ignore_labels: 29 | - sourcery-ignore 30 | request_review: author 31 | sourcery_branch: sourcery/{base_branch} 32 | 33 | clone_detection: 34 | min_lines: 3 35 | min_duplicates: 2 36 | identical_clones_only: false 37 | 38 | proxy: 39 | no_ssl_verify: false 40 | -------------------------------------------------------------------------------- /koku/masu/management/commands/migrate.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """command overrides.""" 6 | from django_tenants.management.commands import migrate 7 | from django_tenants.utils import django_is_in_test_mode 8 | 9 | from .migrate_schemas import Command as MigrateSchemasCommand 10 | 11 | 12 | class Command(migrate.Command): 13 | """Override the migrate command from django-tenant-schemas. 14 | 15 | This override is here to workaround a dead upstream. 16 | This enables django-tenant_schemas to work with Django 3.1.x 17 | """ 18 | 19 | requires_system_checks = [] 20 | 21 | 22 | if django_is_in_test_mode(): 23 | Command = MigrateSchemasCommand # noqa: F811 24 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0309_auto_20231004_1009.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.21 on 2023-10-04 10:09 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0308_ocpusagelineitemdailysummary_all_labels"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="subslastprocessed", 15 | name="resource_id", 16 | field=models.TextField(default=""), 17 | ), 18 | migrations.AlterUniqueTogether( 19 | name="subslastprocessed", 20 | unique_together={("source_uuid", "resource_id", "year", "month")}, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /koku/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "koku.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | 16 | if "migrate_schemas" in sys.argv: 17 | import multiprocessing 18 | 19 | multiprocessing.set_start_method("fork") 20 | 21 | execute_from_command_line(sys.argv) 22 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0323_remove_awscostentrylineitemsummarybyec2compute_availability_zone_and_org_unit.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-05-20 13:56 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0322_alter_awscostentrylineitemsummarybyec2compute_instance_name"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="awscostentrylineitemsummarybyec2compute", 14 | name="availability_zone", 15 | ), 16 | migrations.RemoveField( 17 | model_name="awscostentrylineitemsummarybyec2compute", 18 | name="organizational_unit", 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /koku/cost_models/migrations/0007_net_stor_distribute_default.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.13 on 2024-06-05 14:10 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("cost_models", "0006_add_distribution_info"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RunSQL( 13 | sql="UPDATE cost_model SET distribution_info = to_jsonb(distribution_info) || jsonb '{\"network_unattributed\":true, \"storage_unattributed\":true}' WHERE source_type='OCP';", 14 | reverse_sql="UPDATE cost_model SET distribution_info = to_jsonb(distribution_info) - '{network_unattributed,storage_unattributed}'::text[] WHERE source_type='OCP';", 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /koku/masu/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Masu URL Configuration. 6 | 7 | The `urlpatterns` list routes URLs to views. For more information please see: 8 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 9 | """ 10 | from django.conf import settings 11 | from django.conf.urls import include 12 | from django.urls import path 13 | 14 | API_PATH_PREFIX = settings.API_PATH_PREFIX 15 | if API_PATH_PREFIX != "": 16 | if API_PATH_PREFIX.startswith("/"): 17 | API_PATH_PREFIX = API_PATH_PREFIX[1:] 18 | if not API_PATH_PREFIX.endswith("/"): 19 | API_PATH_PREFIX = API_PATH_PREFIX + "/" 20 | 21 | urlpatterns = [path(f"{API_PATH_PREFIX}v1/", include("masu.api.urls"))] 22 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0307_ingressreports_customer.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.21 on 2023-09-22 17:06 2 | import django.db.models.deletion 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("api", "0060_provider_polling_timestamp"), 11 | ("reporting", "0306_subsidmap"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="ingressreports", 17 | name="schema_name", 18 | field=models.ForeignKey( 19 | null=True, on_delete=django.db.models.deletion.PROTECT, to="api.customer", to_field="schema_name" 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /koku/api/migrations/0052_sources_provider.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2022-01-07 16:23 2 | import django.db.models.deletion 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("api", "0051_reapply_partition_trigger_func")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="sources", 14 | name="provider", 15 | field=models.ForeignKey( 16 | db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to="api.provider" 17 | ), 18 | ), 19 | migrations.RunSQL("UPDATE public.api_sources SET provider_id = koku_uuid::uuid"), 20 | ] 21 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0027_auto_20210412_1731.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-04-12 17:31 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("reporting_common", "0001_initial")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="costusagereportmanifest", 13 | name="s3_csv_cleared", 14 | field=models.BooleanField(default=False, null=True), 15 | ), 16 | migrations.AddField( 17 | model_name="costusagereportmanifest", 18 | name="s3_parquet_cleared", 19 | field=models.BooleanField(default=False, null=True), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /dev/scripts/delete_cost_models.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | import requests 4 | 5 | if __name__ == "__main__": 6 | koku_host = environ.get("KOKU_API_HOSTNAME") 7 | koku_port = environ.get("KOKU_PORT") 8 | url = f"http://{koku_host}:{koku_port}/api/cost-management/v1/cost-models/" 9 | 10 | r = requests.get(url).json() 11 | cost_model_uuids = [] 12 | for model in r.get("data", []): 13 | cost_model_uuid = model.get("uuid") 14 | if cost_model_uuid: 15 | cost_model_uuids.append(cost_model_uuid) 16 | 17 | for cost_model_uuid in cost_model_uuids: 18 | delete_url = url + f"{cost_model_uuid}/" 19 | print(f"Calling {delete_url}") 20 | r = requests.delete(delete_url) 21 | print(r) 22 | -------------------------------------------------------------------------------- /dev/scripts/delete_test_sources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from os import environ 3 | 4 | import requests 5 | 6 | if __name__ == "__main__": 7 | koku_host = environ.get("KOKU_API_HOSTNAME") 8 | koku_port = environ.get("KOKU_PORT") 9 | url = f"http://{koku_host}:{koku_port}/api/cost-management/v1/sources/" 10 | 11 | r = requests.get(url, params={"limit": 1000}).json() 12 | source_uuids = [] 13 | for source in r.get("data", []): 14 | if source_uuid := source.get("uuid"): 15 | source_uuids.append(source_uuid) 16 | 17 | for source_uuid in source_uuids: 18 | delete_url = f"{url}{source_uuid}/" 19 | print(f"Calling DELETE {delete_url}") 20 | r = requests.delete(delete_url) 21 | print(r) 22 | -------------------------------------------------------------------------------- /koku/api/migrations/0033_sources_name_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-12-16 20:12 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("api", "0032_presto_delete_log_trigger_func")] 9 | 10 | operations = [ 11 | migrations.AlterField(model_name="sources", name="account_id", field=models.TextField(null=True)), 12 | migrations.AlterField(model_name="sources", name="koku_uuid", field=models.TextField(null=True, unique=True)), 13 | migrations.AlterField(model_name="sources", name="name", field=models.TextField(null=True)), 14 | migrations.AlterField(model_name="sources", name="source_type", field=models.TextField()), 15 | ] 16 | -------------------------------------------------------------------------------- /koku/sources/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Sources URL Configuration. 6 | 7 | The `urlpatterns` list routes URLs to views. For more information please see: 8 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 9 | """ 10 | from django.conf import settings 11 | from django.conf.urls import include 12 | from django.urls import path 13 | 14 | 15 | API_PATH_PREFIX = settings.API_PATH_PREFIX 16 | if API_PATH_PREFIX != "": 17 | if API_PATH_PREFIX.startswith("/"): 18 | API_PATH_PREFIX = API_PATH_PREFIX[1:] 19 | if not API_PATH_PREFIX.endswith("/"): 20 | API_PATH_PREFIX = API_PATH_PREFIX + "/" 21 | 22 | urlpatterns = [path(f"{API_PATH_PREFIX}v1/", include("sources.api.urls"))] 23 | -------------------------------------------------------------------------------- /koku/masu/external/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Initializer for external module.""" 6 | # Compression formats 7 | UNCOMPRESSED = "PLAIN" 8 | GZIP_COMPRESSED = "GZIP" 9 | 10 | # Valid AWS Regions from https://docs.aws.amazon.com/general/latest/gr/rande.html 11 | AWS_REGIONS = [ 12 | "us-east-2", 13 | "us-east-1", 14 | "us-west-1", 15 | "us-west-2", 16 | "ap-south-1", 17 | "ap-northeast-2", 18 | "ap-northeast-3", 19 | "ap-southeast-1", 20 | "ap-southeast-2", 21 | "ap-northeast-1", 22 | "ca-central-1", 23 | "cn-north-1", 24 | "cn-northwest-1", 25 | "eu-central-1", 26 | "eu-west-1", 27 | "eu-west-2", 28 | "eu-west-3", 29 | "sa-east-1", 30 | ] 31 | -------------------------------------------------------------------------------- /koku/api/organizations/aws/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """AWS Organization views.""" 6 | from api.common.permissions.aws_access import AWSOUAccessPermission 7 | from api.models import Provider 8 | from api.organizations.aws.queries import AWSOrgQueryHandler 9 | from api.organizations.serializers import AWSOrgQueryParamSerializer 10 | from api.organizations.view import OrganizationView 11 | 12 | 13 | class AWSOrgView(OrganizationView): 14 | """AWS Org Base View.""" 15 | 16 | provider = Provider.PROVIDER_AWS 17 | query_handler = AWSOrgQueryHandler 18 | serializer = AWSOrgQueryParamSerializer 19 | report = "organizations" 20 | tag_providers = [] 21 | permission_classes = [AWSOUAccessPermission] 22 | -------------------------------------------------------------------------------- /koku/subs/trino_sql/aws/determine_resource_ids_for_usage_account.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | lineitem_resourceid, 3 | max(lineitem_usagestartdate) 4 | FROM hive.{{schema | sqlsafe}}.aws_line_items 5 | WHERE source={{source_uuid | string}} 6 | AND year={{year}} 7 | AND month={{month}} 8 | AND lineitem_productcode = 'AmazonEC2' 9 | AND strpos(lower(resourcetags), 'com_redhat_rhel') > 0 10 | AND lineitem_usageaccountid = {{usage_account}} 11 | AND lineitem_resourceid NOT IN ( 12 | SELECT 13 | DISTINCT resource_id 14 | FROM postgres.{{schema | sqlsafe}}.reporting_subs_last_processed_time 15 | WHERE source_uuid != {{source_uuid}} 16 | AND year={{year}} 17 | AND month={{month}} 18 | ) 19 | GROUP BY lineitem_resourceid 20 | -------------------------------------------------------------------------------- /koku/cost_models/migrations/0003_auto_20210615_2011.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-06-15 20:11 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("cost_models", "0002_auto_20210318_1514")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="costmodel", 13 | name="distribution", 14 | field=models.TextField(choices=[("memory", "memory"), ("cpu", "cpu")], default="cpu"), 15 | ), 16 | migrations.AddField( 17 | model_name="costmodelaudit", 18 | name="distribution", 19 | field=models.TextField(choices=[("memory", "memory"), ("cpu", "cpu")], default="cpu"), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0313_remove_deprecated_tags.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-07-29 20:11 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ("reporting", "0312_create_ocppod_by_node_p"), 9 | ] 10 | 11 | operations = [ 12 | migrations.DeleteModel( 13 | name="AWSEnabledTagKeys", 14 | ), 15 | migrations.DeleteModel( 16 | name="AzureEnabledTagKeys", 17 | ), 18 | migrations.DeleteModel( 19 | name="GCPEnabledTagKeys", 20 | ), 21 | migrations.DeleteModel( 22 | name="OCIEnabledTagKeys", 23 | ), 24 | migrations.DeleteModel( 25 | name="OCPEnabledTagKeys", 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /koku/api/migrations/0062_add_infra_map_account_region.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.10 on 2024-02-21 19:12 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("api", "0061_alter_providerinfrastructuremap_unique_together"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="providerinfrastructuremap", 15 | name="infrastructure_account", 16 | field=models.CharField(max_length=50, null=True), 17 | ), 18 | migrations.AddField( 19 | model_name="providerinfrastructuremap", 20 | name="infrastructure_region", 21 | field=models.CharField(max_length=50, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /koku/koku/env.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Obtain project environment.""" 6 | import environ 7 | 8 | ROOT_DIR = environ.Path(__file__) - 3 9 | 10 | ENVIRONMENT = environ.Env() 11 | 12 | # .env file, should load only in development environment 13 | READ_DOT_ENV_FILE = ENVIRONMENT.bool("DJANGO_READ_DOT_ENV_FILE", default=False) 14 | 15 | if READ_DOT_ENV_FILE: 16 | # Operating System Environment variables have precedence over variables 17 | # defined in the .env file, that is to say variables from the .env files 18 | # will only be used if not defined as environment variables. 19 | ENV_FILE = str(ROOT_DIR.path(".env")) 20 | print(f"Loading : {ENV_FILE}") 21 | ENVIRONMENT.read_env(ENV_FILE) 22 | print("The .env file has been loaded.") 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Context (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser: [e.g. chrome, safari] 26 | - Commit Hash from status endpoint: 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /koku/cost_models/test/test_sql_parameters.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2025 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Test the Cost Model serializers.""" 6 | from pydantic import ValidationError 7 | 8 | from cost_models.sql_parameters import BaseCostModelParams 9 | from masu.test import MasuTestCase 10 | 11 | 12 | class CostModelSQLParameterTest(MasuTestCase): 13 | """Tests for Cost Model Manager.""" 14 | 15 | def test_invalid_dates(self): 16 | with self.assertRaises(ValidationError): 17 | BaseCostModelParams( 18 | schema=self.schema_name, 19 | start_date=self.dh.this_month_end.date(), 20 | end_date=self.dh.this_month_start.date(), 21 | source_uuid=self.ocp_provider_uuid, 22 | report_period_id=1, 23 | ) 24 | -------------------------------------------------------------------------------- /koku/koku/urls.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """koku URL Configuration. 6 | 7 | The `urlpatterns` list routes URLs to views. For more information please see: 8 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 9 | """ 10 | from django.conf import settings 11 | from django.conf.urls import include 12 | from django.urls import path 13 | 14 | API_PATH_PREFIX = settings.API_PATH_PREFIX 15 | if API_PATH_PREFIX != "": 16 | if API_PATH_PREFIX.startswith("/"): 17 | API_PATH_PREFIX = API_PATH_PREFIX[1:] 18 | if not API_PATH_PREFIX.endswith("/"): 19 | API_PATH_PREFIX = API_PATH_PREFIX + "/" 20 | 21 | urlpatterns = [ 22 | path(f"{API_PATH_PREFIX}v1/", include("api.urls")), 23 | path(f"{API_PATH_PREFIX}v1/", include("cost_models.urls")), 24 | ] 25 | -------------------------------------------------------------------------------- /koku/providers/gcp_local/provider.py: -------------------------------------------------------------------------------- 1 | """GCP provider implementation to be used by Koku.""" 2 | from rest_framework import serializers 3 | 4 | from ..gcp.provider import GCPProvider 5 | from api.common import error_obj 6 | from api.models import Provider 7 | 8 | 9 | class GCPLocalProvider(GCPProvider): 10 | """GCP local provider.""" 11 | 12 | def name(self): 13 | """Return name of the provider.""" 14 | return Provider.PROVIDER_GCP_LOCAL 15 | 16 | def cost_usage_source_is_reachable(self, credentials, data_source): 17 | """Verify that GCP local bucket name is given.""" 18 | if not data_source: 19 | key = "table_id" 20 | message = "Project id is a required parameter for GCP." 21 | raise serializers.ValidationError(error_obj(key, message)) 22 | return True 23 | -------------------------------------------------------------------------------- /koku/api/common/filters.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Common filters for the views.""" 6 | from functools import reduce 7 | from operator import and_ 8 | 9 | from django.db.models import Q 10 | from django_filters import CharFilter 11 | from django_filters.filters import BaseCSVFilter 12 | 13 | 14 | class CharListFilter(BaseCSVFilter, CharFilter): 15 | """Add query filter capability to provide an anded list of filter values.""" 16 | 17 | def filter(self, qs, value): 18 | """Filter to create a composite and filter of the value list.""" 19 | if not value: 20 | return qs 21 | value_list = ",".join(value).split(",") 22 | queries = [Q(**{self.lookup_expr: val}) for val in value_list] 23 | return qs.filter(reduce(and_, queries)) 24 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0035_alter_costusagereportmanifest_s3_parquet_cleared.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-07-27 18:07 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0034_costusagereportmanifest_operator_daily_files"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="costusagereportmanifest", 15 | name="s3_parquet_cleared", 16 | field=models.BooleanField(default=True, null=True), 17 | ), 18 | migrations.AddField( 19 | model_name="costusagereportmanifest", 20 | name="daily_archive_start_date", 21 | field=models.DateTimeField(null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /koku/api/migrations/0048_new_partition_manager_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.10 on 2021-05-17 19:48 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_public_func_updates(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | for funcfile in ( 12 | # trigger func is the same as original apply (interface not changed) but does not have the drop in the file. 13 | "partitioned_manager_trigger_function.sql", 14 | ): 15 | msh.apply_sql_file(schema_editor, os.path.join(path, funcfile), literal_placeholder=True) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [("api", "0047_update_django_migration_sequences")] 21 | 22 | operations = [migrations.RunPython(code=apply_public_func_updates)] 23 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0034_costusagereportmanifest_operator_daily_files.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2023-07-06 19:52 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0033_costusagereportmanifest_reports_tracker"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="costusagereportmanifest", 15 | name="operator_daily_reports", 16 | field=models.BooleanField(default=False, null=True), 17 | ), 18 | migrations.AddField( 19 | model_name="costusagereportmanifest", 20 | name="s3_parquet_cleared_tracker", 21 | field=models.JSONField(default=dict, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /koku/api/model_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Django model mixins and utilities.""" 6 | 7 | 8 | class RunTextFieldValidators: 9 | """ 10 | Mixin to run all field validators on a save method call 11 | This mixin should appear BEFORE Model. 12 | """ 13 | 14 | def save(self, *args, **kwargs): 15 | """ 16 | For all fields, run any default and specified validators before calling save 17 | """ 18 | for f in ( 19 | c for c in self._meta.get_fields() if hasattr(self, c.name) and c.get_internal_type() == "TextField" 20 | ): 21 | val = getattr(self, f.name) 22 | if val is not None: 23 | val = str(val) 24 | f.run_validators(val) 25 | 26 | super().save(*args, **kwargs) 27 | -------------------------------------------------------------------------------- /koku/api/tags/all/openshift/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for OCP-on-All tags.""" 6 | from api.common.permissions.openshift_all_access import OpenshiftAllAccessPermission 7 | from api.provider.models import Provider 8 | from api.tags.all.openshift.queries import OCPAllTagQueryHandler 9 | from api.tags.all.openshift.serializers import OCPAllTagsQueryParamSerializer 10 | from api.tags.view import TagView 11 | 12 | 13 | class OCPAllTagView(TagView): 14 | """Get OpenShift-on-All tags.""" 15 | 16 | provider = "ocp_all" 17 | serializer = OCPAllTagsQueryParamSerializer 18 | query_handler = OCPAllTagQueryHandler 19 | tag_providers = [Provider.PROVIDER_AWS, Provider.PROVIDER_AZURE, Provider.PROVIDER_GCP, Provider.PROVIDER_OCP] 20 | permission_classes = [OpenshiftAllAccessPermission] 21 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/catalog/glue.properties: -------------------------------------------------------------------------------- 1 | connector.name=hive 2 | hive.metastore=glue 3 | hive.auto-purge=true 4 | hive.collect-column-statistics-on-write=true 5 | hive.compression-codec=SNAPPY 6 | hive.non-managed-table-writes-enabled=true 7 | hive.recursive-directories=true 8 | hive.storage-format=Parquet 9 | 10 | fs.hadoop.enabled=false 11 | fs.native-s3.enabled=true 12 | s3.region=${ENV:AWS_REGION} 13 | s3.endpoint=${ENV:MINIO_ENDPOINT} 14 | s3.aws-access-key=${ENV:S3_ACCESS_KEY} 15 | s3.aws-secret-key=${ENV:S3_SECRET} 16 | s3.path-style-access=true 17 | 18 | hive.metastore.glue.default-warehouse-dir=${ENV:TRINO_S3A_OR_S3}://${ENV:S3_BUCKET_NAME}/data 19 | hive.metastore.glue.region=${ENV:AWS_REGION} 20 | hive.metastore.glue.aws-access-key=${ENV:AWS_ACCESS_KEY_ID} 21 | hive.metastore.glue.aws-secret-key=${ENV:AWS_SECRET_ACCESS_KEY} 22 | hive.metastore.glue.skip-archive=true 23 | -------------------------------------------------------------------------------- /koku/api/common/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | from uuid import UUID 3 | 4 | from django.utils.translation import gettext 5 | 6 | RH_IDENTITY_HEADER = "HTTP_X_RH_IDENTITY" 7 | 8 | # Django will add the HTTP automatically when checking headers 9 | CACHE_RH_IDENTITY_HEADER = "X_RH_IDENTITY" 10 | 11 | 12 | def error_obj(key, message): 13 | """Create an error object.""" 14 | return {key: [gettext(message)]} 15 | 16 | 17 | def log_json(tracing_id="", *, msg, context=None, **kwargs): 18 | """Create JSON object for logging data.""" 19 | stmt = {"message": msg, "tracing_id": tracing_id} 20 | if context: 21 | stmt |= context 22 | stmt |= kwargs 23 | for key, value in stmt.items(): 24 | if key == "split_files": 25 | stmt[key] = len(value) 26 | if isinstance(value, UUID): 27 | stmt[key] = str(value) 28 | return stmt 29 | -------------------------------------------------------------------------------- /dev/scripts/common/logging.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # colors 4 | ERR=$(tput setaf 1) 5 | INFO=$(tput setaf 178) 6 | WARN=$(tput setaf 165) 7 | TRACE=$(tput setaf 27) 8 | TS=$(tput setaf 2) 9 | TAG=$(tput setaf 10) 10 | RESET=$(tput sgr0) 11 | 12 | log(){ 13 | local _tag_name=${1} 14 | local _msg=${@:2} 15 | # timestamp 16 | local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") 17 | 18 | printf "${TS}${TIMESTAMP} ${TAG}[${_tag_name}\t] ${_msg}\n" 19 | printf ${RESET} 20 | } 21 | 22 | log-info() { 23 | log "INFO" "${INFO} $@" 24 | } 25 | 26 | log-warn() { 27 | log "WARNING" "${WARN} $@" 28 | } 29 | 30 | log-err() { 31 | log "ERROR" "${ERR} $@" 32 | } 33 | 34 | log-debug() { 35 | local _debug=$(tr '[:upper:]' '[:lower:]' <<<"$DEBUG") 36 | if [[ ! -z "${DEBUG}" && ${_debug} == true ]];then 37 | log "DEBUG" "${TRACE} $@" 38 | fi 39 | } 40 | -------------------------------------------------------------------------------- /koku/api/organizations/aws/provider_map.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """AWS Org Unit Provider Map.""" 6 | from api.models import Provider 7 | from api.report.provider_map import ProviderMap 8 | from reporting.provider.aws.models import AWSOrganizationalUnit 9 | 10 | 11 | class AWSOrgProviderMap(ProviderMap): 12 | """AWS Provider Map.""" 13 | 14 | def __init__(self, provider, report_type, schema_name): 15 | """Constructor.""" 16 | self._mapping = [ 17 | { 18 | "provider": Provider.PROVIDER_AWS, 19 | "report_type": {"organizations": {"filter": [{}], "default_ordering": {}}, "tags": {}}, 20 | } 21 | ] 22 | self.views = {"organizations": {"default": AWSOrganizationalUnit}} 23 | super().__init__(provider, report_type, schema_name) 24 | -------------------------------------------------------------------------------- /koku/api/settings/aws_category_keys/serializers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2023 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Serializers for Masu API `manifest`.""" 6 | from rest_framework import serializers 7 | 8 | from reporting.provider.aws.models import AWSEnabledCategoryKeys 9 | 10 | 11 | class SettingsAWSCategoryKeySerializer(serializers.Serializer): 12 | """Serializer for Tag Settings.""" 13 | 14 | uuid = serializers.UUIDField() 15 | key = serializers.CharField() 16 | enabled = serializers.BooleanField() 17 | 18 | class Meta: 19 | model = AWSEnabledCategoryKeys 20 | 21 | 22 | class SettingsAWSCategoryKeyIDSerializer(serializers.Serializer): 23 | """Serializer for id list for enabling/disabling tags""" 24 | 25 | id_list = serializers.ListField(child=serializers.UUIDField(error_messages={"invalid": "invalid uuid supplied."})) 26 | -------------------------------------------------------------------------------- /koku/api/currency/test/test_views.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | from django.urls import reverse 6 | from rest_framework import status 7 | from rest_framework.test import APIClient 8 | 9 | from api.currency.currencies import CURRENCIES 10 | from api.iam.test.iam_test_case import IamTestCase 11 | 12 | 13 | class CurrencyViewTest(IamTestCase): 14 | """Tests for the currency view.""" 15 | 16 | def test_supported_currencies(self): 17 | """Test that a list GET call returns the supported currencies.""" 18 | qs = "?limit=25" 19 | url = reverse("currency") + qs 20 | client = APIClient() 21 | 22 | response = client.get(url, **self.headers) 23 | self.assertEqual(response.status_code, status.HTTP_200_OK) 24 | 25 | data = response.data 26 | self.assertEqual(data.get("data"), CURRENCIES) 27 | -------------------------------------------------------------------------------- /koku/api/tags/aws/openshift/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for OCP-on-AWS tags.""" 6 | from api.common.permissions.aws_access import AwsAccessPermission 7 | from api.common.permissions.openshift_access import OpenShiftAccessPermission 8 | from api.provider.models import Provider 9 | from api.tags.aws.openshift.queries import OCPAWSTagQueryHandler 10 | from api.tags.aws.openshift.serializers import OCPAWSTagsQueryParamSerializer 11 | from api.tags.view import TagView 12 | 13 | 14 | class OCPAWSTagView(TagView): 15 | """Get OpenShift-on-AWS tags.""" 16 | 17 | provider = "ocp_aws" 18 | serializer = OCPAWSTagsQueryParamSerializer 19 | query_handler = OCPAWSTagQueryHandler 20 | tag_providers = [Provider.PROVIDER_AWS, Provider.PROVIDER_OCP] 21 | permission_classes = [AwsAccessPermission & OpenShiftAccessPermission] 22 | -------------------------------------------------------------------------------- /koku/api/tags/gcp/openshift/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for OCP-on-GCP tags.""" 6 | from api.common.permissions.gcp_access import GcpAccessPermission 7 | from api.common.permissions.openshift_access import OpenShiftAccessPermission 8 | from api.provider.models import Provider 9 | from api.tags.gcp.openshift.queries import OCPGCPTagQueryHandler 10 | from api.tags.gcp.openshift.serializers import OCPGCPTagsQueryParamSerializer 11 | from api.tags.view import TagView 12 | 13 | 14 | class OCPGCPTagView(TagView): 15 | """Get OpenShift-on-GCP tags.""" 16 | 17 | provider = "ocp_gcp" 18 | serializer = OCPGCPTagsQueryParamSerializer 19 | query_handler = OCPGCPTagQueryHandler 20 | tag_providers = [Provider.PROVIDER_GCP, Provider.PROVIDER_OCP] 21 | permission_classes = [GcpAccessPermission & OpenShiftAccessPermission] 22 | -------------------------------------------------------------------------------- /dev/config.yaml.github-example: -------------------------------------------------------------------------------- 1 | # Bonfire deployment configuration 2 | 3 | # Defines where to fetch the file that defines application configs 4 | appsFile: 5 | host: gitlab 6 | repo: insights-platform/cicd-common 7 | path: bonfire_configs/ephemeral_apps.yaml 8 | 9 | # (optional) define any apps locally. An app defined here with will override config for app 10 | # in above fetched config. 11 | apps: 12 | - name: hccm 13 | components: 14 | - name: koku 15 | host: github 16 | repo: project-koku/koku 17 | path: deploy/clowdapp.yaml 18 | ref: main 19 | 20 | - name: trino 21 | host: github 22 | repo: redhatinsights/ubi-trino 23 | path: deploy/clowdapp.yaml 24 | ref: main 25 | parameters: 26 | IMAGE_TAG: 371-001 27 | S3_BUCKET_NAME: hccm-eph-s3 28 | S3_SSE_ENABLED: false 29 | S3_SELECT_PUSHDOWN_ENABLED: false 30 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0333_ocpcostsummarybyprojectp_infrastructure_markup_cost_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.21 on 2025-05-09 08:46 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0332_remove_ocicomputesummarybyaccountp_source_uuid_and_more"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ocpcostsummarybyprojectp", 15 | name="infrastructure_markup_cost", 16 | field=models.DecimalField(decimal_places=15, max_digits=33, null=True), 17 | ), 18 | migrations.AddField( 19 | model_name="ocpcostsummarybyprojectp", 20 | name="infrastructure_raw_cost", 21 | field=models.DecimalField(decimal_places=15, max_digits=33, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0328_alter_ocpusagelineitemdailysummary_monthly_cost_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.15 on 2025-02-20 13:13 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0327_ocpvirtualmachinesummaryp"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="ocpusagelineitemdailysummary", 15 | name="monthly_cost_type", 16 | field=models.TextField( 17 | choices=[ 18 | ("Node", "Node"), 19 | ("Node_Core_Month", "Node_Core_Month"), 20 | ("Cluster", "Cluster"), 21 | ("PVC", "PVC"), 22 | ("Tag", "Tag"), 23 | ], 24 | null=True, 25 | ), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /koku/api/migrations/0058_exchangeratedictionary.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-06-13 19:16 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | from koku.type_json_transcode import TypedJSONDecoder 6 | from koku.type_json_transcode import TypedJSONEncoder 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ("api", "0057_add_org_ids"), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name="ExchangeRateDictionary", 18 | fields=[ 19 | ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), 20 | ( 21 | "currency_exchange_dictionary", 22 | models.JSONField(null=True, encoder=TypedJSONEncoder, decoder=TypedJSONDecoder), 23 | ), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /koku/api/tags/azure/openshift/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for OCP-on-Azure tags.""" 6 | from api.common.permissions.azure_access import AzureAccessPermission 7 | from api.common.permissions.openshift_access import OpenShiftAccessPermission 8 | from api.provider.models import Provider 9 | from api.tags.azure.openshift.queries import OCPAzureTagQueryHandler 10 | from api.tags.azure.openshift.serializers import OCPAzureTagsQueryParamSerializer 11 | from api.tags.view import TagView 12 | 13 | 14 | class OCPAzureTagView(TagView): 15 | """Get OpenShift-on-Azure tags.""" 16 | 17 | provider = "ocp_azure" 18 | serializer = OCPAzureTagsQueryParamSerializer 19 | query_handler = OCPAzureTagQueryHandler 20 | tag_providers = [Provider.PROVIDER_AZURE, Provider.PROVIDER_OCP] 21 | permission_classes = [AzureAccessPermission & OpenShiftAccessPermission] 22 | -------------------------------------------------------------------------------- /koku/hcs/database/sql/reporting_azure_hcs_daily_summary.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | *, 3 | {{ebs_acct_num}} as ebs_account_id, 4 | {{org_id}} as org_id 5 | FROM 6 | hive.{{schema | sqlsafe}}.{{table | sqlsafe}} 7 | WHERE 8 | source = {{provider_uuid | string}} 9 | AND year = {{year}} 10 | AND month = {{month}} 11 | AND date >= {{date}} 12 | AND date < date_add( 13 | 'day', 1, {{date}} 14 | ) 15 | AND ( 16 | ( 17 | -- CCSP 18 | publishertype = 'Azure' 19 | AND strpos(metersubcategory, 'Red Hat') > 0 20 | ) 21 | OR ( 22 | publishertype = 'Marketplace' 23 | AND ( 24 | strpos(publishername, 'Red Hat') > 0 25 | OR ( 26 | -- Alternate CCSP 27 | ( 28 | publishername = 'Microsoft' 29 | OR publishername = 'Azure' 30 | ) 31 | AND strpos(metersubcategory, 'Red Hat') > 0 32 | ) 33 | ) 34 | ) 35 | ) 36 | -------------------------------------------------------------------------------- /koku/subs/trino_sql/aws/subs_row_count.sql: -------------------------------------------------------------------------------- 1 | SELECT count(*) 2 | FROM 3 | hive.{{schema | sqlsafe}}.aws_line_items 4 | WHERE 5 | source = {{ source_uuid | string }} 6 | AND year = {{ year }} 7 | AND month = {{ month }} 8 | AND lineitem_productcode = 'AmazonEC2' 9 | AND lineitem_lineitemtype IN ('Usage', 'SavingsPlanCoveredUsage', 'DiscountedUsage') 10 | AND product_vcpu != '' 11 | AND strpos(lower(resourcetags), 'com_redhat_rhel') > 0 12 | AND lineitem_usageaccountid = {{usage_account}} 13 | AND ( 14 | {% for item in resources %} 15 | ( 16 | lineitem_resourceid = {{item.rid}} AND 17 | lineitem_usagestartdate >= {{item.start}} AND 18 | lineitem_usagestartdate <= {{item.end}} 19 | ) 20 | {% if not loop.last %} 21 | OR 22 | {% endif %} 23 | {% endfor %} 24 | ) 25 | -------------------------------------------------------------------------------- /koku/masu/external/accounts/hierarchy/account_crawler.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Account Hierarchy Abstract class.""" 6 | import logging 7 | from abc import ABC 8 | from abc import abstractmethod 9 | 10 | LOG = logging.getLogger(__name__) 11 | 12 | 13 | class AccountCrawler(ABC): 14 | """Account Crawler Abstract class.""" 15 | 16 | def __init__(self, account): 17 | """ 18 | Object to crawl the org unit structure for accounts to org units. 19 | 20 | Args: 21 | role_arn (String): AWS IAM RoleArn 22 | """ 23 | self.account = account 24 | self.schema = account.get("schema_name") 25 | 26 | @abstractmethod 27 | def crawl_account_hierarchy(self): 28 | """ 29 | Crawl the account hierarchy structure to get organizational information. 30 | 31 | Returns: 32 | None 33 | 34 | """ 35 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0330_alter_ocpusagelineitemdailysummary_monthly_cost_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-03-13 15:24 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0329_ocp_vm_storage"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="ocpusagelineitemdailysummary", 15 | name="monthly_cost_type", 16 | field=models.TextField( 17 | choices=[ 18 | ("Node", "Node"), 19 | ("Node_Core_Month", "Node_Core_Month"), 20 | ("Cluster", "Cluster"), 21 | ("PVC", "PVC"), 22 | ("Tag", "Tag"), 23 | ("OCP_VM", "OCP_VM"), 24 | ], 25 | null=True, 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0044_alter_diskcapacity_provider_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.21 on 2025-06-13 13:50 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0043_alter_diskcapacity_provider_type"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="diskcapacity", 15 | name="provider_type", 16 | field=models.CharField( 17 | choices=[ 18 | ("AWS", "AWS"), 19 | ("Azure", "Azure"), 20 | ("GCP", "GCP"), 21 | ("AWS-local", "AWS-local"), 22 | ("Azure-local", "Azure-local"), 23 | ("GCP-local", "GCP-local"), 24 | ], 25 | max_length=50, 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /koku/subs/test/__init__.py: -------------------------------------------------------------------------------- 1 | """Shared Class for subs tests.""" 2 | from api.iam.test.iam_test_case import IamTestCase 3 | from api.provider.models import Provider 4 | 5 | 6 | class SUBSTestCase(IamTestCase): 7 | """Subclass of TestCase that automatically create an app and client.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Create test case setup.""" 12 | super().setUpClass() 13 | 14 | cls.schema = "org1234567" 15 | cls.acct = "10001" 16 | cls.org_id = "1234567" 17 | 18 | cls.aws_provider = Provider.objects.filter(type=Provider.PROVIDER_AWS_LOCAL).first() 19 | cls.aws_provider_type = Provider.PROVIDER_AWS_LOCAL 20 | 21 | cls.azure_provider = Provider.objects.filter(type=Provider.PROVIDER_AZURE_LOCAL).first() 22 | cls.azure_tenant = cls.azure_provider.account.get("credentials").get("tenant_id") 23 | cls.azure_provider_type = Provider.PROVIDER_AZURE_LOCAL 24 | -------------------------------------------------------------------------------- /koku/subs/trino_sql/azure/subs_row_count.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | COUNT(*) 3 | FROM 4 | hive.{{schema | sqlsafe}}.azure_line_items 5 | WHERE 6 | source = {{ source_uuid | string }} 7 | AND year = {{ year }} 8 | AND month = {{ month }} 9 | AND metercategory = 'Virtual Machines' 10 | AND json_extract_scalar(lower(additionalinfo), '$.vcpus') IS NOT NULL 11 | AND json_extract_scalar(lower(lower(tags)), '$.com_redhat_rhel') IS NOT NULL 12 | AND (subscriptionid = {{usage_account}} or subscriptionguid = {{usage_account}}) 13 | -- ensure there is usage 14 | AND ceil(quantity) > 0 15 | AND ( 16 | {% for item in resources %} 17 | ( 18 | resourceid = {{item.rid}} AND 19 | date >= {{item.start}} AND 20 | date <= {{item.end}} 21 | ) 22 | {% if not loop.last %} 23 | OR 24 | {% endif %} 25 | {% endfor %} 26 | ) 27 | -------------------------------------------------------------------------------- /dev/scripts/create_test_db_user.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Ensure Koku's postgres user exists as a superuser so Django can manage its test database. 3 | # 4 | # Assumes environment variables have been set: 5 | # - POSTGRES_SQL_SERVICE_PORT 6 | # - POSTGRES_SQL_SERVICE_HOST 7 | # - DATABASE_ADMIN (postgres admin user) 8 | # - DATABASE_PASSWORD (postgres admin user's password) 9 | # - DATABASE_USER (postgres user to be recreated) 10 | 11 | export PGPASSWORD="${DATABASE_PASSWORD}" 12 | export PGPORT="${POSTGRES_SQL_SERVICE_PORT}" 13 | export PGHOST="${POSTGRES_SQL_SERVICE_HOST}" 14 | export PGUSER="${DATABASE_ADMIN}" 15 | 16 | dropuser -w --if-exists "${DATABASE_USER}" || \ 17 | echo "NOTICE: Could not drop user '${DATABASE_USER}'" 18 | createuser -w -s "${DATABASE_USER}" || \ 19 | echo "NOTICE: Could not create user '${DATABASE_USER}'" 20 | psql -w --quiet -d template1 \ 21 | -c "ALTER USER ${DATABASE_USER} WITH PASSWORD '${DATABASE_PASSWORD}'" 22 | -------------------------------------------------------------------------------- /koku/api/metrics/serializers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """CostModelMetricMap Serializer.""" 6 | from rest_framework import serializers 7 | 8 | 9 | class CostModelMetricMapSerializer(serializers.Serializer): 10 | """Serializer for the CostModelMetricsMap.""" 11 | 12 | source_type = serializers.CharField(required=True) 13 | metric = serializers.CharField(required=True) 14 | label_metric = serializers.CharField(required=True) 15 | label_measurement = serializers.CharField(required=True) 16 | label_measurement_unit = serializers.CharField(required=True) 17 | default_cost_type = serializers.CharField(required=True) 18 | 19 | 20 | class QueryParamsSerializer(serializers.Serializer): 21 | """Validate the Query params limit and offset""" 22 | 23 | limit = serializers.IntegerField(required=False, min_value=1) 24 | offset = serializers.IntegerField(required=False, min_value=0) 25 | -------------------------------------------------------------------------------- /koku/koku/sentry.py: -------------------------------------------------------------------------------- 1 | """Sentry configuration file for the Koku project.""" 2 | import sentry_sdk 3 | 4 | from .env import ENVIRONMENT 5 | 6 | BLOCK_LIST = { 7 | "/api/cost-management/v1/status/", 8 | "/api/cost-management/v1/source-status/", 9 | } 10 | 11 | 12 | def traces_sampler(sampling_context): 13 | wsgi_environ = sampling_context.get("wsgi_environ") 14 | if wsgi_environ and wsgi_environ.get("PATH_INFO") in BLOCK_LIST: 15 | # Drop this transaction, by setting its sample rate to 0% 16 | return 0 17 | 18 | # Default sample rate for all others (replaces traces_sample_rate) 19 | return 0.05 20 | 21 | 22 | if ENVIRONMENT.bool("KOKU_ENABLE_SENTRY", default=False): 23 | sentry_sdk.init( 24 | dsn=ENVIRONMENT("KOKU_SENTRY_DSN"), 25 | environment=ENVIRONMENT("KOKU_SENTRY_ENVIRONMENT"), 26 | traces_sampler=traces_sampler, 27 | ) 28 | print("Sentry setup.") 29 | else: 30 | print("Sentry not enabled.") 31 | -------------------------------------------------------------------------------- /koku/api/migrations/0035_reapply_partition_and_clone_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-12-16 21:27 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | from koku import migration_sql_helpers as msh 7 | 8 | 9 | def apply_public_func_updates(apps, schema_editor): 10 | path = msh.find_db_functions_dir() 11 | for funcfile in ( 12 | # trigger func is the same as original apply (interface not changed) but does not have the drop in the file. 13 | "reapply_partitioned_tables_manage_trigger_function.sql", 14 | # Drop/create here is not being used in triggers as of this migration. 15 | "clone_schema.sql", 16 | ): 17 | msh.apply_sql_file(schema_editor, os.path.join(path, funcfile), literal_placeholder=True) 18 | 19 | 20 | class Migration(migrations.Migration): 21 | 22 | dependencies = [("api", "0034_remove_sources_endpoint_id")] 23 | 24 | operations = [migrations.RunPython(code=apply_public_func_updates)] 25 | -------------------------------------------------------------------------------- /dev/scripts/nise_ymls/gcp/gcp_static_data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | generators: 3 | - ComputeEngineGenerator: 4 | start_date: {{start_date}} 5 | end_date: {{end_date}} 6 | cost: 2557 7 | currency: USD 8 | - ComputeEngineGenerator: 9 | start_date: {{start_date}} 10 | end_date: {{end_date}} 11 | cost: 2557 12 | currency: USD 13 | - ComputeEngineGenerator: 14 | start_date: {{start_date}} 15 | end_date: {{end_date}} 16 | cost: 2557 17 | currency: USD 18 | - ComputeEngineGenerator: 19 | start_date: {{start_date}} 20 | end_date: {{end_date}} 21 | cost: 2557 22 | currency: USD 23 | - CloudStorageGenerator: 24 | start_date: {{start_date}} 25 | end_date: {{end_date}} 26 | currency: USD 27 | 28 | projects: 29 | - 30 | billing_account_id: example_account_id 31 | project.name: billion-force-58425800 32 | project.id: example-project-id 33 | project.labels: step:chair;year:each 34 | -------------------------------------------------------------------------------- /.github/postgres/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | db: 5 | image: postgres:16 6 | environment: 7 | - DATABASE_NAME=${DATABASE_NAME-postgres} 8 | - POSTGRES_USER=${DATABASE_USER-postgres} 9 | - POSTGRES_PASSWORD=${DATABASE_PASSWORD-postgres} 10 | ports: 11 | - 5432:5432 12 | command: 13 | - postgres 14 | - -c 15 | - max_connections=1710 16 | - -c 17 | - autovacuum_max_workers=8 18 | - -c 19 | - autovacuum_vacuum_cost_limit=4800 20 | - -c 21 | - autovacuum_vacuum_cost_delay=10 22 | - -c 23 | - max_locks_per_transaction=72 24 | - -c 25 | - shared_preload_libraries=pg_stat_statements 26 | - -c 27 | - pg_stat_statements.max=2000 28 | - -c 29 | - pg_stat_statements.save=off 30 | - -c 31 | - pg_stat_statements.track_utility=off 32 | - -c 33 | - track_activity_query_size=2048 34 | - -c 35 | - track_functions=pl 36 | -------------------------------------------------------------------------------- /koku/api/currency/models.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # from uuid import uuid4 6 | # from env import currency endpoimy 7 | from django.db import models 8 | 9 | from api.currency.currencies import CURRENCIES 10 | from koku.type_json_transcode import TypedJSONDecoder 11 | from koku.type_json_transcode import TypedJSONEncoder 12 | 13 | 14 | class ExchangeRates(models.Model): 15 | SUPPORTED_CURRENCIES = tuple((curr.get("code", "").lower(), curr.get("code")) for curr in CURRENCIES) 16 | 17 | currency_type = models.CharField(max_length=5, choices=SUPPORTED_CURRENCIES, unique=False, blank=True) 18 | exchange_rate = models.FloatField(default=0) 19 | 20 | 21 | class ExchangeRateDictionary(models.Model): 22 | """Model provides exchange rates utilized in conversion process (Change this description)""" 23 | 24 | currency_exchange_dictionary = models.JSONField(null=True, encoder=TypedJSONEncoder, decoder=TypedJSONDecoder) 25 | -------------------------------------------------------------------------------- /koku/api/settings/test/tags/test_view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2023 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | from unittest.mock import patch 6 | 7 | from django.test import TestCase 8 | from rest_framework.exceptions import ValidationError 9 | 10 | from api.settings.tags.view import SettingsTagFilter 11 | 12 | 13 | class TestSettingsTagFilter(TestCase): 14 | """Given invalid order_by parameters, ensure an error is raised""" 15 | 16 | def test_invalid_order_by(self): 17 | with self.assertRaisesRegex(ValidationError, "Invalid order_by parameter"): 18 | SettingsTagFilter()._get_order_by({1}) 19 | 20 | def test_no_request(self): 21 | with patch( 22 | "api.settings.utils.FilterSet.filter_queryset", 23 | side_effect=AttributeError("Raised intentionally"), 24 | ): 25 | with self.assertRaisesRegex(AttributeError, "Raised intentionally"): 26 | SettingsTagFilter().filter_queryset(None) 27 | -------------------------------------------------------------------------------- /koku/sources/api/__init__.py: -------------------------------------------------------------------------------- 1 | # noqa 2 | import binascii 3 | import logging 4 | from base64 import b64decode 5 | from json import loads as json_loads 6 | from json.decoder import JSONDecodeError 7 | 8 | LOG = logging.getLogger(__name__) 9 | HEADER_X_RH_IDENTITY = "X-Rh-Identity" 10 | 11 | 12 | def get_auth_header(request): 13 | """Get the auth header from the request.""" 14 | return request.headers.get(HEADER_X_RH_IDENTITY) 15 | 16 | 17 | def get_param_from_header(request, search_param): 18 | """Get param from header.""" 19 | param = None 20 | auth_header = get_auth_header(request) 21 | if auth_header: 22 | try: 23 | decoded_rh_auth = b64decode(auth_header) 24 | json_rh_auth = json_loads(decoded_rh_auth) 25 | param = json_rh_auth.get("identity", {}).get(search_param) 26 | except (binascii.Error, JSONDecodeError) as error: 27 | LOG.error(f"Error decoding authentication header: {str(error)}") 28 | return param 29 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0336_alter_enabledtagkeys_provider_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.21 on 2025-06-13 13:50 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0335_alter_ocpusagelineitemdailysummary_monthly_cost_type"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="enabledtagkeys", 15 | name="provider_type", 16 | field=models.CharField( 17 | choices=[ 18 | ("AWS", "AWS"), 19 | ("OCP", "OCP"), 20 | ("Azure", "Azure"), 21 | ("GCP", "GCP"), 22 | ("AWS-local", "AWS-local"), 23 | ("Azure-local", "Azure-local"), 24 | ("GCP-local", "GCP-local"), 25 | ], 26 | max_length=50, 27 | ), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /dev/scripts/migrate_schemas.sql: -------------------------------------------------------------------------------- 1 | SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'acct%' 2 | 3 | DROP FUNCTION IF EXISTS schema_rename(); 4 | 5 | CREATE FUNCTION schema_rename() RETURNS integer AS $$ 6 | DECLARE 7 | r RECORD; 8 | orig_name text; 9 | new_name text; 10 | 11 | BEGIN 12 | RAISE NOTICE 'Starting schema rename...'; 13 | 14 | FOR r IN SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'acct%' LOOP 15 | orig_name = r.schema_name; 16 | new_name = split_part(r.schema_name, 'org', 1); 17 | RAISE NOTICE 'Current schema_name %', orig_name; 18 | RAISE NOTICE 'New schema_name %', new_name; 19 | EXECUTE 'ALTER SCHEMA ' || orig_name || ' RENAME TO ' || new_name ; 20 | END LOOP; 21 | 22 | RAISE NOTICE 'Done with schema rename.'; 23 | RETURN 1; 24 | END; 25 | $$ LANGUAGE plpgsql; 26 | 27 | SELECT schema_rename(); 28 | 29 | SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'acct%' 30 | -------------------------------------------------------------------------------- /koku/api/settings/serializers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Serializers for Masu API `manifest`.""" 6 | from rest_framework import serializers 7 | 8 | from api.currency.currencies import CURRENCY_CHOICES 9 | from api.settings.settings import COST_TYPE_CHOICES 10 | from reporting.user_settings.models import UserSettings 11 | 12 | 13 | class UserSettingSerializer(serializers.Serializer): 14 | """Serializer for CostUsageReportManifest.""" 15 | 16 | class Meta: 17 | model = UserSettings 18 | 19 | settings = serializers.JSONField() 20 | 21 | 22 | class UserSettingUpdateCostTypeSerializer(serializers.Serializer): 23 | """Serializer for setting cost type.""" 24 | 25 | cost_type = serializers.ChoiceField(choices=COST_TYPE_CHOICES) 26 | 27 | 28 | class UserSettingUpdateCurrencySerializer(serializers.Serializer): 29 | """Serializer for setting cost type.""" 30 | 31 | currency = serializers.ChoiceField(choices=CURRENCY_CHOICES) 32 | -------------------------------------------------------------------------------- /koku/api/currency/utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | from decimal import Decimal 6 | 7 | from api.currency.models import ExchangeRateDictionary 8 | 9 | 10 | def build_exchange_dictionary(rates): 11 | """Build the exchange rates dictionary""" 12 | exchanged_rates = {} 13 | for currency_key, base_rate in rates.items(): 14 | exchanged = {currency: Decimal(rate / base_rate) for currency, rate in rates.items()} 15 | exchanged_rates[currency_key] = exchanged 16 | return exchanged_rates 17 | 18 | 19 | def exchange_dictionary(rates): 20 | """Posts exchange rates dictionary to DB""" 21 | exchange_data = build_exchange_dictionary(rates) 22 | current_data = ExchangeRateDictionary.objects.all().first() 23 | if not current_data: 24 | ExchangeRateDictionary.objects.create(currency_exchange_dictionary=exchange_data) 25 | else: 26 | current_data.currency_exchange_dictionary = exchange_data 27 | current_data.save() 28 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0335_alter_ocpusagelineitemdailysummary_monthly_cost_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.23 on 2025-06-11 14:56 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0334_subslastprocessed_event_sent"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="ocpusagelineitemdailysummary", 15 | name="monthly_cost_type", 16 | field=models.TextField( 17 | choices=[ 18 | ("Node", "Node"), 19 | ("Node_Core_Month", "Node_Core_Month"), 20 | ("Cluster", "Cluster"), 21 | ("PVC", "PVC"), 22 | ("Tag", "Tag"), 23 | ("OCP_VM", "OCP_VM"), 24 | ("OCP_VM_CORE", "OCP_VM_CORE"), 25 | ], 26 | null=True, 27 | ), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /koku/api/migrations/0051_reapply_partition_trigger_func.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2021-11-12 21:04 2 | import os 3 | import re 4 | 5 | from django.db import migrations 6 | 7 | from koku.migration_sql_helpers import find_db_functions_dir 8 | 9 | 10 | def reapply_partition_manager_func(apps, schema_editor): 11 | func_file = "partitioned_manager_trigger_function.sql" 12 | func_path = os.path.join(find_db_functions_dir(), func_file) 13 | func_sql = open(func_path).read() 14 | # The file also contains a DROP FUNCTION statement. Let's get rid of that troublesome thing for this migration 15 | func_sql = re.sub(r"DROP FUNCTION IF EXISTS.+", "-- THE DROP HAS BEEN ERASED --", func_sql) 16 | 17 | with schema_editor.connection.cursor() as cur: 18 | cur.execute(func_sql) 19 | 20 | 21 | class Migration(migrations.Migration): 22 | 23 | dependencies = [("api", "0050_exchangerates")] 24 | 25 | operations = [migrations.RunPython(code=reapply_partition_manager_func, reverse_code=migrations.RunPython.noop)] 26 | -------------------------------------------------------------------------------- /koku/masu/database/sql/azure/ui_summary/reporting_azure_cost_summary_p.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM {{schema | sqlsafe}}.reporting_azure_cost_summary_p 2 | WHERE usage_start >= {{start_date}}::date 3 | AND usage_start <= {{end_date}}::date 4 | AND source_uuid = {{source_uuid}} 5 | ; 6 | 7 | INSERT INTO {{schema | sqlsafe}}.reporting_azure_cost_summary_p ( 8 | id, 9 | usage_start, 10 | usage_end, 11 | pretax_cost, 12 | markup_cost, 13 | currency, 14 | source_uuid 15 | ) 16 | SELECT uuid_generate_v4() as id, 17 | usage_start as usage_start, 18 | usage_start as usage_end, 19 | sum(pretax_cost) as pretax_cost, 20 | sum(markup_cost) as markup_cost, 21 | max(currency) as currency, 22 | {{source_uuid}}::uuid as source_uuid 23 | FROM {{schema | sqlsafe}}.reporting_azurecostentrylineitem_daily_summary 24 | WHERE usage_start >= {{start_date}}::date 25 | AND usage_start <= {{end_date}}::date 26 | AND source_uuid = {{source_uuid}} 27 | GROUP BY usage_start 28 | ; 29 | -------------------------------------------------------------------------------- /koku/cost_models/migrations/0006_add_distribution_info.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2023-03-06 19:52 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("cost_models", "0005_add_oci_provider"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="costmodel", 15 | name="distribution_info", 16 | field=models.JSONField(default=dict), 17 | ), 18 | migrations.AddField( 19 | model_name="costmodelaudit", 20 | name="distribution_info", 21 | field=models.JSONField(default=dict), 22 | ), 23 | migrations.RunSQL( 24 | "UPDATE cost_model cm SET distribution_info = (SELECT to_jsonb(distribution_opts) FROM (select temp_cm.distribution as distribution_type, false as worker_cost, false as platform_cost from cost_model temp_cm where temp_cm.uuid = cm.uuid) distribution_opts)" 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /koku/masu/database/sql/gcp/openshift/reporting_ocpgcp_matched_tags.sql: -------------------------------------------------------------------------------- 1 | WITH cte_unnested_gcp_tags AS ( 2 | SELECT DISTINCT ts.key, 3 | ts.value 4 | FROM {{schema | sqlsafe}}.reporting_gcptags_values AS ts 5 | JOIN {{schema | sqlsafe}}.reporting_enabledtagkeys as enabled_tags 6 | ON lower(enabled_tags.key) = lower(ts.key) 7 | WHERE enabled_tags.provider_type = 'GCP' 8 | ), 9 | cte_unnested_ocp_tags AS ( 10 | SELECT DISTINCT ts.key, 11 | ts.value 12 | FROM {{schema | sqlsafe}}.reporting_ocptags_values AS ts 13 | JOIN {{schema | sqlsafe}}.reporting_enabledtagkeys as enabled_tags 14 | ON lower(enabled_tags.key) = lower(ts.key) 15 | WHERE enabled_tags.provider_type = 'OCP' 16 | ) 17 | SELECT jsonb_build_object(key, value) as tag 18 | FROM ( 19 | SELECT gcp.key, 20 | gcp.value 21 | FROM cte_unnested_gcp_tags AS gcp 22 | JOIN cte_unnested_ocp_tags AS ocp 23 | ON lower(gcp.key) = lower(ocp.key) 24 | AND lower(gcp.value) = lower(ocp.value) 25 | ) AS matches 26 | ; 27 | -------------------------------------------------------------------------------- /dev/scripts/delete_gh_action_cache.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from os import environ 3 | 4 | import requests 5 | 6 | if __name__ == "__main__": 7 | token = environ.get("GH_TOKEN") 8 | if not token: 9 | print("must set GH_TOKEN environment variable") 10 | print("docs: https://docs.github.com/en/rest/actions/cache") 11 | exit(1) 12 | 13 | headers = {"Authorization": f"Bearer {token}"} 14 | 15 | owner = environ.get("GH_OWNER") or "project-koku" 16 | repo = environ.get("GH_REPO") or "koku" 17 | url = f"http://api.github.com/repos/{owner}/{repo}/actions/caches" 18 | 19 | r = requests.get(url, params={"limit": 1000}, headers=headers).json() 20 | cache_ids = [] 21 | for resp in r.get("actions_caches", []): 22 | if cache_id := resp.get("id"): 23 | cache_ids.append(cache_id) 24 | 25 | for cache_id in cache_ids: 26 | delete_url = f"{url}/{cache_id}" 27 | print(f"Calling DELETE {delete_url}") 28 | r = requests.delete(delete_url, headers=headers) 29 | print(r) 30 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0043_alter_diskcapacity_provider_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.20 on 2025-05-07 10:42 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting_common", "0042_alter_costusagereportmanifest_s3_parquet_cleared"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="diskcapacity", 15 | name="provider_type", 16 | field=models.CharField( 17 | choices=[ 18 | ("AWS", "AWS"), 19 | ("Azure", "Azure"), 20 | ("GCP", "GCP"), 21 | ("IBM", "IBM"), 22 | ("AWS-local", "AWS-local"), 23 | ("Azure-local", "Azure-local"), 24 | ("GCP-local", "GCP-local"), 25 | ("IBM-local", "IBM-local"), 26 | ], 27 | max_length=50, 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /dev/containers/trino/etc/jvm.config: -------------------------------------------------------------------------------- 1 | -server 2 | -XX:+UseContainerSupport 3 | -XX:+UseG1GC 4 | -XX:InitialRAMPercentage=50.0 5 | -XX:MaxRAMPercentage=75.0 6 | -XX:+ExplicitGCInvokesConcurrent 7 | -XX:+HeapDumpOnOutOfMemoryError 8 | -XX:HeapDumpPath=/data/trino/logs/heap_dump.bin 9 | -XX:+ExitOnOutOfMemoryError 10 | -XX:ErrorFile=/data/trino/logs/java_error%p.log 11 | -verbose:gc 12 | -Xlog:gc*:/data/trino/logs/gc.log 13 | -Dcom.sun.management.jmxremote 14 | -Dcom.sun.management.jmxremote.local.only=false 15 | -Dcom.sun.management.jmxremote.ssl=false 16 | -Dcom.sun.management.jmxremote.authenticate=false 17 | -Dcom.sun.management.jmxremote.port=10000 18 | -Dcom.sun.management.jmxremote.rmi.port=10000 19 | -Dfile.encoding=UTF-8 20 | -Djava.rmi.server.hostname=127.0.0.1 21 | # Reduce starvation of threads by GClocker, recommend to set about the number of cpu cores (JDK-8192647) 22 | -XX:+UnlockDiagnosticVMOptions 23 | -XX:GCLockerRetryAllocationCount=8 24 | # Allow loading dynamic agent used by JOL 25 | -XX:+EnableDynamicAgentLoading 26 | -XX:G1NumCollectionsKeepPinned=10000000 27 | -------------------------------------------------------------------------------- /koku/api/report/gcp/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """GCP views.""" 6 | from api.common.permissions.gcp_access import GcpAccessPermission 7 | from api.models import Provider 8 | from api.report.gcp.query_handler import GCPReportQueryHandler 9 | from api.report.gcp.serializers import GCPQueryParamSerializer 10 | from api.report.view import ReportView 11 | 12 | 13 | class GCPView(ReportView): 14 | """GCP Base View.""" 15 | 16 | permission_classes = [GcpAccessPermission] 17 | provider = Provider.PROVIDER_GCP 18 | serializer = GCPQueryParamSerializer 19 | query_handler = GCPReportQueryHandler 20 | tag_providers = [Provider.PROVIDER_GCP] 21 | 22 | 23 | class GCPCostView(GCPView): 24 | """Get cost data.""" 25 | 26 | report = "costs" 27 | 28 | 29 | class GCPInstanceTypeView(GCPView): 30 | """Get inventory data.""" 31 | 32 | report = "instance_type" 33 | 34 | 35 | class GCPStorageView(GCPView): 36 | """Get inventory storage data.""" 37 | 38 | report = "storage" 39 | -------------------------------------------------------------------------------- /koku/api/status/views.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for server status.""" 6 | from django.utils.decorators import method_decorator 7 | from django.views.decorators.cache import never_cache 8 | from rest_framework import permissions 9 | from rest_framework.response import Response 10 | from rest_framework.views import APIView 11 | 12 | from api.status.models import Status 13 | from api.status.serializers import StatusSerializer 14 | 15 | 16 | class StatusView(APIView): 17 | """Provide the server status information.""" 18 | 19 | permission_classes = [permissions.AllowAny] 20 | serializer_class = StatusSerializer 21 | 22 | @method_decorator(never_cache) 23 | def get(self, request): 24 | """Return the server status.""" 25 | status_info = Status() 26 | serializer = StatusSerializer(status_info) 27 | server_info = serializer.data 28 | server_info["server_address"] = request.META.get("HTTP_HOST", "localhost") 29 | return Response(server_info) 30 | -------------------------------------------------------------------------------- /dev/scripts/migrate_tenant_schema_names.sql: -------------------------------------------------------------------------------- 1 | SELECT id, schema_name FROM public.api_tenant WHERE schema_name LIKE 'acct%'; 2 | 3 | DROP FUNCTION IF EXISTS tenant_schema_rename(); 4 | 5 | CREATE FUNCTION tenant_schema_rename() RETURNS integer AS $$ 6 | DECLARE 7 | r RECORD; 8 | orig_name text; 9 | new_name text; 10 | 11 | BEGIN 12 | RAISE NOTICE 'Starting tenant schema rename...'; 13 | 14 | FOR r IN SELECT id, schema_name FROM public.api_tenant WHERE schema_name LIKE 'acct%' LOOP 15 | orig_name = r.schema_name; 16 | new_name = split_part(r.schema_name, 'org', 1); 17 | RAISE NOTICE 'Current api_tenant schema_name %', orig_name; 18 | RAISE NOTICE 'New api_tenant schema_name %', new_name; 19 | UPDATE public.api_tenant SET schema_name=new_name WHERE id=r.id; 20 | END LOOP; 21 | 22 | 23 | RAISE NOTICE 'Done with tenant schema rename.'; 24 | RETURN 1; 25 | END; 26 | $$ LANGUAGE plpgsql; 27 | 28 | SELECT tenant_schema_rename(); 29 | 30 | SELECT id, schema_name FROM public.api_tenant WHERE schema_name LIKE 'acct%'; 31 | -------------------------------------------------------------------------------- /koku/reporting_common/migrations/0029_costusagereportmanifest_operator_info.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.12 on 2021-07-29 19:02 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("reporting_common", "0028_costusagereportmanifest_operator_version")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="costusagereportmanifest", name="cluster_channel", field=models.TextField(null=True) 13 | ), 14 | migrations.AddField( 15 | model_name="costusagereportmanifest", name="operator_airgapped", field=models.BooleanField(null=True) 16 | ), 17 | migrations.AddField( 18 | model_name="costusagereportmanifest", name="operator_certified", field=models.BooleanField(null=True) 19 | ), 20 | migrations.AddField( 21 | model_name="costusagereportmanifest", 22 | name="operator_errors", 23 | field=models.JSONField(default=dict, null=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /koku/reporting_common/states.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Common util functions.""" 6 | from django.db.models import IntegerChoices 7 | 8 | from common.enum import StrEnum 9 | 10 | 11 | class ReportStep(IntegerChoices): 12 | DOWNLOADING = 3 13 | PROCESSING = 4 14 | OCP_CLOUD_PROCESSING = (5, "OCP-Cloud-Processing") 15 | 16 | 17 | class Status(IntegerChoices): 18 | DONE = 1 19 | FAILED = 2 20 | 21 | 22 | class CombinedChoices(IntegerChoices): 23 | DOWNLOADING = ReportStep.DOWNLOADING 24 | PROCESSING = ReportStep.PROCESSING 25 | OCP_CLOUD_PROCESSING = ReportStep.OCP_CLOUD_PROCESSING 26 | DONE = Status.DONE 27 | FAILED = Status.FAILED 28 | 29 | 30 | class ManifestStep(StrEnum): 31 | DOWNLOAD = "download" 32 | PROCESSING = "processing" 33 | SUMMARY = "summary" 34 | 35 | 36 | class ManifestState(StrEnum): 37 | START = "start" 38 | END = "end" 39 | FAILED = "failed" 40 | PENDING = "pending" 41 | IN_PROGRESS = "in-progress" 42 | COMPLETE = "complete" 43 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file 2 | 3 | exclude: 4 | global: 5 | # Exclude a single file. For example, - test.spec.js 6 | # - file_name.ext 7 | # Exclude a single directory. For example, - src/lib 8 | # - source/directory_name 9 | # Exclude any file with a specific extension in the specific directory. For example, - tests/.js 10 | # - directory_name/.ext 11 | # Exclude files with a specific ending in any directory. For example, - “*.spec.js” 12 | - "*tests?*.py" 13 | - "*.html" 14 | - "*.ya?ml" 15 | - "*.json" 16 | # Exclude files in directories that have the same name with a different ending, like “test” and “tests”. The last character before the question mark is optional. For example, - tests?/ 17 | - directory_name?/ 18 | # Exclude all files and directories in a specific directory. For example, - tests/ 19 | - ci/** 20 | - dashboards/** 21 | - dev/** 22 | - grafana/** 23 | - testing/** 24 | -------------------------------------------------------------------------------- /koku/providers/aws_local/provider.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """AWS-local service provider implementation to be used by Koku.""" 6 | from rest_framework import serializers 7 | 8 | from ..aws.provider import AWSProvider 9 | from ..provider_errors import ProviderErrors 10 | from api.common import error_obj 11 | from api.models import Provider 12 | 13 | 14 | class AWSLocalProvider(AWSProvider): 15 | """Provider interface defnition.""" 16 | 17 | def name(self): 18 | """Return name of the provider.""" 19 | return Provider.PROVIDER_AWS_LOCAL 20 | 21 | def cost_usage_source_is_reachable(self, _, data_source): 22 | """Verify that the cost usage source exists and is reachable.""" 23 | storage_resource_name = data_source.get("bucket") 24 | if not storage_resource_name: 25 | key = ProviderErrors.AWS_BUCKET_MISSING 26 | message = ProviderErrors.AWS_BUCKET_MISSING_MESSAGE 27 | raise serializers.ValidationError(error_obj(key, message)) 28 | return True 29 | -------------------------------------------------------------------------------- /koku/api/resource_types/openshift_gpus/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2025 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """View for Openshift GPU resource types.""" 6 | from django.db.models import F 7 | 8 | from api.resource_types.view import OCPGpuResourceTypesView 9 | from reporting.provider.ocp.models import OCPGpuSummaryP 10 | 11 | 12 | class OCPGpuModelsView(OCPGpuResourceTypesView): 13 | """API GET list view for Openshift GPU models.""" 14 | 15 | query_filter_keys = OCPGpuResourceTypesView.query_filter_keys + ["vendor_name"] 16 | 17 | queryset = ( 18 | OCPGpuSummaryP.objects.annotate(**{"value": F("model_name")}) 19 | .values("value") 20 | .distinct() 21 | .filter(model_name__isnull=False) 22 | ) 23 | 24 | 25 | class OCPGpuVendorsView(OCPGpuResourceTypesView): 26 | """API GET list view for Openshift GPU vendors.""" 27 | 28 | queryset = ( 29 | OCPGpuSummaryP.objects.annotate(**{"value": F("vendor_name")}) 30 | .values("value") 31 | .distinct() 32 | .filter(vendor_name__isnull=False) 33 | ) 34 | -------------------------------------------------------------------------------- /koku/api/report/azure/view.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Azure views.""" 6 | from api.common.permissions.azure_access import AzureAccessPermission 7 | from api.models import Provider 8 | from api.report.azure.query_handler import AzureReportQueryHandler 9 | from api.report.azure.serializers import AzureQueryParamSerializer 10 | from api.report.view import ReportView 11 | 12 | 13 | class AzureView(ReportView): 14 | """Azure Base View.""" 15 | 16 | permission_classes = [AzureAccessPermission] 17 | provider = Provider.PROVIDER_AZURE 18 | serializer = AzureQueryParamSerializer 19 | query_handler = AzureReportQueryHandler 20 | tag_providers = [Provider.PROVIDER_AZURE] 21 | 22 | 23 | class AzureCostView(AzureView): 24 | """Get cost data.""" 25 | 26 | report = "costs" 27 | 28 | 29 | class AzureInstanceTypeView(AzureView): 30 | """Get inventory data.""" 31 | 32 | report = "instance_type" 33 | 34 | 35 | class AzureStorageView(AzureView): 36 | """Get inventory storage data.""" 37 | 38 | report = "storage" 39 | -------------------------------------------------------------------------------- /koku/api/common/permissions/settings_access.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Defines the Settings Access Permissions class.""" 6 | from django.conf import settings 7 | from rest_framework import permissions 8 | 9 | 10 | class SettingsAccessPermission(permissions.BasePermission): 11 | """Determines if a user can update Settings data.""" 12 | 13 | resource_type = "settings" 14 | 15 | def has_permission(self, request, view): 16 | """Check permission based on the defined access.""" 17 | if settings.ENHANCED_ORG_ADMIN and request.user.admin: 18 | return True 19 | 20 | if not request.user.access: 21 | return False 22 | 23 | if request.method in permissions.SAFE_METHODS: 24 | if request.user.access.get(self.resource_type, {}).get("read", []): 25 | return True 26 | else: 27 | setting_write = request.user.access.get(self.resource_type, {}).get("write", []) 28 | if "*" in setting_write: 29 | return True 30 | return False 31 | -------------------------------------------------------------------------------- /koku/hcs/test/test_exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Test HCS csv_file_handler.""" 6 | from hcs.exceptions import HCSTableNotFoundError 7 | from hcs.test import HCSTestCase 8 | 9 | 10 | class TestHCSExceptions(HCSTestCase): 11 | """Test cases for HCS Daily Report""" 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | """Set up the class.""" 16 | super().setUpClass() 17 | 18 | def test_HCSTableNotFoundError_init(self): 19 | """Test HCSTableNotFoundError exception initialization""" 20 | test_table = "bad_table" 21 | htfe = HCSTableNotFoundError(test_table) 22 | 23 | self.assertEqual(htfe.table_name, test_table) 24 | self.assertEqual(htfe.message, "table not found") 25 | 26 | def test_HCSTableNotFoundError_str(self): 27 | """Test HCSTableNotFoundError exception string""" 28 | test_table = "bad_table" 29 | htfe = HCSTableNotFoundError(test_table) 30 | 31 | result = htfe.__str__() 32 | self.assertEqual(result, "bad_table table not found") 33 | -------------------------------------------------------------------------------- /koku/masu/database/sql/azure/openshift/reporting_ocpazure_matched_tags.sql: -------------------------------------------------------------------------------- 1 | WITH cte_unnested_azure_tags AS ( 2 | SELECT DISTINCT ts.key, 3 | ts.value 4 | FROM {{schema | sqlsafe}}.reporting_azuretags_values AS ts 5 | JOIN {{schema | sqlsafe}}.reporting_enabledtagkeys as enabled_tags 6 | ON lower(enabled_tags.key) = lower(ts.key) 7 | AND enabled_tags.enabled = true 8 | AND enabled_tags.provider_type = 'Azure' 9 | ), 10 | cte_unnested_ocp_tags AS ( 11 | SELECT DISTINCT ts.key, 12 | ts.value 13 | FROM {{schema | sqlsafe}}.reporting_ocptags_values AS ts 14 | JOIN {{schema | sqlsafe}}.reporting_enabledtagkeys as enabled_tags 15 | ON lower(enabled_tags.key) = lower(ts.key) 16 | WHERE enabled_tags.provider_type = 'OCP' 17 | ) 18 | SELECT jsonb_build_object(key, value) as tag 19 | FROM ( 20 | SELECT azure.key, 21 | azure.value 22 | FROM cte_unnested_azure_tags AS azure 23 | JOIN cte_unnested_ocp_tags AS ocp 24 | ON lower(azure.key) = lower(ocp.key) 25 | AND lower(azure.value) = lower(ocp.value) 26 | ) AS matches 27 | ; 28 | -------------------------------------------------------------------------------- /koku/api/report/test/gcp_static_data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | generators: 3 | - ComputeEngineGenerator: 4 | start_date: {{start_date}} 5 | end_date: {{end_date}} 6 | cost: 2557 7 | currency: USD 8 | - ComputeEngineGenerator: 9 | start_date: {{start_date}} 10 | end_date: {{end_date}} 11 | cost: 25 12 | currency: USD 13 | - ComputeEngineGenerator: 14 | start_date: {{start_date}} 15 | end_date: {{end_date}} 16 | cost: 231 17 | currency: USD 18 | - ComputeEngineGenerator: 19 | start_date: {{start_date}} 20 | end_date: {{end_date}} 21 | cost: 29 22 | currency: USD 23 | #Adding a different service for limit testing 24 | service.description: "Stackdriver Logging" 25 | service.id: "5490-F7B7-8DF6" 26 | - CloudStorageGenerator: 27 | start_date: {{start_date}} 28 | end_date: {{end_date}} 29 | currency: USD 30 | 31 | projects: 32 | - 33 | billing_account_id: example_account_id 34 | project.name: billion-force-58425800 35 | project.id: example-project-id 36 | project.labels: step:chair;year:each 37 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0306_subsidmap.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-09-11 13:06 2 | import django.db.models.deletion 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("reporting", "0305_pvc"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="SubsIDMap", 16 | fields=[ 17 | ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), 18 | ("usage_id", models.TextField(unique=True)), 19 | ( 20 | "source_uuid", 21 | models.ForeignKey( 22 | db_column="source_uuid", 23 | on_delete=django.db.models.deletion.CASCADE, 24 | to="reporting.tenantapiprovider", 25 | ), 26 | ), 27 | ], 28 | options={ 29 | "db_table": "reporting_subs_id_map", 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /docs/generating-release-notes.md: -------------------------------------------------------------------------------- 1 | # Generating Release Notes 2 | 3 | Under [actions](https://github.com/project-koku/koku/actions), select the [Create Pre-Release](https://github.com/project-koku/koku/actions/workflows/pre-release.yaml) action on the left. Click the **Run workflow** dropdown and enter the full commit hash for the commit pushed into Production. Select Run workflow, and after a few seconds, the action will run. 4 | 5 | 6 | ![run workflow](./images/generating-release-notes/run-workflow.png) 7 | 8 | This action publishes a [Pre-release](https://github.com/project-koku/koku/releases). Select the newly created pre-release and auto-generate the release notes. Auto-generating the notes will add a **What's Changed** section listing each commit since the last release and a link to the full changelog. 9 | 10 | ![auto-gen notes](./images/generating-release-notes/auto-generate-notes.png) 11 | 12 | The release engineer will fill in a high level summary of the changes that were released by replacing **TODO: insert summary**. Once done writing the summary, uncheck **Set as a pre-release** and check **Set as the latest release**. 13 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | relative_files = True 4 | branch = True 5 | omit = 6 | .tox/* 7 | scripts/* 8 | dev/scripts/* 9 | *virtualenv/* 10 | *virtualenvs/* 11 | *manage.py 12 | *celery.py 13 | *configurator.py 14 | database.py 15 | *feature_flags.py 16 | *probe_server.py 17 | *settings.py 18 | *sentry.py 19 | *urls.py 20 | *wsgi.py 21 | */migrations/* 22 | */test/* 23 | *test/* 24 | */tests_*.py 25 | */test_*.py 26 | *provider_interface.py 27 | *dev_middleware.py 28 | 29 | [paths] 30 | source = koku 31 | 32 | [report] 33 | # Regexes for lines to exclude from consideration 34 | exclude_lines = 35 | # Have to re-enable the standard pragma 36 | pragma: no cover 37 | 38 | # Don't complain about missing debug-only code: 39 | def __repr__ 40 | if self\.debug 41 | 42 | # Don't complain if tests don't hit defensive assertion code: 43 | raise AssertionError 44 | raise NotImplementedError 45 | 46 | # Don't complain if non-runnable code isn't run: 47 | if 0: 48 | if __name__ == .__main__.: 49 | 50 | ignore_errors = True 51 | -------------------------------------------------------------------------------- /licenses/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Red Hat, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /koku/api/common/permissions/azure_access.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Defines the Azure Access Permissions class.""" 6 | from django.conf import settings 7 | from rest_framework import permissions 8 | 9 | 10 | class AzureAccessPermission(permissions.BasePermission): 11 | """Determines if a user can view Azure data.""" 12 | 13 | resource_type = "azure.subscription_guid" 14 | 15 | def has_permission(self, request, view): 16 | """Check permission to view Azure data.""" 17 | if settings.ENHANCED_ORG_ADMIN and request.user.admin: 18 | return True 19 | 20 | resource_access = request.user.access 21 | if resource_access is None or not isinstance(resource_access, dict): 22 | return False 23 | 24 | res_type_access = resource_access.get(AzureAccessPermission.resource_type, {}) 25 | if request.method in permissions.SAFE_METHODS: 26 | # Check permissions for read-only request 27 | read_access = res_type_access.get("read", []) 28 | return len(read_access) > 0 29 | 30 | return False 31 | -------------------------------------------------------------------------------- /dev/scripts/validate_dashboards.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import json 3 | import pathlib 4 | import sys 5 | 6 | import yaml 7 | 8 | 9 | def main() -> None: 10 | errors = {} 11 | files = [pathlib.Path(file) for file in sys.argv[1:]] 12 | for file in files: 13 | try: 14 | yaml_data = yaml.safe_load(file.read_text()) 15 | except yaml.error.YAMLError as yaml_exc: 16 | errors[file.name] = f"Error loading YAML: {yaml_exc}" 17 | continue 18 | 19 | try: 20 | dashboards = yaml_data["data"] 21 | except (KeyError, TypeError) as exc: 22 | errors[file.name] = f"Error getting 'data' field: {exc}" 23 | continue 24 | 25 | for dashboard in dashboards: 26 | try: 27 | json.loads(yaml_data["data"][dashboard]) 28 | except (json.decoder.JSONDecodeError, TypeError) as exc: 29 | errors[file.name] = f"Error loading JSON data from '{dashboard}': {exc}" 30 | 31 | if errors: 32 | sys.exit("\n".join(f"{k}: {v}" for k, v in errors.items())) 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /koku/api/settings/tags/mapping/query_handler.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 Red Hat Inc. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | """Query handler for Tag Mappings.""" 6 | import dataclasses 7 | import uuid 8 | from collections import OrderedDict 9 | from typing import Union 10 | 11 | 12 | @dataclasses.dataclass(frozen=True) 13 | class TagKey: 14 | uuid: str 15 | key: uuid.UUID 16 | source_type: str 17 | 18 | 19 | @dataclasses.dataclass(frozen=True) 20 | class Relationship: 21 | parent: TagKey 22 | children: list[TagKey] = dataclasses.field(default_factory=list) 23 | 24 | @classmethod 25 | def create_list_of_relationships( 26 | cls, data: list[dict[str : dict[str, Union[uuid.UUID, str]]]] 27 | ) -> list["Relationship"]: 28 | result = OrderedDict() 29 | 30 | for item in data: 31 | parent = TagKey(**item["parent"]) 32 | child = TagKey(**item["child"]) 33 | if parent in result: 34 | result[parent].children.append(child) 35 | else: 36 | result[parent] = Relationship(parent, [child]) 37 | 38 | return list(result.values()) 39 | -------------------------------------------------------------------------------- /koku/reporting/migrations/0305_pvc.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-07-31 19:40 2 | from django.db import migrations 3 | from django.db import models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("reporting", "0304_subslastprocessed"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="ocpvolumesummarybyprojectp", 15 | name="persistentvolumeclaim", 16 | field=models.CharField(max_length=253, null=True), 17 | ), 18 | migrations.AddField( 19 | model_name="ocpvolumesummarybyprojectp", 20 | name="storageclass", 21 | field=models.CharField(max_length=253, null=True), 22 | ), 23 | migrations.AddField( 24 | model_name="ocpvolumesummaryp", 25 | name="persistentvolumeclaim", 26 | field=models.CharField(max_length=253, null=True), 27 | ), 28 | migrations.AddField( 29 | model_name="ocpvolumesummaryp", 30 | name="storageclass", 31 | field=models.CharField(max_length=253, null=True), 32 | ), 33 | ] 34 | --------------------------------------------------------------------------------