├── __init__.py ├── Squest ├── __init__.py ├── api │ ├── __init__.py │ ├── celery_tasks_views.py │ └── authentication.py ├── utils │ ├── __init__.py │ ├── squest_encoder.py │ ├── squest_table.py │ └── ansible_when.py ├── middleware │ └── __init__.py ├── models │ ├── __init__.py │ └── singleton_model.py ├── version.py ├── wsgi.py └── asgi.py ├── tests ├── __init__.py ├── test_plugins │ ├── __init__.py │ ├── field_validators_test │ │ ├── __init__.py │ │ ├── superior_to_10.py │ │ └── even_number.py │ ├── survey_validators_test │ │ └── __init__.py │ └── test_provided_validators │ │ └── __init__.py ├── test_profiles │ ├── __init__.py │ ├── base │ │ └── __init__.py │ ├── test_api │ │ ├── __init__.py │ │ ├── test_urls │ │ │ ├── __init__.py │ │ │ └── test_quota │ │ │ │ └── __init__.py │ │ ├── test_user │ │ │ └── __init__.py │ │ └── test_notification_filter │ │ │ ├── __init__.py │ │ │ ├── test_instance_notification │ │ │ └── __init__.py │ │ │ └── test_request_notification │ │ │ └── __init__.py │ ├── test_forms │ │ ├── __init__.py │ │ └── test_notification_filter_form.py │ ├── test_model │ │ └── __init__.py │ ├── test_urls │ │ ├── __init__.py │ │ └── test_user.py │ ├── test_views │ │ └── __init__.py │ └── test_notification_filters │ │ └── __init__.py ├── test_service_catalog │ ├── __init__.py │ ├── test_api │ │ ├── __init__.py │ │ ├── test_urls │ │ │ ├── __init__.py │ │ │ ├── test_instance │ │ │ │ └── __init__.py │ │ │ ├── test_request │ │ │ │ ├── __init__.py │ │ │ │ └── test_state_machine │ │ │ │ │ └── __init__.py │ │ │ └── test_approvalworkflow │ │ │ │ └── __init__.py │ │ ├── test_views │ │ │ ├── __init__.py │ │ │ └── test_approvalworkflow │ │ │ │ └── __init__.py │ │ ├── test_instance │ │ │ ├── __init__.py │ │ │ ├── test_spec │ │ │ │ └── __init__.py │ │ │ └── test_user_spec │ │ │ │ ├── __init__.py │ │ │ │ ├── test_user_spec_put.py │ │ │ │ └── test_user_spec_patch.py │ │ ├── test_operation │ │ │ ├── __init__.py │ │ │ └── test_survey │ │ │ │ └── __init__.py │ │ ├── test_portfolio │ │ │ └── __init__.py │ │ ├── test_request │ │ │ ├── __init__.py │ │ │ └── test_request_state_machine │ │ │ │ └── __init__.py │ │ ├── test_service │ │ │ └── __init__.py │ │ ├── test_custom_link │ │ │ └── __init__.py │ │ ├── test_serializers │ │ │ └── __init__.py │ │ └── test_tower_server │ │ │ ├── __init__.py │ │ │ └── test_job_template │ │ │ └── __init__.py │ ├── test_urls │ │ ├── __init__.py │ │ ├── test_instance │ │ │ └── __init__.py │ │ ├── test_request │ │ │ ├── __init__.py │ │ │ └── test_state_machine │ │ │ │ └── __init__.py │ │ └── test_doc.py │ ├── test_filters │ │ └── __init__.py │ ├── test_forms │ │ └── __init__.py │ ├── test_models │ │ ├── __init__.py │ │ ├── test_doc.py │ │ └── test_service.py │ ├── test_views │ │ ├── __init__.py │ │ ├── test_admin │ │ │ ├── __init__.py │ │ │ ├── test_request │ │ │ │ └── __init__.py │ │ │ ├── test_support │ │ │ │ └── __init__.py │ │ │ ├── test_tools │ │ │ │ ├── __init__.py │ │ │ │ ├── test_tower │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── base_test_tower.py │ │ │ │ ├── test_catalog │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── test_services │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ └── test_list.py │ │ │ │ │ ├── test_operations │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ └── test_list.py │ │ │ │ │ └── test_portfolio │ │ │ │ │ │ └── __init__.py │ │ │ │ ├── test_custom_links │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── test_list.py │ │ │ │ │ └── base_test_custom_link.py │ │ │ │ ├── test_instance_hook │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── test_list.py │ │ │ │ └── test_request_hook │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── test_list.py │ │ │ ├── test_announcement │ │ │ │ └── __init__.py │ │ │ ├── test_approval │ │ │ │ └── __init__.py │ │ │ └── test_instance │ │ │ │ └── __init__.py │ │ ├── test_common │ │ │ └── __init__.py │ │ └── test_customer │ │ │ ├── __init__.py │ │ │ ├── test_catalog │ │ │ └── __init__.py │ │ │ ├── test_instance │ │ │ ├── __init__.py │ │ │ └── test_instance_details.py │ │ │ └── test_request │ │ │ └── __init__.py │ ├── test_serializers │ │ └── __init__.py │ └── test_template_tags │ │ └── __init__.py ├── test_resource_tracker_v2 │ ├── __init__.py │ ├── test_api │ │ ├── __init__.py │ │ ├── test_urls │ │ │ └── __init__.py │ │ └── test_serializers │ │ │ └── __init__.py │ ├── test_forms │ │ └── __init__.py │ ├── test_model │ │ ├── __init__.py │ │ └── test_attribute_definition.py │ ├── test_urls │ │ └── __init__.py │ └── test_views │ │ └── __init__.py ├── setup │ ├── __init__.py │ ├── setup_org.py │ ├── setup_team.py │ └── setup_service.py └── test_swagger_view.py ├── monitoring ├── __init__.py ├── migrations │ └── __init__.py ├── admin.py ├── urls.py └── apps.py ├── plugins ├── __init__.py ├── field_validators │ ├── __init__.py │ └── is_json.py └── survey_validators │ └── __init__.py ├── profiles ├── __init__.py ├── api │ ├── __init__.py │ ├── serializers │ │ ├── team_serializer.py │ │ ├── role_serializer.py │ │ ├── contenttype_serializer.py │ │ ├── permission_serializers.py │ │ ├── globalscope_serializer.py │ │ ├── rbac_serializers.py │ │ ├── organization_serializer.py │ │ └── __init__.py │ └── views │ │ ├── __init__.py │ │ ├── role_api_views.py │ │ ├── team_api_views.py │ │ ├── globalscope_api_views.py │ │ ├── user_api_views.py │ │ ├── permission_api_views.py │ │ └── organization_api_views.py ├── default_rbac │ └── __init__.py ├── migrations │ ├── __init__.py │ ├── 0013_delete_billinggroup.py │ ├── 0015_profile_theme.py │ ├── 0022_alter_permission_options.py │ ├── 0019_alter_quota_options.py │ ├── 0007_auto_20221005_1107.py │ └── 0002_auto_20211105_0946.py ├── templatetags │ └── __init__.py ├── admin.py ├── filters │ ├── __init__.py │ ├── role_filter.py │ ├── team_filter.py │ ├── organization_filter.py │ ├── notification_filter_filter.py │ ├── quota.py │ ├── permission_filter.py │ └── user_filter.py ├── forms │ ├── __init__.py │ ├── role_forms.py │ ├── team_forms.py │ ├── organization_forms.py │ ├── globalscope_forms.py │ ├── permission_form.py │ ├── token_forms.py │ ├── model_permission_form.py │ └── scope_form.py ├── tables │ ├── __init__.py │ ├── team_quota_limit_table.py │ ├── instance_consumption_table.py │ ├── permission_table.py │ ├── approval_workflow.py │ └── notification_filter_table.py ├── views │ └── __init__.py └── models │ ├── rbac.py │ ├── __init__.py │ ├── notification_filter.py │ └── squest_permission.py ├── scripts └── __init__.py ├── resource_tracker_v2 ├── __init__.py ├── api │ ├── __init__.py │ ├── views │ │ ├── __init__.py │ │ ├── resource_api_view.py │ │ ├── transformer_api_views.py │ │ ├── resource_group_api_views.py │ │ └── attribute_definition_api_views.py │ └── serializers │ │ ├── __init__.py │ │ ├── attribute_definition_serializers.py │ │ └── resource_group_serializers.py ├── forms │ ├── __init__.py │ ├── resource_group_form.py │ └── attribute_definition_form.py ├── tables │ ├── __init__.py │ ├── attribute_defintion_table.py │ └── transformer_table.py ├── filters │ ├── __init__.py │ ├── resource_group_filter.py │ ├── attribute_definition_filter.py │ ├── resource_filter.py │ └── transformer_filter.py ├── management │ ├── __init__.py │ └── commands │ │ └── __init__.py ├── migrations │ ├── __init__.py │ └── 0004_alter_transformer_factor.py ├── views │ ├── utils │ │ └── __init__.py │ └── __init__.py ├── templatetags │ ├── __init__.py │ └── resource_filters.py ├── tests.py ├── admin.py ├── apps.py └── models │ ├── __init__.py │ ├── resource_group.py │ └── resource_attribute.py ├── service_catalog ├── api │ ├── __init__.py │ ├── serializers │ │ ├── task_result_serializer.py │ │ ├── job_template_serializer.py │ │ ├── custom_link_serializer.py │ │ ├── portfolio_serializer.py │ │ ├── __init__.py │ │ ├── service_serializers.py │ │ ├── operation_serializers.py │ │ ├── request_message_serializer.py │ │ ├── tower_server_serializer.py │ │ ├── approval_step_state_serializer.py │ │ └── approval_workflow_state_serializer.py │ └── views │ │ ├── job_template_api_views.py │ │ ├── portfolio_api_views.py │ │ ├── custom_link_api_views.py │ │ ├── approval_step_api_views.py │ │ ├── __init__.py │ │ └── tower_server_api_views.py ├── filters │ ├── __init__.py │ ├── approval_step_filter.py │ ├── service_filter.py │ ├── tower_server_filter.py │ ├── portfolio_filter.py │ ├── job_template_filter.py │ ├── approval_workflow_filter.py │ ├── email_template_filter.py │ ├── global_hook_filter.py │ ├── doc_filter.py │ ├── operation_filter.py │ ├── custom_link_filter.py │ └── support_filter.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── martor_cleanup.py ├── migrations │ ├── __init__.py │ ├── 0045_alter_doc_options.py │ ├── 0046_alter_doc_options.py │ ├── 0007_service_external_support_url.py │ ├── 0033_alter_approvalstepstate_options_and_more.py │ ├── 0020_alter_approvalstep_options.py │ ├── 0010_doc_operations.py │ ├── 0021_approvalstep_auto_accept_condition.py │ ├── 0025_alter_approvalstep_auto_accept_condition.py │ ├── 0043_operation_when.py │ ├── 0034_alter_request_approval_workflow_state.py │ ├── 0031_service_attribute_definitions.py │ ├── 0029_alter_instance_requester.py │ ├── 0032_alter_operation_name_alter_operation_type.py │ ├── 0036_approvalworkflow_enabled.py │ ├── 0041_operation_validators.py │ ├── 0038_alter_towersurveyfield_unique_together_and_more.py │ ├── 0016_alter_request_options.py │ ├── 0027_alter_approvalworkflow_scopes.py │ ├── 0028_set_last_update_on_request.py │ ├── 0035_alter_request_options.py │ └── 0022_auto_20230906_1539.py ├── tables │ ├── __init__.py │ ├── doc_tables.py │ ├── portfolio_tables.py │ ├── email_template_table.py │ ├── custom_link_table.py │ ├── support_tables.py │ ├── annoucement_tables.py │ ├── service_tables.py │ └── tower_server_tables.py ├── templatetags │ ├── __init__.py │ ├── maintenance.py │ └── version.py ├── __init__.py ├── models │ ├── approval_state.py │ ├── exceptions.py │ ├── operation_type.py │ ├── bootstrap_type.py │ ├── squest_settings.py │ ├── request_state.py │ ├── instance_state.py │ ├── inventory.py │ └── credential.py ├── forms │ ├── request_forms.py │ ├── custom_link_form.py │ ├── doc_forms.py │ ├── __init__.py │ ├── global_hook_forms.py │ ├── tower_server_update_token_forms.py │ └── request_message_forms.py ├── celery.py ├── views │ ├── login.py │ ├── __init__.py │ └── custom_link.py └── admin.py ├── docker ├── gitconfig ├── environment_variables │ ├── redis.env │ ├── phpmyadmin.env │ ├── rabbitmq.env │ └── db.env ├── scripts │ ├── mariadb-init.sh │ └── psql-init.sh ├── maintenance.nginx.conf ├── entrypoint.sh └── Caddyfile ├── templates ├── 403_csrf.html ├── generics │ ├── custom_columns │ │ ├── generic_boolean.html │ │ ├── generic_date_format.html │ │ ├── generic_actions.html │ │ └── generic_boolean_check.html │ ├── ajax-option.html │ ├── buttons │ │ ├── bulk_delete_button.html │ │ ├── add_button.html │ │ ├── edit_button.html │ │ └── delete_button.html │ ├── table │ │ └── table.html │ ├── generic_form.html │ └── breadcrumbs.html ├── service_catalog │ ├── custom_columns │ │ ├── tower_server_host.html │ │ ├── operation_type.html │ │ ├── support_state.html │ │ ├── preview_workflow.html │ │ ├── service_operations.html │ │ ├── tower_server_job_templates.html │ │ ├── service_actions.html │ │ ├── create_operation_request.html │ │ ├── global_hook_state.html │ │ ├── job_template_compliant.html │ │ ├── operation_request.html │ │ ├── tower_server_actions.html │ │ └── job_template_actions.html │ ├── buttons │ │ ├── instance-archived-list.html │ │ ├── request-archived-list.html │ │ ├── reject_button.html │ │ ├── tower_server_new_token.html │ │ ├── manage_docs.html │ │ ├── operation_survey_button.html │ │ ├── delete_operation_button.html │ │ ├── edit_operation_button.html │ │ ├── instance_edit_button.html │ │ └── request_state_machine.html │ ├── admin │ │ └── service │ │ │ └── operation │ │ │ └── operation-button-edit-survey.html │ └── mails │ │ └── utils │ │ └── header.html ├── resource_tracker_v2 │ └── resource_group │ │ ├── custom_columns │ │ ├── attribute_value.html │ │ ├── tags.html │ │ ├── resource_group_resource.html │ │ ├── resource_group_attributes_button.html │ │ └── resource_group_attribute_actions.html │ │ ├── button_switch_table.html │ │ ├── button_switch_csv.html │ │ └── resources │ │ ├── resource-delete-button.html │ │ ├── resource-edit-button.html │ │ ├── resource-move-button.html │ │ ├── custom_columns │ │ └── resource_operations.html │ │ └── resource_list_buttons.html ├── profiles │ ├── custom_columns │ │ ├── preview_workflow.html │ │ ├── delete_all_user_roles.html │ │ └── user_roles.html │ ├── buttons │ │ └── manage_all_users.html │ └── forms │ │ └── form_headers │ │ ├── instance_notification_filter_header.html │ │ └── request_notification_filter_header.html ├── 404.html ├── 500.html └── 403.html ├── k8s ├── requirements.txt ├── requirements.yml ├── squest_k8s │ ├── defaults │ │ └── main.yml │ ├── templates │ │ ├── common │ │ │ └── labels.yml │ │ └── squest_secrets.yaml │ └── tasks │ │ ├── 06-celery.yml │ │ ├── 03-redis.yml │ │ └── 02-rabbitmq.yml ├── .ansible-lint └── deploy.yml ├── docs ├── images │ ├── squest_logo_v2.png │ ├── rbac.png │ ├── quota.png │ ├── vm_rg.png │ ├── attribute.png │ ├── cluster_rg.png │ ├── k8s_example.png │ ├── permissions.png │ ├── survey_quota.png │ ├── approval_workflow.png │ ├── squest_full_logo.png │ ├── squest_logo_white.png │ └── grafana_panel_example.png └── index.md ├── playbook_examples ├── squest_inventory │ ├── inventory │ ├── squest_extra_vars │ │ ├── delete.yml │ │ ├── update.yml │ │ └── create.yml │ ├── group_vars │ │ └── squest_testing.yml │ └── README.md ├── test_get_api.yml └── file_service_example │ └── delete.yml ├── .dockerignore ├── project-static └── squest │ ├── img │ ├── admin.png │ ├── user.png │ ├── no_image.png │ ├── squest_logo.png │ ├── squest_logo_v2_100_100.png │ ├── squest_logo_v2_300_300.png │ ├── squest_logo_v2_600_600.png │ ├── squest_logo_v2_1417_1417.png │ └── squest_full_logo_transparent.v1.png │ ├── js │ ├── dashboards.js │ └── resource_group_attributes.js │ └── css │ └── squest-dark.css ├── default_data.yml ├── .coveragerc ├── docker-compose.override.yml ├── .github └── ISSUE_TEMPLATE │ ├── miscellaneous.md │ ├── feature_request.md │ └── bug_report.md ├── package.json ├── dev.docker-compose.yml ├── tls.docker-compose.yml ├── psql.docker-compose.yaml ├── ldap.docker-compose.yml └── manage.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /monitoring/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/models/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /monitoring/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/default_rbac/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/field_validators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/survey_validators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/tables/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/filters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/tables/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/filters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/views/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service_catalog/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/gitconfig: -------------------------------------------------------------------------------- 1 | [safe] 2 | directory = /app 3 | -------------------------------------------------------------------------------- /profiles/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /resource_tracker_v2/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/403_csrf.html: -------------------------------------------------------------------------------- 1 | {% include "403.html" %} 2 | -------------------------------------------------------------------------------- /tests/test_plugins/field_validators_test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_plugins/survey_validators_test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_user/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_filters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resource_tracker_v2/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /tests/test_plugins/test_provided_validators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_notification_filters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_serializers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_template_tags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /k8s/requirements.txt: -------------------------------------------------------------------------------- 1 | kubernetes==24.2.0 2 | ansible==11.6.0 3 | -------------------------------------------------------------------------------- /resource_tracker_v2/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_urls/test_quota/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_api/test_urls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_instance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_operation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_portfolio/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_request/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_service/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_urls/test_instance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_urls/test_request/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_customer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_notification_filter/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_api/test_serializers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_custom_link/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_serializers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_tower_server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Squest/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.8.1" 2 | VERSION = __version__ 3 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_instance/test_spec/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_urls/test_instance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_urls/test_request/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_request/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_support/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_instance/test_user_spec/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_operation/test_survey/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_announcement/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_approval/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_instance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_customer/test_catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_customer/test_instance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_customer/test_request/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_tower_server/test_job_template/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_urls/test_approvalworkflow/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_views/test_approvalworkflow/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_urls/test_request/test_state_machine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_tower/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/squest_logo_v2.png: -------------------------------------------------------------------------------- 1 | ../../project-static/squest/img/squest_logo_v2_300_300.png -------------------------------------------------------------------------------- /templates/generics/custom_columns/generic_boolean.html: -------------------------------------------------------------------------------- 1 | {{ value | display_boolean }} 2 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_request/test_request_state_machine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /k8s/requirements.yml: -------------------------------------------------------------------------------- 1 | collections: 2 | - name: kubernetes.core 3 | version: 5.2.0 4 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_notification_filter/test_instance_notification/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_profiles/test_api/test_notification_filter/test_request_notification/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_urls/test_request/test_state_machine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_custom_links/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_instance_hook/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_request_hook/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/rbac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/rbac.png -------------------------------------------------------------------------------- /monitoring/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /templates/generics/custom_columns/generic_date_format.html: -------------------------------------------------------------------------------- 1 | {{ value | squest_date_format }} 2 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/test_services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/environment_variables/redis.env: -------------------------------------------------------------------------------- 1 | TZ=Europe/Paris 2 | REDIS_PASSWORD=redis_secret_password 3 | -------------------------------------------------------------------------------- /docs/images/quota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/quota.png -------------------------------------------------------------------------------- /docs/images/vm_rg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/vm_rg.png -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/inventory: -------------------------------------------------------------------------------- 1 | [squest_testing] 2 | localhost ansible_connection=local -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/test_operations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/test_portfolio/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/attribute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/attribute.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /static 2 | /node_modules 3 | /media 4 | /docker/certs/* 5 | /backup 6 | /site 7 | /htmlcov 8 | -------------------------------------------------------------------------------- /docs/images/cluster_rg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/cluster_rg.png -------------------------------------------------------------------------------- /docs/images/k8s_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/k8s_example.png -------------------------------------------------------------------------------- /docs/images/permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/permissions.png -------------------------------------------------------------------------------- /docs/images/survey_quota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/survey_quota.png -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/tower_server_host.html: -------------------------------------------------------------------------------- 1 | {{ record.url }} -------------------------------------------------------------------------------- /docs/images/approval_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/approval_workflow.png -------------------------------------------------------------------------------- /docs/images/squest_full_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/squest_full_logo.png -------------------------------------------------------------------------------- /docs/images/squest_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/squest_logo_white.png -------------------------------------------------------------------------------- /project-static/squest/img/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/admin.png -------------------------------------------------------------------------------- /project-static/squest/img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/user.png -------------------------------------------------------------------------------- /default_data.yml: -------------------------------------------------------------------------------- 1 | users: 2 | - username: admin 3 | email: admin@squest.domain 4 | password: admin 5 | is_admin: true 6 | -------------------------------------------------------------------------------- /docs/images/grafana_panel_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/docs/images/grafana_panel_example.png -------------------------------------------------------------------------------- /project-static/squest/img/no_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/no_image.png -------------------------------------------------------------------------------- /project-static/squest/img/squest_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_logo.png -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/custom_columns/attribute_value.html: -------------------------------------------------------------------------------- 1 | {{ record | v2_get_attribute_value_from_name:attribute_name }} 2 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/operation_type.html: -------------------------------------------------------------------------------- 1 | {{ record.type }} -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | 3 | omit = 4 | tests/* 5 | Squest/* 6 | manage.py 7 | */apps.py 8 | service_catalog/management/commands/* 9 | -------------------------------------------------------------------------------- /docker/environment_variables/phpmyadmin.env: -------------------------------------------------------------------------------- 1 | TZ=Europe/Paris 2 | PMA_HOST=db 3 | PMA_ARBITRARY=1 4 | MYSQL_USERNAME=root 5 | MYSQL_ROOT_PASSWORD=p@ssw0rd 6 | -------------------------------------------------------------------------------- /project-static/squest/img/squest_logo_v2_100_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_logo_v2_100_100.png -------------------------------------------------------------------------------- /project-static/squest/img/squest_logo_v2_300_300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_logo_v2_300_300.png -------------------------------------------------------------------------------- /project-static/squest/img/squest_logo_v2_600_600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_logo_v2_600_600.png -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/support_state.html: -------------------------------------------------------------------------------- 1 | {{ record.get_state_display }} 2 | -------------------------------------------------------------------------------- /docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | # this file is loaded automatically when running "docker-compose up" 2 | services: 3 | nginx: 4 | ports: 5 | - "8080:8080" 6 | -------------------------------------------------------------------------------- /docker/environment_variables/rabbitmq.env: -------------------------------------------------------------------------------- 1 | TZ=Europe/Paris 2 | RABBITMQ_DEFAULT_USER=rabbitmq 3 | RABBITMQ_DEFAULT_PASS=rabbitmq 4 | RABBITMQ_DEFAULT_VHOST=squest 5 | -------------------------------------------------------------------------------- /project-static/squest/img/squest_logo_v2_1417_1417.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_logo_v2_1417_1417.png -------------------------------------------------------------------------------- /templates/profiles/custom_columns/preview_workflow.html: -------------------------------------------------------------------------------- 1 | 2 | Preview 3 | 4 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/preview_workflow.html: -------------------------------------------------------------------------------- 1 | 2 | Preview 3 | 4 | -------------------------------------------------------------------------------- /k8s/squest_k8s/defaults/main.yml: -------------------------------------------------------------------------------- 1 | mariadb_chart_version: 20.5.6 2 | phpmyadmin_chart_version: 18.1.8 3 | rabbitmq_chart_version: 16.0.3 4 | redis_chart_version: 21.1.7 5 | 6 | 7 | -------------------------------------------------------------------------------- /project-static/squest/img/squest_full_logo_transparent.v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HewlettPackard/squest/HEAD/project-static/squest/img/squest_full_logo_transparent.v1.png -------------------------------------------------------------------------------- /templates/generics/ajax-option.html: -------------------------------------------------------------------------------- 1 | 2 | {% for option in options %} 3 | 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/generics/custom_columns/generic_actions.html: -------------------------------------------------------------------------------- 1 | {% include 'generics/custom_columns/generic_edit.html' %} 2 | {% include 'generics/custom_columns/generic_delete.html' %} 3 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/custom_columns/tags.html: -------------------------------------------------------------------------------- 1 | {% for tag in record.tags.all %} 2 | {{ tag }} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/profiles/buttons/manage_all_users.html: -------------------------------------------------------------------------------- 1 | 2 | Manage all users 3 | 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/miscellaneous.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Miscellaneous 3 | about: All other issue than bug of feature request 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /docker/scripts/mariadb-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | mariadb --user=root --password=${MYSQL_ROOT_PASSWORD} <<-EOSQL 5 | GRANT ALL PRIVILEGES ON test_squest_db.* TO '${MYSQL_USER}'@'%'; 6 | EOSQL 7 | -------------------------------------------------------------------------------- /monitoring/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from monitoring import views 3 | 4 | app_name = 'monitoring' 5 | 6 | 7 | urlpatterns = [ 8 | path('', views.metrics, name='metrics'), 9 | ] 10 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/button_switch_table.html: -------------------------------------------------------------------------------- 1 | 2 | List view 3 | 4 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/button_switch_csv.html: -------------------------------------------------------------------------------- 1 | 2 | Table view 3 | 4 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/instance-archived-list.html: -------------------------------------------------------------------------------- 1 | 2 | Archived instances 3 | 4 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/request-archived-list.html: -------------------------------------------------------------------------------- 1 | 2 | Archived requests 3 | 4 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/service_operations.html: -------------------------------------------------------------------------------- 1 | 2 | {{ record.operations.count }} 3 | -------------------------------------------------------------------------------- /k8s/squest_k8s/templates/common/labels.yml: -------------------------------------------------------------------------------- 1 | {% if squest_django.extra_labels is defined %} 2 | {% for key, value in squest_django.extra_labels.items() %} 3 | {{ key }}: '{{ value }}' 4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /resource_tracker_v2/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ResourceTrackerV2Config(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'resource_tracker_v2' 7 | -------------------------------------------------------------------------------- /service_catalog/__init__.py: -------------------------------------------------------------------------------- 1 | # This will make sure the app is always imported when 2 | # Django starts so that shared_task will use this app. 3 | from .celery import app as celery_app 4 | 5 | __all__ = ('celery_app',) 6 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/reject_button.html: -------------------------------------------------------------------------------- 1 | 4 | Reject 5 | 6 | -------------------------------------------------------------------------------- /templates/generics/custom_columns/generic_boolean_check.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /service_catalog/models/approval_state.py: -------------------------------------------------------------------------------- 1 | from django.db.models import IntegerChoices 2 | 3 | 4 | class ApprovalState(IntegerChoices): 5 | PENDING = 1, 'PENDING' 6 | APPROVED = 2, 'APPROVED' 7 | REJECTED = 3, 'REJECTED' 8 | -------------------------------------------------------------------------------- /profiles/filters/__init__.py: -------------------------------------------------------------------------------- 1 | from .permission_filter import * 2 | from .notification_filter_filter import * 3 | from .organization_filter import * 4 | from .team_filter import * 5 | from .user_filter import * 6 | from .role_filter import * 7 | -------------------------------------------------------------------------------- /profiles/filters/role_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models import Role 3 | 4 | 5 | class RoleFilter(SquestFilter): 6 | class Meta: 7 | model = Role 8 | fields = ['name'] 9 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/custom_columns/resource_group_resource.html: -------------------------------------------------------------------------------- 1 | 4 | {{ record.resources.all|length }} 5 | 6 | -------------------------------------------------------------------------------- /profiles/forms/__init__.py: -------------------------------------------------------------------------------- 1 | from .token_forms import * 2 | from .request_notification_form import * 3 | from .instance_notification_form import * 4 | from .organization_forms import * 5 | from .role_forms import * 6 | from .globalscope_forms import * 7 | -------------------------------------------------------------------------------- /profiles/forms/role_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models import Role 3 | 4 | 5 | class RoleForm(SquestModelForm): 6 | class Meta: 7 | model = Role 8 | fields = '__all__' 9 | -------------------------------------------------------------------------------- /resource_tracker_v2/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .attribute_definition_views import * 2 | from .resource_group_views import * 3 | from .transformer_views import * 4 | from .resource_group_resource_views import * 5 | from .resource_tracker_graph import * 6 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/tower_server_new_token.html: -------------------------------------------------------------------------------- 1 | 4 | Update token 5 | 6 | -------------------------------------------------------------------------------- /profiles/filters/team_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models.team import Team 3 | 4 | 5 | class TeamFilter(SquestFilter): 6 | class Meta: 7 | model = Team 8 | fields = ['name','org'] 9 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/manage_docs.html: -------------------------------------------------------------------------------- 1 | {% if request.user.is_staff %} 2 | 3 | Manage docs 4 | 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /Squest/utils/squest_encoder.py: -------------------------------------------------------------------------------- 1 | from json import JSONEncoder 2 | 3 | 4 | class SquestEncoder(JSONEncoder): 5 | def default(self, obj): 6 | if isinstance(obj, set): 7 | return list(obj) 8 | return JSONEncoder.default(self, obj) 9 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/custom_columns/resource_group_attributes_button.html: -------------------------------------------------------------------------------- 1 | 4 | {{ record.transformers.all|length }} 5 | 6 | -------------------------------------------------------------------------------- /profiles/api/serializers/team_serializer.py: -------------------------------------------------------------------------------- 1 | from profiles.api.serializers import ScopeSerializer 2 | from profiles.models import Team 3 | 4 | 5 | class TeamSerializer(ScopeSerializer): 6 | class Meta: 7 | model = Team 8 | fields = '__all__' 9 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/tower_server_job_templates.html: -------------------------------------------------------------------------------- 1 | 2 | {{ record.jobtemplate_set.all | length }} 3 | -------------------------------------------------------------------------------- /docker/environment_variables/db.env: -------------------------------------------------------------------------------- 1 | TZ=Europe/Paris 2 | # MySql variables 3 | MYSQL_ROOT_PASSWORD=p@ssw0rd 4 | MYSQL_DATABASE=squest_db 5 | MYSQL_USER=squest_user 6 | MYSQL_PASSWORD=squest_password 7 | 8 | # postgres variables 9 | POSTGRES_PASSWORD=p@ssw0rd 10 | -------------------------------------------------------------------------------- /profiles/tables/__init__.py: -------------------------------------------------------------------------------- 1 | from .notification_filter_table import * 2 | from .organization_table import * 3 | from .role_table import * 4 | from .team_table import * 5 | from .user_table import * 6 | from .permission_table import * 7 | from .approval_workflow import * 8 | -------------------------------------------------------------------------------- /templates/service_catalog/admin/service/operation/operation-button-edit-survey.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /profiles/forms/team_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models.team import Team 3 | 4 | 5 | class TeamForm(SquestModelForm): 6 | class Meta: 7 | model = Team 8 | fields = ["org", "name", "description", "roles"] 9 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/resources/resource-delete-button.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /profiles/api/serializers/role_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from profiles.models import Role 4 | 5 | 6 | class RoleSerializer(ModelSerializer): 7 | 8 | class Meta: 9 | model = Role 10 | fields = '__all__' 11 | -------------------------------------------------------------------------------- /profiles/filters/organization_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models.organization import Organization 3 | 4 | 5 | class OrganizationFilter(SquestFilter): 6 | class Meta: 7 | model = Organization 8 | fields = ['name'] 9 | -------------------------------------------------------------------------------- /service_catalog/filters/approval_step_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import ApprovalStep 3 | 4 | 5 | class ApprovalStepFilter(SquestFilter): 6 | class Meta: 7 | model = ApprovalStep 8 | fields = ["name"] 9 | -------------------------------------------------------------------------------- /service_catalog/models/exceptions.py: -------------------------------------------------------------------------------- 1 | class ExceptionServiceCatalog: 2 | class JobTemplateNotFound(Exception): 3 | def __init__(self, job_template_id, tower_name): 4 | super().__init__(f"Job Template ID {job_template_id} was not found in the server {tower_name}") 5 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/resources/resource-edit-button.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /service_catalog/filters/service_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import Service 3 | 4 | 5 | class ServiceFilter(SquestFilter): 6 | 7 | class Meta: 8 | model = Service 9 | fields = ['name', 'enabled'] 10 | -------------------------------------------------------------------------------- /service_catalog/filters/tower_server_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import TowerServer 3 | 4 | 5 | class TowerServerFilter(SquestFilter): 6 | class Meta: 7 | model = TowerServer 8 | fields = ['name', 'host'] 9 | -------------------------------------------------------------------------------- /service_catalog/filters/portfolio_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import Portfolio 3 | 4 | 5 | class PortfolioFilter(SquestFilter): 6 | class Meta: 7 | model = Portfolio 8 | fields = ['name', 'parent_portfolio'] 9 | -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/squest_extra_vars/delete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # fake squest data (make sure to have an instance with ID=1) 3 | squest: 4 | request: 5 | instance: 6 | id: 1 7 | spec: 8 | file_name: 'my_file.txt' 9 | uuid_file: 51b1d14c-cedf-5837-9063-b8cb45f950fe -------------------------------------------------------------------------------- /profiles/forms/organization_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models import Organization 3 | 4 | 5 | class OrganizationForm(SquestModelForm): 6 | class Meta: 7 | model = Organization 8 | fields = ["name", "description", "roles"] 9 | -------------------------------------------------------------------------------- /profiles/forms/globalscope_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models import GlobalScope 3 | 4 | 5 | class GlobalScopeForm(SquestModelForm): 6 | class Meta: 7 | model = GlobalScope 8 | fields = ["global_permissions", "owner_permissions"] 9 | -------------------------------------------------------------------------------- /service_catalog/models/operation_type.py: -------------------------------------------------------------------------------- 1 | from django.db.models import TextChoices 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class OperationType(TextChoices): 6 | CREATE = 'CREATE', _('Create') 7 | UPDATE = 'UPDATE', _('Update') 8 | DELETE = 'DELETE', _('Delete') 9 | -------------------------------------------------------------------------------- /profiles/api/serializers/contenttype_serializer.py: -------------------------------------------------------------------------------- 1 | from django.contrib.contenttypes.models import ContentType 2 | from rest_framework.serializers import ModelSerializer 3 | 4 | 5 | class ContentTypeSerializer(ModelSerializer): 6 | class Meta: 7 | model = ContentType 8 | fields = '__all__' 9 | -------------------------------------------------------------------------------- /docker/maintenance.nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /usr/share/nginx/html; 3 | listen 80; 4 | location / { 5 | try_files $uri $uri/ /index.html; 6 | } 7 | location ~ \.(ttf|ttc|otf|eot|woff|font.css|css|js)$ { 8 | add_header Access-Control-Allow-Origin "*"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docker/scripts/psql-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | psql <<-EOSQL 5 | CREATE USER ${DB_USER} PASSWORD '${DB_PASSWORD}' NOSUPERUSER CREATEDB CREATEROLE INHERIT LOGIN; 6 | CREATE DATABASE ${DB_DATABASE} OWNER ${DB_USER}; 7 | GRANT ALL PRIVILEGES ON `test\_squest\_db` . * TO '${DB_USER}'@'%'; 8 | EOSQL -------------------------------------------------------------------------------- /resource_tracker_v2/filters/resource_group_filter.py: -------------------------------------------------------------------------------- 1 | from resource_tracker_v2.filters.tag_filter import TagFilterset 2 | from resource_tracker_v2.models import ResourceGroup 3 | 4 | 5 | class ResourceGroupFilter(TagFilterset): 6 | class Meta: 7 | model = ResourceGroup 8 | fields = ['name', 'tag'] 9 | -------------------------------------------------------------------------------- /service_catalog/filters/job_template_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import JobTemplate 3 | 4 | 5 | class JobTemplateFilter(SquestFilter): 6 | class Meta: 7 | model = JobTemplate 8 | fields = ['tower_server', 'name', 'is_compliant'] 9 | -------------------------------------------------------------------------------- /profiles/api/serializers/permission_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from profiles.models.squest_permission import Permission 4 | 5 | 6 | class PermissionSerializer(ModelSerializer): 7 | class Meta: 8 | model = Permission 9 | fields = '__all__' 10 | -------------------------------------------------------------------------------- /k8s/squest_k8s/templates/squest_secrets.yaml: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | name: "squest-secrets" 5 | labels: 6 | app: squest 7 | service: django 8 | data: 9 | {% for key, value in squest_django.env_secrets.items() %} 10 | {{ key }}: "{{ value | b64encode }}" 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /templates/generics/buttons/bulk_delete_button.html: -------------------------------------------------------------------------------- 1 | {% has_perm request.user django_content_type.app_label|add:".delete_"|add:django_content_type.model as can_delete %} 2 | {% if can_delete %} 3 | 6 | {% endif %} 7 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/resources/resource-move-button.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /service_catalog/filters/approval_workflow_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models.approval_workflow import ApprovalWorkflow 3 | 4 | 5 | class ApprovalWorkflowFilter(SquestFilter): 6 | class Meta: 7 | model = ApprovalWorkflow 8 | fields = ["name", "enabled"] 9 | -------------------------------------------------------------------------------- /resource_tracker_v2/filters/attribute_definition_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from resource_tracker_v2.models import AttributeDefinition 3 | 4 | 5 | class AttributeDefinitionFilter(SquestFilter): 6 | class Meta: 7 | model = AttributeDefinition 8 | fields = ['name', 'services'] 9 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/task_result_serializer.py: -------------------------------------------------------------------------------- 1 | from django_celery_results.models import TaskResult 2 | from rest_framework import serializers 3 | 4 | 5 | class TaskResultSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = TaskResult 8 | fields = ['id', 'meta', 'status'] 9 | 10 | -------------------------------------------------------------------------------- /service_catalog/forms/request_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from service_catalog.models import Request 3 | 4 | 5 | class RequestForm(SquestModelForm): 6 | class Meta: 7 | model = Request 8 | exclude = ["periodic_task", "periodic_task_date_expire", "approval_workflow_state"] 9 | -------------------------------------------------------------------------------- /service_catalog/models/bootstrap_type.py: -------------------------------------------------------------------------------- 1 | from django.db.models import TextChoices 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class BootstrapType(TextChoices): 6 | SUCCESS = 'SUCCESS', _('SUCCESS') 7 | DANGER = 'DANGER', _('DANGER') 8 | WARNING = 'WARNING', _('WARNING') 9 | INFO = 'INFO', _('INFO') -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/service_actions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include 'generics/custom_columns/generic_actions.html' %} 5 | -------------------------------------------------------------------------------- /resource_tracker_v2/filters/resource_filter.py: -------------------------------------------------------------------------------- 1 | from resource_tracker_v2.filters.tag_filter import TagFilterset 2 | from resource_tracker_v2.models import Resource 3 | 4 | 5 | class ResourceFilter(TagFilterset): 6 | class Meta: 7 | model = Resource 8 | fields = ['resource_group','service_catalog_instance', 'name', 'tag'] 9 | -------------------------------------------------------------------------------- /resource_tracker_v2/filters/transformer_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from resource_tracker_v2.models import Transformer 3 | 4 | 5 | class TransformerFilter(SquestFilter): 6 | 7 | class Meta: 8 | model = Transformer 9 | fields = ['resource_group', 'attribute_definition'] 10 | -------------------------------------------------------------------------------- /profiles/api/serializers/globalscope_serializer.py: -------------------------------------------------------------------------------- 1 | from profiles.api.serializers import AbstractScopeSerializer 2 | from profiles.models import GlobalScope 3 | 4 | 5 | class GlobalScopeSerializer(AbstractScopeSerializer): 6 | class Meta: 7 | model = GlobalScope 8 | fields = ('global_permissions', 'owner_permissions', 'rbac') 9 | -------------------------------------------------------------------------------- /service_catalog/models/squest_settings.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.db.models import BooleanField 3 | 4 | from Squest.models.singleton_model import SingletonModel 5 | 6 | 7 | class SquestSettings(SingletonModel): 8 | maintenance_mode_enabled = BooleanField(default=False) 9 | 10 | 11 | admin.site.register(SquestSettings) 12 | -------------------------------------------------------------------------------- /templates/generics/buttons/add_button.html: -------------------------------------------------------------------------------- 1 | {% with perm=django_content_type.app_label|add:".add_"|add:django_content_type.model %} 2 | {% has_perm request.user perm as can_add %} 3 | {% if can_add %} 4 | 5 | Add 6 | 7 | {% endif %} 8 | {% endwith %} 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@highlightjs/cdn-assets": "^11.4.0", 4 | "admin-lte": "^3.1.0", 5 | "bootstrap": "^4.6.1", 6 | "bootstrap-select": "^1.13.18", 7 | "datatables.net": "^1.13.1", 8 | "datatables.net-bs4": "^1.13.1", 9 | "datatables.net-buttons": "^2.3.3", 10 | "datatables.net-buttons-bs4": "^2.3.3" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/squest_extra_vars/update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # survey variables 3 | file_content: 'this is my content' 4 | 5 | # fake squest data (make sure to have an instance with ID=1) 6 | squest: 7 | request: 8 | instance: 9 | id: 1 10 | spec: 11 | file_name: 'my_file.txt' 12 | uuid_file: 51b1d14c-cedf-5837-9063-b8cb45f950fe -------------------------------------------------------------------------------- /service_catalog/forms/custom_link_form.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from service_catalog.models.custom_link import CustomLink 3 | 4 | 5 | class CustomLinkForm(SquestModelForm): 6 | class Meta: 7 | model = CustomLink 8 | fields = ["name", "services", "text", "url", "button_class", "when", "loop", "enabled", "is_admin_only"] 9 | -------------------------------------------------------------------------------- /templates/profiles/forms/form_headers/instance_notification_filter_header.html: -------------------------------------------------------------------------------- 1 |
2 |
Support notification filter
3 |

