├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------