diff --git a/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js b/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js index b2ddbb3ab7..42cda6e630 100644 --- a/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js +++ b/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js @@ -16,10 +16,10 @@ import { InventoriesAPI } from 'api'; import useRequest, { useDismissableError } from 'hooks/useRequest'; import { Inventory } from 'types'; import { relatedResourceDeleteRequests } from 'util/getRelatedResourceDeleteDetails'; +import helpText from '../shared/Inventory.helptext'; function InventoryDetail({ inventory }) { const history = useHistory(); - const { result: instanceGroups, isLoading, @@ -106,6 +106,7 @@ function InventoryDetail({ inventory }) { inventory.summary_fields.labels?.results?.length > 0 && ( { fetchSourceChoices(); }, [fetchSourceChoices]); @@ -123,21 +129,33 @@ function InventorySourceDetail({ inventorySource }) { {overwrite && ( {t`Overwrite local groups and hosts from remote inventory source`} + )} {overwrite_vars && ( {t`Overwrite local variables from remote inventory source`} + )} {update_on_launch && ( {t`Update on launch`} + )} {update_on_project_update && ( {t`Update on project update`} + )} @@ -226,17 +244,35 @@ function InventorySourceDetail({ inventorySource }) { {source === 'scm' ? ( ) : null} - + + + + - - - {credentials?.length > 0 && ( diff --git a/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js b/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js new file mode 100644 index 0000000000..e92c5927f8 --- /dev/null +++ b/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js @@ -0,0 +1,196 @@ +/* eslint-disable react/destructuring-assignment */ +import React from 'react'; +import { t, Trans } from '@lingui/macro'; +import { Link } from 'react-router-dom'; + +const ansibleDocUrls = { + ec2: 'https://docs.ansible.com/ansible/latest/collections/amazon/aws/aws_ec2_inventory.html', + azure_rm: + 'https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_inventory.html', + controller: + 'https://docs.ansible.com/ansible/latest/collections/awx/awx/tower_inventory.html', + gce: 'https://docs.ansible.com/ansible/latest/collections/google/cloud/gcp_compute_inventory.html', + insights: + 'https://docs.ansible.com/ansible/latest/collections/redhatinsights/insights/insights_inventory.html', + openstack: + 'https://docs.ansible.com/ansible/latest/collections/openstack/cloud/openstack_inventory.html', + satellite6: + 'https://docs.ansible.com/ansible/latest/collections/theforeman/foreman/foreman_inventory.html', + rhv: 'https://docs.ansible.com/ansible/latest/collections/ovirt/ovirt/ovirt_inventory.html', + vmware: + 'https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_vm_inventory_inventory.html', +}; + +const getInventoryHelpTextStrings = { + labels: t`Optional labels that describe this inventory, + such as 'dev' or 'test'. Labels can be used to group and filter + inventories and completed jobs.`, + variables: () => { + const jsonExample = ` + { + "somevar": "somevalue" + "somepassword": "Magic" + } + `; + const yamlExample = ` + --- + somevar: somevalue + somepassword: magic + `; + + return ( + <> + + Variables must be in JSON or YAML syntax. Use the radio button to + toggle between the two. + +
+
+ JSON: +
{jsonExample}
+
+ YAML: +
{yamlExample}
+
+ + View JSON examples at{' '} + + www.json.org + + +
+ + View YAML examples at{' '} + + docs.ansible.com + + + + ); + }, + subFormVerbosityFields: t`Control the level of output Ansible + will produce for inventory source update jobs.`, + subFormOptions: { + overwrite: ( + <> + {t`If checked, any hosts and groups that were + previously present on the external source but are now removed + will be removed from the inventory. Hosts and groups + that were not managed by the inventory source will be promoted + to the next manually created group or if there is no manually + created group to promote them into, they will be left in the "all" + default group for the inventory.`} +
+
+ {t`When not checked, local child + hosts and groups not found on the external source will remain + untouched by the inventory update process.`} + + ), + overwriteVariables: ( + <> + {t`If checked, all variables for child groups + and hosts will be removed and replaced by those found + on the external source.`} +
+
+ {t`When not checked, a merge will be performed, + combining local variables with those found on the + external source.`} + + ), + updateOnLaunch: ({ value }) => ( + <> +
+ {t`Each time a job runs using this inventory, + refresh the inventory from the selected source before + executing job tasks.`} +
+
+ {value && ( +
+ {t`If you want the Inventory Source to update on + launch and on project update, click on Update on launch, and also go to`} + {value.name} + {t`and click on Update Revision on Launch`} +
+ )} + + ), + updateOnProjectUpdate: ({ value }) => ( + <> +
+ {t`After every project update where the SCM revision + changes, refresh the inventory from the selected source + before executing job tasks. This is intended for static content, + like the Ansible inventory .ini file format.`} +
+
+ {value && ( +
+ {t`If you want the Inventory Source to update on + launch and on project update, click on Update on launch, and also go to`} + {value.name} + {t`and click on Update Revision on Launch`} +
+ )} + + ), + cachedTimeOut: t`Time in seconds to consider an inventory sync + to be current. During job runs and callbacks the task system will + evaluate the timestamp of the latest sync. If it is older than + Cache Timeout, it is not considered current, and a new + inventory sync will be performed.`, + }, + enabledVariableField: t`Retrieve the enabled state from the given dict of host variables. + The enabled variable may be specified using dot notation, e.g: 'foo.bar'`, + enabledValue: t`This field is ignored unless an Enabled Variable is set. If the enabled variable matches this value, the host will be enabled on import.`, + hostFilter: t`Regular expression where only matching host names will be imported. The filter is applied as a post-processing step after any inventory plugin filters are applied.`, + sourceVars: (docsBaseUrl, source) => { + const docsUrl = `${docsBaseUrl}/html/userguide/inventories.html#inventory-plugins`; + let sourceType = ''; + if (source && source !== 'scm') { + const type = ansibleDocUrls[source].split(/[/,.]/); + sourceType = type[type.length - 2]; + } + return ( + <> + + Variables used to configure the inventory source. For a detailed + description of how to configure this plugin, see{' '} + + Inventory Plugins + {' '} + in the documentation and the{' '} + + {sourceType} + {' '} + plugin configuration guide. + +
+
+ + ); + }, + sourcePath: t`The inventory file + to be synced by this source. You can select from + the dropdown or enter a file within the input.`, +}; + +export default getInventoryHelpTextStrings; diff --git a/awx/ui/src/screens/Inventory/shared/InventoryForm.js b/awx/ui/src/screens/Inventory/shared/InventoryForm.js index 25ada203a9..9955e9c059 100644 --- a/awx/ui/src/screens/Inventory/shared/InventoryForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventoryForm.js @@ -13,6 +13,7 @@ import InstanceGroupsLookup from 'components/Lookup/InstanceGroupsLookup'; import OrganizationLookup from 'components/Lookup/OrganizationLookup'; import ContentError from 'components/ContentError'; import { FormColumnLayout, FormFullWidthLayout } from 'components/FormLayout'; +import helpText from './Inventory.helptext'; function InventoryFormFields({ inventory }) { const [contentError, setContentError] = useState(false); @@ -72,13 +73,7 @@ function InventoryFormFields({ inventory }) { - } + labelIcon={} fieldId="inventory-labels" > { const { setFieldValue, setFieldTouched } = useFormikContext(); @@ -27,12 +28,7 @@ const AzureSubForm = ({ autoPopulateCredential }) => { }, [setFieldValue, setFieldTouched] ); - - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_inventory.html'; + const docsBaseUrl = getDocsBaseUrl(config); return ( <> @@ -54,24 +50,7 @@ const AzureSubForm = ({ autoPopulateCredential }) => { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - azure_rm - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars(docsBaseUrl, 'azure_rm')} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/ControllerSubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/ControllerSubForm.js index 542f4ffd13..3e3eefb03b 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/ControllerSubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/ControllerSubForm.js @@ -1,11 +1,11 @@ import React, { useCallback } from 'react'; import { useField, useFormikContext } from 'formik'; -import { t, Trans } from '@lingui/macro'; -import CredentialLookup from 'components/Lookup/CredentialLookup'; -import { required } from 'util/validators'; +import { t } from '@lingui/macro'; import getDocsBaseUrl from 'util/getDocsBaseUrl'; import { useConfig } from 'contexts/Config'; +import CredentialLookup from 'components/Lookup/CredentialLookup'; +import { required } from 'util/validators'; import { OptionsField, VerbosityField, @@ -14,13 +14,13 @@ import { HostFilterField, SourceVarsField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const ControllerSubForm = ({ autoPopulateCredential }) => { const { setFieldValue, setFieldTouched } = useFormikContext(); const [credentialField, credentialMeta, credentialHelpers] = useField('credential'); const config = useConfig(); - const handleCredentialUpdate = useCallback( (value) => { setFieldValue('credential', value); @@ -29,12 +29,6 @@ const ControllerSubForm = ({ autoPopulateCredential }) => { [setFieldValue, setFieldTouched] ); - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/awx/awx/tower_inventory.html'; - return ( <> { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - Tower - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars( + getDocsBaseUrl(config), + 'controller' + )} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/EC2SubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/EC2SubForm.js index 93f3eae90b..e83f6f9a84 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/EC2SubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/EC2SubForm.js @@ -1,9 +1,9 @@ import React, { useCallback } from 'react'; import { useField, useFormikContext } from 'formik'; -import { t, Trans } from '@lingui/macro'; -import CredentialLookup from 'components/Lookup/CredentialLookup'; +import { t } from '@lingui/macro'; import getDocsBaseUrl from 'util/getDocsBaseUrl'; import { useConfig } from 'contexts/Config'; +import CredentialLookup from 'components/Lookup/CredentialLookup'; import { OptionsField, SourceVarsField, @@ -12,12 +12,12 @@ import { EnabledValueField, HostFilterField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const EC2SubForm = () => { const { setFieldValue, setFieldTouched } = useFormikContext(); const [credentialField, credentialMeta] = useField('credential'); const config = useConfig(); - const handleCredentialUpdate = useCallback( (value) => { setFieldValue('credential', value); @@ -25,12 +25,7 @@ const EC2SubForm = () => { }, [setFieldValue, setFieldTouched] ); - - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/amazon/aws/aws_ec2_inventory.html'; + const docsBaseUrl = getDocsBaseUrl(config); return ( <> @@ -48,24 +43,7 @@ const EC2SubForm = () => { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - aws_ec2 - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars(docsBaseUrl, 'ec2')} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/GCESubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/GCESubForm.js index 955f8d5f3f..e151659d9e 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/GCESubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/GCESubForm.js @@ -1,10 +1,10 @@ import React, { useCallback } from 'react'; import { useField, useFormikContext } from 'formik'; -import { t, Trans } from '@lingui/macro'; -import CredentialLookup from 'components/Lookup/CredentialLookup'; -import { required } from 'util/validators'; +import { t } from '@lingui/macro'; import getDocsBaseUrl from 'util/getDocsBaseUrl'; import { useConfig } from 'contexts/Config'; +import CredentialLookup from 'components/Lookup/CredentialLookup'; +import { required } from 'util/validators'; import { OptionsField, VerbosityField, @@ -13,13 +13,13 @@ import { HostFilterField, SourceVarsField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const GCESubForm = ({ autoPopulateCredential }) => { const { setFieldValue, setFieldTouched } = useFormikContext(); const [credentialField, credentialMeta, credentialHelpers] = useField('credential'); const config = useConfig(); - const handleCredentialUpdate = useCallback( (value) => { setFieldValue('credential', value); @@ -27,12 +27,7 @@ const GCESubForm = ({ autoPopulateCredential }) => { }, [setFieldValue, setFieldTouched] ); - - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/google/cloud/gcp_compute_inventory.html'; + const docsBaseUrl = getDocsBaseUrl(config); return ( <> @@ -54,24 +49,7 @@ const GCESubForm = ({ autoPopulateCredential }) => { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - gcp_compute - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars(docsBaseUrl, 'gce')} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.js index 31ec3f1879..2639047382 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/InsightsSubForm.js @@ -1,11 +1,11 @@ import React, { useCallback } from 'react'; import { useField, useFormikContext } from 'formik'; -import { t, Trans } from '@lingui/macro'; -import CredentialLookup from 'components/Lookup/CredentialLookup'; -import { required } from 'util/validators'; +import { t } from '@lingui/macro'; import getDocsBaseUrl from 'util/getDocsBaseUrl'; import { useConfig } from 'contexts/Config'; +import CredentialLookup from 'components/Lookup/CredentialLookup'; +import { required } from 'util/validators'; import { OptionsField, VerbosityField, @@ -14,13 +14,13 @@ import { HostFilterField, SourceVarsField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const InsightsSubForm = ({ autoPopulateCredential }) => { const { setFieldValue, setFieldTouched } = useFormikContext(); const [credentialField, credentialMeta, credentialHelpers] = useField('credential'); const config = useConfig(); - const handleCredentialUpdate = useCallback( (value) => { setFieldValue('credential', value); @@ -28,12 +28,7 @@ const InsightsSubForm = ({ autoPopulateCredential }) => { }, [setFieldValue, setFieldTouched] ); - - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/redhatinsights/insights/insights_inventory.html'; + const docsBaseUrl = getDocsBaseUrl(config); return ( <> @@ -55,24 +50,7 @@ const InsightsSubForm = ({ autoPopulateCredential }) => { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - Insights - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars(docsBaseUrl, 'insights')} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/OpenStackSubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/OpenStackSubForm.js index 0467f15f53..0d06906d24 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/OpenStackSubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/OpenStackSubForm.js @@ -1,10 +1,10 @@ import React, { useCallback } from 'react'; import { useField, useFormikContext } from 'formik'; -import { t, Trans } from '@lingui/macro'; +import { t } from '@lingui/macro'; +import { useConfig } from 'contexts/Config'; +import getDocsBaseUrl from 'util/getDocsBaseUrl'; import CredentialLookup from 'components/Lookup/CredentialLookup'; import { required } from 'util/validators'; -import getDocsBaseUrl from 'util/getDocsBaseUrl'; -import { useConfig } from 'contexts/Config'; import { OptionsField, SourceVarsField, @@ -13,6 +13,7 @@ import { EnabledValueField, HostFilterField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const OpenStackSubForm = ({ autoPopulateCredential }) => { const { setFieldValue, setFieldTouched } = useFormikContext(); @@ -28,12 +29,6 @@ const OpenStackSubForm = ({ autoPopulateCredential }) => { [setFieldValue, setFieldTouched] ); - const pluginLink = `${getDocsBaseUrl( - config - )}/html/userguide/inventories.html#inventory-plugins`; - const configLink = - 'https://docs.ansible.com/ansible/latest/collections/openstack/cloud/openstack_inventory.html'; - return ( <> { - - Enter variables to configure the inventory source. For a detailed - description of how to configure this plugin, see{' '} - - Inventory Plugins - {' '} - in the documentation and the{' '} - - openstack - {' '} - plugin configuration guide. - -
-
- - } + popoverContent={helpText.sourceVars( + getDocsBaseUrl(config), + 'openstack' + )} /> ); diff --git a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.js b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.js index b96d5a1254..a71b6020bc 100644 --- a/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.js @@ -21,12 +21,14 @@ import { EnabledValueField, HostFilterField, } from './SharedFields'; +import helpText from '../Inventory.helptext'; const SCMSubForm = ({ autoPopulateProject }) => { const [isOpen, setIsOpen] = useState(false); const [sourcePath, setSourcePath] = useState([]); const { setFieldValue, setFieldTouched } = useFormikContext(); const [credentialField] = useField('credential'); + const [projectField, projectMeta, projectHelpers] = useField('source_project'); const [sourcePathField, sourcePathMeta, sourcePathHelpers] = useField({ @@ -104,13 +106,7 @@ const SCMSubForm = ({ autoPopulateProject }) => { } isRequired label={t`Inventory file`} - labelIcon={ - - } + labelIcon={} >