When: Ansible like condition. instance is available in the context.

4 |

E.g: Access the instance spec: instance.spec['location'] == 'grenoble'

5 |
6 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/serializers/attribute_definition_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from resource_tracker_v2.models import AttributeDefinition 4 | 5 | 6 | class AttributeDefinitionSerializer(serializers.ModelSerializer): 7 | 8 | class Meta: 9 | model = AttributeDefinition 10 | fields = ["id", "name", "description"] 11 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/operation_survey_button.html: -------------------------------------------------------------------------------- 1 | {% has_perm request.user "service_catalog.change_operation" as can_change %} 2 | {% if can_change %} 3 | 5 | Survey 6 | 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /resource_tracker_v2/models/__init__.py: -------------------------------------------------------------------------------- 1 | from resource_tracker_v2.models.attribute_definition import AttributeDefinition 2 | from resource_tracker_v2.models.transformer import Transformer 3 | from resource_tracker_v2.models.resource import Resource 4 | from resource_tracker_v2.models.resource_attribute import ResourceAttribute 5 | from resource_tracker_v2.models.resource_group import ResourceGroup 6 | -------------------------------------------------------------------------------- /profiles/api/serializers/rbac_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from profiles.api.serializers import RoleSerializer 4 | from profiles.models import RBAC 5 | 6 | 7 | class RBACSerializer(ModelSerializer): 8 | role = RoleSerializer(read_only=True) 9 | 10 | class Meta: 11 | model = RBAC 12 | fields = ['role', 'user_set'] 13 | -------------------------------------------------------------------------------- /service_catalog/filters/email_template_filter.py: -------------------------------------------------------------------------------- 1 | from django.forms import SelectMultiple 2 | from django_filters import MultipleChoiceFilter 3 | 4 | from Squest.utils.squest_filter import SquestFilter 5 | from service_catalog.models import EmailTemplate 6 | 7 | 8 | class EmailTemplateFilter(SquestFilter): 9 | class Meta: 10 | model = EmailTemplate 11 | fields = ['name'] 12 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/create_operation_request.html: -------------------------------------------------------------------------------- 1 | {% has_perm request.user record.permission.permission_str as can_request_operation %} 2 | {% if can_request_operation %} 3 | 5 | 6 | 7 | {% endif %} -------------------------------------------------------------------------------- /service_catalog/api/serializers/job_template_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from service_catalog.models import JobTemplate 4 | 5 | 6 | class JobTemplateSerializer(serializers.ModelSerializer): 7 | class Meta: 8 | model = JobTemplate 9 | fields = '__all__' 10 | read_only_fields = ['tower_id', 'tower_server', 'survey', 'tower_job_template_data'] 11 | -------------------------------------------------------------------------------- /project-static/squest/js/dashboards.js: -------------------------------------------------------------------------------- 1 | function createPieChart(data, id) { 2 | 3 | var config = { 4 | type: 'pie', 5 | data: data, 6 | options: { 7 | responsive: true, 8 | maintainAspectRatio: false, 9 | 10 | } 11 | }; 12 | 13 | var ctx = document.getElementById(id).getContext('2d'); 14 | window.myPie = new Chart(ctx, config); 15 | } 16 | -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/squest_extra_vars/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # user provided survey variables 3 | file_name: 'my_file.txt' 4 | file_content: 'this is my content' 5 | # admin provided survey variables 6 | create_resource: False 7 | 8 | # fake squest data (make sure to have an instance with ID=1) 9 | squest: 10 | request: 11 | instance: 12 | id: 1 13 | name: "My file" 14 | spec: { } 15 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/resources/custom_columns/resource_operations.html: -------------------------------------------------------------------------------- 1 | {% include 'resource_tracker_v2/resource_group/resources/resource-move-button.html' with resource=record %} 2 | {% include 'resource_tracker_v2/resource_group/resources/resource-edit-button.html' with resource=record %} 3 | {% include 'resource_tracker_v2/resource_group/resources/resource-delete-button.html' with resource=record %} 4 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/delete_operation_button.html: -------------------------------------------------------------------------------- 1 | {% with perm="service_catalog.delete_operation" %} 2 | {% has_perm request.user perm object as can_delete %} 3 | {% if can_delete %} 4 | 6 | 7 | 8 | {% endif %} 9 | {% endwith %} 10 | -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/group_vars/squest_testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # playbook variable 3 | squest_token: "xxxxxxxxxxxaaaaaeeee1234" 4 | squest_bearer_token: "Bearer {{ squest_token }}" 5 | 6 | # Resource tracking : resource group ID to create a new resource when provisioning the service (make sure to have a resource group with ID=1) 7 | resource_group_file_id: 1 8 | 9 | squest_api: "http://127.0.0.1:8000/api" 10 | -------------------------------------------------------------------------------- /service_catalog/models/request_state.py: -------------------------------------------------------------------------------- 1 | from django.db.models import IntegerChoices 2 | 3 | 4 | class RequestState(IntegerChoices): 5 | SUBMITTED = 1, "SUBMITTED" 6 | ON_HOLD = 2, "ON_HOLD" 7 | REJECTED = 3, "REJECTED" 8 | CANCELED = 4, "CANCELED" 9 | ACCEPTED = 5, "ACCEPTED" 10 | PROCESSING = 6, "PROCESSING" 11 | COMPLETE = 7, "COMPLETE" 12 | FAILED = 8, "FAILED" 13 | ARCHIVED = 9, "ARCHIVED" 14 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/global_hook_state.html: -------------------------------------------------------------------------------- 1 | {% if record.model == "Instance" %} 2 | {{ record.get_state_display }} 3 | {% elif record.model == "Request" %} 4 | {{ record.get_state_display }} 5 | {% else %} 6 | {{ record.get_state_display }} 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/edit_operation_button.html: -------------------------------------------------------------------------------- 1 | {% with perm="service_catalog.change_operation" %} 2 | {% has_perm request.user perm object as can_change %} 3 | {% if can_change %} 4 | 6 | 7 | 8 | {% endif %} 9 | {% endwith %} 10 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/custom_link_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from service_catalog.models import CustomLink 4 | 5 | 6 | class CustomLinkSerializer(ModelSerializer): 7 | class Meta: 8 | model = CustomLink 9 | fields = ('id', 'name', 'services', 'text', 'url', 'button_class', 'when', 'loop', 'enabled', 'is_admin_only') 10 | read_only_fields = ('id',) 11 | -------------------------------------------------------------------------------- /service_catalog/forms/doc_forms.py: -------------------------------------------------------------------------------- 1 | from martor.fields import MartorFormField 2 | from martor.widgets import AdminMartorWidget 3 | 4 | from Squest.utils.squest_model_form import SquestModelForm 5 | from service_catalog.models.documentation import Doc 6 | 7 | 8 | class DocForm(SquestModelForm): 9 | content = MartorFormField(widget=AdminMartorWidget()) 10 | 11 | class Meta: 12 | model = Doc 13 | fields = '__all__' 14 | -------------------------------------------------------------------------------- /playbook_examples/squest_inventory/README.md: -------------------------------------------------------------------------------- 1 | # Inventory usage 2 | 3 | Please replace all variables in upercase in this directory: 4 | 5 | In inventory replace: 6 | - `localhost ansible_connection=local` by your machine host where playbooks will be executed 7 | 8 | In group_vars/squest_testing.yml replace: 9 | - `SQUEST_TOKEN` by a token generated in Squest by an admin 10 | - `SQUEST_API_URL` the URL of your Squest API (e.g. localhost:8000) -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Applying database migration" 4 | python manage.py migrate --database=${DATABASE:-default} 5 | 6 | echo "Collect static files" 7 | python manage.py collectstatic --noinput 8 | 9 | echo "Inserting default data" 10 | python manage.py insert_default_data 11 | 12 | echo "Starting web server" 13 | gunicorn --bind 0.0.0.0:8000 --workers ${GUNICORN_WORKERS:-4} --pythonpath /app/squest Squest.wsgi 14 | -------------------------------------------------------------------------------- /service_catalog/templatetags/maintenance.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.conf import settings 3 | 4 | from service_catalog.models import SquestSettings 5 | 6 | register = template.Library() 7 | 8 | 9 | @register.simple_tag() 10 | def is_maintenance_mode_enabled(): 11 | return SquestSettings.load().maintenance_mode_enabled 12 | 13 | 14 | @register.simple_tag() 15 | def is_dev_server(): 16 | return settings.IS_DEV_SERVER 17 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/job_template_compliant.html: -------------------------------------------------------------------------------- 1 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /Squest/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for Squest project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | from django.core.wsgi import get_wsgi_application 12 | 13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Squest.settings') 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/portfolio_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from service_catalog.models import Portfolio 4 | 5 | 6 | class PortfolioSerializer(ModelSerializer): 7 | class Meta: 8 | model = Portfolio 9 | fields = ('id', 'name', 'description', 'parent_portfolio', 'image', 'portfolio_list', 'service_list') 10 | read_only_fields = ('id', 'portfolio_list', 'service_list') 11 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .accept_request_serializer import * 2 | from .dynamic_survey_serializer import * 3 | from .instance_serializer import * 4 | from .job_template_serializer import * 5 | from .operation_serializers import * 6 | from .request_message_serializer import * 7 | from .request_serializers import * 8 | from .service_serializers import * 9 | from .task_result_serializer import * 10 | from .tower_server_serializer import * 11 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_tower/base_test_tower.py: -------------------------------------------------------------------------------- 1 | from django_celery_results.models import TaskResult 2 | 3 | from service_catalog.models import Request, Instance, TowerServer 4 | from tests.test_service_catalog.base import BaseTest 5 | 6 | 7 | class BaseTestTower(BaseTest): 8 | 9 | def setUp(self): 10 | super(BaseTestTower, self).setUp() 11 | 12 | self.test_task_result = TaskResult.objects.create() 13 | -------------------------------------------------------------------------------- /profiles/api/serializers/organization_serializer.py: -------------------------------------------------------------------------------- 1 | from profiles.api.serializers import ScopeSerializer 2 | from profiles.api.serializers.quota_serializer import QuotaSerializer 3 | from profiles.models import Organization 4 | 5 | 6 | class OrganizationSerializer(ScopeSerializer): 7 | class Meta: 8 | model = Organization 9 | fields = '__all__' 10 | read_only_fields = ['teams', ] 11 | 12 | quotas = QuotaSerializer(many=True, read_only=True) 13 | -------------------------------------------------------------------------------- /profiles/migrations/0013_delete_billinggroup.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-06-22 15:29 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0014_auto_20230622_1722'), 10 | ('profiles', '0012_auto_20230622_1722'), 11 | ] 12 | 13 | operations = [ 14 | migrations.DeleteModel( 15 | name='BillingGroup', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /Squest/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for Squest project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Squest.settings.development') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /service_catalog/filters/global_hook_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from service_catalog.models import InstanceHook, RequestHook 3 | 4 | 5 | class InstanceHookFilter(SquestFilter): 6 | class Meta: 7 | model = InstanceHook 8 | fields = ['name', 'state', 'services'] 9 | 10 | 11 | class RequestHookFilter(SquestFilter): 12 | class Meta: 13 | model = RequestHook 14 | fields = ['name', 'state', 'operations'] 15 | -------------------------------------------------------------------------------- /dev.docker-compose.yml: -------------------------------------------------------------------------------- 1 | # add this file to the docker compose execution when developing Squest 2 | services: 3 | 4 | db: 5 | ports: 6 | - "3306:3306" 7 | 8 | phpmyadmin: 9 | image: phpmyadmin/phpmyadmin:5.1.3 10 | env_file: docker/environment_variables/phpmyadmin.env 11 | ports: 12 | - "8082:80" 13 | 14 | rabbitmq: 15 | ports: 16 | - "15672:15672" 17 | - "5672:5672" 18 | 19 | redis-cache: 20 | ports: 21 | - "6379:6379" 22 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/serializers/resource_group_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from taggit.serializers import TagListSerializerField, TaggitSerializer 3 | 4 | from resource_tracker_v2.models import ResourceGroup 5 | 6 | 7 | class ResourceGroupSerializer(TaggitSerializer, serializers.ModelSerializer): 8 | 9 | tags = TagListSerializerField() 10 | 11 | class Meta: 12 | model = ResourceGroup 13 | fields = ["id", "name", "tags"] 14 | -------------------------------------------------------------------------------- /k8s/.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | profile: production 4 | 5 | skip_list: 6 | - key-order 7 | - no-changed-when 8 | - yaml[line-length] 9 | 10 | enable_list: 11 | - args 12 | - empty-string-compare # opt-in 13 | - no-log-password # opt-in 14 | - no-same-owner # opt-in 15 | - galaxy-version-incorrect # opt-in 16 | # add yaml here if you want to avoid ignoring yaml checks when yamllint 17 | # library is missing. Normally its absence just skips using that rule. 18 | - yaml 19 | -------------------------------------------------------------------------------- /profiles/filters/notification_filter_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models import RequestNotification, InstanceNotification 3 | 4 | 5 | class RequestNotificationFilterFilter(SquestFilter): 6 | class Meta: 7 | model = RequestNotification 8 | fields = ['name'] 9 | 10 | 11 | class InstanceNotificationFilterFilter(SquestFilter): 12 | class Meta: 13 | model = InstanceNotification 14 | fields = ['name'] 15 | -------------------------------------------------------------------------------- /profiles/filters/quota.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models import Quota 3 | 4 | 5 | class QuotaFilter(SquestFilter): 6 | class Meta: 7 | model = Quota 8 | fields = ['scope', 'attribute_definition', 'attribute_definition__services'] 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(QuotaFilter, self).__init__(*args, **kwargs) 12 | self.filters['attribute_definition__services'].field.label = 'Services' 13 | -------------------------------------------------------------------------------- /templates/profiles/custom_columns/delete_all_user_roles.html: -------------------------------------------------------------------------------- 1 | {# can_delete_user come from {organization,team,globalscope}_detail.html#} 2 | {% with class_name=object|to_class_name|lower %} 3 | {% if can_delete_user %} 4 | 7 | 8 | 9 | {% endif %} 10 | {% endwith %} 11 | -------------------------------------------------------------------------------- /service_catalog/migrations/0045_alter_doc_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.13 on 2025-05-19 13:46 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0044_alter_operation_options_operation_permission'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='doc', 15 | options={'ordering': ['title']}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /profiles/filters/permission_filter.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_filter import SquestFilter 2 | from profiles.models.squest_permission import Permission 3 | 4 | 5 | class PermissionFilter(SquestFilter): 6 | class Meta: 7 | model = Permission 8 | fields = ['name', 'codename', 'content_type__model'] 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(PermissionFilter, self).__init__(*args, **kwargs) 12 | self.filters['content_type__model'].field.label = 'Model' 13 | -------------------------------------------------------------------------------- /profiles/migrations/0015_profile_theme.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-06-30 14:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('profiles', '0014_quota'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='profile', 15 | name='theme', 16 | field=models.CharField(default='dark', max_length=20), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/custom_columns/resource_group_attribute_actions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /templates/resource_tracker_v2/resource_group/resources/resource_list_buttons.html: -------------------------------------------------------------------------------- 1 | {% include 'generics/buttons/bulk_delete_button.html' %} 2 | 4 | Add 5 | 6 | 8 | Edit resource group 9 | 10 | -------------------------------------------------------------------------------- /service_catalog/forms/__init__.py: -------------------------------------------------------------------------------- 1 | from .portfolio_form import * 2 | from .announcement_form import * 3 | from .doc_forms import * 4 | from .global_hook_forms import * 5 | from .instance_forms import * 6 | from .operation_forms import * 7 | from .operation_request_forms import * 8 | from .request_message_forms import * 9 | from .service_forms import * 10 | from .service_request_forms import * 11 | from .support_message_forms import * 12 | from .support_request_forms import * 13 | from .tower_server_forms import * -------------------------------------------------------------------------------- /tls.docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose -f docker-compose.yml -f tls.docker-compose.yml up 2 | version: '3.7' 3 | 4 | services: 5 | tls: 6 | image: caddy:2.6.4-alpine 7 | depends_on: 8 | - nginx 9 | volumes: 10 | - ./docker/certs/squest.crt:/etc/ssl/private/squest.crt 11 | - ./docker/certs/squest.key:/etc/ssl/private/squest.key 12 | - ./docker/Caddyfile:/etc/caddy/Caddyfile:ro 13 | ports: 14 | - "80:80" # Allows for http redirection 15 | - "443:443" 16 | -------------------------------------------------------------------------------- /service_catalog/tables/doc_tables.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | from django_tables2.utils import A 3 | 4 | from Squest.utils.squest_table import SquestTable 5 | from service_catalog.models import Doc 6 | 7 | 8 | class DocTable(SquestTable): 9 | title = LinkColumn("service_catalog:doc_details", args=[A("id")]) 10 | 11 | class Meta: 12 | model = Doc 13 | attrs = {"id": "doc_table", "class": "table squest-pagination-tables"} 14 | fields = ("title",) 15 | -------------------------------------------------------------------------------- /profiles/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .permission_api_views import * 2 | from .organization_api_views import * 3 | from .requestnotification import * 4 | from .instancenotification import * 5 | from .team_api_views import * 6 | from .user_api_views import * 7 | 8 | from .requestnotification import * 9 | from .instancenotification import * 10 | from .user_api_views import * 11 | from .role_api_views import * 12 | from .globalscope_api_views import * 13 | from .scope_api_views import * 14 | from .quota_api_views import * 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description of the feature** 11 | A clear and concise description of what the problem is. 12 | Ex. I would like to be able to [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | Keep in mind that the Squest project trend to keep a generic approach for all features. 17 | -------------------------------------------------------------------------------- /templates/profiles/forms/form_headers/request_notification_filter_header.html: -------------------------------------------------------------------------------- 1 |
2 |
Request notification filter
3 |

