# Python import logging # Django from django.core.checks import Error from django.utils.translation import gettext_lazy as _ # Django REST Framework from rest_framework import serializers # AWX from awx.conf import fields, register, register_validate from awx.main.models import ExecutionEnvironment from awx.main.constants import SUBSCRIPTION_USAGE_MODEL_UNIQUE_HOSTS from awx.main.tasks.policy import OPA_AUTH_TYPES logger = logging.getLogger('awx.main.conf') register( 'ACTIVITY_STREAM_ENABLED', field_class=fields.BooleanField, label=_('Enable Activity Stream'), help_text=_('Enable capturing activity for the activity stream.'), category=_('System'), category_slug='system', ) register( 'ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC', field_class=fields.BooleanField, label=_('Enable Activity Stream for Inventory Sync'), help_text=_('Enable capturing activity for the activity stream when running inventory sync.'), category=_('System'), category_slug='system', ) register( 'ORG_ADMINS_CAN_SEE_ALL_USERS', field_class=fields.BooleanField, label=_('All Users Visible to Organization Admins'), help_text=_('Controls whether any Organization Admin can view all users and teams, even those not associated with their Organization.'), category=_('System'), category_slug='system', ) register( 'MANAGE_ORGANIZATION_AUTH', field_class=fields.BooleanField, label=_('Organization Admins Can Manage Users and Teams'), help_text=_('Controls whether any Organization Admin has the privileges to create and manage users and teams.'), category=_('System'), category_slug='system', ) register( 'TOWER_URL_BASE', field_class=fields.URLField, schemes=('http', 'https'), allow_plain_hostname=True, # Allow hostname only without TLD. label=_('Base URL of the service'), help_text=_('This setting is used by services like notifications to render a valid url to the service.'), category=_('System'), category_slug='system', ) register( 'REMOTE_HOST_HEADERS', field_class=fields.StringListField, label=_('Remote Host Headers'), help_text=_( 'HTTP headers and meta keys to search to determine remote host ' 'name or IP. Add additional items to this list, such as ' '"HTTP_X_FORWARDED_FOR", if behind a reverse proxy. ' 'See the "Proxy Support" section of the AAP Installation guide ' 'for more details.' ), category=_('System'), category_slug='system', ) register( 'PROXY_IP_ALLOWED_LIST', field_class=fields.StringListField, label=_('Proxy IP Allowed List'), help_text=_( "If the service is behind a reverse proxy/load balancer, use this setting " "to configure the proxy IP addresses from which the service should trust " "custom REMOTE_HOST_HEADERS header values. " "If this setting is an empty list (the default), the headers specified by " "REMOTE_HOST_HEADERS will be trusted unconditionally')" ), category=_('System'), category_slug='system', ) register( 'CSRF_TRUSTED_ORIGINS', default=[], field_class=fields.StringListField, label=_('CSRF Trusted Origins List'), help_text=_( "If the service is behind a reverse proxy/load balancer, use this setting " "to configure the schema://addresses from which the service should trust " "Origin header values. " ), category=_('System'), category_slug='system', hidden=True, ) register( 'LICENSE', field_class=fields.DictField, default=lambda: {}, label=_('License'), help_text=_('The license controls which features and functionality are enabled. Use /api/v2/config/ to update or change the license.'), category=_('System'), category_slug='system', ) register( 'REDHAT_USERNAME', field_class=fields.CharField, default='', allow_blank=True, encrypted=False, read_only=False, label=_('Red Hat Client ID for Analytics'), help_text=_('Client ID used to send data to Automation Analytics'), category=_('System'), category_slug='system', ) register( 'REDHAT_PASSWORD', field_class=fields.CharField, default='', allow_blank=True, encrypted=True, read_only=False, label=_('Red Hat Client Secret for Analytics'), help_text=_('Client secret used to send data to Automation Analytics'), category=_('System'), category_slug='system', ) register( 'SUBSCRIPTIONS_USERNAME', field_class=fields.CharField, default='', allow_blank=True, encrypted=False, read_only=False, label=_('Red Hat Username for Subscriptions'), help_text=_('Username used to retrieve subscription and content information'), # noqa category=_('System'), category_slug='system', hidden=True, ) register( 'SUBSCRIPTIONS_PASSWORD', field_class=fields.CharField, default='', allow_blank=True, encrypted=True, read_only=False, label=_('Red Hat Password for Subscriptions'), help_text=_('Password used to retrieve subscription and content information'), # noqa category=_('System'), category_slug='system', hidden=True, ) register( 'SUBSCRIPTIONS_CLIENT_ID', field_class=fields.CharField, default='', allow_blank=True, encrypted=False, read_only=False, label=_('Red Hat Client ID for Subscriptions'), help_text=_('Client ID used to retrieve subscription and content information'), # noqa category=_('System'), category_slug='system', hidden=True, ) register( 'SUBSCRIPTIONS_CLIENT_SECRET', field_class=fields.CharField, default='', allow_blank=True, encrypted=True, read_only=False, label=_('Red Hat Client Secret for Subscriptions'), help_text=_('Client secret used to retrieve subscription and content information'), # noqa category=_('System'), category_slug='system', hidden=True, ) register( 'AUTOMATION_ANALYTICS_URL', field_class=fields.URLField, default='https://example.com', schemes=('http', 'https'), allow_plain_hostname=True, # Allow hostname only without TLD. label=_('Automation Analytics upload URL'), help_text=_('This setting is used to to configure the upload URL for data collection for Automation Analytics.'), category=_('System'), category_slug='system', ) register( 'INSTALL_UUID', field_class=fields.CharField, label=_('Unique identifier for an installation'), category=_('System'), category_slug='system', read_only=True, ) register( 'DEFAULT_CONTROL_PLANE_QUEUE_NAME', field_class=fields.CharField, label=_('The instance group where control plane tasks run'), category=_('System'), category_slug='system', read_only=True, ) register( 'DEFAULT_EXECUTION_QUEUE_NAME', field_class=fields.CharField, label=_('The instance group where user jobs run (currently only on non-VM installs)'), category=_('System'), category_slug='system', read_only=True, ) register( 'DEFAULT_EXECUTION_ENVIRONMENT', field_class=fields.PrimaryKeyRelatedField, allow_null=True, default=None, queryset=ExecutionEnvironment.objects.all(), label=_('Global default execution environment'), help_text=_('The Execution Environment to be used when one has not been configured for a job template.'), category=_('System'), category_slug='system', ) register( 'CUSTOM_VENV_PATHS', field_class=fields.StringListPathField, label=_('Custom virtual environment paths'), help_text=_('Paths where Tower will look for custom virtual environments (in addition to /var/lib/awx/venv/). Enter one path per line.'), category=_('System'), category_slug='system', default=[], ) register( 'AD_HOC_COMMANDS', field_class=fields.StringListField, label=_('Ansible Modules Allowed for Ad Hoc Jobs'), help_text=_('List of modules allowed to be used by ad-hoc jobs.'), category=_('Jobs'), category_slug='jobs', ) register( 'ALLOW_JINJA_IN_EXTRA_VARS', field_class=fields.ChoiceField, choices=[ ('always', _('Always')), ('never', _('Never')), ('template', _('Only On Job Template Definitions')), ], label=_('When can extra variables contain Jinja templates?'), help_text=_( 'Ansible allows variable substitution via the Jinja2 templating ' 'language for --extra-vars. This poses a potential security ' 'risk where users with the ability to specify extra vars at job ' 'launch time can use Jinja2 templates to run arbitrary Python. It is ' 'recommended that this value be set to "template" or "never".' ), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_ISOLATION_BASE_PATH', field_class=fields.CharField, label=_('Job execution path'), help_text=_('The directory in which the service will create new temporary directories for job execution and isolation (such as credential files).'), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_ISOLATION_SHOW_PATHS', field_class=fields.StringListIsolatedPathField, label=_('Paths to expose to isolated jobs'), help_text=_( 'List of paths that would otherwise be hidden to expose to isolated jobs. Enter one path per line. ' 'Volumes will be mounted from the execution node to the container. ' 'The supported format is HOST-DIR[:CONTAINER-DIR[:OPTIONS]]. ' ), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_TASK_ENV', field_class=fields.KeyValueField, default={}, label=_('Extra Environment Variables'), help_text=_('Additional environment variables set for playbook runs, inventory updates, project updates, and notification sending.'), category=_('Jobs'), category_slug='jobs', placeholder={'HTTP_PROXY': 'myproxy.local:8080'}, ) register( 'AWX_RUNNER_KEEPALIVE_SECONDS', field_class=fields.IntegerField, label=_('K8S Ansible Runner Keep-Alive Message Interval'), help_text=_('Only applies to jobs running in a Container Group. If not 0, send a message every so-many seconds to keep connection open.'), category=_('Jobs'), category_slug='jobs', placeholder=240, # intended to be under common 5 minute idle timeout ) register( 'GALAXY_TASK_ENV', field_class=fields.KeyValueField, label=_('Environment Variables for Galaxy Commands'), help_text=_( 'Additional environment variables set for invocations of ansible-galaxy within project updates. ' 'Useful if you must use a proxy server for ansible-galaxy but not git.' ), category=_('Jobs'), category_slug='jobs', placeholder={'HTTP_PROXY': 'myproxy.local:8080'}, ) register( 'INSIGHTS_TRACKING_STATE', field_class=fields.BooleanField, default=False, label=_('Gather data for Automation Analytics'), help_text=_('Enables the service to gather data on automation and send it to Automation Analytics.'), category=_('System'), category_slug='system', ) register( 'PROJECT_UPDATE_VVV', field_class=fields.BooleanField, label=_('Run Project Updates With Higher Verbosity'), help_text=_('Adds the CLI -vvv flag to ansible-playbook runs of project_update.yml used for project updates.'), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_ROLES_ENABLED', field_class=fields.BooleanField, default=True, label=_('Enable Role Download'), help_text=_('Allows roles to be dynamically downloaded from a requirements.yml file for SCM projects.'), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_COLLECTIONS_ENABLED', field_class=fields.BooleanField, default=True, label=_('Enable Collection(s) Download'), help_text=_('Allows collections to be dynamically downloaded from a requirements.yml file for SCM projects.'), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_SHOW_PLAYBOOK_LINKS', field_class=fields.BooleanField, default=False, label=_('Follow symlinks'), help_text=_( 'Follow symbolic links when scanning for playbooks. Be aware that setting this to True can lead ' 'to infinite recursion if a link points to a parent directory of itself.' ), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_MOUNT_ISOLATED_PATHS_ON_K8S', field_class=fields.BooleanField, default=False, label=_('Expose host paths for Container Groups'), help_text=_( 'Expose paths via hostPath for the Pods created by a Container Group. ' 'HostPath volumes present many security risks, and it is a best practice to avoid the use of HostPaths when possible. ' ), category=_('Jobs'), category_slug='jobs', ) register( 'GALAXY_IGNORE_CERTS', field_class=fields.BooleanField, default=False, label=_('Ignore Ansible Galaxy SSL Certificate Verification'), help_text=_('If set to true, certificate validation will not be done when installing content from any Galaxy server.'), category=_('Jobs'), category_slug='jobs', ) register( 'STDOUT_MAX_BYTES_DISPLAY', field_class=fields.IntegerField, min_value=0, label=_('Standard Output Maximum Display Size'), help_text=_('Maximum Size of Standard Output in bytes to display before requiring the output be downloaded.'), category=_('Jobs'), category_slug='jobs', ) register( 'EVENT_STDOUT_MAX_BYTES_DISPLAY', field_class=fields.IntegerField, min_value=0, label=_('Job Event Standard Output Maximum Display Size'), help_text=_( u'Maximum Size of Standard Output in bytes to display for a single job or ad hoc command event. `stdout` will end with `\u2026` when truncated.' ), category=_('Jobs'), category_slug='jobs', ) register( 'MAX_WEBSOCKET_EVENT_RATE', field_class=fields.IntegerField, min_value=0, default=30, label=_('Job Event Maximum Websocket Messages Per Second'), help_text=_('Maximum number of messages to update the UI live job output with per second. Value of 0 means no limit.'), category=_('Jobs'), category_slug='jobs', ) register( 'SCHEDULE_MAX_JOBS', field_class=fields.IntegerField, min_value=1, label=_('Maximum Scheduled Jobs'), help_text=_('Maximum number of the same job template that can be waiting to run when launching from a schedule before no more are created.'), category=_('Jobs'), category_slug='jobs', ) register( 'AWX_ANSIBLE_CALLBACK_PLUGINS', field_class=fields.StringListField, label=_('Ansible Callback Plugins'), help_text=_('List of paths to search for extra callback plugins to be used when running jobs. Enter one path per line.'), category=_('Jobs'), category_slug='jobs', ) register( 'DEFAULT_JOB_TIMEOUT', field_class=fields.IntegerField, min_value=0, default=0, label=_('Default Job Timeout'), help_text=_( 'Maximum time in seconds to allow jobs to run. Use value of 0 to indicate that no ' 'timeout should be imposed. A timeout set on an individual job template will override this.' ), category=_('Jobs'), category_slug='jobs', unit=_('seconds'), ) register( 'DEFAULT_JOB_IDLE_TIMEOUT', field_class=fields.IntegerField, min_value=0, default=0, label=_('Default Job Idle Timeout'), help_text=_( 'If no output is detected from ansible in this number of seconds the execution will be terminated. ' 'Use value of 0 to indicate that no idle timeout should be imposed.' ), category=_('Jobs'), category_slug='jobs', unit=_('seconds'), ) register( 'DEFAULT_INVENTORY_UPDATE_TIMEOUT', field_class=fields.IntegerField, min_value=0, default=0, label=_('Default Inventory Update Timeout'), help_text=_( 'Maximum time in seconds to allow inventory updates to run. Use value of 0 to indicate that no ' 'timeout should be imposed. A timeout set on an individual inventory source will override this.' ), category=_('Jobs'), category_slug='jobs', unit=_('seconds'), ) register( 'DEFAULT_PROJECT_UPDATE_TIMEOUT', field_class=fields.IntegerField, min_value=0, default=0, label=_('Default Project Update Timeout'), help_text=_( 'Maximum time in seconds to allow project updates to run. Use value of 0 to indicate that no ' 'timeout should be imposed. A timeout set on an individual project will override this.' ), category=_('Jobs'), category_slug='jobs', unit=_('seconds'), ) register( 'ANSIBLE_FACT_CACHE_TIMEOUT', field_class=fields.IntegerField, min_value=0, default=0, label=_('Per-Host Ansible Fact Cache Timeout'), help_text=_( 'Maximum time, in seconds, that stored Ansible facts are considered valid since ' 'the last time they were modified. Only valid, non-stale, facts will be accessible by ' 'a playbook. Note, this does not influence the deletion of ansible_facts from the database. ' 'Use a value of 0 to indicate that no timeout should be imposed.' ), category=_('Jobs'), category_slug='jobs', unit=_('seconds'), ) register( 'MAX_FORKS', field_class=fields.IntegerField, allow_null=False, default=200, label=_('Maximum number of forks per job'), help_text=_('Saving a Job Template with more than this number of forks will result in an error. When set to 0, no limit is applied.'), category=_('Jobs'), category_slug='jobs', ) register( 'LOG_AGGREGATOR_HOST', field_class=fields.CharField, allow_null=True, default=None, label=_('Logging Aggregator'), help_text=_('Hostname/IP where external logs will be sent to.'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_PORT', field_class=fields.IntegerField, allow_null=True, default=None, label=_('Logging Aggregator Port'), help_text=_('Port on Logging Aggregator to send logs to (if required and not provided in Logging Aggregator).'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_TYPE', field_class=fields.ChoiceField, choices=['logstash', 'splunk', 'loggly', 'sumologic', 'other'], allow_null=True, default=None, label=_('Logging Aggregator Type'), help_text=_('Format messages for the chosen log aggregator.'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_USERNAME', field_class=fields.CharField, allow_blank=True, default='', label=_('Logging Aggregator Username'), help_text=_('Username for external log aggregator (if required; HTTP/s only).'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_PASSWORD', field_class=fields.CharField, allow_blank=True, default='', encrypted=True, label=_('Logging Aggregator Password/Token'), help_text=_('Password or authentication token for external log aggregator (if required; HTTP/s only).'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_LOGGERS', field_class=fields.StringListField, default=['awx', 'activity_stream', 'job_events', 'system_tracking', 'broadcast_websocket', 'job_lifecycle'], label=_('Loggers Sending Data to Log Aggregator Form'), help_text=_( 'List of loggers that will send HTTP logs to the collector, these can ' 'include any or all of: \n' 'awx - service logs\n' 'activity_stream - activity stream records\n' 'job_events - callback data from Ansible job events\n' 'system_tracking - facts gathered from scan jobs\n' 'broadcast_websocket - errors pertaining to websockets broadcast metrics\n' 'job_lifecycle - logs related to processing of a job\n' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_INDIVIDUAL_FACTS', field_class=fields.BooleanField, default=False, label=_('Log System Tracking Facts Individually'), help_text=_( 'If set, system tracking facts will be sent for each package, service, or ' 'other item found in a scan, allowing for greater search query granularity. ' 'If unset, facts will be sent as a single dictionary, allowing for greater ' 'efficiency in fact processing.' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_ENABLED', field_class=fields.BooleanField, default=False, label=_('Enable External Logging'), help_text=_('Enable sending logs to external log aggregator.'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_TOWER_UUID', field_class=fields.CharField, allow_blank=True, default='', label=_('Cluster-wide unique identifier.'), help_text=_('Useful to uniquely identify instances.'), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_PROTOCOL', field_class=fields.ChoiceField, choices=[('https', 'HTTPS/HTTP'), ('tcp', 'TCP'), ('udp', 'UDP')], default='https', label=_('Logging Aggregator Protocol'), help_text=_( 'Protocol used to communicate with log aggregator. ' 'HTTPS/HTTP assumes HTTPS unless http:// is explicitly used in ' 'the Logging Aggregator hostname.' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_TCP_TIMEOUT', field_class=fields.IntegerField, default=5, label=_('TCP Connection Timeout'), help_text=_('Number of seconds for a TCP connection to external log aggregator to timeout. Applies to HTTPS and TCP log aggregator protocols.'), category=_('Logging'), category_slug='logging', unit=_('seconds'), ) register( 'LOG_AGGREGATOR_VERIFY_CERT', field_class=fields.BooleanField, default=True, label=_('Enable/disable HTTPS certificate verification'), help_text=_( 'Flag to control enable/disable of certificate verification' ' when LOG_AGGREGATOR_PROTOCOL is "https". If enabled, the' ' log handler will verify certificate sent by external log aggregator' ' before establishing connection.' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_LEVEL', field_class=fields.ChoiceField, choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='WARNING', label=_('Logging Aggregator Level Threshold'), help_text=_( 'Level threshold used by log handler. Severities from lowest to highest' ' are DEBUG, INFO, WARNING, ERROR, CRITICAL. Messages less severe ' 'than the threshold will be ignored by log handler. (messages under category ' 'awx.anlytics ignore this setting)' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_ACTION_QUEUE_SIZE', field_class=fields.IntegerField, default=131072, min_value=1, label=_('Maximum number of messages that can be stored in the log action queue'), help_text=_( 'Defines how large the rsyslog action queue can grow in number of messages ' 'stored. This can have an impact on memory utilization. When the queue ' 'reaches 75% of this number, the queue will start writing to disk ' '(queue.highWatermark in rsyslog). When it reaches 90%, NOTICE, INFO, and ' 'DEBUG messages will start to be discarded (queue.discardMark with ' 'queue.discardSeverity=5).' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_ACTION_MAX_DISK_USAGE_GB', field_class=fields.IntegerField, default=1, min_value=1, label=_('Maximum disk persistence for rsyslogd action queuing (in GB)'), help_text=_( 'Amount of data to store (in gigabytes) if an rsyslog action takes time ' 'to process an incoming message (defaults to 1). ' 'Equivalent to the rsyslogd queue.maxdiskspace setting on the action (e.g. omhttp). ' 'It stores files in the directory specified by LOG_AGGREGATOR_MAX_DISK_USAGE_PATH.' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_MAX_DISK_USAGE_PATH', field_class=fields.CharField, default='/var/lib/awx', label=_('File system location for rsyslogd disk persistence'), help_text=_( 'Location to persist logs that should be retried after an outage ' 'of the external log aggregator (defaults to /var/lib/awx). ' 'Equivalent to the rsyslogd queue.spoolDirectory setting.' ), category=_('Logging'), category_slug='logging', ) register( 'LOG_AGGREGATOR_RSYSLOGD_DEBUG', field_class=fields.BooleanField, default=False, label=_('Enable rsyslogd debugging'), help_text=_('Enabled high verbosity debugging for rsyslogd. Useful for debugging connection issues for external log aggregation.'), category=_('Logging'), category_slug='logging', ) register( 'API_400_ERROR_LOG_FORMAT', field_class=fields.CharField, default='status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}', label=_('Log Format For API 4XX Errors'), help_text=_( 'The format of logged messages when an API 4XX error occurs, ' 'the following variables will be substituted: \n' 'status_code - The HTTP status code of the error\n' 'user_name - The user name attempting to use the API\n' 'url_path - The URL path to the API endpoint called\n' 'remote_addr - The remote address seen for the user\n' 'error - The error set by the api endpoint\n' 'Variables need to be in the format {}.' ), category=_('Logging'), category_slug='logging', ) register( 'AUTOMATION_ANALYTICS_LAST_GATHER', field_class=fields.DateTimeField, label=_('Last gather date for Automation Analytics.'), allow_null=True, category=_('System'), category_slug='system', hidden=True, ) register( 'AUTOMATION_ANALYTICS_LAST_ENTRIES', field_class=fields.CharField, label=_('Last gathered entries from the data collection service of Automation Analytics'), default='', allow_blank=True, category=_('System'), category_slug='system', ) register( 'AUTOMATION_ANALYTICS_GATHER_INTERVAL', field_class=fields.IntegerField, label=_('Automation Analytics Gather Interval'), help_text=_('Interval (in seconds) between data gathering.'), default=14400, # every 4 hours min_value=1800, # every 30 minutes category=_('System'), category_slug='system', unit=_('seconds'), ) register( 'IS_K8S', field_class=fields.BooleanField, read_only=True, category=_('System'), category_slug='system', help_text=_('Indicates whether the instance is part of a kubernetes-based deployment.'), ) register( 'BULK_JOB_MAX_LAUNCH', field_class=fields.IntegerField, default=100, label=_('Max jobs to allow bulk jobs to launch'), help_text=_('Max jobs to allow bulk jobs to launch'), category=_('Bulk Actions'), category_slug='bulk', hidden=True, ) register( 'BULK_HOST_MAX_CREATE', field_class=fields.IntegerField, default=100, label=_('Max number of hosts to allow to be created in a single bulk action'), help_text=_('Max number of hosts to allow to be created in a single bulk action'), category=_('Bulk Actions'), category_slug='bulk', hidden=True, ) register( 'BULK_HOST_MAX_DELETE', field_class=fields.IntegerField, default=250, label=_('Max number of hosts to allow to be deleted in a single bulk action'), help_text=_('Max number of hosts to allow to be deleted in a single bulk action'), category=_('Bulk Actions'), category_slug='bulk', hidden=True, ) register( 'SUBSCRIPTION_USAGE_MODEL', field_class=fields.ChoiceField, choices=[ ('', _('No subscription. Deletion of host_metrics will not be considered for purposes of managed host counting')), ( SUBSCRIPTION_USAGE_MODEL_UNIQUE_HOSTS, _('Usage based on unique managed nodes in a large historical time frame and delete functionality for no longer used managed nodes'), ), ], default='', allow_blank=True, label=_('Defines subscription usage model and shows Host Metrics'), category=_('System'), category_slug='system', ) register( 'CLEANUP_HOST_METRICS_LAST_TS', field_class=fields.DateTimeField, label=_('Last cleanup date for HostMetrics'), allow_null=True, category=_('System'), category_slug='system', hidden=True, ) register( 'HOST_METRIC_SUMMARY_TASK_LAST_TS', field_class=fields.DateTimeField, label=_('Last computing date of HostMetricSummaryMonthly'), allow_null=True, category=_('System'), category_slug='system', hidden=True, ) register( 'AWX_CLEANUP_PATHS', field_class=fields.BooleanField, label=_('Enable or Disable tmp dir cleanup'), default=True, help_text=_('Enable or Disable TMP Dir cleanup'), category=('Debug'), category_slug='debug', ) register( 'AWX_REQUEST_PROFILE', field_class=fields.BooleanField, label=_('Debug Web Requests'), default=False, help_text=_('Debug web request python timing'), category=('Debug'), category_slug='debug', ) register( 'DEFAULT_CONTAINER_RUN_OPTIONS', field_class=fields.StringListField, label=_('Container Run Options'), default=['--network', 'slirp4netns:enable_ipv6=true'], help_text=_("List of options to pass to podman run example: ['--network', 'slirp4netns:enable_ipv6=true', '--log-level', 'debug']"), category=('Jobs'), category_slug='jobs', ) register( 'RECEPTOR_RELEASE_WORK', field_class=fields.BooleanField, label=_('Release Receptor Work'), default=True, help_text=_('Release receptor work'), category=('Debug'), category_slug='debug', ) register( 'RECEPTOR_KEEP_WORK_ON_ERROR', field_class=fields.BooleanField, label=_('Keep receptor work on error'), default=False, help_text=_('Prevent receptor work from being released on when error is detected'), category=('Debug'), category_slug='debug', ) def logging_validate(serializer, attrs): if not serializer.instance or not hasattr(serializer.instance, 'LOG_AGGREGATOR_HOST') or not hasattr(serializer.instance, 'LOG_AGGREGATOR_TYPE'): return attrs errors = [] if attrs.get('LOG_AGGREGATOR_ENABLED', False): if ( not serializer.instance.LOG_AGGREGATOR_HOST and not attrs.get('LOG_AGGREGATOR_HOST', None) or serializer.instance.LOG_AGGREGATOR_HOST and not attrs.get('LOG_AGGREGATOR_HOST', True) ): errors.append('Cannot enable log aggregator without providing host.') if ( not serializer.instance.LOG_AGGREGATOR_TYPE and not attrs.get('LOG_AGGREGATOR_TYPE', None) or serializer.instance.LOG_AGGREGATOR_TYPE and not attrs.get('LOG_AGGREGATOR_TYPE', True) ): errors.append('Cannot enable log aggregator without providing type.') if errors: raise serializers.ValidationError(_('\n'.join(errors))) return attrs register_validate('logging', logging_validate) def csrf_trusted_origins_validate(serializer, attrs): if not serializer.instance or not hasattr(serializer.instance, 'CSRF_TRUSTED_ORIGINS'): return attrs if 'CSRF_TRUSTED_ORIGINS' not in attrs: return attrs errors = [] for origin in attrs['CSRF_TRUSTED_ORIGINS']: if "://" not in origin: errors.append( Error( "As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS " "setting must start with a scheme (usually http:// or " "https://) but found %s. See the release notes for details." % origin, ) ) if errors: error_messages = [error.msg for error in errors] raise serializers.ValidationError(_('\n'.join(error_messages))) return attrs register_validate('system', csrf_trusted_origins_validate) register( 'OPA_HOST', field_class=fields.CharField, label=_('OPA server hostname'), default='', help_text=_('The hostname used to connect to the OPA server. If empty, policy enforcement will be disabled.'), category=('PolicyAsCode'), category_slug='policyascode', allow_blank=True, ) register( 'OPA_PORT', field_class=fields.IntegerField, label=_('OPA server port'), default=8181, help_text=_('The port used to connect to the OPA server. Defaults to 8181.'), category=('PolicyAsCode'), category_slug='policyascode', ) register( 'OPA_SSL', field_class=fields.BooleanField, label=_('Use SSL for OPA connection'), default=False, help_text=_('Enable or disable the use of SSL to connect to the OPA server. Defaults to false.'), category=('PolicyAsCode'), category_slug='policyascode', ) register( 'OPA_AUTH_TYPE', field_class=fields.ChoiceField, label=_('OPA authentication type'), choices=[OPA_AUTH_TYPES.NONE, OPA_AUTH_TYPES.TOKEN, OPA_AUTH_TYPES.CERTIFICATE], default=OPA_AUTH_TYPES.NONE, help_text=_('The authentication type that will be used to connect to the OPA server: "None", "Token", or "Certificate".'), category=('PolicyAsCode'), category_slug='policyascode', ) register( 'OPA_AUTH_TOKEN', field_class=fields.CharField, label=_('OPA authentication token'), default='', help_text=_( 'The token for authentication to the OPA server. Required when OPA_AUTH_TYPE is "Token". If an authorization header is defined in OPA_AUTH_CUSTOM_HEADERS, it will be overridden by OPA_AUTH_TOKEN.' ), category=('PolicyAsCode'), category_slug='policyascode', allow_blank=True, encrypted=True, ) register( 'OPA_AUTH_CLIENT_CERT', field_class=fields.CharField, label=_('OPA client certificate content'), default='', help_text=_('The content of the client certificate file for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate".'), category=('PolicyAsCode'), category_slug='policyascode', allow_blank=True, ) register( 'OPA_AUTH_CLIENT_KEY', field_class=fields.CharField, label=_('OPA client key content'), default='', help_text=_('The content of the client key for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate".'), category=('PolicyAsCode'), category_slug='policyascode', allow_blank=True, encrypted=True, ) register( 'OPA_AUTH_CA_CERT', field_class=fields.CharField, label=_('OPA CA certificate content'), default='', help_text=_('The content of the CA certificate for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate".'), category=('PolicyAsCode'), category_slug='policyascode', allow_blank=True, ) register( 'OPA_AUTH_CUSTOM_HEADERS', field_class=fields.DictField, label=_('OPA custom authentication headers'), default={}, help_text=_('Optional custom headers included in requests to the OPA server. Defaults to empty dictionary ({}).'), category=('PolicyAsCode'), category_slug='policyascode', ) register( 'OPA_REQUEST_TIMEOUT', field_class=fields.FloatField, label=_('OPA request timeout'), default=1.5, help_text=_('The number of seconds after which the connection to the OPA server will time out. Defaults to 1.5 seconds.'), category=('PolicyAsCode'), category_slug='policyascode', ) register( 'OPA_REQUEST_RETRIES', field_class=fields.IntegerField, label=_('OPA request retry count'), default=2, help_text=_('The number of retry attempts for connecting to the OPA server. Default is 2.'), category=('PolicyAsCode'), category_slug='policyascode', ) def policy_as_code_validate(serializer, attrs): opa_host = attrs.get('OPA_HOST', '') if opa_host and (opa_host.startswith('http://') or opa_host.startswith('https://')): raise serializers.ValidationError({'OPA_HOST': _("OPA_HOST should not include 'http://' or 'https://' prefixes. Please enter only the hostname.")}) return attrs register_validate('policyascode', policy_as_code_validate)