When: Ansible like condition. request is available in the context.

4 |

E.g with a day 1 operation. Access the user survey: request.fill_in_survey['location'] == 'grenoble'

5 |

E.g with a day 2 operation. Access the spec of instance: request.instance.spec['configvar'] == 'value' 6 |

7 | -------------------------------------------------------------------------------- /profiles/tables/team_quota_limit_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from profiles.models import Quota 5 | 6 | 7 | class TeamQuotaLimitTable(SquestTable): 8 | scope = LinkColumn() 9 | limit = LinkColumn() 10 | consumed = LinkColumn(orderable=False) 11 | 12 | class Meta: 13 | model = Quota 14 | attrs = {"id": "quota_team_table", "class": "table squest-pagination-tables"} 15 | fields = ("scope", "limit", "consumed") 16 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/operation_request.html: -------------------------------------------------------------------------------- 1 |
2 | 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /resource_tracker_v2/migrations/0004_alter_transformer_factor.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-07-24 18:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('resource_tracker_v2', '0003_auto_20230724_0940'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='transformer', 15 | name='factor', 16 | field=models.FloatField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /service_catalog/migrations/0046_alter_doc_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.13 on 2025-05-22 12:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0045_alter_doc_options'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='doc', 15 | options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'ordering': ['title']}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/forms/global_hook_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from service_catalog.models import InstanceHook, RequestHook 3 | 4 | 5 | class InstanceHookForm(SquestModelForm): 6 | class Meta: 7 | model = InstanceHook 8 | fields = ["name", "services", "state", "job_template", "extra_vars"] 9 | 10 | 11 | class RequestHookForm(SquestModelForm): 12 | class Meta: 13 | model = RequestHook 14 | fields = ["name", "operations", "state", "job_template", "extra_vars"] 15 | -------------------------------------------------------------------------------- /service_catalog/models/instance_state.py: -------------------------------------------------------------------------------- 1 | from django.db.models import IntegerChoices 2 | 3 | 4 | class InstanceState(IntegerChoices): 5 | PENDING = 1, "PENDING" 6 | PROVISION_FAILED = 2, "PROVISION_FAILED" 7 | PROVISIONING = 3, "PROVISIONING" 8 | UPDATING = 4, "UPDATING" 9 | UPDATE_FAILED = 5, "UPDATE_FAILED" 10 | DELETING = 6, "DELETING" 11 | DELETED = 7, "DELETED" 12 | DELETE_FAILED = 8, "DELETE_FAILED" 13 | ARCHIVED = 9, "ARCHIVED" 14 | AVAILABLE = 10, "AVAILABLE" 15 | ABORTED = 11, "ABORTED" 16 | -------------------------------------------------------------------------------- /tests/test_resource_tracker_v2/test_model/test_attribute_definition.py: -------------------------------------------------------------------------------- 1 | from tests.test_resource_tracker_v2.base_test_resource_tracker_v2 import BaseTestResourceTrackerV2 2 | 3 | 4 | class TestModelAttributeDefinition(BaseTestResourceTrackerV2): 5 | 6 | def setUp(self) -> None: 7 | super(TestModelAttributeDefinition, self).setUp() 8 | 9 | def test_delete_attribute_definition(self): 10 | self._validate_state_before_deletion() 11 | self.vcpu_attribute.delete() 12 | self._validate_state_after_deletion() 13 | -------------------------------------------------------------------------------- /profiles/views/__init__.py: -------------------------------------------------------------------------------- 1 | from profiles.views.team import * 2 | from profiles.views.token import * 3 | from profiles.views.profile import * 4 | from profiles.views.request_notification_views import * 5 | from profiles.views.instance_notification_views import * 6 | from profiles.views.organization import * 7 | from profiles.views.scope import * 8 | from profiles.views.user import * 9 | from profiles.views.role import * 10 | from profiles.views.globalscope import * 11 | from profiles.views.quota import * 12 | from profiles.views.permission import * 13 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/service_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer, ValidationError 2 | 3 | from service_catalog.models import Service 4 | 5 | 6 | class ServiceSerializer(ModelSerializer): 7 | class Meta: 8 | model = Service 9 | fields = '__all__' 10 | 11 | def validate_extra_vars(self, value): 12 | if value is None or not isinstance(value, dict): 13 | raise ValidationError("Please enter a valid JSON. Empty value is {} for JSON.") 14 | return value 15 | -------------------------------------------------------------------------------- /service_catalog/management/commands/martor_cleanup.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.core.management import BaseCommand 4 | 5 | from service_catalog.maintenance_jobs import cleanup_ghost_docs_images 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class Command(BaseCommand): 11 | 12 | def __init__(self): 13 | super().__init__() 14 | 15 | def handle(self, *args, **options): 16 | print("[Cleanup Martor images] Start") 17 | cleanup_ghost_docs_images() 18 | print("[Cleanup Martor images] End") 19 | -------------------------------------------------------------------------------- /docker/Caddyfile: -------------------------------------------------------------------------------- 1 | squest.domain.local { # This line should match the ALLOWED_HOSTS in your Squest settings 2 | reverse_proxy nginx:8080 maintenance:80 { 3 | # try to use django in first place. If not available we switch to maintenance 4 | lb_policy first 5 | lb_try_duration 5s 6 | fail_duration 30s 7 | } 8 | encode gzip zstd 9 | tls /etc/ssl/private/squest.crt /etc/ssl/private/squest.key 10 | # or: 11 | # tls /etc/ssl/private/cert.pem 12 | 13 | log { 14 | level error 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/operation_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer, ValidationError 2 | 3 | from service_catalog.models import Operation 4 | 5 | 6 | class OperationSerializer(ModelSerializer): 7 | class Meta: 8 | model = Operation 9 | fields = '__all__' 10 | 11 | def validate_extra_vars(self, value): 12 | if value is None or not isinstance(value, dict): 13 | raise ValidationError("Please enter a valid JSON. Empty value is {} for JSON.") 14 | return value 15 | -------------------------------------------------------------------------------- /service_catalog/tables/portfolio_tables.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import Portfolio 5 | 6 | 7 | class PortfolioTable(SquestTable): 8 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 9 | 10 | class Meta: 11 | model = Portfolio 12 | attrs = {"id": "portfolio_table", "class": "table squest-pagination-tables"} 13 | fields = ("name", "description", "actions") 14 | -------------------------------------------------------------------------------- /service_catalog/migrations/0007_service_external_support_url.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-07-01 14:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0006_auto_20220630_1636'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='service', 15 | name='external_support_url', 16 | field=models.CharField(blank=True, max_length=2000), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /profiles/models/rbac.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import Group 2 | from django.db.models import ForeignKey, CASCADE 3 | 4 | from profiles.models.role import Role 5 | 6 | 7 | class RBAC(Group): 8 | class Meta: 9 | unique_together = ('scope', 'role') 10 | default_permissions = ('add', 'change', 'delete', 'view', 'list') 11 | 12 | role = ForeignKey(Role, null=False, on_delete=CASCADE) 13 | scope = ForeignKey("AbstractScope", null=False, on_delete=CASCADE, related_name="rbac", 14 | related_query_name="rbac", ) 15 | -------------------------------------------------------------------------------- /profiles/migrations/0022_alter_permission_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-10-18 13:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('profiles', '0021_create_default_permissions'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='permission', 15 | options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'ordering': ['content_type__model', 'codename']}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/tables/email_template_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import EmailTemplate 5 | 6 | 7 | class EmailTemplateTable(SquestTable): 8 | name = LinkColumn() 9 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 10 | 11 | class Meta: 12 | model = EmailTemplate 13 | attrs = {"class": "table squest-pagination-tables "} 14 | fields = ("name", "actions") 15 | -------------------------------------------------------------------------------- /service_catalog/templatetags/version.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.simple_tag 7 | def commit_sha(): 8 | """ 9 | Return Squest 6 first characters of commit SHA. 10 | """ 11 | from django.conf import settings 12 | return settings.SQUEST_COMMIT_SHA6 13 | 14 | 15 | @register.simple_tag 16 | def squest_version(): 17 | """ 18 | Return Squest version as listed in `__version__` in `init.py` of settings package 19 | """ 20 | from Squest.version import __version__ 21 | return __version__ 22 | -------------------------------------------------------------------------------- /templates/generics/table/table.html: -------------------------------------------------------------------------------- 1 | {% extends 'django_tables2/bootstrap4.html' %} 2 | {% block table %} 3 | {{ block.super }} 4 |
5 | 6 | {% if table.page %} 7 | Showing {{ table.page.start_index }}-{{ table.page.end_index }} of {{ table.page.paginator.count }} 8 | {% else %} 9 | {% if tables.rows|length != 0 %} 10 | Showing 1-{{ table.rows|length }} of {{ table.rows|length }} 11 | {% endif %} 12 | {% endif %} 13 | 14 | {% endblock table %} 15 | -------------------------------------------------------------------------------- /resource_tracker_v2/templatetags/resource_filters.py: -------------------------------------------------------------------------------- 1 | from django.template.defaulttags import register 2 | 3 | from resource_tracker_v2.models import ResourceAttribute 4 | 5 | 6 | @register.filter(name='v2_get_attribute_value_from_name') 7 | def v2_get_attribute_value_from_name(resource, attribute_name): 8 | try: 9 | attribute = ResourceAttribute.objects.get(resource=resource, 10 | attribute_definition__name=attribute_name) 11 | return attribute.value 12 | except ResourceAttribute.DoesNotExist: 13 | return "" 14 | -------------------------------------------------------------------------------- /service_catalog/migrations/0033_alter_approvalstepstate_options_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-13 15:07 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('service_catalog', '0032_alter_operation_name_alter_operation_type'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='approvalstepstate', 16 | options={'ordering': ('approval_step__position',)}, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /profiles/api/views/role_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from profiles.api.serializers import RoleSerializer 3 | from profiles.filters import RoleFilter 4 | from profiles.models import Role 5 | 6 | 7 | class RoleDetails(SquestRetrieveUpdateDestroyAPIView): 8 | serializer_class = RoleSerializer 9 | queryset = Role.objects.all() 10 | 11 | 12 | class RoleListCreate(SquestListCreateAPIView): 13 | serializer_class = RoleSerializer 14 | queryset = Role.objects.all() 15 | filterset_class = RoleFilter 16 | -------------------------------------------------------------------------------- /profiles/api/views/team_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 2 | from profiles.api.serializers import TeamSerializer 3 | from profiles.filters import TeamFilter 4 | from profiles.models import Team 5 | 6 | 7 | class TeamDetails(SquestRetrieveUpdateDestroyAPIView): 8 | serializer_class = TeamSerializer 9 | queryset = Team.objects.all() 10 | 11 | 12 | class TeamListCreate(SquestListCreateAPIView): 13 | serializer_class = TeamSerializer 14 | queryset = Team.objects.all() 15 | filterset_class = TeamFilter 16 | -------------------------------------------------------------------------------- /profiles/api/views/globalscope_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateAPIView 2 | from profiles.api.serializers import GlobalScopeSerializer 3 | from profiles.models import GlobalScope 4 | 5 | 6 | class GlobalScopeDetails(SquestRetrieveUpdateAPIView): 7 | serializer_class = GlobalScopeSerializer 8 | queryset = GlobalScope.objects.all() 9 | 10 | def dispatch(self, request, *args, **kwargs): 11 | self.kwargs['pk'] = GlobalScope.load().id 12 | kwargs['pk'] = self.kwargs.get('pk') 13 | return super().dispatch(request, *args, **kwargs) 14 | -------------------------------------------------------------------------------- /templates/generics/buttons/edit_button.html: -------------------------------------------------------------------------------- 1 | {% with app=object|to_app_name|lower %} 2 | {% with class=object|to_class_name|lower %} 3 | {% with perm=app|add:".change_"|add:class %} 4 | {% has_perm request.user perm object as can_change %} 5 | {% if can_change %} 6 | 8 | 9 | 10 | {% endif %} 11 | {% endwith %} 12 | {% endwith %} 13 | {% endwith %} 14 | -------------------------------------------------------------------------------- /profiles/api/serializers/__init__.py: -------------------------------------------------------------------------------- 1 | from .contenttype_serializer import * 2 | from .permission_serializers import * 3 | from .request_notification_filter_serializer import * 4 | from .support_notification_filter_serializer import * 5 | from .request_notification_filter_serializer import * 6 | from .support_notification_filter_serializer import * 7 | from .role_serializer import * 8 | from .user_serializers import * 9 | from .rbac_serializers import * 10 | from .scope_serializer import * 11 | from .globalscope_serializer import * 12 | from .team_serializer import * 13 | from .organization_serializer import * 14 | -------------------------------------------------------------------------------- /service_catalog/filters/doc_filter.py: -------------------------------------------------------------------------------- 1 | from django.forms import SelectMultiple 2 | from django_filters import ModelMultipleChoiceFilter 3 | 4 | from Squest.utils.squest_filter import SquestFilter 5 | from service_catalog.models import Doc, Service 6 | 7 | 8 | class ServiceFilter(ModelMultipleChoiceFilter): 9 | 10 | def __init__(self, *args, **kwargs): 11 | kwargs.setdefault('queryset', Service.objects.filter()) 12 | super().__init__(label='Services', *args, **kwargs) 13 | 14 | 15 | class DocFilter(SquestFilter): 16 | class Meta: 17 | model = Doc 18 | fields = ['title'] 19 | -------------------------------------------------------------------------------- /k8s/squest_k8s/tasks/06-celery.yml: -------------------------------------------------------------------------------- 1 | - name: Deploy celery worker 2 | kubernetes.core.k8s: 3 | apply: true 4 | namespace: "{{ squest_namespace }}" 5 | definition: "{{ lookup('template', 'celery.deployment.yaml') | from_yaml }}" 6 | vars: 7 | celery_name: "celery-worker" 8 | celery_cmd_args: "worker" 9 | 10 | - name: Deploy celery beat 11 | kubernetes.core.k8s: 12 | apply: true 13 | namespace: "{{ squest_namespace }}" 14 | definition: "{{ lookup('template', 'celery.deployment.yaml') | from_yaml }}" 15 | vars: 16 | celery_name: "celery-beat" 17 | celery_cmd_args: "beat" 18 | 19 | -------------------------------------------------------------------------------- /service_catalog/migrations/0020_alter_approvalstep_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-08-16 16:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0019_auto_20230804_1516'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='approvalstep', 15 | options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'permissions': [('approve_reject_approvalstep', 'Can approve/reject an approval step')]}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/tables/custom_link_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import CustomLink 5 | 6 | 7 | class CustomLinkTable(SquestTable): 8 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 9 | 10 | class Meta: 11 | model = CustomLink 12 | attrs = {"id": "custom_link_table", "class": "table squest-pagination-tables "} 13 | fields = ("name", "actions") 14 | 15 | def render_name(self, record): 16 | return f"{record}" 17 | -------------------------------------------------------------------------------- /templates/generics/buttons/delete_button.html: -------------------------------------------------------------------------------- 1 | {% with app=object|to_app_name|lower %} 2 | {% with class=object|to_class_name|lower %} 3 | {% with perm=app|add:".delete_"|add:class %} 4 | {% has_perm request.user perm object as can_delete %} 5 | {% if can_delete %} 6 | 8 | 9 | 10 | {% endif %} 11 | {% endwith %} 12 | {% endwith %} 13 | {% endwith %} 14 | -------------------------------------------------------------------------------- /profiles/tables/instance_consumption_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from resource_tracker_v2.models import ResourceAttribute 5 | 6 | 7 | class InstanceConsumptionTable(SquestTable): 8 | 9 | resource__service_catalog_instance = LinkColumn(verbose_name="Instance") 10 | 11 | class Meta: 12 | model = ResourceAttribute 13 | attrs = {"id": "instance_consumption_table", "class": "table squest-pagination-tables"} 14 | fields = ("resource__service_catalog_instance", "resource__service_catalog_instance__requester", "value") 15 | -------------------------------------------------------------------------------- /playbook_examples/test_get_api.yml: -------------------------------------------------------------------------------- 1 | # ansible-playbook playbook_examples/test_get_api.yml \ 2 | # -i playbook_examples/squest_inventory/inventory 3 | 4 | --- 5 | - name: Get info from squest API 6 | hosts: squest_testing 7 | gather_facts: false 8 | 9 | tasks: 10 | - name: Get all resource group 11 | uri: 12 | url: "{{ squest_api }}/resource-tracker/resource-group/" 13 | headers: 14 | Authorization: "{{ squest_bearer_token }}" 15 | method: GET 16 | status_code: 200 17 | body_format: json 18 | register: output 19 | 20 | - debug: 21 | var: output 22 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/instance_edit_button.html: -------------------------------------------------------------------------------- 1 | 2 | {% has_perm request.user "service_catalog.rename_instance" object as can_rename_instance %} 3 | {% has_perm request.user "service_catalog.change_instance" object as can_change_requester_instance %} 4 | {% has_perm request.user "service_catalog.change_owner_instance" object as can_change_instance %} 5 | {% if can_rename_instance or can_change_requester_instance or can_change_instance %} 6 | 8 | 9 | 10 | {% endif %} 11 | -------------------------------------------------------------------------------- /tests/test_plugins/field_validators_test/superior_to_10.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | from rest_framework import serializers 3 | from django.utils.translation import gettext as _ 4 | 5 | 6 | def validate_api(value): 7 | if int(value) < 10: 8 | raise serializers.ValidationError('Must be superior to 10') 9 | 10 | 11 | def validate_ui(value): 12 | try: 13 | if int(value) < 10: 14 | raise ValidationError( 15 | _('%(value)s not superior to 10'), 16 | params={'value': value}, 17 | ) 18 | except ValueError: 19 | pass 20 | -------------------------------------------------------------------------------- /profiles/tables/permission_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, Column 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from profiles.models.squest_permission import Permission 5 | 6 | 7 | class PermissionTable(SquestTable): 8 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 9 | content_type__model = Column(verbose_name="Model") 10 | 11 | class Meta: 12 | model = Permission 13 | attrs = {"id": "permission_table", "class": "table squest-pagination-tables"} 14 | fields = ("name", "codename", "content_type__model") 15 | -------------------------------------------------------------------------------- /tests/setup/__init__.py: -------------------------------------------------------------------------------- 1 | from tests.setup.setup_org import SetupOrg, SetupOrgAPI, SetupOrgCommon 2 | from tests.setup.setup_team import SetupTeam, SetupTeamAPI, SetupTeamCommon 3 | 4 | from tests.setup.setup_awx import SetupDummyAWX, SetupDummyAWXAPI, SetupDummyAWXCommon 5 | from tests.setup.setup_service import SetupService, SetupServiceAPI, SetupServiceCommon 6 | from tests.setup.setup_operation import SetupOperation, SetupOperationAPI, SetupOperationCommon 7 | from tests.setup.setup_instance import SetupInstance, SetupInstanceAPI, SetupInstanceCommon 8 | from tests.setup.setup_request import SetupRequest, SetupRequestAPI, SetupRequestCommon 9 | -------------------------------------------------------------------------------- /profiles/migrations/0019_alter_quota_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-05 14:48 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('profiles', '0018_auto_20230803_1126'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='quota', 15 | options={'default_permissions': ('add', 'delete', 'view', 'list'), 'permissions': [('change_team_quota', 'Can change quota at team level'), ('change_organization_quota', 'Can change quota at organization level')]}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/migrations/0010_doc_operations.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-09-12 15:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0009_auto_20220804_1441'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='doc', 15 | name='operations', 16 | field=models.ManyToManyField(blank=True, help_text='Operations linked to this doc.', related_name='docs', related_query_name='doc', to='service_catalog.Operation'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /profiles/api/views/user_api_views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | 3 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 4 | from profiles.api.serializers.user_serializers import UserSerializer 5 | from profiles.filters.user_filter import UserFilter 6 | 7 | 8 | class UserDetails(SquestRetrieveUpdateDestroyAPIView): 9 | serializer_class = UserSerializer 10 | queryset = User.objects.all() 11 | 12 | 13 | class UserListCreate(SquestListCreateAPIView): 14 | serializer_class = UserSerializer 15 | queryset = User.objects.all() 16 | filterset_class = UserFilter 17 | -------------------------------------------------------------------------------- /service_catalog/forms/tower_server_update_token_forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from Squest.utils.squest_model_form import SquestModelForm 4 | from service_catalog.models import TowerServer 5 | 6 | 7 | class TowerServerUpdateTokenForm(SquestModelForm): 8 | token = forms.CharField(label="Token", 9 | widget=forms.TextInput(), 10 | ) 11 | 12 | def __init__(self, *args, **kwargs): 13 | super(TowerServerUpdateTokenForm, self).__init__(*args, **kwargs) 14 | self.initial['token'] = "" 15 | 16 | 17 | class Meta: 18 | model = TowerServer 19 | fields = ["token"] 20 | -------------------------------------------------------------------------------- /resource_tracker_v2/tables/attribute_defintion_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from resource_tracker_v2.models import AttributeDefinition 5 | 6 | 7 | class AttributeDefinitionTable(SquestTable): 8 | class Meta: 9 | model = AttributeDefinition 10 | attrs = {"id": "attribute_definition_table", "class": "table squest-pagination-tables"} 11 | fields = ("name", "description", "actions") 12 | 13 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 14 | name = LinkColumn() 15 | -------------------------------------------------------------------------------- /tests/test_plugins/field_validators_test/even_number.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | from rest_framework import serializers 3 | from django.utils.translation import gettext as _ 4 | 5 | 6 | def validate_api(value): 7 | if int(value) % 2 != 0: 8 | raise serializers.ValidationError('This field must be an even number.') 9 | 10 | 11 | def validate_ui(value): 12 | try: 13 | if int(value) % 2 != 0: 14 | raise ValidationError( 15 | _('%(value)s is not an even number'), 16 | params={'value': value}, 17 | ) 18 | except ValueError: 19 | pass 20 | -------------------------------------------------------------------------------- /profiles/forms/permission_form.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models.squest_permission import Permission 3 | 4 | 5 | class PermissionForm(SquestModelForm): 6 | class Meta: 7 | model = Permission 8 | fields = "__all__" 9 | 10 | def __init__(self, *args, **kwargs): 11 | super().__init__(*args, **kwargs) 12 | self.fields['name'].help_text = f'A short description of the permission.' 13 | self.fields['codename'].help_text = f'Unique identifier of the permission in camel case format. ' \ 14 | f'E.g: approve_custom_step' 15 | -------------------------------------------------------------------------------- /service_catalog/celery.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from celery import Celery 4 | 5 | # set the default Django settings module for the 'celery' program. 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Squest.settings') 7 | 8 | app = Celery('Squest') 9 | 10 | # Using a string here means the worker doesn't have to serialize 11 | # the configuration object to child processes. 12 | # - namespace='CELERY' means all celery-related configuration keys 13 | # should have a `CELERY_` prefix. 14 | app.config_from_object('django.conf:settings', namespace='CELERY') 15 | 16 | # Load task modules from all registered Django app configs. 17 | app.autodiscover_tasks() 18 | -------------------------------------------------------------------------------- /service_catalog/migrations/0021_approvalstep_auto_accept_condition.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-01 12:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0020_alter_approvalstep_options'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='approvalstep', 15 | name='auto_accept_condition', 16 | field=models.TextField(blank=True, help_text="Ansible like 'when' with `request` as context. No Jinja brackets needed", null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /profiles/models/__init__.py: -------------------------------------------------------------------------------- 1 | from profiles.models.token import Token 2 | from profiles.models.role import Role 3 | from profiles.models.rbac import RBAC 4 | from profiles.models.organization import Organization 5 | from profiles.models.team import Team 6 | from profiles.models.profile import Profile 7 | from profiles.models.request_notification import RequestNotification 8 | from profiles.models.instance_notification import InstanceNotification 9 | from profiles.models.scope import AbstractScope, Scope 10 | from profiles.models.globalscope import GlobalScope 11 | from profiles.models.quota import Quota 12 | from profiles.models.squest_permission import Permission 13 | -------------------------------------------------------------------------------- /service_catalog/migrations/0025_alter_approvalstep_auto_accept_condition.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-14 13:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0024_alter_globalhook_state'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='approvalstep', 15 | name='auto_accept_condition', 16 | field=models.TextField(blank=True, default=None, help_text="Ansible like 'when' with `request` as context. No Jinja brackets needed", null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /service_catalog/views/login.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import views as auth_views 2 | 3 | 4 | class LoginView(auth_views.LoginView): 5 | template_name = 'registration/login.html' 6 | 7 | def get_context_data(self, **kwargs): 8 | context = super().get_context_data(**kwargs) 9 | from django.conf import settings 10 | context.update({ 11 | 'login_helper_text': settings.LOGIN_HELPER_TEXT, 12 | 'openid_active': settings.SOCIAL_AUTH_OIDC_ENABLED, 13 | 'openid_btn_text': settings.SOCIAL_AUTH_OIDC_BTN_TEXT, 14 | 'password_enabled': settings.PASSWORD_ENABLED 15 | }) 16 | return context 17 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/test_services/test_list.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from service_catalog.models import Service 4 | from tests.test_service_catalog.base import BaseTest 5 | 6 | 7 | class ServiceListViewsTest(BaseTest): 8 | 9 | def setUp(self): 10 | super(ServiceListViewsTest, self).setUp() 11 | self.url = reverse('service_catalog:service_list') 12 | 13 | def test_get_list(self): 14 | response = self.client.get(self.url) 15 | self.assertEqual(200, response.status_code) 16 | self.assertEqual(len(response.context["table"].data.data), Service.objects.count()) 17 | -------------------------------------------------------------------------------- /profiles/api/views/permission_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 2 | from profiles.api.serializers import PermissionSerializer 3 | from profiles.filters import PermissionFilter 4 | from profiles.models.squest_permission import Permission 5 | 6 | 7 | class PermissionDetails(SquestRetrieveUpdateDestroyAPIView): 8 | serializer_class = PermissionSerializer 9 | queryset = Permission.objects.all() 10 | 11 | 12 | class PermissionListCreate(SquestListCreateAPIView): 13 | serializer_class = PermissionSerializer 14 | queryset = Permission.objects.all() 15 | filterset_class = PermissionFilter 16 | -------------------------------------------------------------------------------- /psql.docker-compose.yaml: -------------------------------------------------------------------------------- 1 | 2 | services: 3 | db: 4 | image: postgres:latest 5 | env_file: docker/environment_variables/db.env 6 | ports: 7 | - 5432:5432 8 | volumes: 9 | - db_data:/var/lib/postgresql/data 10 | - "./docker/scripts/psql-init.sh:/docker-entrypoint-initdb.d/db-init.sh" 11 | 12 | django: 13 | environment: 14 | WAIT_HOSTS: db:5432,rabbitmq:5672 15 | 16 | celery-worker: 17 | environment: 18 | WAIT_HOSTS: db:5432,rabbitmq:5672,django:8000 19 | WAIT_TIMEOUT: 60 20 | 21 | celery-beat: 22 | environment: 23 | WAIT_HOSTS: db:5432,rabbitmq:5672,django:8000 24 | WAIT_TIMEOUT: 60 -------------------------------------------------------------------------------- /service_catalog/api/views/job_template_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListAPIView, SquestRetrieveAPIView 2 | from service_catalog.api.serializers import JobTemplateSerializer 3 | from service_catalog.filters.job_template_filter import JobTemplateFilter 4 | from service_catalog.models import JobTemplate 5 | 6 | 7 | class JobTemplateDetails(SquestRetrieveAPIView): 8 | serializer_class = JobTemplateSerializer 9 | queryset = JobTemplate.objects.all() 10 | 11 | 12 | class JobTemplateList(SquestListAPIView): 13 | serializer_class = JobTemplateSerializer 14 | filterset_class = JobTemplateFilter 15 | queryset = JobTemplate.objects.all() 16 | -------------------------------------------------------------------------------- /Squest/utils/squest_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import tables, RequestConfig 2 | 3 | 4 | class SquestTable(tables.Table): 5 | 6 | def __init__(self, *args, **kwargs): 7 | hide_field = list() 8 | if 'hide_fields' in kwargs: 9 | hide_field = kwargs.pop("hide_fields") 10 | super(SquestTable, self).__init__(*args, **kwargs) 11 | 12 | for field in hide_field: 13 | if field in self.columns.columns: 14 | self.columns.hide(field) 15 | 16 | 17 | class SquestRequestConfig(RequestConfig): 18 | 19 | def __init__(self, request, paginate=False): 20 | self.request = request 21 | self.paginate = paginate 22 | -------------------------------------------------------------------------------- /monitoring/apps.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from django.apps import AppConfig 4 | import prometheus_client 5 | from django.db.models.signals import post_migrate 6 | 7 | 8 | def run_prometheus_metrics(sender, **kwargs): 9 | from .models import ComponentCollector 10 | prometheus_client.REGISTRY.register(ComponentCollector()) 11 | 12 | 13 | class MonitoringConfig(AppConfig): 14 | default_auto_field = 'django.db.models.BigAutoField' 15 | name = 'monitoring' 16 | 17 | def ready(self): 18 | banned_words = ['test'] 19 | if not any(banned_word in sys.argv for banned_word in banned_words): 20 | post_migrate.connect(run_prometheus_metrics, sender=self) 21 | -------------------------------------------------------------------------------- /profiles/api/views/organization_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from profiles.api.serializers import OrganizationSerializer 3 | from profiles.filters import OrganizationFilter 4 | from profiles.models import Organization 5 | 6 | 7 | class OrganizationDetails(SquestRetrieveUpdateDestroyAPIView): 8 | serializer_class = OrganizationSerializer 9 | queryset = Organization.objects.all() 10 | 11 | 12 | class OrganizationListCreate(SquestListCreateAPIView): 13 | serializer_class = OrganizationSerializer 14 | queryset = Organization.objects.all() 15 | filterset_class = OrganizationFilter 16 | -------------------------------------------------------------------------------- /plugins/field_validators/is_json.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | # For testing 4 | try: 5 | from django.core.exceptions import ValidationError as UIValidationError 6 | from rest_framework.serializers import ValidationError as APIValidationError 7 | except ImportError: 8 | pass 9 | 10 | 11 | def is_json(json_str): 12 | try: 13 | json.loads(json_str) 14 | except ValueError as e: 15 | return False 16 | return True 17 | 18 | 19 | def validate_api(value): 20 | if not is_json(value): 21 | raise APIValidationError("is not JSON") 22 | 23 | 24 | def validate_ui(value): 25 | if not is_json(value): 26 | raise UIValidationError("is not JSON") 27 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/request_message_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer, Serializer, CharField 2 | 3 | from service_catalog.models import RequestMessage 4 | 5 | 6 | class MessageSerializer(Serializer): 7 | content = CharField( 8 | label="Message", 9 | help_text="Message attached to this request" 10 | ) 11 | 12 | 13 | class RequestMessageReadSerializer(ModelSerializer): 14 | class Meta: 15 | model = RequestMessage 16 | fields = '__all__' 17 | 18 | 19 | class RequestMessageSerializer(ModelSerializer): 20 | class Meta: 21 | model = RequestMessage 22 | fields = ('sender', 'request', 'content') 23 | -------------------------------------------------------------------------------- /service_catalog/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .login import * 2 | from .common import * 3 | from .portfolio import * 4 | from .request import * 5 | from .service import * 6 | from .instance import * 7 | from .catalog_views import * 8 | from .filters import * 9 | from .announcement import * 10 | from .custom_link import * 11 | from .request import * 12 | from .mail_test import * 13 | from .operation import * 14 | from .support import * 15 | from .doc import * 16 | from .tower_server import * 17 | from .job_template import * 18 | from .global_hook import * 19 | from .tower_survey_field import * 20 | from .approval_step_views import * 21 | from .approval_workflow_views import * 22 | from .email_template import * 23 | -------------------------------------------------------------------------------- /service_catalog/migrations/0043_operation_when.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.13 on 2024-11-15 08:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0042_alter_instance_options'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='operation', 15 | name='when', 16 | field=models.CharField(blank=True, help_text="Ansible like 'when' with `instance` as context. No Jinja brackets needed. Cannot be set on 'create' type of operation as the instance does not exist yet", max_length=2000, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 |

404

10 |
11 |

Not Found

12 |

The requested resource was not found on this server.

13 |
14 |
15 |
16 |
17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /templates/service_catalog/mails/utils/header.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 14 | 15 | 16 |
8 | 9 |

11 | {{ title }}

12 | 13 |
17 | -------------------------------------------------------------------------------- /ldap.docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose -f docker-compose.yml -f docker-compose.override.yml -f tls.docker-compose.yml -f ldap.docker-compose.yml up 2 | services: 3 | 4 | django: 5 | volumes: 6 | - ./Squest/ldap_config.py:/app/Squest/ldap_config.py 7 | - ./docker/certs/ldap_ca.crt:/usr/local/share/ca-certificates/ldap_ca.crt 8 | celery-worker: 9 | volumes: 10 | - ./Squest/ldap_config.py:/app/Squest/ldap_config.py 11 | - ./docker/certs/ldap_ca.crt:/usr/local/share/ca-certificates/ldap_ca.crt 12 | celery-beat: 13 | volumes: 14 | - ./Squest/ldap_config.py:/app/Squest/ldap_config.py 15 | - ./docker/certs/ldap_ca.crt:/usr/local/share/ca-certificates/ldap_ca.crt 16 | -------------------------------------------------------------------------------- /service_catalog/migrations/0034_alter_request_approval_workflow_state.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-14 13:27 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('service_catalog', '0033_alter_approvalstepstate_options_and_more'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='request', 16 | name='approval_workflow_state', 17 | field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='service_catalog.approvalworkflowstate'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/views/resource_api_view.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from resource_tracker_v2.api.serializers.resource_serializer import ResourceSerializer 3 | from resource_tracker_v2.filters.resource_filter import ResourceFilter 4 | from resource_tracker_v2.models import Resource 5 | 6 | 7 | class ResourceListCreate(SquestListCreateAPIView): 8 | serializer_class = ResourceSerializer 9 | filterset_class = ResourceFilter 10 | queryset = Resource.objects.all() 11 | 12 | 13 | class ResourceDetails(SquestRetrieveUpdateDestroyAPIView): 14 | serializer_class = ResourceSerializer 15 | queryset = Resource.objects.all() 16 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_instance/test_user_spec/test_user_spec_put.py: -------------------------------------------------------------------------------- 1 | from rest_framework.reverse import reverse 2 | 3 | from tests.test_service_catalog.test_api.test_instance.test_spec.test_spec_put import TestApiSpecPut 4 | 5 | 6 | class TestApiUserSpecPut(TestApiSpecPut): 7 | 8 | def setUp(self): 9 | super(TestApiUserSpecPut, self).setUp() 10 | self.target_spec = "user-spec" 11 | self.get_spec_details_url = reverse('api_instance_user_spec_details', kwargs=self.kwargs) 12 | 13 | def test_admin_put_user_spec(self): 14 | self.test_admin_put_spec() 15 | 16 | def test_cannot_put_user_spec_when_logout(self): 17 | self.test_cannot_put_spec_when_logout() 18 | -------------------------------------------------------------------------------- /resource_tracker_v2/forms/resource_group_form.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from taggit.forms import TagField, TagWidget 3 | 4 | from Squest.utils.squest_model_form import SquestModelForm 5 | from resource_tracker_v2.models import ResourceGroup 6 | 7 | 8 | class ResourceGroupForm(SquestModelForm): 9 | class Meta: 10 | model = ResourceGroup 11 | fields = ["name", "tags"] 12 | 13 | name = forms.CharField(label="Name", 14 | widget=forms.TextInput()) 15 | 16 | tags = TagField(label="Tags", 17 | required=False, 18 | help_text="Comma-separated list of tags (more details in documentation)", 19 | widget=TagWidget()) 20 | -------------------------------------------------------------------------------- /service_catalog/api/views/portfolio_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 2 | from service_catalog.api.serializers.portfolio_serializer import PortfolioSerializer 3 | from service_catalog.filters.portfolio_filter import PortfolioFilter 4 | from service_catalog.models.portfolio import Portfolio 5 | 6 | 7 | class PortfolioDetails(SquestRetrieveUpdateDestroyAPIView): 8 | queryset = Portfolio.objects.all() 9 | serializer_class = PortfolioSerializer 10 | 11 | 12 | class PortfolioListCreate(SquestListCreateAPIView): 13 | filterset_class = PortfolioFilter 14 | serializer_class = PortfolioSerializer 15 | queryset = Portfolio.objects.all() 16 | -------------------------------------------------------------------------------- /service_catalog/models/inventory.py: -------------------------------------------------------------------------------- 1 | from django.db.models import CharField, IntegerField, ForeignKey, CASCADE 2 | 3 | from Squest.utils.squest_model import SquestModel 4 | from service_catalog.models import TowerServer 5 | 6 | 7 | class Inventory(SquestModel): 8 | class Meta: 9 | unique_together = ('tower_id', 'tower_server',) 10 | default_permissions = ('add', 'change', 'delete', 'view', 'list') 11 | 12 | name = CharField(max_length=100) 13 | tower_id = IntegerField() 14 | tower_server = ForeignKey(TowerServer, 15 | on_delete=CASCADE, 16 | related_name="inventories", 17 | related_query_name="inventory") 18 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_models/test_doc.py: -------------------------------------------------------------------------------- 1 | from service_catalog.models import Doc 2 | from tests.setup import SetupInstance 3 | 4 | 5 | class TestDoc(SetupInstance): 6 | 7 | def test_render(self): 8 | # no instance, render return content 9 | new_doc = Doc.objects.create(title="test", content="test") 10 | self.assertEqual(new_doc.render(), "test") 11 | 12 | # with an instance with use the templating 13 | self.instance_1_org1.spec["dns"] = "name.domain.local" 14 | self.instance_1_org1.save() 15 | new_doc.content = "test {{ instance.spec.dns }}" 16 | new_doc.save() 17 | self.assertEqual(new_doc.render(self.instance_1_org1), "test name.domain.local") 18 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Squest 2 | 3 |

4 | 5 |

6 | 7 | # Welcome to the Squest documentation! 8 | 9 | Squest is a self-service portal that works on top of Red Hat Ansible Automation Platform/AWX. 10 | 11 | - Follow the [installation guide](getting_started.md) to get your own deployment up and running 12 | - Discuss with developers or the community on [Gitter Chat](https://app.gitter.im/#/room/#HewlettPackard_squest:gitter.im) 13 | - Check out our [GitHub repository](https://github.com/HewlettPackard/squest) if you are interested into contributing to Squest 14 | - Don't hesitate to [raise an issue](https://github.com/HewlettPackard/squest/issues) to propose new features or raise a bug 15 | -------------------------------------------------------------------------------- /profiles/migrations/0007_auto_20221005_1107.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-10-05 09:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('profiles', '0006_auto_20220804_1441'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='notificationfilter', 15 | name='instance_states', 16 | ), 17 | migrations.AlterField( 18 | model_name='notificationfilter', 19 | name='when', 20 | field=models.TextField(blank=True, help_text="Ansible like 'when' with `request` as context. No Jinja brackets needed", null=True), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /service_catalog/api/views/custom_link_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 2 | from service_catalog.api.serializers.custom_link_serializer import CustomLinkSerializer 3 | from service_catalog.filters.custom_link_filter import CustomLinkFilter 4 | from service_catalog.models.custom_link import CustomLink 5 | 6 | 7 | class CustomLinkDetails(SquestRetrieveUpdateDestroyAPIView): 8 | queryset = CustomLink.objects.all() 9 | serializer_class = CustomLinkSerializer 10 | 11 | 12 | class CustomLinkListCreate(SquestListCreateAPIView): 13 | filterset_class = CustomLinkFilter 14 | serializer_class = CustomLinkSerializer 15 | queryset = CustomLink.objects.all() 16 | -------------------------------------------------------------------------------- /service_catalog/tables/support_tables.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import Support 5 | 6 | 7 | class SupportTable(SquestTable): 8 | state = TemplateColumn(template_name='service_catalog/custom_columns/support_state.html') 9 | date_opened = TemplateColumn(template_name='generics/custom_columns/generic_date_format.html') 10 | title = LinkColumn() 11 | instance = LinkColumn() 12 | 13 | class Meta: 14 | model = Support 15 | attrs = {"id": "support_table", "class": "table squest-pagination-tables"} 16 | fields = ("title", "instance", "instance__service", "opened_by", "date_opened", "state") 17 | -------------------------------------------------------------------------------- /tests/setup/setup_org.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.test.testcases import TransactionTestCase 3 | from rest_framework.test import APITestCase 4 | 5 | from profiles.models import Organization 6 | 7 | 8 | class SetupOrgCommon(TransactionTestCase): 9 | 10 | def setUp(self): 11 | # Organization 12 | self.org1 = Organization.objects.create(name='Organization 1') 13 | self.org2 = Organization.objects.create(name='Organization 2') 14 | self.org3 = Organization.objects.create(name='Organization 3') 15 | 16 | print("SetupOrgCommon finished") 17 | 18 | class SetupOrg(TestCase, SetupOrgCommon): 19 | pass 20 | 21 | 22 | class SetupOrgAPI(APITestCase, SetupOrgCommon): 23 | pass 24 | -------------------------------------------------------------------------------- /profiles/models/notification_filter.py: -------------------------------------------------------------------------------- 1 | from django.db.models import ManyToManyField, TextField, CharField 2 | 3 | from Squest.utils.squest_model import SquestModel 4 | 5 | 6 | class NotificationFilter(SquestModel): 7 | class Meta: 8 | abstract = True 9 | 10 | name = CharField(max_length=100) 11 | services = ManyToManyField( 12 | "service_catalog.Service", 13 | blank=True, 14 | ) 15 | # "E.G: request.instance.spec['configvar'] == 'value' and request.fill_in_survey['location'] == 'value'" 16 | when = TextField(blank=True, null=True, 17 | help_text="Ansible like 'when' with `request` as context. No Jinja brackets needed") 18 | 19 | def __str__(self): 20 | return self.name 21 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Squest.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /profiles/models/squest_permission.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import Permission 2 | from django.urls import reverse 3 | 4 | from Squest.utils.squest_model import SquestRBAC, SquestDeleteCascadeMixIn 5 | 6 | 7 | class Permission(SquestRBAC, Permission, SquestDeleteCascadeMixIn): 8 | class Meta: 9 | proxy = True 10 | default_permissions = ('add', 'change', 'delete', 'view', 'list') 11 | ordering = ["content_type__model", "codename"] 12 | 13 | @property 14 | def permission_str(self): 15 | return f"{self.content_type.app_label}.{self.codename}" 16 | 17 | def get_absolute_url(self): 18 | return reverse(f"profiles:permission_list") 19 | 20 | def __str__(self): 21 | return self.codename 22 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/views/transformer_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestRetrieveUpdateDestroyAPIView, SquestListCreateAPIView 2 | from resource_tracker_v2.api.serializers.transformer_serializer import TransformerSerializer 3 | from resource_tracker_v2.filters.transformer_filter import TransformerFilter 4 | from resource_tracker_v2.models import Transformer 5 | 6 | 7 | class TransformerListCreate(SquestListCreateAPIView): 8 | serializer_class = TransformerSerializer 9 | filterset_class = TransformerFilter 10 | queryset = Transformer.objects.all() 11 | 12 | 13 | class TransformerDetails(SquestRetrieveUpdateDestroyAPIView): 14 | serializer_class = TransformerSerializer 15 | queryset = Transformer.objects.all() 16 | -------------------------------------------------------------------------------- /service_catalog/api/views/approval_step_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from service_catalog.api.serializers.approval_step_serializer import ApprovalStepSerializer 3 | from service_catalog.filters.approval_step_filter import ApprovalStepFilter 4 | from service_catalog.models import ApprovalStep 5 | 6 | 7 | class ApprovalStepListCreate(SquestListCreateAPIView): 8 | serializer_class = ApprovalStepSerializer 9 | filterset_class = ApprovalStepFilter 10 | queryset = ApprovalStep.objects.all() 11 | 12 | 13 | class ApprovalStepDetails(SquestRetrieveUpdateDestroyAPIView): 14 | serializer_class = ApprovalStepSerializer 15 | queryset = ApprovalStep.objects.all() 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | 29 | - Squest version or GIT commit number: 30 | - Ansible Tower/AWX version: 31 | -------------------------------------------------------------------------------- /service_catalog/migrations/0031_service_attribute_definitions.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-15 15:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('resource_tracker_v2', '0005_auto_20230803_1126'), 10 | ('service_catalog', '0030_emailtemplate'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='service', 16 | name='attribute_definitions', 17 | field=models.ManyToManyField(blank=True, help_text='List of attributes linked to the service, they could be used on operation fields.', related_name='services', to='resource_tracker_v2.attributedefinition'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /service_catalog/models/credential.py: -------------------------------------------------------------------------------- 1 | from django.db.models import CharField, IntegerField, ForeignKey, CASCADE 2 | 3 | from Squest.utils.squest_model import SquestModel 4 | from service_catalog.models import TowerServer 5 | 6 | 7 | class Credential(SquestModel): 8 | class Meta: 9 | unique_together = ('tower_id', 'tower_server',) 10 | default_permissions = ('add', 'change', 'delete', 'view', 'list') 11 | 12 | name = CharField(max_length=100) 13 | tower_id = IntegerField() 14 | tower_server = ForeignKey(TowerServer, 15 | on_delete=CASCADE, 16 | related_name="credentials", 17 | related_query_name="credential" 18 | ) 19 | -------------------------------------------------------------------------------- /profiles/tables/approval_workflow.py: -------------------------------------------------------------------------------- 1 | from django.utils.html import format_html 2 | from django_tables2 import LinkColumn, TemplateColumn 3 | 4 | from Squest.utils.squest_table import SquestTable 5 | from profiles.models import Scope 6 | 7 | 8 | class ApprovalWorkflowPreviewTable(SquestTable): 9 | 10 | name = LinkColumn() 11 | preview = TemplateColumn(template_name='profiles/custom_columns/preview_workflow.html', orderable=False) 12 | 13 | class Meta: 14 | model = Scope 15 | attrs = {"id": "role_table", "class": "table squest-pagination-tables"} 16 | fields = ("name", "preview") 17 | 18 | def render_name(self, value, record): 19 | return format_html(f'{record}') 20 | -------------------------------------------------------------------------------- /profiles/migrations/0002_auto_20211105_0946.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2021-11-05 08:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0001_initial'), 10 | ('profiles', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='profile', 16 | name='notification_enabled', 17 | field=models.BooleanField(default=True), 18 | ), 19 | migrations.AddField( 20 | model_name='profile', 21 | name='subscribed_services_notification', 22 | field=models.ManyToManyField(to='service_catalog.Service'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /tests/setup/setup_team.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework.test import APITestCase 3 | 4 | from profiles.models import Team 5 | from tests.setup import SetupOrgCommon 6 | 7 | 8 | class SetupTeamCommon(SetupOrgCommon): 9 | 10 | def setUp(self): 11 | super().setUp() 12 | # Team 13 | self.team1org2 = Team.objects.create(name='Org2 - Team 1', org=self.org2) 14 | self.team2org2 = Team.objects.create(name='Org2 - Team 2', org=self.org2) 15 | self.team1org3 = Team.objects.create(name='Org3 - Team 1', org=self.org3) 16 | print("SetupTeamCommon finished") 17 | 18 | 19 | class SetupTeam(TestCase, SetupTeamCommon): 20 | pass 21 | 22 | 23 | class SetupTeamAPI(APITestCase, SetupTeamCommon): 24 | pass 25 | -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 |

500

10 |
11 |

Oops! Something went wrong.

12 |

We will work on fixing that right away. Meanwhile, you may return to homepage

13 |
14 |
15 |
16 |
17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /tests/setup/setup_service.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework.test import APITestCase 3 | 4 | from service_catalog.models import Service 5 | from tests.setup import SetupDummyAWXCommon 6 | 7 | 8 | class SetupServiceCommon(SetupDummyAWXCommon): 9 | 10 | def setUp(self): 11 | super().setUp() 12 | # Team 13 | self.service_1 = Service.objects.create(name="Service 1", description="Description of service 1") 14 | self.service_2 = Service.objects.create(name="Service 2", description="Description of service 2") 15 | 16 | print("SetupServiceCommon finished") 17 | 18 | class SetupService(TestCase, SetupServiceCommon): 19 | pass 20 | 21 | 22 | class SetupServiceAPI(APITestCase, SetupServiceCommon): 23 | pass 24 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/views/resource_group_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from resource_tracker_v2.api.serializers.resource_group_serializers import ResourceGroupSerializer 3 | from resource_tracker_v2.filters.resource_group_filter import ResourceGroupFilter 4 | from resource_tracker_v2.models import ResourceGroup 5 | 6 | 7 | class ResourceGroupList(SquestListCreateAPIView): 8 | queryset = ResourceGroup.objects.all() 9 | serializer_class = ResourceGroupSerializer 10 | filterset_class = ResourceGroupFilter 11 | 12 | 13 | class ResourceGroupDetails(SquestRetrieveUpdateDestroyAPIView): 14 | queryset = ResourceGroup.objects.all() 15 | serializer_class = ResourceGroupSerializer 16 | -------------------------------------------------------------------------------- /resource_tracker_v2/models/resource_group.py: -------------------------------------------------------------------------------- 1 | from django.db.models import CharField 2 | from django.urls import reverse 3 | from taggit.managers import TaggableManager 4 | 5 | from Squest.utils.squest_model import SquestModel 6 | 7 | 8 | class ResourceGroup(SquestModel): 9 | 10 | name = CharField(max_length=100, 11 | blank=False, 12 | unique=True) 13 | 14 | tags = TaggableManager() 15 | 16 | def __str__(self): 17 | return self.name 18 | 19 | def create_resource(self, name): 20 | resource, _ = self.resources.get_or_create(name=name, resource_group=self) 21 | return resource 22 | 23 | def get_absolute_url(self): 24 | return reverse("resource_tracker_v2:resourcegroup_details",args=[self.pk]) 25 | -------------------------------------------------------------------------------- /service_catalog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.db import models 3 | from martor.widgets import AdminMartorWidget 4 | 5 | from service_catalog.models.documentation import Doc 6 | 7 | 8 | class DocAdmin(admin.ModelAdmin): 9 | formfield_overrides = { 10 | models.TextField: {'widget': AdminMartorWidget}, 11 | } 12 | 13 | list_filter = ['services', 'operations'] 14 | list_display = ['title', 'linked_services', 'linked_operations'] 15 | 16 | def linked_services(self, obj): 17 | return ", ".join([str(service) for service in obj.services.all()]) 18 | 19 | def linked_operations(self, obj): 20 | return ", ".join([str(operation) for operation in obj.operations.all()]) 21 | 22 | 23 | admin.site.register(Doc, DocAdmin) 24 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/tower_server_actions.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_api/test_instance/test_user_spec/test_user_spec_patch.py: -------------------------------------------------------------------------------- 1 | from rest_framework.reverse import reverse 2 | 3 | from tests.test_service_catalog.test_api.test_instance.test_spec.test_spec_patch import TestApiSpecPatch 4 | 5 | 6 | class TestApiUserSpecPatch(TestApiSpecPatch): 7 | 8 | def setUp(self): 9 | super(TestApiUserSpecPatch, self).setUp() 10 | self.get_spec_details_url = reverse('api_instance_user_spec_details', kwargs=self.kwargs) 11 | self.target_spec = "user-spec" 12 | self.expected_data = self.expected_user_spec 13 | 14 | def test_admin_patch_user_spec(self): 15 | self.test_admin_patch_spec() 16 | 17 | def test_cannot_patch_user_spec_when_logout(self): 18 | self.test_cannot_patch_spec_when_logout() 19 | -------------------------------------------------------------------------------- /profiles/filters/user_filter.py: -------------------------------------------------------------------------------- 1 | import django_filters 2 | from django import forms 3 | from django.contrib.auth.models import User 4 | 5 | from Squest.utils.squest_filter import SquestFilter 6 | 7 | 8 | class UserFilter(SquestFilter): 9 | class Meta: 10 | model = User 11 | fields = ['username', 'email'] 12 | 13 | no_organization = django_filters.BooleanFilter(method='has_no_organization', 14 | label="No organization", 15 | widget=forms.CheckboxInput(attrs={'class': 'form-control-checkbox'})) 16 | 17 | def has_no_organization(self, queryset, field_name, value): 18 | if not value: 19 | return queryset 20 | return queryset.filter(groups__isnull=True) 21 | -------------------------------------------------------------------------------- /service_catalog/migrations/0029_alter_instance_requester.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-10-26 14:10 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('service_catalog', '0028_set_last_update_on_request'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='instance', 18 | name='requester', 19 | field=models.ForeignKey(help_text='Initial requester', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Owner'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /templates/generics/generic_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block main %} 3 |
4 |
5 | {% if form_header %}{% include form_header %}{% endif %} 6 |
7 |
8 | {% include "generics/form_edit.html" %} 9 |
10 |
11 |
12 |
13 | {% load static %} 14 | 15 | 16 | 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_instance_hook/test_list.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from tests.test_service_catalog.base import BaseTest 4 | 5 | 6 | class InstanceHooksListViewsTest(BaseTest): 7 | 8 | def setUp(self): 9 | super(InstanceHooksListViewsTest, self).setUp() 10 | self.url = reverse('service_catalog:instancehook_list') 11 | 12 | def test_get_list(self): 13 | response = self.client.get(self.url) 14 | self.assertEqual(200, response.status_code) 15 | 16 | def test_user_cannot_list(self): 17 | self.client.logout() 18 | self.client.login(username=self.standard_user, password=self.common_password) 19 | response = self.client.get(self.url) 20 | self.assertEqual(403, response.status_code) 21 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_request_hook/test_list.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from tests.test_service_catalog.base import BaseTest 4 | 5 | 6 | class RequestHooksListViewsTest(BaseTest): 7 | 8 | def setUp(self): 9 | super(RequestHooksListViewsTest, self).setUp() 10 | self.url = reverse('service_catalog:requesthook_list') 11 | 12 | def test_get_list(self): 13 | response = self.client.get(self.url) 14 | self.assertEqual(200, response.status_code) 15 | 16 | def test_user_cannot_list(self): 17 | self.client.logout() 18 | self.client.login(username=self.standard_user, password=self.common_password) 19 | response = self.client.get(self.url) 20 | self.assertEqual(403, response.status_code) 21 | -------------------------------------------------------------------------------- /Squest/models/singleton_model.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Model 2 | from django.core.cache import cache 3 | 4 | 5 | class SingletonModel(Model): 6 | 7 | class Meta: 8 | abstract = True 9 | 10 | def save(self, *args, **kwargs): 11 | self.pk = 1 12 | super(SingletonModel, self).save(*args, **kwargs) 13 | self.set_cache() 14 | 15 | def delete(self, *args, **kwargs): 16 | pass 17 | 18 | def set_cache(self): 19 | cache.set(self.__class__.__name__, self) 20 | 21 | @classmethod 22 | def load(cls): 23 | if cache.get(cls.__name__) is None: 24 | obj, created = cls.objects.get_or_create(pk=1) 25 | if not created: 26 | obj.set_cache() 27 | return obj 28 | return cache.get(cls.__name__) 29 | -------------------------------------------------------------------------------- /k8s/squest_k8s/tasks/03-redis.yml: -------------------------------------------------------------------------------- 1 | - when: redis.enabled 2 | name: Install Redis Helm chart 3 | kubernetes.core.helm: 4 | name: redis 5 | release_namespace: "{{ squest_namespace }}" 6 | chart_version: "{{ redis_chart_version }}" 7 | chart_ref: oci://registry-1.docker.io/bitnamicharts/redis 8 | values: "{{ redis.helm_values }}" 9 | 10 | - when: not redis.enabled and squest_redis.existing_secret is not defined 11 | name: Create a secret for redis 12 | kubernetes.core.k8s: 13 | namespace: "{{ squest_namespace }}" 14 | definition: 15 | kind: Secret 16 | apiVersion: v1 17 | metadata: 18 | name: "redis" 19 | labels: 20 | app: squest 21 | service: redis 22 | data: 23 | redis-password: "{{ squest_redis.password | b64encode }}" 24 | -------------------------------------------------------------------------------- /project-static/squest/css/squest-dark.css: -------------------------------------------------------------------------------- 1 | footer{ 2 | z-index: auto !important; 3 | } 4 | /*fix select button color*/ 5 | .btn-light { 6 | background-color: #343a40 !important; 7 | color: #fff !important; 8 | } 9 | 10 | .bg-dark table tr,.bg-dark blockquote,details.bg-dark pre { 11 | background-color: #343a40 !important; 12 | color: #fff !important; 13 | } 14 | 15 | .dtfc-fixed-left { 16 | background-color: #343a40 !important; 17 | z-index: 1; 18 | } 19 | 20 | input.form-control[disabled] { 21 | opacity: 0.4; 22 | } 23 | 24 | div.martor-preview.bg-dark > h1, 25 | div.martor-preview.bg-dark > h2, 26 | div.martor-preview.bg-dark > h3, 27 | div.martor-preview.bg-dark > h4, 28 | div.martor-preview.bg-dark > h5, 29 | div.martor-preview.bg-dark > h6 { 30 | color: white !important; 31 | } 32 | -------------------------------------------------------------------------------- /service_catalog/api/views/__init__.py: -------------------------------------------------------------------------------- 1 | from service_catalog.api.views.instance_api_views import * 2 | from service_catalog.api.views.job_template_api_views import * 3 | from service_catalog.api.views.job_template_sync import * 4 | from service_catalog.api.views.operation_api_views import * 5 | from service_catalog.api.views.request_api_views import * 6 | from service_catalog.api.views.request_state_machine_api_view import * 7 | from service_catalog.api.views.service_api_views import * 8 | from service_catalog.api.views.tower_server_api_views import * 9 | from service_catalog.api.views.operation_survey_api_views import * 10 | from service_catalog.api.views.portfolio_api_views import * 11 | from service_catalog.api.views.approval_workflow_api_views import * 12 | from service_catalog.api.views.approval_step_api_views import * 13 | -------------------------------------------------------------------------------- /service_catalog/migrations/0032_alter_operation_name_alter_operation_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-20 11:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0031_service_attribute_definitions'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='operation', 15 | name='name', 16 | field=models.CharField(max_length=100), 17 | ), 18 | migrations.AlterField( 19 | model_name='operation', 20 | name='type', 21 | field=models.CharField(choices=[('CREATE', 'Create'), ('UPDATE', 'Update'), ('DELETE', 'Delete')], default='CREATE', max_length=10), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /tests/test_swagger_view.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class TestMonitoring(TestCase): 7 | 8 | def test_can_get_swagger_view(self): 9 | url = reverse('schema-swagger-ui') + "?format=openapi" 10 | testing_user = User.objects.create_user('stan1234', 'standard_user@hpe.com', 'password') 11 | self.client.force_login(testing_user) 12 | response = self.client.get(url) 13 | self.assertEqual(200, response.status_code) 14 | 15 | def test_cannot_get_swagger_view_when_logout(self): 16 | url = reverse('schema-swagger-ui') + "?format=openapi" 17 | self.client.logout() 18 | response = self.client.get(url) 19 | self.assertEqual(403, response.status_code) 20 | -------------------------------------------------------------------------------- /project-static/squest/js/resource_group_attributes.js: -------------------------------------------------------------------------------- 1 | function load_resource_group_attributes(target_resource_group_id){ 2 | var url = $("#ResourceGroupLinkForm").attr("data-attribute-url"); 3 | var current_resource_group_id = $("#ResourceGroupLinkForm").attr("current-resource-group-id"); 4 | $.ajax({ 5 | url: url, 6 | data: { 7 | 'csrfmiddlewaretoken': csrf_token, 8 | 'current_resource_group_id': current_resource_group_id, 9 | 'target_resource_group_id': target_resource_group_id 10 | }, 11 | success: function (data) { 12 | $("#id_consume_from_attribute_definition").html(data); 13 | }, 14 | error: function(){ 15 | console.log("Error during ajax call 'load_resource_group_attributes'"); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /service_catalog/filters/operation_filter.py: -------------------------------------------------------------------------------- 1 | from django.forms import SelectMultiple 2 | from django_filters import MultipleChoiceFilter 3 | 4 | from Squest.utils.squest_filter import SquestFilter 5 | from service_catalog.models import Operation 6 | from service_catalog.models.operations import OperationType 7 | 8 | 9 | class OperationFilter(SquestFilter): 10 | class Meta: 11 | model = Operation 12 | fields = ['service', 'name', 'type', 'job_template__name', 'auto_accept', 'auto_process', 'process_timeout_second'] 13 | 14 | type = MultipleChoiceFilter( 15 | choices=OperationType.choices, 16 | widget=SelectMultiple(attrs={'data-live-search': "true"})) 17 | 18 | 19 | class OperationFilterLimited(SquestFilter): 20 | class Meta: 21 | model = Operation 22 | fields = ['name'] 23 | -------------------------------------------------------------------------------- /service_catalog/migrations/0036_approvalworkflow_enabled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-21 19:48 2 | 3 | from django.db import migrations, models 4 | def set_enabled_to_all_workflow(apps, schema_editor): 5 | ApprovalWorkflow = apps.get_model("service_catalog", "ApprovalWorkflow") 6 | ApprovalWorkflow.objects.all().update(enabled=True) 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('service_catalog', '0035_alter_request_options'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='approvalworkflow', 17 | name='enabled', 18 | field=models.BooleanField(default=False, help_text='Set to True to use the workflow'), 19 | ), 20 | migrations.RunPython(set_enabled_to_all_workflow) 21 | ] 22 | -------------------------------------------------------------------------------- /templates/service_catalog/custom_columns/job_template_actions.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 11 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_models/test_service.py: -------------------------------------------------------------------------------- 1 | from service_catalog.forms.form_utils import FormUtils 2 | from tests.test_service_catalog.base_test_request import BaseTestRequest 3 | 4 | 5 | class TestService(BaseTestRequest): 6 | 7 | def setUp(self): 8 | super(TestService, self).setUp() 9 | 10 | def test_bulk_set_permission_on_operation(self): 11 | for current_perm in self.service_test.operations.values_list("permission", flat=True): 12 | self.assertEqual(current_perm, FormUtils.get_default_permission_for_operation()) 13 | 14 | # apply new perm 15 | self.service_test.bulk_set_permission_on_operation(self.admin_operation) 16 | for current_perm in self.service_test.operations.values_list("permission", flat=True): 17 | self.assertEqual(current_perm, self.admin_operation.id) -------------------------------------------------------------------------------- /service_catalog/filters/custom_link_filter.py: -------------------------------------------------------------------------------- 1 | from django.forms import SelectMultiple 2 | from django_filters import MultipleChoiceFilter 3 | 4 | from Squest.utils.squest_filter import SquestFilter 5 | from service_catalog.models import CustomLink 6 | 7 | 8 | class CustomLinkFilter(SquestFilter): 9 | class Meta: 10 | model = CustomLink 11 | fields = ['name', 'services'] 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(CustomLinkFilter, self).__init__(*args, **kwargs) 15 | from service_catalog.models import Service 16 | self.filters['services'].field.choices = [(service.id, service.name) for service in Service.objects.all()] 17 | 18 | services = MultipleChoiceFilter( 19 | label="Service", 20 | choices=[], 21 | widget=SelectMultiple(attrs={'data-live-search': "true"})) 22 | -------------------------------------------------------------------------------- /resource_tracker_v2/api/views/attribute_definition_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from resource_tracker_v2.api.serializers.attribute_definition_serializers import AttributeDefinitionSerializer 3 | from resource_tracker_v2.filters.attribute_definition_filter import AttributeDefinitionFilter 4 | from resource_tracker_v2.models import AttributeDefinition 5 | 6 | 7 | class AttributeDefinitionList(SquestListCreateAPIView): 8 | queryset = AttributeDefinition.objects.all() 9 | serializer_class = AttributeDefinitionSerializer 10 | filterset_class = AttributeDefinitionFilter 11 | 12 | 13 | class AttributeDefinitionDetails(SquestRetrieveUpdateDestroyAPIView): 14 | queryset = AttributeDefinition.objects.all() 15 | serializer_class = AttributeDefinitionSerializer 16 | -------------------------------------------------------------------------------- /service_catalog/tables/annoucement_tables.py: -------------------------------------------------------------------------------- 1 | from django.utils.html import format_html 2 | from django_tables2 import TemplateColumn, Column 3 | 4 | from Squest.utils.squest_table import SquestTable 5 | from service_catalog.models import Announcement 6 | 7 | 8 | class AnnouncementTable(SquestTable): 9 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 10 | created_by__username = Column(verbose_name='Owner') 11 | 12 | class Meta: 13 | model = Announcement 14 | attrs = {"id": "announcement_table", "class": "table squest-pagination-tables"} 15 | fields = ("title", "date_start", "date_stop", "created_by__username", "type", "actions") 16 | 17 | def render_type(self, value, record): 18 | return format_html(f'{ value }') 19 | -------------------------------------------------------------------------------- /Squest/api/celery_tasks_views.py: -------------------------------------------------------------------------------- 1 | from django_celery_results.models import TaskResult 2 | from drf_yasg.utils import swagger_auto_schema 3 | from rest_framework import status 4 | from rest_framework.generics import get_object_or_404 5 | from rest_framework.permissions import IsAdminUser 6 | from rest_framework.response import Response 7 | from rest_framework.views import APIView 8 | 9 | from service_catalog.api.serializers import TaskResultSerializer 10 | 11 | 12 | class CeleryTaskView(APIView): 13 | 14 | permission_classes = [IsAdminUser] 15 | 16 | @swagger_auto_schema(responses={200: TaskResultSerializer()}) 17 | def get(self, request, task_id): 18 | task_result = get_object_or_404(TaskResult, id=task_id) 19 | serialized_task = TaskResultSerializer(task_result) 20 | return Response(serialized_task.data, status=status.HTTP_200_OK) 21 | -------------------------------------------------------------------------------- /templates/profiles/custom_columns/user_roles.html: -------------------------------------------------------------------------------- 1 | {% with class_name=object|to_class_name|lower %} 2 | {% for group in record.groups.all %} 3 | {% has_perm request.user "profiles.view_role" group.role as can_view_role %} 4 |
5 | {{ group.role.name }} 7 | {% if can_delete_user %} 8 | 11 | 12 | 13 | {% endif %} 14 |
15 | {% endfor %} 16 | {% endwith %} 17 | -------------------------------------------------------------------------------- /service_catalog/migrations/0041_operation_validators.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-12-20 13:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0040_alter_towersurveyfield_unique_together'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='operation', 15 | name='validators', 16 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Survey validators'), 17 | ), 18 | migrations.CreateModel( 19 | name="FakeInstance", 20 | fields=[], 21 | options={ 22 | "managed": False, 23 | "proxy": True, 24 | }, 25 | bases=("service_catalog.instance",), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /tests/test_profiles/test_forms/test_notification_filter_form.py: -------------------------------------------------------------------------------- 1 | from profiles.forms import InstanceNotificationForm, RequestNotificationForm 2 | from tests.test_profiles.base.base_test_profile import BaseTestProfile 3 | 4 | 5 | class TestNotificationForm(BaseTestProfile): 6 | 7 | def test_m2m_saved(self): 8 | data = { 9 | "name": "test_notification_form", 10 | "services": [self.service_test] 11 | } 12 | form = InstanceNotificationForm(user=self.standard_user, data=data) 13 | form.is_valid() 14 | saved_filter = form.save() 15 | self.assertIn(self.service_test, saved_filter.services.all()) 16 | 17 | form = RequestNotificationForm(user=self.standard_user, data=data) 18 | form.is_valid() 19 | saved_filter = form.save() 20 | self.assertIn(self.service_test, saved_filter.services.all()) 21 | -------------------------------------------------------------------------------- /playbook_examples/file_service_example/delete.yml: -------------------------------------------------------------------------------- 1 | # ansible-playbook playbook_examples/file_service_example/create.yml \ 2 | # -i playbook_examples/squest_inventory/inventory \ 3 | # --extra-vars @playbook_examples/squest_inventory/squest_extra_vars/delete.yml 4 | 5 | --- 6 | - hosts: squest_testing 7 | become: False 8 | gather_facts: False 9 | 10 | tasks: 11 | - name: Get UUID 12 | set_fact: 13 | uuid_file: "{{ squest['request']['instance']['spec']['uuid_file'] }}" 14 | 15 | - name: Generate path 16 | set_fact: 17 | file_path: "/tmp/squest_functional_test/{{ uuid_file }}" 18 | 19 | - name: Prints variables 20 | ansible.builtin.debug: 21 | msg: 22 | - "UUID: {{ uuid_file }}" 23 | 24 | - name: Recursively remove directory 25 | ansible.builtin.file: 26 | path: "{{ file_path }}" 27 | state: absent 28 | -------------------------------------------------------------------------------- /service_catalog/tables/service_tables.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import Service 5 | 6 | 7 | class ServiceTable(SquestTable): 8 | actions = TemplateColumn(template_name='service_catalog/custom_columns/service_actions.html', orderable=False) 9 | enabled = TemplateColumn(template_name='generics/custom_columns/generic_boolean_check.html') 10 | operations = TemplateColumn(template_name='service_catalog/custom_columns/service_operations.html', 11 | verbose_name="Operations", orderable=False) 12 | name = LinkColumn() 13 | 14 | class Meta: 15 | model = Service 16 | attrs = {"id": "service_table", "class": "table squest-pagination-tables"} 17 | fields = ("name", "description", "enabled", "operations", "actions") 18 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_customer/test_instance/test_instance_details.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from service_catalog.models import InstanceState 4 | from tests.test_service_catalog.base_test_request import BaseTestRequest 5 | 6 | 7 | class TestCustomerInstanceDetails(BaseTestRequest): 8 | 9 | def setUp(self): 10 | super(TestCustomerInstanceDetails, self).setUp() 11 | self.test_instance.state = InstanceState.AVAILABLE 12 | self.test_instance.save() 13 | self.args = { 14 | "pk": self.test_instance.id 15 | } 16 | 17 | def test_get_instance_details(self): 18 | url = reverse('service_catalog:instance_details', kwargs=self.args) 19 | response = self.client.get(url) 20 | self.assertEqual(200, response.status_code) 21 | self.assertIsNotNone(response.context['operations_table']) 22 | -------------------------------------------------------------------------------- /service_catalog/migrations/0038_alter_towersurveyfield_unique_together_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-12-08 14:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0037_alter_request_options_remove_approvalstep_next_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name='towersurveyfield', 15 | unique_together=set(), 16 | ), 17 | migrations.AddField( 18 | model_name='towersurveyfield', 19 | name='position', 20 | field=models.IntegerField(default=0), 21 | ), 22 | migrations.AlterUniqueTogether( 23 | name='towersurveyfield', 24 | unique_together={('operation', 'position', 'variable')}, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /service_catalog/tables/tower_server_tables.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from service_catalog.models import TowerServer 5 | 6 | 7 | class TowerServerTable(SquestTable): 8 | name = LinkColumn() 9 | host = TemplateColumn(template_name='service_catalog/custom_columns/tower_server_host.html') 10 | jobtemplate = TemplateColumn(template_name='service_catalog/custom_columns/tower_server_job_templates.html', 11 | verbose_name="Job templates") 12 | actions = TemplateColumn(template_name='service_catalog/custom_columns/tower_server_actions.html', orderable=False) 13 | 14 | class Meta: 15 | model = TowerServer 16 | attrs = {"id": "tower_server_table", "class": "table squest-pagination-tables"} 17 | fields = ("name", "host", "jobtemplate", "actions") 18 | -------------------------------------------------------------------------------- /service_catalog/views/custom_link.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_views import * 2 | from service_catalog.filters.custom_link_filter import CustomLinkFilter 3 | from service_catalog.forms.custom_link_form import CustomLinkForm 4 | from service_catalog.models import CustomLink 5 | from service_catalog.tables.custom_link_table import CustomLinkTable 6 | 7 | 8 | class CustomLinkListView(SquestListView): 9 | table_class = CustomLinkTable 10 | model = CustomLink 11 | filterset_class = CustomLinkFilter 12 | 13 | 14 | class CustomLinkCreateView(SquestCreateView): 15 | model = CustomLink 16 | form_class = CustomLinkForm 17 | 18 | 19 | class CustomLinkEditView(SquestUpdateView): 20 | model = CustomLink 21 | form_class = CustomLinkForm 22 | 23 | 24 | class CustomLinkDeleteView(SquestDeleteView): 25 | model = CustomLink 26 | template_name = 'generics/confirm-delete-template.html' 27 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/tower_server_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer, ValidationError 2 | 3 | from service_catalog.models import TowerServer 4 | 5 | 6 | class TowerServerSerializer(ModelSerializer): 7 | class Meta: 8 | model = TowerServer 9 | exclude = ('token',) 10 | 11 | 12 | class TowerServerCreateSerializer(ModelSerializer): 13 | class Meta: 14 | model = TowerServer 15 | fields = '__all__' 16 | 17 | def validate_extra_vars(self, value): 18 | if value is None or not isinstance(value, dict): 19 | raise ValidationError("Please enter a valid JSON. Empty value is {} for JSON.") 20 | return value 21 | 22 | def to_representation(self, tower_server): 23 | representation = super().to_representation(tower_server) 24 | representation.pop("token") 25 | return representation 26 | -------------------------------------------------------------------------------- /service_catalog/migrations/0016_alter_request_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-07-31 10:12 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0015_auto_20230728_1442'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='request', 15 | options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'ordering': ['-date_submitted'], 'permissions': [('accept_request', 'Can accept request'), ('cancel_request', 'Can cancel request'), ('reject_request', 'Can reject request'), ('archive_request', 'Can archive request'), ('unarchive_request', 'Can unarchive request'), ('resubmit_request', 'Can re-submit request'), ('process_request', 'Can process request'), ('need_info_request', 'Can ask info request')]}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/migrations/0027_alter_approvalworkflow_scopes.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-22 12:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ('profiles', '0019_alter_quota_options'), 9 | ('service_catalog', '0026_auto_20230919_1353'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='approvalworkflow', 15 | name='scopes', 16 | field=models.ManyToManyField(blank=True, 17 | help_text='This workflow will be triggered for the following scopes. Leave empty to trigger for all scopes', 18 | related_name='approval_workflows', to='profiles.Scope', 19 | verbose_name='Restricted scopes'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /tests/test_profiles/test_urls/test_user.py: -------------------------------------------------------------------------------- 1 | from tests.test_profiles.base.base_test_profile import BaseTestProfile 2 | from tests.permission_endpoint import TestingGetContextView, TestPermissionEndpoint 3 | 4 | 5 | class TestProfilesUserPermissionsViews(BaseTestProfile, TestPermissionEndpoint): 6 | def test_user_views(self): 7 | testing_view_list = [ 8 | # TODO: to be tested when User has been replaced by SquestUser (list_user permission not defined) 9 | # TestingGetUIViews( 10 | # url='profiles:user_list', 11 | # perm_str_list=['auth.list_user'], 12 | # ), 13 | TestingGetContextView( 14 | url='profiles:user_details', 15 | perm_str_list=['auth.view_user'], 16 | url_kwargs={'pk': self.standard_user.id} 17 | ) 18 | ] 19 | self.run_permissions_tests(testing_view_list) -------------------------------------------------------------------------------- /k8s/deploy.yml: -------------------------------------------------------------------------------- 1 | - name: "Deploy Squest" 2 | hosts: localhost 3 | gather_facts: false 4 | module_defaults: 5 | kubernetes.core.k8s: 6 | kubeconfig: "{{ k8s_kubeconfig_path }}" 7 | kubernetes.core.k8s_info: 8 | kubeconfig: "{{ k8s_kubeconfig_path }}" 9 | kubernetes.core.helm: 10 | kubeconfig: "{{ k8s_kubeconfig_path }}" 11 | 12 | pre_tasks: 13 | 14 | - when: k8s_kubeconfig_path is undefined 15 | name: 'Fail if k8s_kubeconfig_path is not defined' 16 | ansible.builtin.fail: 17 | msg: 'Please make sure you set k8s_kubeconfig_path' 18 | 19 | - when: 20 | - ssh_private_key_path is undefined 21 | - squest_django.externalize_backup_via_rsync.enabled | bool 22 | name: 'Fail if ssh_private_key_path is not defined' 23 | ansible.builtin.fail: 24 | msg: 'Please make sure you set ssh_private_key_path' 25 | 26 | roles: 27 | - role: squest_k8s 28 | -------------------------------------------------------------------------------- /k8s/squest_k8s/tasks/02-rabbitmq.yml: -------------------------------------------------------------------------------- 1 | - when: rabbitmq.enabled 2 | name: Install RabbitMQ Helm chart 3 | kubernetes.core.helm: 4 | name: rabbitmq 5 | release_namespace: "{{ squest_namespace }}" 6 | chart_version: "{{ rabbitmq_chart_version }}" 7 | chart_ref: oci://registry-1.docker.io/bitnamicharts/rabbitmq 8 | values: "{{ rabbitmq.helm_values }}" 9 | 10 | - when: not rabbitmq.enabled and squest_rabbitmq.existing_secret is not defined 11 | name: Create a secret for rabbitmq 12 | kubernetes.core.k8s: 13 | namespace: "{{ squest_namespace }}" 14 | definition: 15 | kind: Secret 16 | apiVersion: v1 17 | metadata: 18 | name: "rabbitmq" 19 | labels: 20 | app: squest 21 | service: rabbitmq 22 | data: 23 | rabbitmq-erlang-cookie: "{{ squest_rabbitmq.erlang_cookie | b64encode }}" 24 | rabbitmq-password: "{{ squest_rabbitmq.password | b64encode }}" 25 | -------------------------------------------------------------------------------- /resource_tracker_v2/tables/transformer_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn, LinkColumn, Column 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from resource_tracker_v2.models import Transformer 5 | 6 | 7 | class TransformerTable(SquestTable): 8 | class Meta: 9 | model = Transformer 10 | attrs = {"id": "attribute_definition_table", "class": "table squest-pagination-tables"} 11 | fields = ("attribute_definition__name", "consume_from_resource_group", "consume_from_attribute_definition", 12 | "factor", "actions") 13 | 14 | attribute_definition__name = Column() 15 | consume_from_resource_group = LinkColumn() 16 | consume_from_attribute_definition = Column(verbose_name="On attribute") 17 | actions = TemplateColumn( 18 | template_name='resource_tracker_v2/resource_group/custom_columns/resource_group_attribute_actions.html', 19 | orderable=False) 20 | -------------------------------------------------------------------------------- /profiles/forms/token_forms.py: -------------------------------------------------------------------------------- 1 | from django.utils import timezone 2 | from tempus_dominus.widgets import DateTimePicker 3 | 4 | from Squest.utils.squest_model_form import SquestModelForm 5 | from profiles.models import Token 6 | 7 | 8 | class TokenForm(SquestModelForm): 9 | class Meta: 10 | model = Token 11 | fields = ['description', 'expires'] 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(TokenForm, self).__init__(*args, **kwargs) 15 | self.fields['expires'].widget = DateTimePicker( 16 | options={ 17 | 'useCurrent': True, 18 | 'timeZone': str(timezone.get_current_timezone()), 19 | 'collapse': False, 20 | 'minDate': str(timezone.now().astimezone().strftime("%Y-%m-%d %H:%M:%S")), 21 | }, attrs={ 22 | 'append': 'fa fa-calendar', 23 | 'icon_toggle': True, 24 | } 25 | ) 26 | -------------------------------------------------------------------------------- /resource_tracker_v2/models/resource_attribute.py: -------------------------------------------------------------------------------- 1 | from django.db.models import PositiveIntegerField, ForeignKey, CASCADE 2 | 3 | from Squest.utils.squest_model import SquestModel 4 | 5 | 6 | class ResourceAttribute(SquestModel): 7 | value = PositiveIntegerField(default=0) 8 | 9 | resource = ForeignKey('Resource', 10 | on_delete=CASCADE, 11 | related_name='resource_attributes', 12 | related_query_name='resource_attribute', 13 | null=True) 14 | 15 | attribute_definition = ForeignKey('AttributeDefinition', 16 | on_delete=CASCADE, 17 | related_name='resource_attributes', 18 | related_query_name='resource_attribute', 19 | null=True) 20 | 21 | def __str__(self): 22 | return str(self.value) 23 | -------------------------------------------------------------------------------- /service_catalog/migrations/0028_set_last_update_on_request.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-27 08:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | def update_requests(apps, schema_editor): 7 | Request = apps.get_model('service_catalog', 'Request') 8 | for request in Request.objects.all(): 9 | Request.objects.filter(pk=request.pk).update(created=request.date_submitted, 10 | last_updated=request.date_submitted) 11 | from service_catalog.models import RequestState 12 | if request.state == RequestState.COMPLETE: 13 | Request.objects.filter(pk=request.pk).update(last_updated=request.date_complete) 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [ 19 | ('service_catalog', '0027_alter_approvalworkflow_scopes'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(update_requests), 24 | ] 25 | -------------------------------------------------------------------------------- /Squest/api/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework import authentication, exceptions 2 | 3 | from profiles.models import Token 4 | 5 | 6 | class TokenAuthentication(authentication.TokenAuthentication): 7 | """ 8 | A custom authentication scheme which enforces Token expiration times. 9 | """ 10 | model = Token 11 | keyword = 'Bearer' 12 | 13 | def authenticate_credentials(self, key): 14 | model = self.get_model() 15 | try: 16 | token = model.objects.prefetch_related('user').get(key=key) 17 | except model.DoesNotExist: 18 | raise exceptions.AuthenticationFailed("Invalid token") 19 | # Enforce the Token's expiration time, if one has been set. 20 | if token.is_expired(): 21 | raise exceptions.AuthenticationFailed("Token expired") 22 | if not token.user.is_active: 23 | raise exceptions.AuthenticationFailed("User inactive") 24 | return token.user, token 25 | -------------------------------------------------------------------------------- /service_catalog/api/serializers/approval_step_state_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from service_catalog.models import ApprovalStepState 4 | 5 | 6 | class ApprovalStepStateSerializer(ModelSerializer): 7 | 8 | class Meta: 9 | model = ApprovalStepState 10 | fields = ['id', 'approval_step', 'state', 'updated_by', 'date_updated', 'fill_in_survey'] 11 | 12 | def __init__(self, *args, **kwargs): 13 | self.user = kwargs.pop('user') 14 | super(ApprovalStepStateSerializer, self).__init__(*args, **kwargs) 15 | 16 | def to_representation(self, current_step): 17 | representation = super().to_representation(current_step) 18 | if not self.user.has_perm(current_step.approval_step.permission.permission_str, 19 | current_step.approval_workflow_state.request): 20 | representation.pop("fill_in_survey") 21 | return representation 22 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_catalog/test_operations/test_list.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from tests.test_service_catalog.base import BaseTest 4 | 5 | 6 | class OperationListTestCase(BaseTest): 7 | 8 | def setUp(self): 9 | super(OperationListTestCase, self).setUp() 10 | 11 | self.url = reverse('service_catalog:operation_list') 12 | 13 | def test_get_operation_list(self): 14 | response = self.client.get(self.url) 15 | self.assertEqual(200, response.status_code) 16 | 17 | def test_customer_can_get_operation_list(self): 18 | self.client.force_login(self.standard_user) 19 | response = self.client.get(self.url) 20 | self.assertEqual(200, response.status_code) 21 | 22 | def test_cannot_get_operation_list_logout(self): 23 | self.client.logout() 24 | response = self.client.get(self.url) 25 | self.assertEqual(302, response.status_code) 26 | -------------------------------------------------------------------------------- /service_catalog/forms/request_message_forms.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from service_catalog.models import RequestMessage 3 | 4 | 5 | class RequestMessageForm(SquestModelForm): 6 | class Meta: 7 | model = RequestMessage 8 | fields = ["content"] 9 | 10 | def __init__(self, *args, **kwargs): 11 | self.sender = kwargs.pop('sender') 12 | self.request = kwargs.pop('target_request') 13 | super(RequestMessageForm, self).__init__(*args, **kwargs) 14 | 15 | def save(self, commit=True, send_notification=True): 16 | message = super(RequestMessageForm, self).save(commit=False) 17 | message.request = self.request 18 | message.sender = self.sender 19 | message.save() 20 | if send_notification: 21 | from service_catalog.mail_utils import send_mail_new_comment_on_request 22 | send_mail_new_comment_on_request(message) 23 | return message 24 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_custom_links/test_list.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from tests.test_service_catalog.test_views.test_admin.test_tools.test_custom_links.base_test_custom_link import \ 4 | BaseTestCustomLink 5 | 6 | 7 | class CustomLinksListViewsTest(BaseTestCustomLink): 8 | 9 | def setUp(self): 10 | super(CustomLinksListViewsTest, self).setUp() 11 | self.url = reverse('service_catalog:customlink_list') 12 | 13 | def test_get_list(self): 14 | response = self.client.get(self.url) 15 | self.assertEqual(200, response.status_code) 16 | self.assertEqual(len(response.context["table"].data.data), 1) 17 | 18 | def test_user_cannot_list(self): 19 | self.client.logout() 20 | self.client.login(username=self.standard_user, password=self.common_password) 21 | response = self.client.get(self.url) 22 | self.assertEqual(403, response.status_code) 23 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_views/test_admin/test_tools/test_custom_links/base_test_custom_link.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework.test import APITestCase 3 | 4 | from service_catalog.models import CustomLink 5 | from tests.test_service_catalog.base_test_request import BaseTestRequestCommon 6 | 7 | class BaseTestCustomLinkCommon(BaseTestRequestCommon): 8 | 9 | def setUp(self): 10 | super(BaseTestCustomLinkCommon, self).setUp() 11 | 12 | self.test_custom_link = CustomLink.objects.create(name="test_custom_link", 13 | text="custom_link", 14 | url="https://custom-link.domain") 15 | self.test_custom_link.services.set([self.service_test]) 16 | 17 | class BaseTestCustomLink(TestCase, BaseTestCustomLinkCommon): 18 | pass 19 | 20 | 21 | class BaseTestCustomLinkAPI(APITestCase, BaseTestCustomLinkCommon): 22 | pass 23 | -------------------------------------------------------------------------------- /templates/generics/breadcrumbs.html: -------------------------------------------------------------------------------- 1 | {% if breadcrumbs %} 2 | 19 | {% elif title %} 20 |

{{ title }}

21 | {% endif %} 22 | -------------------------------------------------------------------------------- /service_catalog/filters/support_filter.py: -------------------------------------------------------------------------------- 1 | from django.forms import SelectMultiple, HiddenInput 2 | from django_filters import MultipleChoiceFilter 3 | 4 | from Squest.utils.squest_filter import SquestFilter 5 | from service_catalog.models import Support 6 | from service_catalog.models.support import SupportState 7 | 8 | 9 | class SupportFilter(SquestFilter): 10 | class Meta: 11 | model = Support 12 | fields = ['title', 'instance__id', 'instance__name', 'instance__service', 'opened_by', 'state'] 13 | 14 | state = MultipleChoiceFilter( 15 | choices=SupportState.choices, 16 | widget=SelectMultiple(attrs={'data-live-search': "true"})) 17 | 18 | def __init__(self, *args, **kwargs): 19 | super(SupportFilter, self).__init__(*args, **kwargs) 20 | self.filters['instance__name'].field.label = "Instance" 21 | self.filters['instance__service'].field.label = "Service" 22 | self.filters['instance__id'].field.widget = HiddenInput() 23 | -------------------------------------------------------------------------------- /profiles/forms/model_permission_form.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_model_form import SquestModelForm 2 | from profiles.models.squest_permission import Permission 3 | 4 | 5 | class ModelPermissionForm(SquestModelForm): 6 | class Meta: 7 | model = Permission 8 | exclude = ['content_type'] 9 | 10 | def __init__(self, content_type, *args, **kwargs): 11 | # get arguments from instance 12 | self.permission_content_type = content_type 13 | super().__init__(*args, **kwargs) 14 | self.fields['name'].help_text = f'A short description of the permission.' 15 | self.fields['codename'].help_text = f'Unique identifier for the permission in camel case format. ' \ 16 | f'E.g: approve_custom_step' 17 | 18 | def save(self, commit=True): 19 | permission = super().save(False) 20 | permission.content_type = self.permission_content_type 21 | permission.save() 22 | return permission 23 | -------------------------------------------------------------------------------- /profiles/tables/notification_filter_table.py: -------------------------------------------------------------------------------- 1 | from django_tables2 import TemplateColumn 2 | 3 | from Squest.utils.squest_table import SquestTable 4 | from profiles.models import InstanceNotification, RequestNotification 5 | 6 | 7 | class RequestNotificationFilterTable(SquestTable): 8 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 9 | 10 | class Meta: 11 | model = RequestNotification 12 | attrs = {"id": "request_notification_filter__table", "class": "table squest-pagination-tables "} 13 | fields = ("name", "actions") 14 | 15 | 16 | class InstanceNotificationFilterTable(SquestTable): 17 | actions = TemplateColumn(template_name='generics/custom_columns/generic_actions.html', orderable=False) 18 | 19 | class Meta: 20 | model = InstanceNotification 21 | attrs = {"id": "support_notification_filter__table", "class": "table squest-pagination-tables "} 22 | fields = ("name", "actions") 23 | -------------------------------------------------------------------------------- /Squest/utils/ansible_when.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from jinja2 import Template, UndefinedError, TemplateSyntaxError 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | class AnsibleWhen(object): 8 | 9 | @classmethod 10 | def when_render(cls, context, when_string): 11 | if when_string is None or when_string == "" or context is None: 12 | return False 13 | template_string = "{% if " + when_string + " %}True{% else %}{% endif %}" 14 | try: 15 | template = Template(template_string) 16 | except TemplateSyntaxError: 17 | logger.warning(f"when_render error when templating: {context} with string '{when_string}'") 18 | return False 19 | try: 20 | template_rendered = template.render(context) 21 | return bool(template_rendered) 22 | except UndefinedError: 23 | logger.warning(f"when_render error when templating: {context} with string '{when_string}'") 24 | return False 25 | -------------------------------------------------------------------------------- /profiles/forms/scope_form.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.forms import ModelMultipleChoiceField 3 | 4 | from Squest.utils.squest_form import SquestForm 5 | from profiles.models import Role 6 | 7 | 8 | class ScopeCreateRBACForm(SquestForm): 9 | roles = ModelMultipleChoiceField( 10 | queryset=Role.objects.all(), 11 | required=True, 12 | ) 13 | users = ModelMultipleChoiceField( 14 | queryset=User.objects.none(), 15 | required=True, 16 | ) 17 | 18 | def __init__(self, *args, **kwargs): 19 | self.scope = kwargs.pop('scope') 20 | super(ScopeCreateRBACForm, self).__init__(*args, **kwargs) 21 | self.fields["users"].queryset = self.scope.get_potential_users() 22 | 23 | def save(self): 24 | for role in self.cleaned_data.get('roles'): 25 | group = self.scope.get_rbac(role) 26 | for user in self.cleaned_data.get('users'): 27 | self.scope.add_user_in_role(user, role) 28 | -------------------------------------------------------------------------------- /resource_tracker_v2/forms/attribute_definition_form.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelMultipleChoiceField 2 | 3 | from Squest.utils.squest_model_form import SquestModelForm 4 | from resource_tracker_v2.models import AttributeDefinition 5 | from service_catalog.models import Service 6 | 7 | 8 | class AttributeDefinitionForm(SquestModelForm): 9 | class Meta: 10 | model = AttributeDefinition 11 | fields = ["name", "description"] 12 | 13 | services = ModelMultipleChoiceField(queryset=Service.objects.all(), required=False) 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(AttributeDefinitionForm, self).__init__(*args, **kwargs) 17 | if self.instance.id: 18 | self.fields['services'].initial = [service for service in self.instance.services.all()] 19 | 20 | def save(self, commit=True): 21 | attribute_definition = super().save(commit) 22 | attribute_definition.services.set(self.cleaned_data['services']) 23 | return attribute_definition 24 | -------------------------------------------------------------------------------- /templates/service_catalog/buttons/request_state_machine.html: -------------------------------------------------------------------------------- 1 | {% has_perm request.user "service_catalog.archive_request" object as can_archive_request %} 2 | {% has_perm request.user "service_catalog.unarchive_request" object as can_unarchive_request %} 3 | {% with args_filter="archive,"|addstr:object.id %} 4 | {% if args_filter|can_proceed_request_action and can_archive_request %} 5 | 8 | 9 | 10 | {% endif %} 11 | {% endwith %} 12 | {% with args_filter="unarchive,"|addstr:object.id %} 13 | {% if args_filter|can_proceed_request_action and can_unarchive_request %} 14 | 17 | 18 | 19 | {% endif %} 20 | {% endwith %} 21 | -------------------------------------------------------------------------------- /service_catalog/api/views/tower_server_api_views.py: -------------------------------------------------------------------------------- 1 | from Squest.utils.squest_api_views import SquestListCreateAPIView, SquestRetrieveUpdateDestroyAPIView 2 | from service_catalog.api.serializers import TowerServerSerializer, TowerServerCreateSerializer 3 | from service_catalog.filters.tower_server_filter import TowerServerFilter 4 | from service_catalog.models import TowerServer 5 | 6 | 7 | class TowerServerList(SquestListCreateAPIView): 8 | queryset = TowerServer.objects.all() 9 | filterset_class = TowerServerFilter 10 | 11 | def get_serializer_class(self): 12 | if self.request.method in ["POST"]: 13 | return TowerServerCreateSerializer 14 | return TowerServerSerializer 15 | 16 | 17 | class TowerServerDetails(SquestRetrieveUpdateDestroyAPIView): 18 | queryset = TowerServer.objects.all() 19 | 20 | def get_serializer_class(self): 21 | if self.request.method in ["PATCH", "PUT"]: 22 | return TowerServerCreateSerializer 23 | return TowerServerSerializer 24 | -------------------------------------------------------------------------------- /service_catalog/migrations/0035_alter_request_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.6 on 2023-11-16 13:19 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('service_catalog', '0034_alter_request_approval_workflow_state'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='request', 15 | options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'ordering': ['-last_updated'], 'permissions': [('accept_request', 'Can accept request'), ('cancel_request', 'Can cancel request'), ('reject_request', 'Can reject request'), ('archive_request', 'Can archive request'), ('unarchive_request', 'Can unarchive request'), ('re_submit_request', 'Can re-submit request'), ('process_request', 'Can process request'), ('need_info_request', 'Can ask info request'), ('view_admin_survey', 'Can view admin survey'), ('list_approvers', 'Can view who can accept')]}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /service_catalog/migrations/0022_auto_20230906_1539.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2023-09-06 13:39 2 | 3 | from django.db import migrations 4 | 5 | 6 | 7 | def update_current_pending_instances(apps, schema_editor): 8 | from service_catalog.models import InstanceState, RequestState 9 | Instance = apps.get_model('service_catalog', 'Instance') 10 | for pending_instance in Instance.objects.filter(state=InstanceState.PENDING): 11 | if pending_instance.request_set.count() == 1: 12 | request = pending_instance.request_set.first() 13 | if request.state in [RequestState.REJECTED, RequestState.CANCELED]: 14 | pending_instance.state = InstanceState.ABORTED 15 | pending_instance.save() 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('service_catalog', '0021_approvalstep_auto_accept_condition'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(update_current_pending_instances), 26 | ] 27 | -------------------------------------------------------------------------------- /tests/test_service_catalog/test_urls/test_doc.py: -------------------------------------------------------------------------------- 1 | 2 | from service_catalog.models import Doc 3 | from tests.test_service_catalog.base_test_request import BaseTestRequest 4 | from tests.permission_endpoint import TestingGetContextView, TestPermissionEndpoint 5 | 6 | 7 | class TestServiceCatalogDocPermissionsViews(BaseTestRequest, TestPermissionEndpoint): 8 | def setUp(self): 9 | super().setUp() 10 | self.doc = Doc.objects.create(title="test_doc", content="# tittle 1") 11 | 12 | def test_doc_views(self): 13 | testing_view_list = [ 14 | TestingGetContextView( 15 | url='service_catalog:doc_list', 16 | perm_str_list=['service_catalog.list_doc'], 17 | ), 18 | TestingGetContextView( 19 | url='service_catalog:doc_details', 20 | perm_str_list=['service_catalog.view_doc'], 21 | url_kwargs={'pk': self.doc.id} 22 | ), 23 | 24 | ] 25 | self.run_permissions_tests(testing_view_list) -------------------------------------------------------------------------------- /service_catalog/api/serializers/approval_workflow_state_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework.relations import PrimaryKeyRelatedField 2 | from rest_framework.serializers import ModelSerializer 3 | 4 | from service_catalog.api.serializers.approval_step_state_serializer import ApprovalStepStateSerializer 5 | from service_catalog.models import ApprovalWorkflowState 6 | 7 | 8 | class ApprovalWorkflowStateSerializer(ModelSerializer): 9 | approval_workflow = PrimaryKeyRelatedField(read_only=True) 10 | current_step = PrimaryKeyRelatedField(read_only=True) 11 | 12 | class Meta: 13 | model = ApprovalWorkflowState 14 | fields = ['id', 'approval_workflow', 'approval_step_states', 'current_step'] 15 | 16 | def __init__(self, *args, **kwargs): 17 | super(ApprovalWorkflowStateSerializer, self).__init__(*args, **kwargs) 18 | self.fields["approval_step_states"] = ApprovalStepStateSerializer(many=True, 19 | user=self.context.get("user")) 20 | -------------------------------------------------------------------------------- /templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 |

403

10 |
11 |

Access denied

12 |

13 | {% if exception %} 14 | {{ exception }} 15 | {% else %} 16 | The page you are trying to access has restricted access. 17 | {% endif %} 18 |
19 |

20 |
21 |
22 |
23 |
24 | 25 | {% endblock %} 26 | --------------------------------------------------------------------------------