From 073a2af1908d3860427058db8dc0ef16d0217a3b Mon Sep 17 00:00:00 2001 From: Blai Peidro Date: Tue, 20 Oct 2026 22:13:59 +0200 Subject: [PATCH] feat: add the Spanish documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every page of the 7.x docs in Spanish, under /es/, in the app's Spanish: Spain Spanish with "tú", the app's on-screen labels, and "trabajo" for an AnsibleForms job as the app writes it. Español joins the language selector, and a Spanish browser lands on it from the root and the old addresses. - i18n/es/: the 189 translated pages and the environment variable groups - i18n/es.yml: the menu groups, site title and version labels - source/_data/ui.yml: the Spanish interface strings --- CONTRIBUTING.md | 2 +- README.md | 2 +- i18n/es.yml | 40 + .../_build_forms/expressions/credentials.md | 54 + i18n/es/_build_forms/expressions/index.md | 76 + i18n/es/_build_forms/expressions/jq.md | 59 + i18n/es/_build_forms/expressions/local.md | 232 +++ .../_build_forms/expressions/placeholders.md | 123 ++ i18n/es/_build_forms/expressions/remote.md | 252 ++++ i18n/es/_build_forms/expressions/rest-apis.md | 158 ++ i18n/es/_build_forms/expressions/sorting.md | 51 + i18n/es/_build_forms/formfields/checkbox.md | 131 ++ i18n/es/_build_forms/formfields/datetime.md | 131 ++ i18n/es/_build_forms/formfields/enum.md | 131 ++ i18n/es/_build_forms/formfields/expression.md | 131 ++ i18n/es/_build_forms/formfields/file.md | 131 ++ i18n/es/_build_forms/formfields/html.md | 131 ++ i18n/es/_build_forms/formfields/index.md | 62 + i18n/es/_build_forms/formfields/list.md | 396 +++++ i18n/es/_build_forms/formfields/number.md | 131 ++ i18n/es/_build_forms/formfields/password.md | 131 ++ i18n/es/_build_forms/formfields/radio.md | 131 ++ i18n/es/_build_forms/formfields/text.md | 131 ++ i18n/es/_build_forms/formfields/textarea.md | 131 ++ i18n/es/_build_forms/formfields/yaml.md | 188 +++ i18n/es/_build_forms/forms/ansible.md | 110 ++ i18n/es/_build_forms/forms/approval.md | 101 ++ i18n/es/_build_forms/forms/awx.md | 110 ++ i18n/es/_build_forms/forms/common.md | 104 ++ i18n/es/_build_forms/forms/index.md | 87 ++ i18n/es/_build_forms/forms/jenkins.md | 110 ++ .../_build_forms/forms/job-status-action.md | 86 ++ i18n/es/_build_forms/forms/multistep.md | 240 ++++ i18n/es/_build_forms/forms/notifications.md | 120 ++ i18n/es/_build_forms/forms/rundeck.md | 110 ++ i18n/es/_build_forms/forms/semaphore.md | 110 ++ i18n/es/_build_forms/forms/subform.md | 49 + i18n/es/_build_forms/forms/wizard.md | 210 +++ i18n/es/_build_forms/forms/workflow.md | 111 ++ .../launch-validation/file-uploads.md | 63 + .../_build_forms/launch-validation/index.md | 78 + .../list-rows-and-subforms.md | 58 + .../launch-validation/log-and-enforce.md | 108 ++ .../launch-validation/passwords.md | 52 + .../launch-validation/relaunching.md | 78 + .../launch-validation/rolling-it-out.md | 61 + .../launch-validation/switching-it-on.md | 71 + i18n/es/_build_forms/workflows/designer.md | 107 ++ i18n/es/_build_forms/workflows/file.md | 165 +++ i18n/es/_build_forms/workflows/index.md | 99 ++ i18n/es/_build_forms/workflows/running.md | 103 ++ i18n/es/_configure/announcements.md | 48 + .../es/_configure/announcements/adding-one.md | 29 + i18n/es/_configure/announcements/general.md | 33 + i18n/es/_configure/announcements/list.md | 44 + i18n/es/_configure/appearance.md | 40 + i18n/es/_configure/appearance/defaults.md | 46 + i18n/es/_configure/appearance/icon.md | 23 + i18n/es/_configure/appearance/labels.md | 79 + i18n/es/_configure/appearance/links.md | 33 + i18n/es/_configure/appearance/logo.md | 24 + i18n/es/_configure/appearance/name.md | 16 + i18n/es/_configure/authentication/entra-id.md | 146 ++ i18n/es/_configure/authentication/index.md | 108 ++ i18n/es/_configure/authentication/ldap.md | 240 ++++ i18n/es/_configure/authentication/local.md | 108 ++ i18n/es/_configure/authentication/oidc.md | 133 ++ i18n/es/_configure/config/categories.md | 32 + i18n/es/_configure/config/constants.md | 21 + i18n/es/_configure/config/index.md | 73 + i18n/es/_configure/config/roles.md | 125 ++ i18n/es/_configure/credentials.md | 74 + .../_configure/credentials/credential-page.md | 31 + i18n/es/_configure/credentials/list.md | 45 + i18n/es/_configure/credentials/types.md | 65 + .../_configure/customization/configuration.md | 62 + i18n/es/_configure/customization/features.md | 45 + i18n/es/_configure/customization/index.md | 81 ++ i18n/es/_configure/customization/logging.md | 49 + i18n/es/_configure/customization/paths.md | 45 + i18n/es/_configure/customization/retention.md | 42 + i18n/es/_configure/customization/security.md | 61 + i18n/es/_configure/customization/server.md | 49 + i18n/es/_configure/disclaimer.md | 55 + i18n/es/_configure/disclaimer/acceptances.md | 41 + i18n/es/_configure/disclaimer/general.md | 58 + i18n/es/_configure/disclaimer/versions.md | 43 + i18n/es/_configure/notification-channels.md | 83 ++ .../notification-channels/in-a-form.md | 41 + .../_configure/notification-channels/mail.md | 24 + .../notification-channels/slack-and-teams.md | 22 + .../notification-channels/telegram.md | 21 + .../notification-channels/testing.md | 18 + .../notification-channels/webhook.md | 53 + i18n/es/_configure/production/backups.md | 111 ++ i18n/es/_configure/production/index.md | 59 + i18n/es/_configure/production/logs.md | 126 ++ i18n/es/_configure/production/monitoring.md | 121 ++ .../es/_configure/production/reverse-proxy.md | 264 ++++ i18n/es/_configure/production/scaling.md | 134 ++ i18n/es/_configure/rte/another-datacenter.md | 488 +++++++ i18n/es/_configure/rte/images.md | 67 + i18n/es/_configure/rte/index.md | 275 ++++ i18n/es/_configure/rte/modes.md | 159 ++ i18n/es/_configure/rte/run.md | 162 +++ i18n/es/_configure/rte/several.md | 71 + i18n/es/_configure/runners.md | 67 + i18n/es/_configure/runners/list.md | 70 + i18n/es/_configure/runners/permissions.md | 18 + i18n/es/_configure/runners/registration.md | 123 ++ i18n/es/_configure/runners/runner-page.md | 91 ++ i18n/es/_configure/runners/types.md | 30 + i18n/es/_configure/secret-stores/cyberark.md | 71 + .../secret-stores/hashicorp-vault.md | 44 + i18n/es/_configure/secret-stores/index.md | 171 +++ i18n/es/_configure/security/hardening.md | 185 +++ i18n/es/_configure/security/https.md | 97 ++ i18n/es/_configure/security/index.md | 34 + i18n/es/_configure/security/secrets.md | 85 ++ .../_configure/seed/environment-and-audit.md | 59 + i18n/es/_configure/seed/example.md | 211 +++ i18n/es/_configure/seed/index.md | 68 + i18n/es/_configure/seed/kubernetes.md | 89 ++ i18n/es/_configure/seed/managed-objects.md | 91 ++ i18n/es/_configure/seed/reloading.md | 83 ++ i18n/es/_configure/users-and-groups.md | 61 + .../users-and-groups/blocking-a-sign-in.md | 20 + .../_configure/users-and-groups/directory.md | 31 + .../users-and-groups/local-group.md | 24 + .../_configure/users-and-groups/local-user.md | 35 + .../users-and-groups/several-groups.md | 18 + i18n/es/_data/env_groups.yml | 53 + i18n/es/_get_started/first-form.md | 224 +++ .../installation/docker-compose.md | 317 ++++ i18n/es/_get_started/installation/docker.md | 253 ++++ .../_get_started/installation/from-source.md | 268 ++++ i18n/es/_get_started/installation/index.md | 87 ++ .../es/_get_started/installation/installer.md | 409 ++++++ .../_get_started/installation/kubernetes.md | 307 ++++ i18n/es/_get_started/installation/native.md | 162 +++ i18n/es/_get_started/upgrade-7.md | 302 ++++ .../_get_started/upgrading/docker-compose.md | 164 +++ i18n/es/_get_started/upgrading/docker.md | 182 +++ i18n/es/_get_started/upgrading/from-source.md | 112 ++ i18n/es/_get_started/upgrading/index.md | 43 + i18n/es/_get_started/upgrading/kubernetes.md | 105 ++ i18n/es/_integrate/api/authentication.md | 134 ++ i18n/es/_integrate/api/events.md | 72 + i18n/es/_integrate/api/index.md | 50 + i18n/es/_integrate/api/jobs.md | 165 +++ i18n/es/_integrate/api/responses.md | 74 + i18n/es/_integrate/chat/index.md | 63 + i18n/es/_integrate/chat/privacy-and-limits.md | 54 + i18n/es/_integrate/chat/providers.md | 83 ++ .../_integrate/chat/relaunching-and-audit.md | 45 + i18n/es/_integrate/chat/switching-it-on.md | 26 + i18n/es/_integrate/mcp/index.md | 52 + i18n/es/_integrate/mcp/limitations.md | 54 + i18n/es/_integrate/mcp/setup.md | 119 ++ i18n/es/_integrate/mcp/tools.md | 172 +++ i18n/es/_introduction/concepts.md | 41 + i18n/es/_introduction/gui/administration.md | 164 +++ i18n/es/_introduction/gui/designer-and-git.md | 45 + i18n/es/_introduction/gui/forms-and-jobs.md | 100 ++ i18n/es/_introduction/gui/index.md | 33 + .../es/_introduction/gui/personal-settings.md | 37 + i18n/es/_introduction/how-it-works.md | 211 +++ i18n/es/_introduction/prerequisites.md | 128 ++ i18n/es/_introduction/what-is-ansibleforms.md | 72 + i18n/es/_reference/faq.md | 1278 +++++++++++++++++ i18n/es/_reference/getting-help.md | 63 + i18n/es/_reference/troubleshooting.md | 760 ++++++++++ i18n/es/_use/designer/index.md | 77 + i18n/es/_use/designer/lock.md | 64 + i18n/es/_use/jobs/actions.md | 198 +++ i18n/es/_use/jobs/cleanup.md | 49 + i18n/es/_use/jobs/following.md | 148 ++ i18n/es/_use/jobs/index.md | 114 ++ i18n/es/_use/jobs/schedules.md | 170 +++ i18n/es/_use/jobs/statuses.md | 58 + i18n/es/_use/jobs/stored-jobs.md | 49 + i18n/es/_use/profile/account.md | 22 + i18n/es/_use/profile/api-token.md | 74 + i18n/es/_use/profile/index.md | 57 + i18n/es/_use/profile/password.md | 19 + i18n/es/_use/profile/permissions.md | 32 + i18n/es/_use/profile/preferences.md | 89 ++ i18n/es/_use/repositories/adding.md | 114 ++ i18n/es/_use/repositories/authentication.md | 61 + i18n/es/_use/repositories/designer.md | 40 + i18n/es/_use/repositories/index.md | 110 ++ i18n/es/_use/repositories/pulling.md | 56 + i18n/es/index.md | 138 ++ source/_config.yml | 2 + source/_data/ui.yml | 27 + 195 files changed, 21108 insertions(+), 2 deletions(-) create mode 100644 i18n/es.yml create mode 100644 i18n/es/_build_forms/expressions/credentials.md create mode 100644 i18n/es/_build_forms/expressions/index.md create mode 100644 i18n/es/_build_forms/expressions/jq.md create mode 100644 i18n/es/_build_forms/expressions/local.md create mode 100644 i18n/es/_build_forms/expressions/placeholders.md create mode 100644 i18n/es/_build_forms/expressions/remote.md create mode 100644 i18n/es/_build_forms/expressions/rest-apis.md create mode 100644 i18n/es/_build_forms/expressions/sorting.md create mode 100644 i18n/es/_build_forms/formfields/checkbox.md create mode 100644 i18n/es/_build_forms/formfields/datetime.md create mode 100644 i18n/es/_build_forms/formfields/enum.md create mode 100644 i18n/es/_build_forms/formfields/expression.md create mode 100644 i18n/es/_build_forms/formfields/file.md create mode 100644 i18n/es/_build_forms/formfields/html.md create mode 100644 i18n/es/_build_forms/formfields/index.md create mode 100644 i18n/es/_build_forms/formfields/list.md create mode 100644 i18n/es/_build_forms/formfields/number.md create mode 100644 i18n/es/_build_forms/formfields/password.md create mode 100644 i18n/es/_build_forms/formfields/radio.md create mode 100644 i18n/es/_build_forms/formfields/text.md create mode 100644 i18n/es/_build_forms/formfields/textarea.md create mode 100644 i18n/es/_build_forms/formfields/yaml.md create mode 100644 i18n/es/_build_forms/forms/ansible.md create mode 100644 i18n/es/_build_forms/forms/approval.md create mode 100644 i18n/es/_build_forms/forms/awx.md create mode 100644 i18n/es/_build_forms/forms/common.md create mode 100644 i18n/es/_build_forms/forms/index.md create mode 100644 i18n/es/_build_forms/forms/jenkins.md create mode 100644 i18n/es/_build_forms/forms/job-status-action.md create mode 100644 i18n/es/_build_forms/forms/multistep.md create mode 100644 i18n/es/_build_forms/forms/notifications.md create mode 100644 i18n/es/_build_forms/forms/rundeck.md create mode 100644 i18n/es/_build_forms/forms/semaphore.md create mode 100644 i18n/es/_build_forms/forms/subform.md create mode 100644 i18n/es/_build_forms/forms/wizard.md create mode 100644 i18n/es/_build_forms/forms/workflow.md create mode 100644 i18n/es/_build_forms/launch-validation/file-uploads.md create mode 100644 i18n/es/_build_forms/launch-validation/index.md create mode 100644 i18n/es/_build_forms/launch-validation/list-rows-and-subforms.md create mode 100644 i18n/es/_build_forms/launch-validation/log-and-enforce.md create mode 100644 i18n/es/_build_forms/launch-validation/passwords.md create mode 100644 i18n/es/_build_forms/launch-validation/relaunching.md create mode 100644 i18n/es/_build_forms/launch-validation/rolling-it-out.md create mode 100644 i18n/es/_build_forms/launch-validation/switching-it-on.md create mode 100644 i18n/es/_build_forms/workflows/designer.md create mode 100644 i18n/es/_build_forms/workflows/file.md create mode 100644 i18n/es/_build_forms/workflows/index.md create mode 100644 i18n/es/_build_forms/workflows/running.md create mode 100644 i18n/es/_configure/announcements.md create mode 100644 i18n/es/_configure/announcements/adding-one.md create mode 100644 i18n/es/_configure/announcements/general.md create mode 100644 i18n/es/_configure/announcements/list.md create mode 100644 i18n/es/_configure/appearance.md create mode 100644 i18n/es/_configure/appearance/defaults.md create mode 100644 i18n/es/_configure/appearance/icon.md create mode 100644 i18n/es/_configure/appearance/labels.md create mode 100644 i18n/es/_configure/appearance/links.md create mode 100644 i18n/es/_configure/appearance/logo.md create mode 100644 i18n/es/_configure/appearance/name.md create mode 100644 i18n/es/_configure/authentication/entra-id.md create mode 100644 i18n/es/_configure/authentication/index.md create mode 100644 i18n/es/_configure/authentication/ldap.md create mode 100644 i18n/es/_configure/authentication/local.md create mode 100644 i18n/es/_configure/authentication/oidc.md create mode 100644 i18n/es/_configure/config/categories.md create mode 100644 i18n/es/_configure/config/constants.md create mode 100644 i18n/es/_configure/config/index.md create mode 100644 i18n/es/_configure/config/roles.md create mode 100644 i18n/es/_configure/credentials.md create mode 100644 i18n/es/_configure/credentials/credential-page.md create mode 100644 i18n/es/_configure/credentials/list.md create mode 100644 i18n/es/_configure/credentials/types.md create mode 100644 i18n/es/_configure/customization/configuration.md create mode 100644 i18n/es/_configure/customization/features.md create mode 100644 i18n/es/_configure/customization/index.md create mode 100644 i18n/es/_configure/customization/logging.md create mode 100644 i18n/es/_configure/customization/paths.md create mode 100644 i18n/es/_configure/customization/retention.md create mode 100644 i18n/es/_configure/customization/security.md create mode 100644 i18n/es/_configure/customization/server.md create mode 100644 i18n/es/_configure/disclaimer.md create mode 100644 i18n/es/_configure/disclaimer/acceptances.md create mode 100644 i18n/es/_configure/disclaimer/general.md create mode 100644 i18n/es/_configure/disclaimer/versions.md create mode 100644 i18n/es/_configure/notification-channels.md create mode 100644 i18n/es/_configure/notification-channels/in-a-form.md create mode 100644 i18n/es/_configure/notification-channels/mail.md create mode 100644 i18n/es/_configure/notification-channels/slack-and-teams.md create mode 100644 i18n/es/_configure/notification-channels/telegram.md create mode 100644 i18n/es/_configure/notification-channels/testing.md create mode 100644 i18n/es/_configure/notification-channels/webhook.md create mode 100644 i18n/es/_configure/production/backups.md create mode 100644 i18n/es/_configure/production/index.md create mode 100644 i18n/es/_configure/production/logs.md create mode 100644 i18n/es/_configure/production/monitoring.md create mode 100644 i18n/es/_configure/production/reverse-proxy.md create mode 100644 i18n/es/_configure/production/scaling.md create mode 100644 i18n/es/_configure/rte/another-datacenter.md create mode 100644 i18n/es/_configure/rte/images.md create mode 100644 i18n/es/_configure/rte/index.md create mode 100644 i18n/es/_configure/rte/modes.md create mode 100644 i18n/es/_configure/rte/run.md create mode 100644 i18n/es/_configure/rte/several.md create mode 100644 i18n/es/_configure/runners.md create mode 100644 i18n/es/_configure/runners/list.md create mode 100644 i18n/es/_configure/runners/permissions.md create mode 100644 i18n/es/_configure/runners/registration.md create mode 100644 i18n/es/_configure/runners/runner-page.md create mode 100644 i18n/es/_configure/runners/types.md create mode 100644 i18n/es/_configure/secret-stores/cyberark.md create mode 100644 i18n/es/_configure/secret-stores/hashicorp-vault.md create mode 100644 i18n/es/_configure/secret-stores/index.md create mode 100644 i18n/es/_configure/security/hardening.md create mode 100644 i18n/es/_configure/security/https.md create mode 100644 i18n/es/_configure/security/index.md create mode 100644 i18n/es/_configure/security/secrets.md create mode 100644 i18n/es/_configure/seed/environment-and-audit.md create mode 100644 i18n/es/_configure/seed/example.md create mode 100644 i18n/es/_configure/seed/index.md create mode 100644 i18n/es/_configure/seed/kubernetes.md create mode 100644 i18n/es/_configure/seed/managed-objects.md create mode 100644 i18n/es/_configure/seed/reloading.md create mode 100644 i18n/es/_configure/users-and-groups.md create mode 100644 i18n/es/_configure/users-and-groups/blocking-a-sign-in.md create mode 100644 i18n/es/_configure/users-and-groups/directory.md create mode 100644 i18n/es/_configure/users-and-groups/local-group.md create mode 100644 i18n/es/_configure/users-and-groups/local-user.md create mode 100644 i18n/es/_configure/users-and-groups/several-groups.md create mode 100644 i18n/es/_data/env_groups.yml create mode 100644 i18n/es/_get_started/first-form.md create mode 100644 i18n/es/_get_started/installation/docker-compose.md create mode 100644 i18n/es/_get_started/installation/docker.md create mode 100644 i18n/es/_get_started/installation/from-source.md create mode 100644 i18n/es/_get_started/installation/index.md create mode 100644 i18n/es/_get_started/installation/installer.md create mode 100644 i18n/es/_get_started/installation/kubernetes.md create mode 100644 i18n/es/_get_started/installation/native.md create mode 100644 i18n/es/_get_started/upgrade-7.md create mode 100644 i18n/es/_get_started/upgrading/docker-compose.md create mode 100644 i18n/es/_get_started/upgrading/docker.md create mode 100644 i18n/es/_get_started/upgrading/from-source.md create mode 100644 i18n/es/_get_started/upgrading/index.md create mode 100644 i18n/es/_get_started/upgrading/kubernetes.md create mode 100644 i18n/es/_integrate/api/authentication.md create mode 100644 i18n/es/_integrate/api/events.md create mode 100644 i18n/es/_integrate/api/index.md create mode 100644 i18n/es/_integrate/api/jobs.md create mode 100644 i18n/es/_integrate/api/responses.md create mode 100644 i18n/es/_integrate/chat/index.md create mode 100644 i18n/es/_integrate/chat/privacy-and-limits.md create mode 100644 i18n/es/_integrate/chat/providers.md create mode 100644 i18n/es/_integrate/chat/relaunching-and-audit.md create mode 100644 i18n/es/_integrate/chat/switching-it-on.md create mode 100644 i18n/es/_integrate/mcp/index.md create mode 100644 i18n/es/_integrate/mcp/limitations.md create mode 100644 i18n/es/_integrate/mcp/setup.md create mode 100644 i18n/es/_integrate/mcp/tools.md create mode 100644 i18n/es/_introduction/concepts.md create mode 100644 i18n/es/_introduction/gui/administration.md create mode 100644 i18n/es/_introduction/gui/designer-and-git.md create mode 100644 i18n/es/_introduction/gui/forms-and-jobs.md create mode 100644 i18n/es/_introduction/gui/index.md create mode 100644 i18n/es/_introduction/gui/personal-settings.md create mode 100644 i18n/es/_introduction/how-it-works.md create mode 100644 i18n/es/_introduction/prerequisites.md create mode 100644 i18n/es/_introduction/what-is-ansibleforms.md create mode 100644 i18n/es/_reference/faq.md create mode 100644 i18n/es/_reference/getting-help.md create mode 100644 i18n/es/_reference/troubleshooting.md create mode 100644 i18n/es/_use/designer/index.md create mode 100644 i18n/es/_use/designer/lock.md create mode 100644 i18n/es/_use/jobs/actions.md create mode 100644 i18n/es/_use/jobs/cleanup.md create mode 100644 i18n/es/_use/jobs/following.md create mode 100644 i18n/es/_use/jobs/index.md create mode 100644 i18n/es/_use/jobs/schedules.md create mode 100644 i18n/es/_use/jobs/statuses.md create mode 100644 i18n/es/_use/jobs/stored-jobs.md create mode 100644 i18n/es/_use/profile/account.md create mode 100644 i18n/es/_use/profile/api-token.md create mode 100644 i18n/es/_use/profile/index.md create mode 100644 i18n/es/_use/profile/password.md create mode 100644 i18n/es/_use/profile/permissions.md create mode 100644 i18n/es/_use/profile/preferences.md create mode 100644 i18n/es/_use/repositories/adding.md create mode 100644 i18n/es/_use/repositories/authentication.md create mode 100644 i18n/es/_use/repositories/designer.md create mode 100644 i18n/es/_use/repositories/index.md create mode 100644 i18n/es/_use/repositories/pulling.md create mode 100644 i18n/es/index.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c202ebf..d44d8a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,7 @@ The site is everything under `source/`. Most of it is written here, but two file | `/helm-charts/` (the Helm chart repository) | the chart packages of the `ansibleforms/helm-charts` releases, indexed at build time | the `helm-charts` repository | | everything else in `source/` | this repository | here | -The docs are served under a language prefix (`/en/` for English), and the language selector in the header lists +The docs are served under a language prefix (`/en/`, `/es/`), and the language selector in the header lists every language in `docs_languages` (`source/_config.yml`). Every page that used to live at the root keeps its address as a redirect, written at build time by `scripts/redirects.sh`, to the reader's language: the one they last picked, else their browser's, else English. The install script (`/install`) and the Helm chart diff --git a/README.md b/README.md index 7082ea2..260b4ad 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The site covers everything a user of AnsibleForms needs, from the first install - The app's changelog, rebuilt into the site on every release - The Helm chart repository, served under `/helm-charts/` - The 6.x documentation, kept alongside under `/v6/` -- The docs under a language prefix (`/en/`), the old addresses redirecting to the reader's language +- The docs in English and Spanish under `/en/` and `/es/`, picked from the reader's browser language ## Previewing locally diff --git a/i18n/es.yml b/i18n/es.yml new file mode 100644 index 0000000..ffe9343 --- /dev/null +++ b/i18n/es.yml @@ -0,0 +1,40 @@ +# The Spanish docs : read after source/_config.yml by scripts/build-language.sh, so only what +# differs from the English site is set here. Jekyll merges the two, nested keys included. + +lang: es +docs_language: "es" + +title: Documentación de AnsibleForms +description: Documentación técnica de AnsibleForms + +footer_content: "Copyright © 2023-2026 AnsibleForms. Todos los derechos reservados." + +# "Edit this page on GitHub" opens the Spanish page's source +gh_edit_link_text: "Editar esta página en GitHub" +gh_edit_source: i18n/es + +just_the_docs: + collections: + introduction: + name: Introducción + get_started: + name: Primeros pasos + configure: + name: Configurar + build_forms: + name: Crear formularios + use: + name: Guía de usuario + integrate: + name: Integrar + reference: + name: Referencia + +# The version selector (_includes/version_select.html) : a list is replaced, not merged +docs_versions: + - name: "7" + title: "7.x (última)" + url: "/es/" + - name: "6" + title: "6.x (en inglés)" + url: "/v6/" diff --git a/i18n/es/_build_forms/expressions/credentials.md b/i18n/es/_build_forms/expressions/credentials.md new file mode 100644 index 0000000..5649358 --- /dev/null +++ b/i18n/es/_build_forms/expressions/credentials.md @@ -0,0 +1,54 @@ +--- +layout: default +title: Credenciales +parent: Expresiones +nav_order: 4 +translated_from: bfa50c90495c68300d189f9a0eb38e25df9e7c8e +--- + +# Credenciales +{: .no_toc } + +Lee una credencial almacenada en una expresión +{: .fs-6 .fw-300 } + +--- + +Lee una credencial almacenada por nombre o por expresión regular, con una alternativa: + +```javascript +fn.fnCredentials('credentialname_or_regex','fallback_credentialname_or_regex') + +// Try to use `fnRestBasic` and `fnRestJwtSecure` if possible, this avoids passwords and tokens going over the network. + +// output : a credential object with all properties (user, password, host, port) +``` + +Primero se prueba el nombre exacto. Si ninguna credencial tiene ese nombre, el nombre se usa como expresión regular y después se prueba la alternativa. + +#### Limitado por rol + +Cuando uno de los roles del usuario incluye `credentials` en `config.yaml`, `fn.fnCredentials` solo devuelve una credencial que ese rol +permita: los patrones del rol se comparan con el nombre de la credencial encontrada (por nombre, expresión regular o alternativa), +y cualquier otra se rechaza. Los roles sin la lista no la limitan, y un administrador nunca está limitado. + +#### Credenciales de un almacén de secretos + +Una credencial que indica un [almacén de secretos](../secret-stores) obtiene su `user` y su `password` +de ese almacén en tiempo de ejecución. `fn.fnCredentials('myapp')` sigue funcionando sin cambios. + +Un secreto también se puede leer sin una fila de credencial, allí donde se acepte un nombre de credencial +(incluidos `fnRestBasic`, `fnRestJwtSecure` y las sustituciones de cabeceras de `fnRestAdvanced`): + +```javascript +fn.fnCredentials('secret:vault:secret/ontap') // secret:: +fn.fnCredentials('vault:secret/ontap') // the store named `vault` +``` + +Las claves del secreto se asignan como se describe en [Nombres de clave](../secret-stores#key-names). +Un secreto con otra forma se transforma con una expresión `jq` como tercer argumento: + +```javascript +// secret: { "creds": { "u": "admin", "p": "Netapp12" } } +fn.fnCredentials('vault:secret/weird','','.creds | { user: .u, password: .p }') +``` diff --git a/i18n/es/_build_forms/expressions/index.md b/i18n/es/_build_forms/expressions/index.md new file mode 100644 index 0000000..e54204e --- /dev/null +++ b/i18n/es/_build_forms/expressions/index.md @@ -0,0 +1,76 @@ +--- +layout: default +title: Expresiones +nav_order: 1 +has_children: true +has_toc: false +permalink: /expressions/ +translated_from: 52d74e32540fba4dfa5078c8976147b39a2ad2cf +--- + +# Expresiones +{: .no_toc } + +Calcula valores de campos con JavaScript, en el navegador o en el servidor +{: .fs-6 .fw-300 } + +--- + +Para autores de formularios: rellena campos de forma dinámica con JavaScript, calculados en el navegador u obtenidos en el servidor. + +El atributo `expression` de un campo de formulario te permite crear formularios dinámicos con expresiones JavaScript. + +--- + +## Introducción + +El atributo `expression` está disponible en varios tipos de campo: +- campo `enum`: para rellenar las opciones del desplegable de forma dinámica +- campo `expression`: para obtener o generar cualquier tipo de datos +- campo `html`: para construir su contenido HTML +- campos `yaml` y `list`: para rellenar sus datos + +Las expresiones JavaScript se evalúan en el **lado del servidor** (por defecto) o en el **lado del cliente**, según la propiedad `runLocal` del campo. +Un campo `html`, y los tipos de campo `local`, `local_out` y `credential`, siempre ejecutan su expresión en el navegador, diga lo que diga +`runLocal`. + +- **En el lado del servidor** (`runLocal: false`, el valor por defecto): funciones predefinidas que obtienen datos de APIs, archivos y bases de datos. +- **En el lado del cliente** (`runLocal: true`): el código aprovecha todo el motor JavaScript en el sandbox del navegador para cálculos, transformaciones y manipulaciones. + +--- + +## Consideraciones de seguridad + +Como las expresiones se evalúan con la función `eval` de JavaScript, la inyección de código es un riesgo potencial: + +- **Las expresiones del lado del servidor** pasan antes por un saneador: cada llamada debe ser una función `fn.` o `fnc.`, y se rechazan las líneas múltiples, + `;`, los template literals, `process.env` y las formas de escapar de `eval` (`import`, `require`, `constructor`, ...). +- **Las expresiones del lado del cliente** se evalúan en el sandbox del navegador y se pueden considerar seguras. + +El grado de rigor del saneador depende de +[`EXPRESSION_SANITIZER`](../customization/security.html#env_EXPRESSION_SANITIZER): + +| Valor | Expresiones del lado del servidor | +|---|---| +| `strict` (por defecto) | llamadas `fn.` y `fnc.`, y métodos sobre su resultado, como `fn.fnTime().format('YYYY')` | +| `paranoid` | solo llamadas directas `fn.` y `fnc.`, sin métodos sobre su resultado | +| `off` | se rechaza toda expresión del lado del servidor; solo funcionan las expresiones `runLocal` | +| `legacy` | las reglas de 6.2.1, que permitían a cualquier usuario autenticado ejecutar código en el servidor; solo para una actualización, y solo en el entorno real (no desde la página de ajustes) | + +Un valor vacío o desconocido equivale a `strict`. Un cambio se aplica a la siguiente expresión, sin reiniciar. + +--- + +## En esta sección + +Cada tema tiene su propia página: + +| Página | Qué cubre | +|---|---| +| [Marcadores](placeholders.html) | Haz referencia a otro campo, una constante o el usuario con `$(...)` | +| [Expresiones locales](local.html) | JavaScript que se ejecuta en el sandbox del navegador | +| [Expresiones remotas](remote.html) | Funciones del lado del servidor para fechas, redes, archivos, SSH y nombres numerados | +| [APIs REST](rest-apis.html) | Llama a APIs REST con autenticación básica, por token o personalizada | +| [Credenciales](credentials.html) | Lee una credencial almacenada | +| [Ordenación](sorting.html) | Ordena arrays con un objeto de ordenación | +| [Consultas jq](jq.html) | Transforma datos con jq | diff --git a/i18n/es/_build_forms/expressions/jq.md b/i18n/es/_build_forms/expressions/jq.md new file mode 100644 index 0000000..49a396d --- /dev/null +++ b/i18n/es/_build_forms/expressions/jq.md @@ -0,0 +1,59 @@ +--- +layout: default +title: Consultas jq +parent: Expresiones +nav_order: 6 +translated_from: b34dbbec6c5d115f9bb41450962c3fce9606035f +--- + +# Consultas jq +{: .no_toc } + +Transforma datos con jq, incluidas las funciones integradas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Ejecutar una consulta JSON (jq) sobre un objeto + +Transforma un objeto, por ejemplo el valor de otro campo, con una expresión jq: + +```javascript +fn.fnJq($(settings),'.mapping | keys') + +// output : a full json object taken from another field called "settings", converted by jq + +// fn.fnJq(object, jq) +// - object, for example read from rest or yaml file +// - jq-expression : a jq-expression (https://jqplay.org) +// - wrap the result in fn.fnSort to order it +``` + +Desde 7.3.0, un filtro jq se ejecuta como máximo [`JQ_TIMEOUT_SECONDS`](../customization/security.html#env_JQ_TIMEOUT_SECONDS) (30 por defecto) +antes de detenerse, y su salida se limita a 64 MB. Esto se aplica a `fn.fnJq` y a la expresión jq de las funciones de REST, archivo, +credencial y consulta a base de datos. + +--- + +## Funciones jq integradas + +Todas las funciones que obtienen datos aceptan una consulta JSON (jq), un lenguaje para manipular objetos y arrays de datos. +Al manipular datos, puede que necesites convertir bytes a KB, MB o GB, o redondear números. +Para ello, AnsibleForms incluye algunas funciones jq propias: + +- **fn2KB**: bytes a KB +- **fn2MB**: bytes a MB +- **fn2GB**: bytes a GB +- **fnRound0**: redondea con 0 decimales +- **fnRound1**: redondea con 1 decimal +- **fnRound2**: redondea con 2 decimales +- **fnRound**: redondea con 2 decimales + +```javascript +fn.fnRestBasic('get','https://youruri','','CREDS', + '[.records[] | {"Available Capacity":.storage_capacity.available | fn2GB | fnRound }]') +// note you need to pipe into the functions +``` diff --git a/i18n/es/_build_forms/expressions/local.md b/i18n/es/_build_forms/expressions/local.md new file mode 100644 index 0000000..5933c4d --- /dev/null +++ b/i18n/es/_build_forms/expressions/local.md @@ -0,0 +1,232 @@ +--- +layout: default +title: Expresiones locales +parent: Expresiones +nav_order: 1 +translated_from: 77a88157a129701065cf770bf1005b4391140aa1 +--- + +# Expresiones locales +{: .no_toc } + +JavaScript que se ejecuta en el sandbox del navegador +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Los siguientes ejemplos se ejecutan con la propiedad de campo `runLocal: true`. +Se ejecutan en el sandbox del navegador y pueden aprovechar todo el motor JavaScript. + +{: .note } +> **Consejo**: usa el tipo `local` como alias de `type: expression, runLocal: true, hide: true, output: false` +> **Consejo**: usa el tipo `local_out` como alias de `type: expression, runLocal: true, hide: true` + +--- + +## Convención de nombres + +Usa manipulaciones de cadenas para aplicar convenciones de nombres. + +```javascript +'$(field1) $(field2)'.replace('-','_').toUpperCase() +``` + +--- + +## Cálculo + +Usa funciones matemáticas para hacer cálculos. + +```javascript +Math.round($(field1)*$(field2)*Math.PI) // Math.round and Math.PI are native javascript +``` + +--- + +## Conversiones + +Convierte bytes a gigabytes. + +```javascript +($(size_bytes)/1024/1024/1024).toFixed(2) // toFixed is native javascript method +``` + +--- + +## Convertir un array de objetos en una tabla HTML + +Convierte un array de objetos, por ejemplo de otro campo, en una tabla HTML para un campo `html`: + +```javascript +fnToTable($(my_array_field),{ + tableClass: '', + escapeHtml: true, + emptyCell: '', + includeHeader: true +}) + +// output : an html-table representation of the array of objects taken from another field called "my_array_field" +// tip : add tableClass: 'table table-striped table-bordered' for bootstrap styling +``` + +--- + +## Obtener un nombre con numeración incremental + +Con la numeración automática, puede que necesites encontrar el siguiente nombre en un array de cadenas numeradas. +Esta función también está disponible como expresión del lado del servidor (con el prefijo `fn.`). + +```javascript +fnGetNumberedName(['server001','server002','server005'],'server###','server001',false) +// result : "server006" + +fnGetNumberedName(['server001','server002','server005'],'server###','server001',true) +// result : "server003" + +fnGetNumberedName($(fieldlist),'server###','server001',true) +// use another expression field as input for the array + +// fnGetNumberedName(array,pattern,default,fillgaps) +// This function searches for a numbered pattern in a list of string, +// increases the highest number and returns a name like the pattern +// - array : an array object, you can use a placeholder to an expression where you know it's an array +// - pattern : a string that hold the # as a digit +// - default : if no value is found, return this default +// - fillgaps : a boolean to indicate it can fill gaps in the numbers, 1,2,3,6 => 4 +``` + +Cuando el primer argumento no es un array, la función local devuelve el valor por defecto. La función del servidor `fn.fnGetNumberedName` +falla en cambio con un error. + +--- + +## Manipulación de arrays de objetos + +Los arrays de objetos a menudo hay que filtrarlos, modificarlos y ordenarlos. +AnsibleForms incluye una ayuda propia que mantiene este código conciso. + +```javascript +/* + fnArray.from($(your_array_field)) // custom helper library + .filterBy({property1:'value1',property2:'value2', ...}) // filters by property value (a string, * wildcards allowed) + .regexBy({property1:'regex1',property2:'regex2', ...}) // filters the array by property matched against regex + .distinctBy('property1','property2', ...) // will make the array entries unique by property + .selectAttr({prop1:'property1',prop2:'property2'}) // only selects a certain property, and you can relabel them + .sortBy('property1','-property2', ...) // will order the array. To have descending add a "-" (minus) before the property + + mylist: + [ + { + name:'Spiderman', + has_ability: true, + ability: 'Can do whatever a spider can' + }, + { + name:'Superman', + has_ability: true, + ability: 'Superstrong,Flying,Laserbeams' + }, + { + name:'FamilyGuy', + has_ability: false + }, + { + name:'Wolverine', + has_ability: true, + ability: 'Enhanced healing' + } + ] +*/ + +fnArray.from($(mylist)) // take data from another field 'mylist' +.regexBy({name:'.*man$'}) // name must end with 'man' +.filterBy({has_ability:'true'}) // must have abilities (a string: 'true', not true) +.selectAttr({name:'name',ability:'ability'}) // only take properties name and ability +.sortBy('-name') // sort by name descending + +/* + result : + [ + {name:'Superman',ability:'Superstrong,Flying,Laserbeams'}, + {name:'Spiderman',ability:'Can do whatever a spider can'} + ] +*/ +``` + +--- + +## Manipulación de arrays de objetos con JavaScript estándar + +Todos los métodos de array estándar de JavaScript siguen disponibles para filtrar y modificar tus datos, como `map`, `forEach`, `find`, `filter` y `reduce`. + +```javascript +// source : https://medium.com/@jeff_long/understanding-foreach-map-filter-and-find-in-javascript-f91da93b9f2c + +mylist= + [ + { + name:'Bob', + age: 5 + }, + { + name:'Tom', + age: 10 + }, + { + name:'Paul', + age: 30 + }, + { + name:'Tom', + age: 40 + } + ] + +// simple filter +$(mylist).filter((x) => x.age<=20) // filter age<=20 +/* result: +[ + {name:'Bob',age:5}, + {name:'Tom',age:10} +] +*/ + +// filter and addition +$(mylist) + .filter((x) => x.age<=20) // filter age<=20 + .map((x) => { return {...x,diff:20-x.age} }) // add new property diff +/* result: +[ + {name:'Bob',age:5,diff:15}, + {name:'Tom',age:10,diff:10} +] +*/ + +// find +$(mylist).find((x) => x.name=='Tom').age // find age of first Tom +// result: 10 + +// make sum with reduce +$(mylist).reduce((sum,x) => sum+x.age,0) // calculate sum of ages +// result: 85 + +// make temp function +((arr,min)=>{ + return arr + .filter(x => x.age>=min) // filter + .map(x => { + var preview=`${x.name} (${x.age})`; // make preview string + return {...x,preview:preview} }) // add new preview property +})($(mylist),20) // feed function with mylist and 20 +/* result: +[ + {name:'Paul',age:30,preview:'Paul (30)'}, + {name:'Tom',age:40,preview:'Tom (40)'} +] +*/ +``` + +--- diff --git a/i18n/es/_build_forms/expressions/placeholders.md b/i18n/es/_build_forms/expressions/placeholders.md new file mode 100644 index 0000000..9eee869 --- /dev/null +++ b/i18n/es/_build_forms/expressions/placeholders.md @@ -0,0 +1,123 @@ +--- +layout: default +title: Marcadores +parent: Expresiones +nav_order: 0.5 +translated_from: bd63e437a4f77c707a1bf8d8ee99b1516063b62a +--- + +# Marcadores +{: .no_toc } + +Haz referencia a otro campo, una constante o el usuario actual con `$(...)` +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Qué es un marcador + +Un marcador es una referencia escrita como `$(reference)`. Se sustituye por un valor antes de usar el texto que lo contiene. + +Los marcadores funcionan en `expression`, `query` y `default` (evaluado con `evalDefault`) de un campo, y en su `label`, +`help` y `placeholder`. Un campo que contiene un marcador depende del campo al que hace referencia: espera hasta que ese campo tenga un +valor, y se vuelve a evaluar cada vez que ese valor cambia. Así se construyen los desplegables en cascada. + +```yaml +- name: city + type: enum + values: [Amsterdam, Brussels] +- name: datacenter + type: enum + dbConfig: { name: CMDB, type: mysql } + query: select name from cmdb.datacenter where city='$(city)' +``` + +--- + +## A qué puede hacer referencia un marcador + +Un marcador nombra un campo del formulario, o uno de estos, que tiene todo formulario: + +| Referencia | Qué contiene | +|---|---| +| `$(field)` | el valor de otro campo del formulario | +| `$(name)`, una constante | una [constante](../config/constants.html) de `config.yaml`, o un valor de los `varsFiles` del formulario | +| `$(__user__)` | el usuario que ha iniciado sesión: `username`, `type`, `groups`, `roles` | +| `$(__parent__.field)` | en un [subformulario](../forms/subform.html) (una fila de lista, un paso de un wizard), un valor del formulario padre | + +```yaml +expression: "'$(__user__.username)'" +expression: $(__user__.groups) +``` + +--- + +## Acceder a una parte de un valor + +La notación de punto e índice lee una parte de un valor, siempre que la ruta solo contenga letras, dígitos, `_`, `-`, `.` y `[n]`: + +| Marcador | Sobre | Devuelve | +|---|---|---| +| `$(field)` | un campo text, number o password | el valor | +| `$(field)` | un enum de registros | la `placeholderColumn` del registro seleccionado, o si no su primera columna | +| `$(field.col)` | un enum de registros | esa columna del registro seleccionado | +| `$(field)` | un enum con `multiple: true` | la columna elegida de cada registro seleccionado, como array | +| `$(field)` | una expression, list o constante | el valor completo (una lista devuelve sus filas) | +| `$(field.a.b)`, `$(f[0].x)` | una expression, list o constante | esa parte del objeto o array | + +Si pones `placeholderColumn: "*"` en un enum, `$(field)` devuelve el registro seleccionado completo en lugar de una columna. + +```yaml +- name: city + type: enum + dbConfig: { name: CMDB, type: mysql } + query: select id, name from cmdb.city + valueColumn: name + placeholderColumn: id # $(city) gives the id; $(city.name) still gives the name +- name: servers + type: expression + expression: "[{name: 'web1'}, {name: 'web2'}]" + runLocal: true +- name: first_server + type: expression + expression: $(servers[0].name) # 'web1' + runLocal: true +``` + +--- + +## Cómo se inserta el valor + +La forma en que un valor se inserta en el texto depende de dónde esté el marcador. + +| Dónde | Ejemplo | Se convierte en | +|---|---|---| +| una expresión, fuera de comillas | `$(count) + 1` | un literal JavaScript: un número sigue siendo un número, un texto pasa a ser una cadena entre comillas, un objeto o array un literal | +| una expresión, toda una cadena entre comillas | `fn.fnLs('$(dir)')` | el mismo literal, comillas incluidas, de modo que un valor con una comilla no puede escaparse | +| una expresión, dentro de una cadena más larga | `'$(dir)/vars'` | el texto, escapado para esa cadena | +| una consulta, una etiqueta, un texto de ayuda | `where city='$(city)'` | el texto | + +Un campo cuyos marcadores aún no se pueden resolver todos no se evalúa; con `ignoreIncomplete: true`, un valor que falta se +inserta en cambio como `undefined`. + +### Backticks y `${` en un valor + +Desde 7.3.0, un valor de campo no puede ejecutarse como código cuando el servidor lo inserta en una expresión (expresiones remotas, validación +del lanzamiento, MCP y chat). Un backtick o `${` en el valor se escribe como escape unicode (`\u0060`, `\u0024{`), así que sigue siendo +texto incluso dentro de un template literal. Las expresiones del lado del servidor rechazan por completo los template literals; usa `runLocal` para ellos. + +Una expresión `runLocal` evaluada en el navegador inserta el valor como indica la tabla anterior, sin ese escape, así que no pongas +un marcador dentro de un template literal en ese caso. + +--- + +## Ver también + +Páginas relacionadas y entradas de las FAQ: + +- [Expresiones locales](local.html) y [Expresiones remotas](remote.html): donde se usan la mayoría de los marcadores +- [FAQ: Desplegables en cascada](../faq.html#cascaded-dropdowns) y [Marcadores de campo](../faq.html#field-placeholders): más ejemplos +- [FAQ: ¿Cómo accedo a los datos del formulario padre dentro de un subformulario?](../faq.html#how-do-i-access-parent-form-data-inside-a-subform) diff --git a/i18n/es/_build_forms/expressions/remote.md b/i18n/es/_build_forms/expressions/remote.md new file mode 100644 index 0000000..52241c7 --- /dev/null +++ b/i18n/es/_build_forms/expressions/remote.md @@ -0,0 +1,252 @@ +--- +layout: default +title: Expresiones remotas +parent: Expresiones +nav_order: 2 +translated_from: 8d0f7560216da4a428e1935ac2b6de7b5428c243 +--- + +# Expresiones remotas +{: .no_toc } + +Funciones del lado del servidor para fechas, redes, archivos, SSH y nombres numerados +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Los siguientes ejemplos se ejecutan con la propiedad de campo `runLocal: false` (por defecto). +Se evalúan en el lado del servidor. + +Como evaluar código en el servidor es un riesgo de seguridad, la expresión se sanea y se limita a las funciones que +incluye AnsibleForms y a las [funciones que añadas tú](#your-own-functions). +Las expresiones del lado del servidor sirven para obtener información de fuentes de datos como archivos, APIs REST y comandos SSH. + +{: .important } +Las funciones integradas del lado del servidor llevan el prefijo `fn.` (por ejemplo, `fn.fnRestBasic`); las tuyas, `fnc.`. + +--- + +## Manipular fechas y horas + +Trabaja con fechas y horas mediante [Day.js](https://day.js.org): + +```javascript +fn.fnTime().diff(fn.fnTime('2019-10-01'),'day') // number of days between now and 2019-10-01 + +// This is the core implementation of https://day.js.org +``` + +--- + +## Obtener información CIDR de una IP + +Obtén la subred de una dirección IP y una máscara de red, y comprueba qué direcciones pertenecen a ella: + +```javascript +fn.fnCidr('172.16.0.1','255.255.0.0') + +// fn.fnCidr(ip,netmask) +// - ip : an ip address +// - netmask : a netmask +// +// it will return the Cidr subnet information as well as expose a `contains` method to check whether an ip is part of this subnet. +// { +// "networkAddress":"172.16.0.0", +// "firstAddress":"172.16.0.1", +// "lastAddress":"172.16.255.254", +// "broadcastAddress":"172.16.255.255", +// "subnetMask":"255.255.0.0", +// "subnetMaskLength":16, +// "numHosts":65534, +// "length":65536, +// "contains":(ip)=>{return ...} +// } +``` + +--- + +## Obtener la salida de SSH + +Ejecuta un comando en un host remoto por SSH y usa su salida: + +```javascript +fn.fnSsh('root','172.16.0.1','ls -la') + +// fn.fnSsh(user,host,command,jq-expression,timeoutSeconds) +// - user : the ssh user +// - host : the host +// - command : the command to trigger by ssh, as the remote host's shell reads it +// - jq-expression : an optional jq-expression (https://jqplay.org), applied to the output lines +// - timeoutSeconds : an optional time limit, 60 seconds by default; running out of time is an error +// +// it returns the output lines (stdout and stderr) and a last line exit() with the exit code. +// ssh itself is started without a local shell, so the user and host can never become a second local command. +// +// You must use 'known_hosts' and 'public-key' to setup non-interactive password-less authentication. +// In the settings you can find the public-key and add your target-host to known_hosts +``` + +--- + +## Listar los archivos de un directorio + +Lista los archivos de un directorio del servidor de AnsibleForms, opcionalmente de forma recursiva y filtrada: + +```javascript +fn.fnLs('/tmp',{ recursive: true, regex: '.*\\.log$', metadata: true }) + +// fn.fnLs(path,options) +// - path : path to directory +// - options : recursive:boolean, regex:string ,metadata:boolean +// - recursive : whether to list files recursively +// - regex : a regular expression, matched against the path relative to the listed directory +// - metadata : whether to return objects with file metadata (path, name, type, size, created, modified, ...) +// +// directories are always listed too, with a trailing / (such as 'logs/'), and the regex sees that slash as well +``` + +--- + +## Analizar una página HTML + +Obtén una página HTML y extrae las partes que coinciden con una expresión regular: + +```javascript +fn.fnParseHtmlWithRegex('https://ansibleguy.com','(.*?)','g') + +// fn.fnParseHtmlWithRegex(url,regex,flags,credential) +// - url : url to html page +// - regex : a regular expression, groups ( ) are optional +// - flags : regex flags (such as g,i,m), defaults to 'g'; without g only the first match is returned +// - credential : an optional credential name, used for basic authentication +// (a credential without both a user and a password is ignored, the page is fetched without authentication) +// +// it will grab the html source and return an array with one object per match, at most 10000 : +// { fullMatch, groups (the numbered group matches), namedGroups } +``` + +La página se obtiene sujeta a [`REST_ALLOWED_HOSTS`](../customization/security.html#env_REST_ALLOWED_HOSTS) y +[`REST_DENIED_HOSTS`](../customization/security.html#env_REST_DENIED_HOSTS), como las funciones REST. A diferencia de ellas, ignora +`REST_IGNORE_CERTS` y `REST_TIMEOUT_SECONDS`: siempre comprueba el certificado y espera como máximo 30 segundos. + +--- + +## Obtener información DNS + +Resuelve un registro DNS de un dominio: + +```javascript +fn.fnDnsResolve('ansibleguy.com','A') + +// fn.fnDnsResolve(fqdn,type) +// - fqdn : a fully qualified domain name +// - type : the type of dns record (A,AAAA,MX,NS,CNAME,TXT,SRV,PTR) +``` + +--- + +## Leer un archivo JSON + +Lee un archivo JSON del servidor, opcionalmente filtrado con jq: + +```javascript +fn.fnReadJsonFile('/tmp/file.json','.[].name') + +// fn.fnReadJsonFile(path,jq-expression) +// - path : path to json file +// - jq-expression : an optional jq-expression (https://jqplay.org) +``` + +--- + +## Leer un archivo YAML + +Lee un archivo YAML del servidor, opcionalmente filtrado con jq: + +```javascript +fn.fnReadYamlFile('/tmp/file.yaml','.[].name') + +// fn.fnReadYamlFile(path,jq-expression) +// - path : path to yaml file +// - jq-expression : an optional jq-expression (https://jqplay.org) +``` + +--- + +## Obtener un nombre con numeración incremental + +En un array de cadenas con números incrementales (server001, server002, ...), puede que necesites encontrar el siguiente nombre disponible. +Esta función también está disponible como expresión local (sin el prefijo `fn.`). + +```javascript +fn.fnGetNumberedName(['server001','server002','server005'],'server###','server001',false) +// result : "server006" + +fn.fnGetNumberedName(['server001','server002','server005'],'server###','server001',true) +// result : "server003" + +fn.fnGetNumberedName($(fieldlist),'server###','server001',true) +// use another expression field as input for the array + +// fn.fnGetNumberedName(array,pattern,default,fillgaps) +// This function searches for a numbered pattern in a list of string, +// increases the highest number and returns a name like the pattern +// - array : an array object, you can use a placeholder to an expression where you know it's an array +// - pattern : a string that hold the # as a digit +// - default : if no value is found, return this default +// - fillgaps : a boolean to indicate it can fill gaps in the numbers, 1,2,3,6 => 4 +``` + +En el servidor, `fn.fnGetNumberedName` falla con un error cuando el primer argumento no es un array. La expresión local +(sin `fn.`) devuelve en cambio el valor por defecto. + +--- + +## Tus propias funciones + +Añade funciones JavaScript propias a `custom.js` y llámalas en una expresión del servidor con el prefijo `fnc.`. + +El archivo exporta un objeto de funciones. El que incluye la imagen contiene dos ejemplos: + +```javascript +import logger from "../lib/logger.js"; + +const fnSum = function(a,b) { + logger.debug("[fnSum] sum is happening") + return a+b +}; +const fnMultiply = function(a,b) { + logger.debug("[fnMultiply] multiply is happening") + return a*b +}; + +export default { + fnSum, + fnMultiply +}; +``` + +```yaml +- name: total + type: expression + expression: fnc.fnSum($(disk_count), 10) +``` + +Del mismo modo, `jq.custom.definitions` contiene definiciones jq propias. Se anteponen a cada consulta jq que ejecuta el +servidor, de modo que las [consultas jq](jq.html) de todas las funciones pueden usarlas. + +En un contenedor, monta ambos archivos sobre los de la imagen y reinicia; se leen al arrancar el servidor: + +```yaml +volumes: + - ./data/functions/custom.js:/app/dist/src/functions/custom.js + - ./data/functions/jq.custom.definitions:/app/dist/src/functions/jq.custom.definitions +``` + +{: .warning } +> Una función personalizada se ejecuta dentro del proceso del servidor, con sus permisos, cada vez que una expresión la llama. El +> [saneador](index.html#security-concerns) comprueba que la expresión solo llama a funciones `fn.` y `fnc.`; lo que una +> función haga con sus argumentos es cosa tuya. diff --git a/i18n/es/_build_forms/expressions/rest-apis.md b/i18n/es/_build_forms/expressions/rest-apis.md new file mode 100644 index 0000000..6ef6140 --- /dev/null +++ b/i18n/es/_build_forms/expressions/rest-apis.md @@ -0,0 +1,158 @@ +--- +layout: default +title: APIs REST +parent: Expresiones +nav_order: 3 +translated_from: e4ceac62125b53a88d479bf7f2a0e7230edce2b2 +--- + +# APIs REST +{: .no_toc } + +Llama a APIs REST con autenticación básica, por token o personalizada +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## API REST con autenticación básica + +Llama a una API REST con el usuario y la contraseña de una credencial almacenada: + +```javascript +fn.fnRestBasic( + 'get', + 'https://resturl/api/', + '', + 'name_of_credential_in_database', + '[.records[] | {name:.name, email:.email, spouse:.relations.spouse}]', + 'name', + false +) + +// output : a full json object coming from rest, json transformed with jq, result sorted and transformed by javascript. + +// fn.fnRestBasic(method,url,body,credentialname,jq-expression,sort-object) +// - method : get,post,put,delete +// - url : url to restapi (can for example contain a placeholder like 'https://$(serverfield.fqdn)/api/' +// - body : in case of post and put +// - credential-name : it will lookup the credentials as you have saved in the gui +// - jq-expression : an optional jq-expression (https://jqplay.org) +// - sort object : a sort object to order the result +// - hasBigInt : a boolean indicating if it should convert Int64 to string +``` + +--- + +## API REST con autenticación por token + +Llama a una API REST con un bearer token escrito en la expresión: + +```javascript +fn.fnRestJwt( + 'get', + 'https://resturl/api/', + '', + 'your_jwt_token_in_text', + '[.records[] | {name:.name, age:.age}]', + ['age','name'], + false +) + +// output : a full json object coming from rest, json transformed with jq, result sorted, first by age, then by name + +// fn.fnRestJwt(method,url,body,token,jq-expression,sort-object) +// - method : get,post,put,delete +// - url : url to restapi (can for example contain a placeholder like 'https://$(serverfield.fqdn)/api/' +// - body : in case of post and put +// - token : it will be add as a Bearer Authorization header +// - jq-expression : an optional jq-expression (https://jqplay.org) +// - sort-object : a sort object to sort the result +// - hasBigInt : a boolean indicating if it should convert Int64 to string +// - tokenPrefix : a prefix, defaults to 'Bearer' (v5.0.0) +``` + +--- + +## API REST con autenticación por token protegido + +Llama a una API REST con un bearer token tomado de una credencial almacenada, de modo que nunca aparece en el formulario: + +```javascript +fn.fnRestJwtSecure( + 'get', + 'https://resturl/api/', + '', + 'name_of_credential_in_database', + '[.records[] | {name:.name, age:.age}]', + ['age','name'], + false +) + +// output : a full json object coming from rest, json transformed with jq, result sorted, first by age, then by name + +// fn.fnRestJwtSecure(method,url,body,token,jq-expression,sort-object) +// - method : get,post,put,delete +// - url : url to restapi (can for example contain a placeholder like 'https://$(serverfield.fqdn)/api/' +// - body : in case of post and put +// - credential_name : Encrypted credential will be looked up. The password will be the token. +// - jq-expression : an optional jq-expression (https://jqplay.org) +// - sort-object : a sort object to sort the result +// - hasBigInt : a boolean indicating if it should convert Int64 to string +// - tokenPrefix : a prefix, defaults to 'Bearer' (v5.0.0) +``` + +--- + +## API REST con cabeceras personalizadas + +Llama a una API REST con cabeceras propias, para cualquier otro tipo de autenticación: + +```javascript +fn.fnRestAdvanced( + 'get', + 'https://resturl/api/', + '', + {'a_custom_http_header':'your_value','Authorization':'basic base64(my_rest_credential)'}, + '.records[].name', + {name:{ignoreCase:true,direction:'desc'}}, + false, + false +) + +// output : a full json object coming from rest, json transformed with jq, result sorted descending by name + +// fn.fnRestAdvanced(method,url,body,{myheader:'value'},jq-expression,sort-object) +// - method : get,post,put,delete +// - url : url to restapi (can for example contain a placeholder like 'https://$(serverfield.fqdn)/api/' +// - body : in case of post and put +// - headers: an object of headers +// - jq-expression : an optional jq-expression (https://jqplay.org) +// - sort-object : a sorting object to order the results +// - hasBigInt : a boolean indicating if it should convert Int64 to string +// - raw : return data and response_headers + +// 3 function-placeholders are allowed in the headers: +// - base64(credential_name) : will create a base64 encoded "username:password". prefix with "basic" if required +// - username(credential_name) : will add the username of the credential +// - password(credential_name) : will add the password of the credential +``` + +--- + +## Certificados y tiempos de espera + +Desde 7.3.0, cada función auxiliar REST (`fnRestBasic`, `fnRestAdvanced`, `fnRestJwt`, `fnRestJwtSecure`) comprueba el certificado +TLS del endpoint y deja de esperar tras un tiempo fijado: + +| Ajuste | Qué cambia | +|---|---| +| [`REST_IGNORE_CERTS`](../customization/security.html#env_REST_IGNORE_CERTS) | `1` omite la comprobación del certificado. Hasta 7.2.0 nunca se comprobaba; mantenlo en `0` y confía en una CA privada con `NODE_EXTRA_CA_CERTS` en su lugar | +| [`REST_TIMEOUT_SECONDS`](../customization/security.html#env_REST_TIMEOUT_SECONDS) | Cuánto espera una llamada una respuesta, 60 segundos por defecto; un endpoint que nunca responde hace fallar el campo en lugar de bloquearlo | + +Desde 7.3.0, un campo cuyo endpoint tiene un certificado autofirmado falla con un error de certificado hasta que se configura uno de los dos. +[Llamadas REST y jq](../customization/security.html#rest-calls-and-jq) describe ambos, y cómo confiar en una CA privada para todas las llamadas REST a la vez. +[`fn.fnParseHtmlWithRegex`](remote.html#parse-html-page) no es una función auxiliar REST: ignora ambos ajustes, siempre comprueba el +certificado y espera como máximo 30 segundos. diff --git a/i18n/es/_build_forms/expressions/sorting.md b/i18n/es/_build_forms/expressions/sorting.md new file mode 100644 index 0000000..0613d95 --- /dev/null +++ b/i18n/es/_build_forms/expressions/sorting.md @@ -0,0 +1,51 @@ +--- +layout: default +title: Ordenación +parent: Expresiones +nav_order: 5 +translated_from: 56b0801754aa08a0a6c4941b373bb43d10982b98 +--- + +# Ordenación +{: .no_toc } + +Ordena arrays con un objeto de ordenación +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Ordenar un array + +Ordena un array procedente de una expresión, una API REST o una base de datos con un objeto de ordenación: + +```javascript +fn.fnSort(['a','b','z','q','c'],{'':{direction:'asc'}}) + +// output : sorts the flat array + +// fn.fnSort(arrayinput,sort-object) +// - array, coming from expression or rest or database +// - sort-object : a sorting object to order the results +``` + +--- + +## El objeto de ordenación + +Las funciones REST (`fn.fnRestBasic`, `fn.fnRestAdvanced`, `fn.fnRestJwt`, `fn.fnRestJwtSecure`) aceptan este objeto de ordenación +como argumento. Para cualquier otro resultado, como `fn.fnJq` o la lectura de un archivo, envuélvelo en `fn.fnSort`. + +```javascript +// The sorting object can have multiple forms, for example : + +'name' // will sort ascending on property name +['name','email'] // will first sort on name, then on email +{name:{direction:'desc'}} // will sort on name descending +{name:{ignoreCase:true}} // will sort on name ascending, ignoring case +['name',{email:{ignoreCase:true,direction:'asc'}}] // will sort on name first, then on email, ignoring case +{'':{}} // a special case for sorting a flat array which has no headername (properties) +{'':{direction:'desc'}} // sort a flat array descending +``` diff --git a/i18n/es/_build_forms/formfields/checkbox.md b/i18n/es/_build_forms/formfields/checkbox.md new file mode 100644 index 0000000..609bc07 --- /dev/null +++ b/i18n/es/_build_forms/formfields/checkbox.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Checkbox +parent: Campos de formulario +nav_order: 8 +translated_from: 6dac54ad9b2bf8aed718b116f4a9d9be4a8184e6 +--- + +# Campo Checkbox +{: .no_toc } + +Un valor verdadero o falso, para activar una opción o fijar un indicador +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `checkbox`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "checkbox" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `checkbox`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign checkbox_type = type_choice.choices | where: "name", "checkbox" | first %} + {%- if checkbox_type.examples %} + {%- for example in checkbox_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/datetime.md b/i18n/es/_build_forms/formfields/datetime.md new file mode 100644 index 0000000..792beb9 --- /dev/null +++ b/i18n/es/_build_forms/formfields/datetime.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Datetime +parent: Campos de formulario +nav_order: 3 +translated_from: be604f929c562ff048c03f1c17e1201a053179b4 +--- + +# Campo Datetime +{: .no_toc } + +Un selector de fecha, hora, semana, mes o año +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `datetime`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "datetime" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `datetime`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign datetime_type = type_choice.choices | where: "name", "datetime" | first %} + {%- if datetime_type.examples %} + {%- for example in datetime_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/enum.md b/i18n/es/_build_forms/formfields/enum.md new file mode 100644 index 0000000..8aa6df7 --- /dev/null +++ b/i18n/es/_build_forms/formfields/enum.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Enum +parent: Campos de formulario +nav_order: 10 +translated_from: 011ba48e2eebc9a26159fdf981d89b86b43de630 +--- + +# Campo Enum +{: .no_toc } + +Un desplegable con búsqueda, para una o varias opciones +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `enum`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "enum" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `enum`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign enum_type = type_choice.choices | where: "name", "enum" | first %} + {%- if enum_type.examples %} + {%- for example in enum_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/expression.md b/i18n/es/_build_forms/formfields/expression.md new file mode 100644 index 0000000..c65c34f --- /dev/null +++ b/i18n/es/_build_forms/formfields/expression.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Expression +parent: Campos de formulario +nav_order: 11 +translated_from: 30a3e37e27e9bc9e8472a027b6eb90c3260e8248 +--- + +# Campo Expression +{: .no_toc } + +Un valor calculado con JavaScript, en el navegador o en el servidor +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `expression`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "expression" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `expression`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign expression_type = type_choice.choices | where: "name", "expression" | first %} + {%- if expression_type.examples %} + {%- for example in expression_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/file.md b/i18n/es/_build_forms/formfields/file.md new file mode 100644 index 0000000..d4513b9 --- /dev/null +++ b/i18n/es/_build_forms/formfields/file.md @@ -0,0 +1,131 @@ +--- +layout: default +title: File +parent: Campos de formulario +nav_order: 5 +translated_from: 664227c4b88ae49a9a43d37b5c0f3ce1b50d3b40 +--- + +# Campo File +{: .no_toc } + +Sube archivos, como archivos de configuración o certificados +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `file`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "file" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `file`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign file_type = type_choice.choices | where: "name", "file" | first %} + {%- if file_type.examples %} + {%- for example in file_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/html.md b/i18n/es/_build_forms/formfields/html.md new file mode 100644 index 0000000..e5bd5d1 --- /dev/null +++ b/i18n/es/_build_forms/formfields/html.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Html +parent: Campos de formulario +nav_order: 4 +translated_from: 473e446a98ddcabefb726ff75033eb3ad7135fc4 +--- + +# Campo Html +{: .no_toc } + +Contenido HTML enriquecido dentro de un formulario +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `html`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "html" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `html`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign html_type = type_choice.choices | where: "name", "html" | first %} + {%- if html_type.examples %} + {%- for example in html_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/index.md b/i18n/es/_build_forms/formfields/index.md new file mode 100644 index 0000000..9376f7d --- /dev/null +++ b/i18n/es/_build_forms/formfields/index.md @@ -0,0 +1,62 @@ +--- +layout: default +title: Campos de formulario +nav_order: 2 +has_children: true +has_toc: false +permalink: /formfields/ +translated_from: f87478aaaa3db6bc7523c8f49e4aea097b90b003 +--- + +# Campos de formulario +{: .no_toc } + +Los controles de entrada de un formulario +{: .fs-6 .fw-300 } + +--- + +Para autores de formularios: los tipos de campo que puede contener un formulario y las propiedades que admite cada uno. + +Los campos de formulario definen los controles de entrada de un formulario. Cada tipo de campo tiene sus propias propiedades y su propio comportamiento para recoger un tipo concreto de datos. + +Elige el tipo de campo que corresponda a los datos que quieres recoger, desde texto simple hasta listas estructuradas. + +--- + +## Propiedades comunes + +Todos los campos de formulario comparten dos propiedades fundamentales: + +- **name** (string, obligatorio): el identificador único del campo +- **type** (string, obligatorio): el tipo de campo, que determina cuál de las páginas siguientes describe las propiedades disponibles + +Cada tipo de campo añade propiedades propias de su función. Selecciona un tipo de campo en la barra lateral de navegación o en la tabla siguiente para abrir la referencia completa de sus propiedades: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield = form_object.help | where: "name", "Formfield" | first %} +{% assign formfile = formfield.items | where: "name", "type" | first %} + + + + + + + + + +{% for type in formfile.choices %} + + + + + +{% endfor %} + +
Tipo de campoDescripción
{{ type.name }}{{ type.description | markdownify }}
diff --git a/i18n/es/_build_forms/formfields/list.md b/i18n/es/_build_forms/formfields/list.md new file mode 100644 index 0000000..db8fadc --- /dev/null +++ b/i18n/es/_build_forms/formfields/list.md @@ -0,0 +1,396 @@ +--- +layout: default +title: List +parent: Campos de formulario +nav_order: 13 +translated_from: b6209effaf1cc9783a27c36669004a239751fb3e +--- + +# Campo List +{: .no_toc } + +Una tabla de filas, cada una editada en un subformulario +{: .fs-6 .fw-300 } + +--- + +Un campo list muestra una tabla de filas, cada una creada y editada en un panel de **subformulario** propio (un patrón de navegación en profundidad). Sustituye al campo `table`, que se eliminó en 7.0.0. + +--- + +## Características principales + +El campo list ofrece las siguientes capacidades: + +- Cada fila se edita en un subformulario a pantalla completa, con acceso a todos los tipos de campo, incluidas las listas anidadas. +- El subformulario es un formulario propio, de tipo `subform`, y se puede **reutilizar en varios campos list** de distintos formularios (p. ej., un único subformulario `Address` compartido por clientes, proveedores, pedidos…). +- Las propiedades de marcador (`insertMarker`, `updateMarker`, `deleteMarker`) permiten patrones de playbook idempotentes. +- Con `allowDelete: false`, las filas preexistentes están protegidas; solo se pueden eliminar las filas añadidas en la sesión actual (las que llevan el `insertMarker`). +- La propiedad `columns` controla qué campos del subformulario aparecen como columnas en la tabla resumen. +- La salida es siempre un array de objetos. `model`, `output`, `outputObject` y `valueColumn` declarados en los campos del subformulario se respetan al generar las extravars. + +--- + +## Declaración del subformulario + +Un subformulario es un formulario propio con `type: subform`, en un archivo de la carpeta de formularios como cualquier otro formulario. Acepta el mismo array `fields` que cualquier otro formulario, pero nunca aparece en la lista de mosaicos de formularios y nunca se envía directamente. + +```yaml +- name: Address + type: subform + fields: + - name: street + type: text + label: Street + required: true + - name: city + type: text + label: City + required: true + - name: country + type: text + label: Country +``` + +--- + +## Propiedades + +Las propiedades disponibles para un campo `list`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + {% assign has_list_props = false %} + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- if var.with_types == nil or item_types contains "list" %} + {%- assign has_list_props = true %} + {%- endif %} + {% endfor %} + {% if has_list_props %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "list" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endif %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ list +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} + {% if var.examples.size > 0 %} +
+ Ejemplos + {% for ex in var.examples %} +

{{ ex.name }}

+
{{ ex.code }}
+ {% endfor %} +
+ {% endif %} +
+ +{%- endif %} + +--- + +## Ejemplos + +### Subformulario de dirección compartido + +Un subformulario `Address` reutilizado por dos campos list del mismo formulario: + +```yaml +- name: Address + type: subform + fields: + - name: street + type: text + label: Street + required: true + - name: city + type: text + label: City + required: true + - name: country + type: text + label: Country + +- name: Create customer + type: ansible + playbook: customer.yaml + roles: [default] + fields: + - name: name + type: text + label: Customer name + required: true + - name: addresses + type: list + label: Addresses + subform: Address + columns: + - city + - country + titleAdd: Add address + titleEdit: Edit address + allowInsert: true + allowDelete: true +``` + +### Seguimiento con marcadores (idempotente) + +Las filas precargadas están protegidas, y los marcadores indican al playbook qué filas son nuevas, se han modificado o se han eliminado: + +```yaml +- name: people + type: list + label: People + subform: Person + allowDelete: false # preloaded rows are protected + insertMarker: added # session-added rows remain removable + updateMarker: updated + deleteMarker: removed + columns: + - name + - email +``` + +### Rellenar filas de antemano + +Una lista se puede rellenar de antemano desde tres fuentes. En todos los casos, las filas resultantes deben ser objetos cuyas claves coincidan con los nombres de campo del subformulario referenciado. + +#### `default` estático + +Filas escritas en el propio formulario: + +```yaml +- name: addresses + type: list + label: Addresses + subform: Address + columns: + - city + - country + default: + - street: Main 1 + city: Brussels + country: BE + - street: Side 22 + city: Paris + country: FR +``` + +#### Desde una API REST (`expression`) + +Filas cargadas desde una API REST mediante una expresión del lado del servidor: + +```yaml +- name: users + type: list + label: Users + subform: User + columns: + - username + - email + expression: fn.fnRestBasic('get','https://api.example.com/users','','my_api_cred','') +``` + +#### Desde una consulta a base de datos (`dbConfig` + `query`) + +Filas cargadas desde una consulta a base de datos: + +```yaml +- name: servers + type: list + label: Servers + subform: Server + columns: + - hostname + - role + dbConfig: CMDB_CONN + query: SELECT hostname, role FROM servers WHERE active = 1 +``` + +#### Desde una expresión local (`runLocal`) + +Filas calculadas en el navegador mediante una expresión local: + +```yaml +- name: ports + type: list + label: Open ports + subform: Port + columns: + - port + - protocol + runLocal: true + expression: | + [ + { port: 22, protocol: 'tcp' }, + { port: 80, protocol: 'tcp' }, + { port: 443, protocol: 'tcp' } + ] +``` + +### Listas anidadas + +Un subformulario puede contener su propio campo list, para tener una lista dentro de cada fila: + +```yaml +- name: Address + type: subform + fields: + - name: street + type: text + label: Street + - name: city + type: text + label: City + +- name: Person + type: subform + fields: + - name: name + type: text + label: Name + required: true + - name: addresses + type: list + label: Addresses + subform: Address + columns: + - street + - city + +- name: My Form + type: ansible + playbook: people.yaml + roles: [default] + fields: + - name: people + type: list + label: People + subform: Person + columns: + - name + - addresses +``` + +### Acceder a los datos del formulario padre con `__parent__` + +Cuando se abre el editor de una fila del subformulario, AnsibleForms inyecta una variable `__parent__` en el subformulario. Contiene una instantánea de **todos los valores de los campos del formulario padre**, incluidas las constantes y las vars. + +Haz referencia a ella con la sintaxis de expresión estándar `$(...)` en las definiciones de campos del subformulario: + +```yaml +- name: NodeConfig + type: subform + fields: + - name: node_name + type: text + label: Node name + required: true + + - name: node_type + type: enum + # only offer gpu in production environments (parent field) + expression: | + '$(__parent__.environment)' === 'production' + ? ['standard', 'high-memory', 'gpu'] + : ['standard', 'high-memory'] + runLocal: true + default: __auto__ + +- name: Deploy cluster + type: ansible + playbook: deploy.yml + roles: [default] + fields: + - name: environment + type: enum + values: [dev, staging, production] + required: true + + - name: nodes + type: list + subform: NodeConfig + columns: [node_name, node_type] +``` + +{: .note } +> `__parent__` es una ayuda **solo del frontend**. Se elimina de las extravars de Ansible y nunca se envía al playbook. + diff --git a/i18n/es/_build_forms/formfields/number.md b/i18n/es/_build_forms/formfields/number.md new file mode 100644 index 0000000..39d7038 --- /dev/null +++ b/i18n/es/_build_forms/formfields/number.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Number +parent: Campos de formulario +nav_order: 6 +translated_from: d9e124b198b3304f9f9894919421d7f938676e75 +--- + +# Campo Number +{: .no_toc } + +Un valor entero o decimal, como un puerto o una cantidad +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `number`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "number" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `number`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign number_type = type_choice.choices | where: "name", "number" | first %} + {%- if number_type.examples %} + {%- for example in number_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/password.md b/i18n/es/_build_forms/formfields/password.md new file mode 100644 index 0000000..162a6db --- /dev/null +++ b/i18n/es/_build_forms/formfields/password.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Password +parent: Campos de formulario +nav_order: 7 +translated_from: 2fa25c0e69e736fb4edc9a31a8c88e3020294fa6 +--- + +# Campo Password +{: .no_toc } + +Un valor oculto, para contraseñas, secretos y tokens +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `password`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "password" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `password`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign password_type = type_choice.choices | where: "name", "password" | first %} + {%- if password_type.examples %} + {%- for example in password_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/radio.md b/i18n/es/_build_forms/formfields/radio.md new file mode 100644 index 0000000..3359d50 --- /dev/null +++ b/i18n/es/_build_forms/formfields/radio.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Radio +parent: Campos de formulario +nav_order: 9 +translated_from: b1deb8f285c46f2be77ed0f920710248c998d6f2 +--- + +# Campo Radio +{: .no_toc } + +Una opción de un conjunto de opciones mutuamente excluyentes +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `radio`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "radio" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `radio`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign radio_type = type_choice.choices | where: "name", "radio" | first %} + {%- if radio_type.examples %} + {%- for example in radio_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/text.md b/i18n/es/_build_forms/formfields/text.md new file mode 100644 index 0000000..89fd562 --- /dev/null +++ b/i18n/es/_build_forms/formfields/text.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Text +parent: Campos de formulario +nav_order: 1 +translated_from: fa7de095c8a640826ee0825522ce2249ace822f0 +--- + +# Campo Text +{: .no_toc } + +Una sola línea de texto, como un nombre o un nombre de host +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `text`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "text" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `text`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign text_type = type_choice.choices | where: "name", "text" | first %} + {%- if text_type.examples %} + {%- for example in text_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/textarea.md b/i18n/es/_build_forms/formfields/textarea.md new file mode 100644 index 0000000..2441b6b --- /dev/null +++ b/i18n/es/_build_forms/formfields/textarea.md @@ -0,0 +1,131 @@ +--- +layout: default +title: Textarea +parent: Campos de formulario +nav_order: 2 +translated_from: 9ce412065c2c12d9f6b76219033303d20447167e +--- + +# Campo Textarea +{: .no_toc } + +Texto de varias líneas, como una descripción o un fragmento de configuración +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `textarea`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "textarea" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `textarea`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign textarea_type = type_choice.choices | where: "name", "textarea" | first %} + {%- if textarea_type.examples %} + {%- for example in textarea_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} diff --git a/i18n/es/_build_forms/formfields/yaml.md b/i18n/es/_build_forms/formfields/yaml.md new file mode 100644 index 0000000..e95d880 --- /dev/null +++ b/i18n/es/_build_forms/formfields/yaml.md @@ -0,0 +1,188 @@ +--- +layout: default +title: YAML +parent: Campos de formulario +nav_order: 14 +translated_from: c7439c165a54cf525a91d0458a268a66ee8a136f +--- + +# Campo YAML +{: .no_toc } + +Datos estructurados en un editor YAML, con validación e importación de archivos +{: .fs-6 .fw-300 } + +--- + +## Propiedades + +Las propiedades disponibles para un campo `yaml`, agrupadas por finalidad: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign formfield_object = form_object.help | where: "name", "Formfield" | first %} + +{%- if formfield_object and formfield_object.items %} + + + + + + + + + + {% assign groups = formfield_object.items | map: "group" | compact | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = formfield_object.items | where: "group", group %} + + + + {% for var in group_properties %} + {%- assign item_types = var.with_types | split: ", " %} + {%- assign type_match = false %} + {%- if var.with_types %} + {%- if item_types contains "yaml" %} + {%- assign type_match = true %} + {%- endif %} + {%- else %} + {%- assign type_match = true %} + {%- endif %} + {%- if type_match %} + + + + + {%- endif %} + {% endfor %} + {% endfor %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type }} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.name == "type" %} +
+ Valor:
+ {{ page.title | downcase }} +
+ {% elsif var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de campos `yaml`, en YAML: + +{%- assign type_choice = formfield_object.items | where: "name", "type" | first %} +{%- if type_choice.choices %} + {%- assign yaml_type = type_choice.choices | where: "name", "yaml" | first %} + {%- if yaml_type.examples %} + {%- for example in yaml_type.examples %} + +### {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición del campo, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` + {%- endfor %} + {%- endif %} +{%- endif %} + +{%- endif %} + +--- + +## Acceder a los datos del formulario padre con `__parent__` + +Cuando un campo `yaml` usa el modo `subform`, AnsibleForms inyecta una variable `__parent__` en el subformulario. Contiene una instantánea de **todos los valores de los campos del formulario padre** en el momento en que se abre el editor, incluidas las constantes y las vars. + +Haz referencia a ella con la sintaxis de expresión estándar `$(...)` en las definiciones de campos del subformulario: + +```yaml +- name: NetworkConfig + type: subform + fields: + - name: interface + type: text + label: Interface + required: true + + - name: vlan + type: enum + label: VLAN + # filter available VLANs based on the parent's selected region + query: "select id,name from vlans where region='$(__parent__.region)'" + dbConfig: + name: MYDB + type: mysql + valueColumn: id + placeholderColumn: name + + - name: mtu + type: number + label: MTU + # default to 9000 in production, 1500 elsewhere + default: "'$(__parent__.environment)' === 'production' ? 9000 : 1500" + evalDefault: true + +- name: Configure server + type: ansible + playbook: configure.yml + roles: [default] + fields: + - name: environment + type: enum + values: [dev, staging, production] + + - name: region + type: enum + values: [eu-west-1, us-east-1, ap-southeast-1] + + - name: nic + type: yaml + label: Network interface + subform: NetworkConfig +``` + +{: .note } +> `__parent__` es una ayuda **solo del frontend**. Se elimina de las extravars de Ansible y nunca se envía al playbook. diff --git a/i18n/es/_build_forms/forms/ansible.md b/i18n/es/_build_forms/forms/ansible.md new file mode 100644 index 0000000..75363b4 --- /dev/null +++ b/i18n/es/_build_forms/forms/ansible.md @@ -0,0 +1,110 @@ +--- +layout: default +title: Formularios de Ansible +parent: Formularios +nav_order: 2 +translated_from: 804c2e6a40f7cec1287a7aa1df63085190c4ac36 +--- + +# Formularios de Ansible +{: .no_toc } + +Formularios que ejecutan un playbook de Ansible local +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign ansible_choice = type_prop.choices | where: "name", "ansible" | first %} + +{{ ansible_choice.description | markdownify }} + +{% if ansible_choice.examples.size > 0 %} +{% for e in ansible_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: ansible`, además de las [propiedades comunes de los formularios](common.html). + +{% assign ansible_props = form_object.items | where_exp: "p", "p.with_types contains 'ansible'" %} +{% assign ansible_groups = ansible_props | map: "group" | uniq | sort_natural %} +{% for group in ansible_groups %} +{% assign group_props = ansible_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/forms/approval.md b/i18n/es/_build_forms/forms/approval.md new file mode 100644 index 0000000..a534dd1 --- /dev/null +++ b/i18n/es/_build_forms/forms/approval.md @@ -0,0 +1,101 @@ +--- +layout: default +title: Puntos de aprobación +parent: Formularios +nav_order: 8 +translated_from: c2784dd4ac7ac41ef7f18fafdc4a6c468575c405 +--- + +# Puntos de aprobación +{: .no_toc } + +Pausa un trabajo hasta que alguien lo apruebe +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Cómo funciona + +Un punto de aprobación pausa el trabajo en su posición dentro del formulario hasta que un aprobador responde: + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign approval_object = form_object.help | where: "name", "Approval point" | first %} + +{{ approval_object.description | markdownify }} + +--- + +## Aprobar tu propio trabajo + +Desde 7.3.0, el usuario que lanzó un trabajo no puede aprobarlo, aunque tenga uno de los roles de la aprobación, así que hace falta una segunda persona; un administrador siempre puede aprobar, también sus propios trabajos. +Quien lo lanzó sí puede rechazar (retirar) su propio trabajo. Pon `allowSelf: true` cuando también pueda aprobarlo, por ejemplo en un formulario de prueba: + +```yaml +approval: + title: Approve the change + message: Please approve this change + roles: + - approvers + allowSelf: true +``` + +--- + +## Atributos + +Los atributos de un punto de aprobación: + + + + + + + + + + {% for var in approval_object.items %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type}} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{var.version}}{% endif %} +
+

+ {{var.short}}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+
+ +--- + +## Ejemplos + +Ejemplos de puntos de aprobación, en YAML: + +{% for example in approval_object.examples %} +### {{ forloop.index }}) {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` +{% endfor %} diff --git a/i18n/es/_build_forms/forms/awx.md b/i18n/es/_build_forms/forms/awx.md new file mode 100644 index 0000000..3dd8e3c --- /dev/null +++ b/i18n/es/_build_forms/forms/awx.md @@ -0,0 +1,110 @@ +--- +layout: default +title: Formularios de AWX +parent: Formularios +nav_order: 3 +translated_from: f5e3e7166f35525eb66bcbd7e085776f44d37c33 +--- + +# Formularios de AWX +{: .no_toc } + +Formularios que lanzan una plantilla de job de AWX, AAP o Ascender +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign awx_choice = type_prop.choices | where: "name", "awx" | first %} + +{{ awx_choice.description | markdownify }} + +{% if awx_choice.examples.size > 0 %} +{% for e in awx_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: awx`, además de las [propiedades comunes de los formularios](common.html). + +{% assign awx_props = form_object.items | where_exp: "p", "p.with_types contains 'awx'" %} +{% assign awx_groups = awx_props | map: "group" | uniq | sort_natural %} +{% for group in awx_groups %} +{% assign group_props = awx_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (por defecto) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Por defecto: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/forms/common.md b/i18n/es/_build_forms/forms/common.md new file mode 100644 index 0000000..24d0481 --- /dev/null +++ b/i18n/es/_build_forms/forms/common.md @@ -0,0 +1,104 @@ +--- +layout: default +title: Propiedades comunes +parent: Formularios +nav_order: 1 +translated_from: ce05875209a1cab9ae70d0168723f3aa615e6635 +--- + +# Propiedades comunes de los formularios +{: .no_toc } + +Propiedades que comparten todos los tipos de formulario +{: .fs-6 .fw-300 } + +--- + +Estas propiedades se aplican a **todos los tipos de formulario**: `ansible`, `awx`, `aap`, `ascender`, `semaphore`, `rundeck`, `jenkins`, +`workflow`, `multistep` (obsoleto) y `subform`. + +Las demás propiedades son específicas de cada tipo y se documentan en la página del tipo de formulario correspondiente. + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign common_props = form_object.items | where_exp: "p", "p.with_types == nil" %} + +{{ form_object.description | markdownify }} + +--- + +## Propiedades + +Las propiedades que comparten todos los tipos de formulario: + + + + + + + + + + {% for var in common_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} + {% if var.unique == true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.docsObjectLink %} + {% assign link_start = var.docsObjectLink | slice: 0 %} + 🔗 + {% endif %} + {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} + {% if var.docsObjectLink %} + + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado:
+ {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% endif %} + {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} +
diff --git a/i18n/es/_build_forms/forms/index.md b/i18n/es/_build_forms/forms/index.md new file mode 100644 index 0000000..82d29a1 --- /dev/null +++ b/i18n/es/_build_forms/forms/index.md @@ -0,0 +1,87 @@ +--- +layout: default +title: Formularios +nav_order: 3 +has_children: true +has_toc: false +permalink: /forms/ +translated_from: 8cf41438dcd6f75b145856a890b8bbbcd674c9fc +--- + +# Formularios +{: .no_toc } + +Define los formularios que rellenan tus usuarios +{: .fs-6 .fw-300 } + +--- + +Para autores de formularios: cómo se escriben los formularios en YAML, desde dónde se cargan y las propiedades de cada tipo de formulario. + +Los formularios son el núcleo de AnsibleForms. Cada formulario es una interfaz web que recoge datos del usuario y, con ellos, ejecuta un +playbook de Ansible, una plantilla de AWX, AAP o Ascender, una plantilla de Semaphore, un job de Rundeck o Jenkins, o un +[workflow](../workflows/). + +Los formularios se definen en archivos YAML dentro de la carpeta de formularios. Cada archivo YAML contiene un solo formulario o una lista de formularios, y puedes usar subcarpetas para organizarlos. + +**VÍDEO**: [Crea tu primer formulario](https://www.youtube.com/watch?v=lIhYZ9Et5Ic) + +--- + +## Entender la estructura de un formulario + +Cada formulario de AnsibleForms se configura con un conjunto de propiedades. **[Propiedades comunes](common.html)** enumera las propiedades que se aplican a todos los tipos de formulario; las páginas de cada tipo describen las demás. + +--- + +## Cómo se cargan los formularios + +AnsibleForms carga los formularios desde las siguientes ubicaciones: + +1. **DESDE REPOSITORIOS** : todos los repositorios con el interruptor "Usar para formularios" activado (se admiten varios repositorios) + - Los formularios de todos los repositorios activados se combinan automáticamente. + - En cada repositorio se busca primero una subcarpeta `forms/`. + - Si no existe la subcarpeta `forms/`, se usa la raíz del repositorio. + - Los formularios creados en el diseñador que aún no se han subido con push se guardan en una carpeta de preparación + ([`FORMS_STAGING_PATH`](../customization/paths.html#env_FORMS_STAGING_PATH)) y se cargan junto con ellos. + +2. **DESDE LA CARPETA LOCAL** : la carpeta local `forms/` + ([`FORMS_FOLDER_PATH`](../customization/paths.html#env_FORMS_FOLDER_PATH)), cuando ningún repositorio tiene activado "Usar para formularios" + +{: .warning } +> **Nota:** Cuando "Usar para formularios" está activado en varios repositorios, todos sus formularios se combinan. Los nombres de los formularios deben ser únicos entre repositorios para evitar conflictos. + +--- + +## Dónde se ejecuta un formulario + +El tipo de un formulario decide en qué clase de [runner](../runners.html) se ejecuta su trabajo: un RTE para `ansible`, un runner de AWX para `awx`, +`aap` y `ascender`, y un runner de su propio tipo para `semaphore`, `rundeck` y `jenkins`. + +- `runner: ` en el formulario elige por nombre un runner de ese tipo; sin él, el trabajo se ejecuta en el runner predeterminado del tipo. +- Un runner desactivado en **Conexiones > Runners** hace fallar el trabajo, tanto si el formulario lo nombra como si es el predeterminado. El trabajo + nunca recurre a otro runner. +- Cuando uno de los roles del usuario enumera `runners` en `config.yaml`, el trabajo solo puede ejecutarse en un runner que enumere uno de esos roles, o se + rechaza. Los roles sin esa lista no lo limitan, y un administrador nunca está limitado. + +--- + +## En esta sección + +Las siguientes páginas describen en detalle las propiedades de los formularios: + +| Página | Qué cubre | +|---|---| +| [Propiedades comunes](common.html) | Propiedades que se aplican a todos los tipos de formulario (`name`, `description`, `help`, `type`, `fields`) | +| [Formularios de Ansible](ansible.html) | Propiedades específicas de `type: ansible` | +| [Formularios de AWX](awx.html) | Propiedades específicas de `type: awx` | +| [Formularios de Semaphore](semaphore.html) | Propiedades específicas de `type: semaphore`, que ejecuta una plantilla de Semaphore | +| [Formularios de Rundeck](rundeck.html) | Propiedades específicas de `type: rundeck`, que ejecuta un job de Rundeck | +| [Formularios de Jenkins](jenkins.html) | Propiedades específicas de `type: jenkins`, que ejecuta un build de un job de Jenkins | +| [Formularios de varios pasos](multistep.html) | Propiedades específicas de `type: multistep`, obsoleto en favor de `workflow` | +| [Formularios de workflow](workflow.html) | Propiedades específicas de `type: workflow`, que ejecuta un [workflow](../workflows/) | +| [Asistente](wizard.html) | Reparte los datos de un formulario en varias páginas (funciona sobre cualquier tipo de formulario ejecutable) | +| [Subform](subform.html) | Los subformularios solo usan las propiedades comunes | +| [Puntos de aprobación](approval.html) | Pausa un trabajo hasta que se apruebe (`approval`) | +| [Notificaciones](notifications.html) | Envía correos y mensajes a canales según el estado o los eventos del trabajo (`notifications`) | +| [Acciones según el estado del trabajo](job-status-action.html) | Actúa sobre el formulario cuando un trabajo cambia de estado (`onSubmit`, `onSuccess`, ...) | diff --git a/i18n/es/_build_forms/forms/jenkins.md b/i18n/es/_build_forms/forms/jenkins.md new file mode 100644 index 0000000..3455b3c --- /dev/null +++ b/i18n/es/_build_forms/forms/jenkins.md @@ -0,0 +1,110 @@ +--- +layout: default +title: Formularios de Jenkins +parent: Formularios +nav_order: 3.3 +translated_from: ff34be9b166e1c7956605787e9e6addc03bc2c57 +--- + +# Formularios de Jenkins +{: .no_toc } + +Formularios que ejecutan un build de un job de Jenkins +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign jenkins_choice = type_prop.choices | where: "name", "jenkins" | first %} + +{{ jenkins_choice.description | markdownify }} + +{% if jenkins_choice.examples.size > 0 %} +{% for e in jenkins_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: jenkins`, además de las [propiedades comunes de los formularios](common.html). + +{% assign jenkins_props = form_object.items | where_exp: "p", "p.with_types contains 'jenkins'" %} +{% assign jenkins_groups = jenkins_props | map: "group" | uniq | sort_natural %} +{% for group in jenkins_groups %} +{% assign group_props = jenkins_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/forms/job-status-action.md b/i18n/es/_build_forms/forms/job-status-action.md new file mode 100644 index 0000000..7ceb0d7 --- /dev/null +++ b/i18n/es/_build_forms/forms/job-status-action.md @@ -0,0 +1,86 @@ +--- +layout: default +title: Acciones según el estado del trabajo +parent: Formularios +nav_order: 10 +translated_from: c374578652c98b4c2c68f068809f16c22e304147 +--- + +# Acciones según el estado del trabajo +{: .no_toc } + +Actúa sobre el formulario cuando su trabajo se envía, termina con éxito, falla o finaliza +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign jobstatus_object = form_object.help | where: "name", "Job Status Action" | first %} + +{{ jobstatus_object.description | markdownify }} + +Están disponibles los siguientes hooks: + +- `onSubmit` - se activa al enviar el formulario +- `onSuccess` - se activa cuando el trabajo termina con éxito +- `onFailure` - se activa cuando el trabajo falla +- `onFinish` - se activa cuando el trabajo finaliza, sea cual sea su estado +- `onAbort` - se activa cuando el trabajo se aborta + +--- + +## Atributos + +Los atributos de una acción según el estado del trabajo: + + + + + + + + + + {% for var in jobstatus_object.items %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type}} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{var.version}}{% endif %} +
+

+ {{var.short}}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+
+ +--- + +## Ejemplos + +Ejemplos de acciones según el estado del trabajo, en YAML: + +{% for example in jobstatus_object.examples %} +### {{ forloop.index }}) {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` +{% endfor %} diff --git a/i18n/es/_build_forms/forms/multistep.md b/i18n/es/_build_forms/forms/multistep.md new file mode 100644 index 0000000..858989f --- /dev/null +++ b/i18n/es/_build_forms/forms/multistep.md @@ -0,0 +1,240 @@ +--- +layout: default +title: Formularios de varios pasos +parent: Formularios +nav_order: 5 +translated_from: b509f2f7365a2066cabfe1061fdb48c12c33ccbd +--- + +# Formularios de varios pasos +{: .no_toc } + +Ejecuta varios playbooks o plantillas en secuencia desde un solo formulario +{: .fs-6 .fw-300 } + +--- + +{: .warning } +> `multistep` está obsoleto desde la 7 y se elimina en la 8: usa un [workflow](workflow.html) en su lugar. Un workflow hace todo lo que +> hace un multistep, con condiciones, valores propios por nodo, nodos de aprobación y ramas que se ejecutan en paralelo. + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign multistep_choice = type_prop.choices | where: "name", "multistep" | first %} +{% assign step_object = form_object.help | where: "name", "Step" | first %} +{% assign steps_prop = form_object.items | where: "name", "steps" | first %} + +{{ multistep_choice.description | markdownify }} + +{% if multistep_choice.examples.size > 0 %} +{% for e in multistep_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedad a nivel de formulario + +Un formulario de varios pasos define sus pasos en una única propiedad a nivel de formulario: + + + + + + + + + + + + + + +
AtributoComentarios
+ {{ steps_prop.name }}
+ {{ steps_prop.type }} / obligatorio +
+

{{ steps_prop.short }}

+ {{ steps_prop.description | markdownify }} +
+ +{% assign multistep_props = form_object.items | where_exp: "p", "p.with_types contains 'multistep'" %} + +Además de `steps`, un formulario de varios pasos acepta estas propiedades a nivel de formulario, documentadas en la página [Formularios de Ansible](ansible.html): +{% for p in multistep_props %}{% if p.name != "steps" %}[`{{ p.name }}`](ansible.html#ansible_{{ p.name }}){% if p.required == true %} (obligatoria){% endif %}{% unless forloop.last %}, {% endunless %}{% endif %}{% endfor %}. + +--- + +## Propiedades de los pasos + +{{ step_object.description | markdownify }} + + + + + + + + + + {% assign groups = step_object.items | map: "group" | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = step_object.items | where: "group",group %} + {% if group %} + + + + {% endif %} + {% for var in group_properties %} + + + + + {% endfor %} + {% endfor %} + {% if step_object.examples %} + + + + + + + {% endif %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type}} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{var.version}}{% endif %} +
+

+ {{var.short}}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado:
+ {{ var.default }} +
+ {% endif %} + {% if var.with_types!=nil %} +
+ Solo disponible con los tipos:
+ {{ var.with_types }} +

+
+ {% endif %} + {% for c in var.changelog %} +
+ {% if c.type == "added" %} +
+ Añadido + {{ c.version }} +
+ {% endif %} +

+ {{ c.description | markdownify }} +

+
+ {% endfor %} + {% if var.examples %} +

Ejemplos:

+ {% endif %} + {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} +
+ Ejemplos +
+ {% for e in step_object.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} +
+ +--- + +## Ejemplo + +Un formulario de varios pasos ejecuta varios trabajos en secuencia, y cada paso lanza su propio playbook o plantilla de AWX. Todos los pasos comparten los campos del formulario: por defecto, cada paso recibe las extravars completas, y la `key` de un paso lo limita a esa parte de ellas. + +### Crear y configurar un servidor + +Cada paso recibe su propia parte de las extravars: un paso de AWX crea el servidor, un paso de Ansible configura su red y un último paso siempre envía un informe: + +```yaml +name: Server Provisioning +type: multistep +roles: + - default +categories: + - Setup +steps: + - name: Create server + type: awx + template: Create VM + key: server # receives only the `server` part of the extravars + - name: Configure network + type: ansible + playbook: configure_network.yml + key: network # receives only the `network` part of the extravars + - name: Send report + type: ansible + playbook: email.yml + always: true # runs even when an earlier step failed +fields: + - name: server_name + type: text + model: server.name + - name: environment + type: enum + values: + - dev + - prod + model: server.environment + - name: ip_address + type: text + model: network.ip + - name: gateway + type: text + model: network.gateway +``` diff --git a/i18n/es/_build_forms/forms/notifications.md b/i18n/es/_build_forms/forms/notifications.md new file mode 100644 index 0000000..e30ce83 --- /dev/null +++ b/i18n/es/_build_forms/forms/notifications.md @@ -0,0 +1,120 @@ +--- +layout: default +title: Notificaciones +parent: Formularios +nav_order: 9 +translated_from: 3c44a310353f7884a0d2d1e3a827f8a757f7d25d +--- + +# Notificaciones +{: .no_toc } + +Envía un correo o un mensaje a un canal cuando un trabajo termina o le ocurre algo +{: .fs-6 .fw-300 } + +--- + +Las `notifications` de un formulario indican quién se entera de sus trabajos, y cuándo: + +- **Quién**: `recipients`, direcciones de correo, y `channels`, los nombres de + [canales de notificación](../notification-channels.html) (correo, Slack, Microsoft Teams, Telegram, un webhook). Se requiere + al menos uno de los dos. +- **Cuándo**: `onStatus`, cuando un trabajo termina (`success`, `failed`, `aborted`, `warning` o `any`), y `onEvent`, cuando + le ocurre algo a un trabajo (`launch`, `relaunch`, `delete`, `approve`, `reject` o `any`). + +```yaml +notifications: + recipients: + - ops@example.com + channels: + - ops-teams + onStatus: + - failed + onEvent: + - approve + - reject +``` + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign notifications_object = form_object.help | where: "name", "Notifications" | first %} + +{{ notifications_object.description | markdownify }} + +--- + +## Atributos + +Los atributos del objeto `notifications`: + + + + + + + + + + {% for var in notifications_object.items %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type}} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{var.version}}{% endif %} +
+

+ {{var.short}}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado:
+ {{ var.default }} +
+ {% endif %} +
+ +--- + +## Ejemplos + +Ejemplos de notificaciones, en YAML: + +{% for example in notifications_object.examples %} +### {{ forloop.index }}) {{ example.name }} + +{% if example.description %}{{ example.description | markdownify }}{% elsif example.short %}{{ example.short }}{% else %}La definición, en YAML:{% endif %} + +```yaml +{{ example.code }} +``` +{% endfor %} diff --git a/i18n/es/_build_forms/forms/rundeck.md b/i18n/es/_build_forms/forms/rundeck.md new file mode 100644 index 0000000..4a4508a --- /dev/null +++ b/i18n/es/_build_forms/forms/rundeck.md @@ -0,0 +1,110 @@ +--- +layout: default +title: Formularios de Rundeck +parent: Formularios +nav_order: 3.2 +translated_from: 2ea4d32ab60fa14c631358d16bc118485437ecb1 +--- + +# Formularios de Rundeck +{: .no_toc } + +Formularios que ejecutan un job de Rundeck +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign rundeck_choice = type_prop.choices | where: "name", "rundeck" | first %} + +{{ rundeck_choice.description | markdownify }} + +{% if rundeck_choice.examples.size > 0 %} +{% for e in rundeck_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: rundeck`, además de las [propiedades comunes de los formularios](common.html). + +{% assign rundeck_props = form_object.items | where_exp: "p", "p.with_types contains 'rundeck'" %} +{% assign rundeck_groups = rundeck_props | map: "group" | uniq | sort_natural %} +{% for group in rundeck_groups %} +{% assign group_props = rundeck_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/forms/semaphore.md b/i18n/es/_build_forms/forms/semaphore.md new file mode 100644 index 0000000..98f2bca --- /dev/null +++ b/i18n/es/_build_forms/forms/semaphore.md @@ -0,0 +1,110 @@ +--- +layout: default +title: Formularios de Semaphore +parent: Formularios +nav_order: 3.1 +translated_from: 34f55c2309cb70bf641b642cafc27b0e703c89e6 +--- + +# Formularios de Semaphore +{: .no_toc } + +Formularios que ejecutan una plantilla de Semaphore UI +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign semaphore_choice = type_prop.choices | where: "name", "semaphore" | first %} + +{{ semaphore_choice.description | markdownify }} + +{% if semaphore_choice.examples.size > 0 %} +{% for e in semaphore_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: semaphore`, además de las [propiedades comunes de los formularios](common.html). + +{% assign semaphore_props = form_object.items | where_exp: "p", "p.with_types contains 'semaphore'" %} +{% assign semaphore_groups = semaphore_props | map: "group" | uniq | sort_natural %} +{% for group in semaphore_groups %} +{% assign group_props = semaphore_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/forms/subform.md b/i18n/es/_build_forms/forms/subform.md new file mode 100644 index 0000000..031f927 --- /dev/null +++ b/i18n/es/_build_forms/forms/subform.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Subform +parent: Formularios +nav_order: 4 +translated_from: 25524e14dc072b76c778d7f92adbfbc5f82d3519 +--- + +# Subform +{: .no_toc } + +Un fragmento de formulario reutilizable para campos list, campos yaml y páginas de asistente +{: .fs-6 .fw-300 } + +--- + +Un subformulario es un fragmento de formulario reutilizable. No aparece en la vista de mosaicos y no se puede enviar directamente. En su lugar, se hace referencia a él desde: + +- Un campo [`list`](../formfields/list.html): abre el subformulario como editor de detalle para cada fila +- Un campo [`yaml`](../formfields/yaml.html) con la propiedad `subform`: abre el subformulario como editor de detalle para un único objeto +- Un paso de [`wizard`](wizard.html): muestra el subformulario como una página del asistente + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign subform_choice = type_prop.choices | where: "name", "subform" | first %} + +{{ subform_choice.description | markdownify }} + +{% if subform_choice.examples.size > 0 %} +{% for e in subform_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Los subformularios **no tienen propiedades específicas de tipo**. Solo usan las [propiedades comunes de los formularios](common.html): `name`, `description`, `help`, `type` y `fields`. + +Cuando un subformulario sirve como página de un asistente, su `description` y su `help` dan los valores predeterminados del `title` y el `help` del paso. También puede definir `showHelp` para desplegar esa ayuda al cargar la página. + +Las propiedades de ejecución (`playbook`, `template`, `roles`, `inventory`, etc.) no se aplican a los subformularios, porque un subformulario nunca se envía directamente. + diff --git a/i18n/es/_build_forms/forms/wizard.md b/i18n/es/_build_forms/forms/wizard.md new file mode 100644 index 0000000..6ca724c --- /dev/null +++ b/i18n/es/_build_forms/forms/wizard.md @@ -0,0 +1,210 @@ +--- +layout: default +title: Asistente +parent: Formularios +nav_order: 7 +translated_from: aec059391571165259ed43449b0450d0ef7f6cf2 +--- + +# Asistente +{: .no_toc } + +Reparte los datos de un formulario en páginas con Atrás y Siguiente +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign wizard_prop = form_object.items | where: "name", "wizard" | first %} +{% assign wizard_object = form_object.help | where: "name", "Wizard step" | first %} + +--- + +## ¿Qué es un asistente? + +Un **asistente** (`wizard`) convierte un único formulario en una secuencia de páginas. Cada página se genera a partir de un [`subform`](subform.html) y el usuario navega con los botones **Atrás** y **Siguiente**. La validación se aplica por página: el usuario no puede avanzar hasta que el paso actual sea válido, salvo que el paso esté marcado como `optional`. + +Al final de cada asistente se añade automáticamente una **página de revisión** de solo lectura. Muestra cada paso con los valores recogidos para que el usuario los confirme antes de enviar. La página de revisión **no** se declara en YAML; su título se toma del idioma activo. + +Al enviar, los **valores recogidos en todos los pasos se combinan en un único conjunto de extravars** y se envían al playbook, plantilla o workflow subyacente, exactamente como si el usuario hubiera rellenado un único formulario grande. + +Un formulario con `wizard` no necesita `fields` propios, pero no puede definir `enableForChat`, y un `subform` no puede tener a su vez un `wizard`. La [validación del lanzamiento](../launch-validation/rolling-it-out.html#wizard-forms) comprueba el lanzamiento de un asistente paso a paso. + +{: .note } +> Para ver en qué se diferencia un `wizard` de un workflow y cómo se combinan, consulta la entrada de las preguntas frecuentes [¿Qué diferencia hay entre un wizard y un workflow?](../faq.html#what-is-the-difference-between-a-wizard-and-a-workflow). + +--- + +## Propiedad a nivel de formulario + +Un asistente se activa con una única propiedad a nivel de formulario: + + + + + + + + + + + + + + +
AtributoComentarios
+ {{ wizard_prop.name }}
+ {{ wizard_prop.type }} + {% if wizard_prop.version %}
añadido en la versión {{ wizard_prop.version }}{% endif %} +
+

{{ wizard_prop.short }}

+ {{ wizard_prop.description | markdownify }} +
+ +{% if wizard_prop.examples %} +**Ejemplos:** +{% for e in wizard_prop.examples %} + +*{{ forloop.index }}) {{ e.name }}* +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades de los pasos del asistente + +{{ wizard_object.description | markdownify }} + + + + + + + + + + {% assign groups = wizard_object.items | map: "group" | uniq | sort_natural %} + {% for group in groups %} + {% assign group_properties = wizard_object.items | where: "group",group %} + {% if group %} + + + + {% endif %} + {% for var in group_properties %} + + + + + {% endfor %} + {% endfor %} + {% if wizard_object.examples %} + + + + + + + {% endif %} + +
AtributoComentarios
+ {{ group }} +
+ {{ var.name }}
+ {{ var.type}} + {% if var.required==true %} / obligatorio{% endif %} + {% if var.unique==true %} / único{% endif %} +
+ {% if var.version %}añadido en la versión {{var.version}}{% endif %} +
+

+ {{var.short}}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

+ {{ var.description | markdownify }} +

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado:
+ {{ var.default }} +
+ {% endif %} + {% for c in var.changelog %} +
+ {% if c.type == "added" %} +
+ Añadido + {{ c.version }} +
+ {% endif %} +

+ {{ c.description | markdownify }} +

+
+ {% endfor %} + {% if var.examples %} +

Ejemplos:

+ {% endif %} + {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} +
+ Ejemplos +
+ {% for e in wizard_object.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} +
+ +--- + +## Combinar un asistente con un workflow + +Un asistente puede ir sobre un formulario de [workflow](workflow.html). Cuando el `defaultModel` de cada paso del asistente coincide con la [`extra_vars_key`](../workflows/file.html#ansibleforms-own-keys) del nodo correspondiente del workflow, cada playbook o plantilla recibe solo los valores de su propia página del asistente. + +Las preguntas frecuentes incluyen un ejemplo completo y la estructura de las extravars combinadas: [¿Qué diferencia hay entre un wizard y un workflow?](../faq.html#what-is-the-difference-between-a-wizard-and-a-workflow) (sección *Combinado (asistente sobre un workflow)*). + +Un asistente sobre un formulario [`multistep`](multistep.html), que está obsoleto, funciona igual, con la [`key`](multistep.html#step_key) del paso en lugar de `extra_vars_key`. + +--- + +## Véase también + +Páginas relacionadas y entradas de las preguntas frecuentes: + +- [Subform](subform.html): las páginas del asistente se generan a partir de subformularios +- [Formularios de workflow](workflow.html): el equivalente del asistente en la capa de ejecución +- [`__parent__` en subformularios](../faq.html#how-do-i-access-parent-form-data-inside-a-subform): cómo hacer referencia a pasos anteriores del asistente desde un paso posterior diff --git a/i18n/es/_build_forms/forms/workflow.md b/i18n/es/_build_forms/forms/workflow.md new file mode 100644 index 0000000..2d968ae --- /dev/null +++ b/i18n/es/_build_forms/forms/workflow.md @@ -0,0 +1,111 @@ +--- +layout: default +title: Formularios de workflow +parent: Formularios +nav_order: 6 +translated_from: 84ff9fbe71945ce4437cf82034f80a6bfa8b3691 +--- + +# Formularios de workflow +{: .no_toc } + +Formularios que ejecutan uno de los workflows del diseñador +{: .fs-6 .fw-300 } + +--- + +{% assign help = site.data.help %} +{% assign formsyaml = help | where: "link", "forms" | first %} +{% assign form_object = formsyaml.help | where: "name", "Form" | first %} +{% assign type_prop = form_object.items | where: "name", "type" | first %} +{% assign workflow_choice = type_prop.choices | where: "name", "workflow" | first %} + +{{ workflow_choice.description | markdownify }} + +{% if workflow_choice.examples.size > 0 %} +{% for e in workflow_choice.examples %} +**{{ e.name }}** +{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +{% endfor %} +{% endif %} + +--- + +## Propiedades + +Propiedades específicas de `type: workflow`, además de las [propiedades comunes de los formularios](common.html). La +sección [Workflows](../workflows/) describe los propios archivos de workflow. + +{% assign workflow_props = form_object.items | where_exp: "p", "p.with_types contains 'workflow'" %} +{% assign workflow_groups = workflow_props | map: "group" | uniq | sort_natural %} +{% for group in workflow_groups %} +{% assign group_props = workflow_props | where: "group", group %} + +--- + +## {{ group | capitalize }} + + + + + + + + + + {% for var in group_props %} + + + + + {% endfor %} + +
AtributoComentarios
+ {{ var.name }}
+ {{ var.type }} + {% if var.required == true %} / obligatorio{% endif %} +
+ {% if var.version %}añadido en la versión {{ var.version }}{% endif %} +
+

+ {{ var.short }}
+ {% if var.allowed != nil %} + {{ var.allowed }} + {% endif %} +

+

{{ var.description | markdownify }}

+ {% if var.choices.size > 0 %} +
+ Opciones:
+
    + {% for c in var.choices %} +
  • + {% if c.name == var.default %} + {{ c.name }} (predeterminado) + {% else %} + {{ c.name }} + {% endif %} +
  • + {% endfor %} +
+
+ {% elsif var.default != nil %} +
+ Valor predeterminado: {{ var.default }} +
+ {% endif %} + {% if var.examples %} +

Ejemplos:

+ {% for e in var.examples %} +
+

{{ forloop.index }}) {{ e.name }}

+{% highlight yaml %} +{{ e.code }} +{% endhighlight %} +
+ {% endfor %} + {% endif %} +
+{% endfor %} diff --git a/i18n/es/_build_forms/launch-validation/file-uploads.md b/i18n/es/_build_forms/launch-validation/file-uploads.md new file mode 100644 index 0000000..dc744e0 --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/file-uploads.md @@ -0,0 +1,63 @@ +--- +layout: default +title: Subida de archivos +parent: Validación del lanzamiento +nav_order: 4 +translated_from: 5756d90b62ddac51996f0bdb05befd6bf5589e0d +--- + +# Subida de archivos +{: .no_toc } + +Cómo se validan los campos de archivo +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Un campo de archivo nunca se fía de lo que la petición de lanzamiento dice sobre un archivo. La validación del lanzamiento comprueba el propio archivo, en el +disco del servidor. + +--- + +## Cómo llega una subida a un trabajo + +Un archivo se sube antes de lanzar el formulario, en dos pasos: + +1. El navegador sube el archivo con `POST /api/v2/job/upload`. El servidor lo guarda en `UPLOAD_PATH`. +2. La petición de lanzamiento describe esa subida en su parte `files`. + +Después, la validación del lanzamiento contrasta la descripción con el disco: + +* El archivo debe existir dentro de `UPLOAD_PATH`. +* Su ruta y su tamaño se leen del disco, nunca de la petición. + +--- + +## Qué se acepta + +Un campo de archivo solo obtiene un valor de una subida verificada. Todo lo demás se descarta: + +* Se ignora una ruta que quien llama escriba en `rawFormData` o `extravars`. +* Un campo de archivo obligatorio sin una subida real se notifica como `missing`. + +--- + +## Reglas de archivo + +Las reglas de un campo de archivo se comprueban contra la subida real, con los tamaños en bytes: + +```yaml +- name: firmware + type: file + required: true + maxSize: 104857600 # 100 MB + regex: + expression: '\.bin$' + description: Upload a .bin firmware image +``` + +`minSize` y `maxSize` comparan el tamaño real del archivo, y `regex` su nombre. Con independencia de cualquier formulario, +`UPLOAD_MAX_GB` limita el tamaño de una sola subida (10 GB por defecto). diff --git a/i18n/es/_build_forms/launch-validation/index.md b/i18n/es/_build_forms/launch-validation/index.md new file mode 100644 index 0000000..5eb6c2e --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/index.md @@ -0,0 +1,78 @@ +--- +layout: default +title: Validación del lanzamiento +nav_order: 4 +has_children: true +has_toc: false +permalink: /launch-validation/ +translated_from: 6b86704480781edc0ab1c24c25993fc2e220abf3 +--- + +# Validación del lanzamiento +{: .no_toc } + +Comprueba en el servidor cada lanzamiento de un formulario +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores y autores de formularios: haz que el servidor compruebe cada lanzamiento, sus reglas y sus extravars, lo envíe quien lo envíe. + +La comprobación se aplica sea quien sea quien lance el formulario: el navegador, un script que llama a la API REST +o un agente de IA a través del servidor MCP. + +--- + +## Por qué existe + +Las reglas de un formulario (`required`, `regex`, `minValue`, `sameAs`, ...) siempre se han comprobado +en el navegador. Después, el navegador envía dos cosas a `POST /api/v2/job`: + +- `rawFormData`: los valores de los campos tal como los rellenó el usuario; +- `extravars`: lo que recibe el playbook, construido a partir de esos valores en el navegador. + +Sin validación del lanzamiento, el servidor se fía de ambos. Cualquiera con un token válido puede llamar +directamente a ese endpoint y enviar cualquier extravars, sin que se aplique ninguna regex, campo obligatorio +ni regla de lista. Para un formulario que solo se usa en el navegador puede ser aceptable; para un +formulario que otros sistemas lanzan por REST, significa que sus reglas no se aplican en absoluto. + +Además, el servidor no tiene forma de distinguir una petición del navegador de una enviada por un +script: ambas usan el mismo endpoint, el mismo token y el mismo cuerpo, y los usuarios pueden modificar +cualquier petición que envíe su propio navegador. Solo se puede confiar en una comprobación hecha en el servidor, y la validación +del lanzamiento es esa comprobación. + +--- + +## Las tres vías de entrada + +Cómo se valida un lanzamiento depende de cómo llega y del modo de validación del lanzamiento: + +| Lanzado por | Validado | Extravars con las que se ejecuta el trabajo | +|---|---|---| +| **Servidor MCP** (agente de IA) | siempre, en el servidor | siempre las construye el servidor | +| **Navegador** o **API REST**, validación del lanzamiento `off` | solo en el navegador | las que envió el navegador (o quien llama) | +| **Navegador** o **API REST**, validación del lanzamiento `log` | en el servidor, los rechazos solo se registran | las enviadas; las diferencias se registran | +| **Navegador** o **API REST**, validación del lanzamiento `enforce` | en el servidor, se rechaza si no es válido | **las construye el servidor** | + +El navegador, el servidor MCP y la validación del lanzamiento usan el mismo código para las reglas (el +motor de formularios compartido): las mismas reglas, en el mismo orden, con los mismos mensajes. Un error que +muestra el navegador lo rechaza el servidor con el mismo texto. + +--- + +## En esta sección + +Cada tema tiene su propia página: + +| Página | Qué cubre | +|---|---| +| [Activarlo](switching-it-on.html) | Para toda la instancia o para un formulario | +| [Registrar e imponer](log-and-enforce.html) | Qué hace cada modo con un lanzamiento que incumple las reglas | +| [Filas de una lista](list-rows-and-subforms.html) | Cómo se validan las filas de los campos de lista | +| [Subida de archivos](file-uploads.html) | Cómo se validan los campos de archivo | +| [Contraseñas](passwords.html) | Cómo se validan los campos de contraseña y cómo se mantienen fuera de las APIs | +| [Relanzar](relaunching.html) | Cómo se valida un trabajo relanzado | +| [Despliegue](rolling-it-out.html) | Llevar una instancia a enforce, y las limitaciones conocidas | diff --git a/i18n/es/_build_forms/launch-validation/list-rows-and-subforms.md b/i18n/es/_build_forms/launch-validation/list-rows-and-subforms.md new file mode 100644 index 0000000..fe3776f --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/list-rows-and-subforms.md @@ -0,0 +1,58 @@ +--- +layout: default +title: Filas de una lista +parent: Validación del lanzamiento +nav_order: 3 +translated_from: 2f28c42b47c253f87a9be9d5b1bd8638f7dca685 +--- + +# Filas de una lista +{: .no_toc } + +Cómo se validan las filas de los campos de lista +{: .fs-6 .fw-300 } + +--- + +Un campo `list` contiene filas; cada fila es una instancia de su subformulario, rellenada mediante el editor +de filas. Un campo `yaml` con un `subform` es una de esas filas. El servidor las trata igual +que el editor de filas del navegador: cada fila se resuelve a través de su subformulario, con los valores del formulario +como `__parent__`, se comprueban sus reglas y se evalúan sus campos calculados. Las listas anidadas funcionan +igual: en la lista propia de una fila, `$(__parent__.__parent__.x)` llega al formulario. + +Las filas se comprueban así: + +- **Una fila que el usuario añadió o editó** en el editor se resuelve y se valida. +- **Una fila que nadie tocó**, producida por el propio default, expresión o consulta de la lista + (como las entradas existentes de una lista de ACL), pasa **exactamente como llegó**, igual que en el + navegador: no se vuelve a validar y no se le añade nada. Una entrada existente que incumple una regla + escrita después no detiene el lanzamiento. +- **Una fila marcada como eliminada** (el `deleteMarker` de la lista) se mantiene tal cual y no se valida. + +Para distinguir estas filas sin fiarse de la petición, el servidor vuelve a ejecutar la propia +fuente de la lista (su default o, si no, su expresión o consulta) bajo la validación del lanzamiento. Una fila +que no pasó por el editor pasa sin cambios **solo cuando es una de las filas que produce esa +fuente**. Cualquier otra fila, como una inventada por quien llama por REST, se resuelve y se +valida como una fila editada. Así, una fila sin más no puede saltarse las comprobaciones, mientras que las +filas no tocadas del navegador siguen pasando sin cambios. + +Un usuario del navegador solo lo nota en un caso: los datos de la fuente cambiaron entre que abrió el +formulario y lo lanzó (una entrada se modificó o eliminó entretanto), **y** esa fila +incumple una regla de su subformulario. La fila deja entonces de contar como una de las filas de la fuente, así que +se valida, y el lanzamiento se rechaza nombrando la fila, por ejemplo +`acls[2].user_or_group (regex)`. + +Las filas que fallan aparecen en `rowErrors` con su `index` (un campo `yaml` no tiene) y en el log como +`disks[1].size (maxValue)`. + +Un lanzamiento puede contener como máximo **500 filas** entre todos los niveles de anidamiento, ya que cada fila ejecuta las consultas de su subformulario. + +--- + +## Marcadores de fila + +Los marcadores que una lista pone en sus filas (`insertMarker`, `updateMarker` y `deleteMarker`; sin +`insertMarker`, las filas nuevas reciben `__inserted__` cuando la lista define `allowDelete: false`, un `deleteMarker` o un `updateMarker`) +llegan al playbook en cada fila que lleva uno. Una fila eliminada se envía con su marcador +de eliminación, para que el playbook pueda eliminarla. Antes de la 6.4.1, una fila de lista solo llevaba un marcador cuando su +subformulario lo declaraba como campo (oculto). diff --git a/i18n/es/_build_forms/launch-validation/log-and-enforce.md b/i18n/es/_build_forms/launch-validation/log-and-enforce.md new file mode 100644 index 0000000..b09d1c6 --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/log-and-enforce.md @@ -0,0 +1,108 @@ +--- +layout: default +title: Registrar e imponer +parent: Validación del lanzamiento +nav_order: 2 +translated_from: 10a187b350155ce68bfc50e379e1b6b8637df57a +--- + +# Registrar e imponer +{: .no_toc } + +Qué hace cada modo con un lanzamiento que incumple las reglas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Qué hace `log` + +Se comprueba cada lanzamiento, pero para el usuario no cambia nada. Se escribe una advertencia en el log cuando: + +- el lanzamiento **se rechazaría**, con el motivo (solo nombres de campos y tipos de regla, nunca + valores): + + ``` + Launch validation would refuse form 'Create VM' for alice (launch validation 'log') : + failing rules : hostname (regex) ; failing rows : disks[1].size (maxValue) + ``` + +- las extravars que envió el cliente **difieren** de las que construye el servidor, con las + claves de primer nivel que difieren: + + ``` + Launch of form 'Create VM' : the extravars differ from the ones the server builds for + created_at, vm (launch validation 'log' ; 'enforce' would run the server's) + ``` + +Ejecuta primero `log` durante un tiempo en una instancia real. Muestra qué formularios se rechazarían y +cuáles calculan valores distintos en el servidor, antes de que afecte a nadie. + +--- + +## Qué hace `enforce` + +Con `enforce`, un lanzamiento no válido se rechaza, y uno válido se ejecuta con las extravars que construye +el servidor. + +### Un lanzamiento que incumple las reglas se rechaza + +`POST /api/v2/job` responde `422` con los campos que fallan: + +```json +{ + "error": "The form data is not valid", + "details": { + "missing": ["owner"], + "invalid": ["hostname", "disks"], + "waiting": [], + "validationErrors": { + "hostname": [{ "type": "regex", "description": "Must start with prod-" }] + }, + "rowErrors": { + "disks": [{ "index": 1, "invalid": ["size"], + "validationErrors": { "size": [{ "type": "maxValue", "description": "size must be at most 500" }] } }] + } + } +} +``` + +- `missing` - campos obligatorios sin valor; +- `invalid` - campos que incumplen una regla, elecciones que no son una de las opciones, y campos + de lista con una fila que falla; +- `waiting` - campos que no se pudieron evaluar (un campo del que dependen nunca recibió un valor); +- `validationErrors` - por campo, cada regla que falla con el mensaje que muestra el navegador; +- `rowErrors` - por campo de lista, las filas que fallan (consulta [Filas de una lista](list-rows-and-subforms.html)); +- `uploads` - campos de archivo cuya subida no se pudo verificar. + +Un lanzamiento **sin `rawFormData`** también se rechaza: sus valores no se pueden comprobar. + +### Un lanzamiento válido se ejecuta con las extravars del servidor + +El trabajo no se ejecuta con las extravars que envió el cliente. El servidor las construye a partir de los +valores comprobados, con el mismo código que el navegador, y ejecuta esas. Quien llama enviando un +`rawFormData` válido junto con unas `extravars` distintas obtiene las extravars que corresponden a los valores, no +las que envió. + +De la petición, el servidor conserva `__verbose__` (solo para usuarios a los que se permite el modo detallado). Las +claves reservadas (`__playbook__`, `__inventory__`, ...) vienen del formulario, como siempre, y +`ansibleforms_user` es el usuario que lanza. + +### Lo que vio el usuario no siempre es lo que se ejecuta + +Construir las extravars significa evaluar de nuevo el formulario: cada expresión y cada consulta se ejecuta +otra vez en el servidor, un momento después de que lo hiciera el navegador, como el usuario que lanza. Un valor +que depende de **cuándo** o **dónde** se evalúa puede diferir, y en ese caso se usa el +valor del servidor: + +| Qué | Por qué puede diferir | Cuánto | +|---|---|---| +| `fn.fnTime()`, una marca de tiempo, un nombre construido a partir de la hora | se evalúa de nuevo, unos segundos después | solo importa en el cambio de segundo o de fecha. `fnTime` ya se ejecuta en el servidor también en el flujo del navegador, con la misma zona horaria (el `TZ` del contenedor) | +| una consulta, `fnRestAdvanced`, `fnSsh`, `fnDnsResolve`, la lectura de un archivo | los datos externos cambiaron entretanto | solo cuando cambiaron | +| una expresión `runLocal` que usa `new Date()`, `toLocaleString`, `Intl` | el navegador usa la zona horaria y el idioma del usuario, el servidor los suyos | solo esas expresiones | + +Lo mismo se aplica a la **validación**: una regla que lee un campo así (`notIn` contra el resultado de una +consulta, `validIf` sobre un indicador calculado) se comprueba contra el valor del servidor. Los formularios cuyas +extravars difieren aparecen en las advertencias de `log` descritas arriba; revísalas antes de imponer la validación. diff --git a/i18n/es/_build_forms/launch-validation/passwords.md b/i18n/es/_build_forms/launch-validation/passwords.md new file mode 100644 index 0000000..28f5aaf --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/passwords.md @@ -0,0 +1,52 @@ +--- +layout: default +title: Contraseñas +parent: Validación del lanzamiento +nav_order: 5 +translated_from: 2fc2d2e8cdd997efaf221bd8e0114df2a4ee873e +--- + +# Contraseñas +{: .no_toc } + +Cómo se validan y se guardan los campos de contraseña, y cómo se mantienen fuera de las APIs +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Un campo de contraseña se valida como cualquier otro campo. Sin embargo, su valor se queda en el servidor y nunca se devuelve a +un cliente. + +--- + +## Dónde se guarda una contraseña + +El servidor guarda la contraseña con el trabajo, en sus extravars: + +* Así, una **aprobación** puede continuar el trabajo con la contraseña que introdujo el usuario. +* Un **relanzamiento tal como se ejecutó** puede reproducirla, cuando la validación del lanzamiento no es `enforce`. + +La contraseña guardada solo se usa en el servidor. + +--- + +## Qué devuelven las APIs + +Ninguna API devuelve nunca una contraseña: + +* Un trabajo leído a través de la API REST o del `get_job` del servidor MCP enmascara todos los campos `password` de su formulario, también dentro de + filas de lista y subformularios YAML. +* `MASK_EXTRAVARS_REGEX` (por defecto `password|secret|token`) enmascara además cada extravar cuyo nombre coincida, para + secretos que no son campos de contraseña. + +--- + +## Qué recibe un relanzamiento + +Para rellenar de antemano un relanzamiento, el navegador recibe los datos de formulario sin procesar del trabajo. Esos datos nunca contienen una contraseña, ni siquiera dentro de +filas de lista o subformularios YAML, así que el usuario vuelve a introducir la contraseña. + +Un campo de contraseña que ocultan las dependencias del formulario no es necesario, y no hay que volver a introducirlo. diff --git a/i18n/es/_build_forms/launch-validation/relaunching.md b/i18n/es/_build_forms/launch-validation/relaunching.md new file mode 100644 index 0000000..d25ce5c --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/relaunching.md @@ -0,0 +1,78 @@ +--- +layout: default +title: Relanzar +parent: Validación del lanzamiento +nav_order: 6 +translated_from: 9f52ec39fd40e3d617d9537c5f7d356033bc9d49 +--- + +# Relanzar +{: .no_toc } + +Cómo se valida un trabajo relanzado +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Un trabajo se puede relanzar de dos formas. Cómo se comprueba el relanzamiento depende de cuál se use, y del modo de validación +del lanzamiento. + +--- + +## Dos tipos de relanzamiento + +Los dos tipos se diferencian en si quien llama envía valores nuevos: + +* **Tal como se ejecutó**: el botón Relanzar, `POST /api/v2/job/{id}/relaunch` sin cuerpo, o `relaunch_job` sin + `values`. +* **Con cambios**: las mismas llamadas con valores nuevos, aplicados sobre los valores de campo guardados del trabajo: + +```bash +curl -X POST https://af.example.com/api/v2/job/42/relaunch \ + -H "Authorization: Bearer " -H "Content-Type: application/json" \ + -d '{ "values": { "vm_size": "large" } }' +``` + +--- + +## Cómo se comprueba cada uno + +El modo de validación del lanzamiento decide si un relanzamiento se reproduce o se vuelve a comprobar: + +| Relanzamiento | `off` / `log` | `enforce` | +|---|---|---| +| **Tal como se ejecutó** | reproduce las extravars guardadas del trabajo, contraseñas incluidas | la comprobación completa, a partir de los valores de campo guardados | +| **Con cambios** | la comprobación completa, sobre los valores guardados con los cambios aplicados encima | la comprobación completa, de la misma forma | + +Una excepción con `enforce`: un trabajo lanzado por una programación de nivel administrador no tiene valores de campo propios, así que relanzarlo tal como se ejecutó +reproduce las extravars guardadas de la programación, para un administrador o un usuario con la opción de rol `allowScheduledJobs`. + +Un relanzamiento comprobado (consulta [Registrar e imponer](log-and-enforce.html)) inicia un trabajo nuevo a nombre de quien llama. + +--- + +## Campos de contraseña + +Los valores de campo guardados no contienen contraseñas, así que un relanzamiento comprobado no tiene ninguna para un **campo de contraseña visible**: + +* El relanzamiento se rechaza como `unsupported`, con los `passwordFields` que le faltan. +* Lanza de nuevo el formulario e introduce la contraseña. +* Un campo de contraseña que las dependencias del formulario **ocultan** no es necesario y no bloquea el relanzamiento. + +Con `enforce`, esto también se aplica al botón Relanzar. + +--- + +## Quién puede relanzar + +Los dos tipos de relanzamiento requieren: + +* que el formulario permita relanzar (`allowRelaunch`, true por defecto); +* la opción de rol `allowJobRelaunch`; +* que el usuario sea **el propietario del trabajo**, un administrador o un usuario con la opción de rol `manageAllJobs`. + +Ver todos los trabajos (`showAllJobLogs`) no basta para relanzar el trabajo de otro usuario. Los aprobadores pueden ver un trabajo que espera su +aprobación, pero tampoco pueden relanzarlo. diff --git a/i18n/es/_build_forms/launch-validation/rolling-it-out.md b/i18n/es/_build_forms/launch-validation/rolling-it-out.md new file mode 100644 index 0000000..7242d65 --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/rolling-it-out.md @@ -0,0 +1,61 @@ +--- +layout: default +title: Despliegue +parent: Validación del lanzamiento +nav_order: 7 +translated_from: 399d39dbea694518926bcc20099d19438cbbac29 +--- + +# Despliegue +{: .no_toc } + +Llevar una instancia a enforce, y las limitaciones conocidas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Despliegue + +`enforce` es el valor predeterminado. En una instancia con scripts u otros sistemas que lanzan formularios por +REST, llega a él por pasos, vigilando el log antes de rechazar nada: + +1. Define `LAUNCH_VALIDATION=log` para toda la instancia (antes de actualizar, en una actualización). +2. Funciona en este modo durante un tiempo y revisa el log: las advertencias `would refuse` indican lanzamientos + que incumplen las reglas; las advertencias `extravars differ` indican formularios que calculan algo + de forma distinta en el servidor (una marca de tiempo, datos externos). Decide para cada formulario si eso + importa. +3. Cambia esos formularios a `launchValidation: enforce`. Quien llama enviando valores no válidos o + extravars inventadas recibe ahora un `422` con el motivo. +4. Cuando todos los formularios se comporten como esperas, quita `LAUNCH_VALIDATION` (o defínelo como `enforce`) para toda la instancia. + +--- + +## Limitaciones + +La validación del lanzamiento todavía no lo cubre todo: + +- Los **formularios con asistente** no se pueden relanzar con cambios, y un trabajo de asistente lanzado antes de la 7.0 + no se puede relanzar con `enforce` (no guardaba los valores de sus pasos). El servidor MCP + no puede lanzar formularios con asistente. +- La **resolución de marcadores** en el navegador sigue usando su propia copia del código del servidor; en + casos límite poco frecuentes (`__undefined__`, comillas dentro de expresiones) los dos podrían resolver un + marcador de forma distinta. `log` revela esas diferencias. +- Las expresiones **`runLocal`** se ejecutan en el motor JavaScript del navegador en el navegador y en un + sandbox en el servidor; el mismo código produce el mismo resultado, salvo cuando depende de + la zona horaria o el idioma del navegador. + +--- + +## Formularios con asistente + +Un [asistente](../forms/wizard.html) se comprueba paso a paso: el navegador envía los valores de +cada paso, y el servidor comprueba cada paso que el usuario enviaría contra su propio subformulario. + +- Un paso se omite cuando es `optional` y el usuario lo saltó, o cuando su `when` es false. +- Un paso lee los pasos anteriores mediante `$(__parent__..)`, con sus valores comprobados. +- Los errores nombran el paso y el campo, como `.`. +- Con `enforce`, las extravars se construyen en el servidor, cada paso bajo su `defaultModel`. +- Los campos de archivo no se admiten en un asistente. diff --git a/i18n/es/_build_forms/launch-validation/switching-it-on.md b/i18n/es/_build_forms/launch-validation/switching-it-on.md new file mode 100644 index 0000000..a17a114 --- /dev/null +++ b/i18n/es/_build_forms/launch-validation/switching-it-on.md @@ -0,0 +1,71 @@ +--- +layout: default +title: Activarlo +parent: Validación del lanzamiento +nav_order: 1 +translated_from: 2e5de5654a03389c7c8a7c05179e9dd79456b50b +--- + +# Activarlo +{: .no_toc } + +Para toda la instancia o para un formulario +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +La validación del lanzamiento tiene dos ajustes. **Se aplica el más estricto de los dos.** + +--- + +## Para toda la instancia: `LAUNCH_VALIDATION` + +La variable de entorno [`LAUNCH_VALIDATION`](../customization/features.html#env_LAUNCH_VALIDATION) se aplica a todos los formularios: + +- `off` - sin comprobación en el servidor: el trabajo se ejecuta con las extravars que envió el navegador o quien llama. +- `log` - se comprueba cada lanzamiento; lo que se rechazaría se escribe en el log como + advertencia, y el lanzamiento se ejecuta igual que antes. +- `enforce` (predeterminado) - un lanzamiento no válido se rechaza, y uno válido se ejecuta con las extravars que + construye el servidor. + +Un valor vacío o desconocido también significa `enforce`. Los cambios surten efecto sin reiniciar. + +{: .warning } +> **Actualizar cambia el comportamiento.** Una instancia que no define `LAUNCH_VALIDATION` impone la validación en +> todos los lanzamientos tras la actualización. Los scripts que llaman a `POST /api/v2/job` sin `rawFormData`, +> o con valores que incumplen las reglas de un formulario, se rechazan con un `422`. Define +> `LAUNCH_VALIDATION=log` antes de actualizar para ver qué se rechazaría, u `off` para mantener el +> comportamiento anterior (consulta [Despliegue](rolling-it-out.html)). + +--- + +## Para un formulario: `launchValidation` + +Un formulario puede definir su propio nivel: + +```yaml +- name: Create VM + type: ansible + roles: + - default + playbook: vm.yml + launchValidation: enforce # off | log | enforce + fields: + - name: vm_name + type: text + required: true +``` + +Usa este ajuste en una instancia que funciona con `off` o `log`, para un formulario que sabes que se va a +lanzar por REST: ese formulario se impone, mientras que el resto de la instancia no. También se puede definir en el diseñador, en los +ajustes del formulario. + +**Un formulario puede ser más estricto que la instancia, nunca más permisivo.** Con `LAUNCH_VALIDATION` +en `enforce`, el `launchValidation: off` de un formulario no tiene efecto: un formulario no puede anular +la decisión de un operador. + +Los formularios con asistente también lo usan: el lanzamiento de un asistente se comprueba paso a paso, cada paso contra su propio +subformulario (consulta [Formularios con asistente](rolling-it-out.html#wizard-forms)). diff --git a/i18n/es/_build_forms/workflows/designer.md b/i18n/es/_build_forms/workflows/designer.md new file mode 100644 index 0000000..c357f10 --- /dev/null +++ b/i18n/es/_build_forms/workflows/designer.md @@ -0,0 +1,107 @@ +--- +layout: default +title: Editar en el diseñador +parent: Workflows +nav_order: 2 +translated_from: dd4e281ec223f5c4995d804e774ac49e4d5934da +--- + +# Editar en el diseñador +{: .no_toc } + +Dibuja un workflow y pruébalo sin ejecutar nada +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## La sección Workflows + +**Diseñador > Workflows** muestra los archivos de workflow en un explorador de archivos, con las pestañas YAML, Visual y Vista previa de todas +las secciones del [diseñador](../designer/). + +- **El botón `+`** de la barra de herramientas crea un archivo de workflow, con un workflow vacío para empezar. +- **YAML** muestra el archivo tal como está escrito. Las demás pestañas también conservan los comentarios y el orden de las claves. +- **Visual** dibuja cada workflow del archivo como un grafo, con su nombre y su descripción encima. Un archivo con dos workflows + muestra ambos, uno debajo del otro. +- **Vista previa** es una simulación del workflow: consulta [Probarlo](#trying-it-out). + +Los problemas se listan encima del grafo, cada uno con un enlace que abre el YAML en su línea: un enlace a un nodo que no +existe, un bucle, un playbook, inventario, credencial, runner o repositorio que el diseñador no conoce. + +--- + +## El grafo + +El grafo se lee de izquierda a derecha, de **START** a los últimos nodos. Cada nodo muestra su identificador y, debajo, lo que +ejecuta; los enlaces se colorean según lo que los dispara, como indica la leyenda dentro del grafo: si tiene éxito, si falla, +siempre. + +- **Haz clic en START** para añadir un primer nodo: un nodo sin padre, que se ejecuta en cuanto arranca el workflow. +- **Haz clic en un nodo** para editarlo. +- **Haz clic en un enlace** para cambiar lo que lo dispara o para quitarlo. + +--- + +## Añadir un nodo + +Un nodo nuevo se añade con un asistente: **Nodo nuevo si tiene éxito**, **Nodo nuevo si falla** o **Nodo nuevo siempre** en un nodo +existente, o START para el primero. + +1. **Qué ejecuta**: el identificador del nodo, **Ejecuta** (qué clase de nodo es y, para un nodo de playbook, dónde se ejecuta) + y lo que ejecuta: un playbook o una plantilla o job, un workflow, el nombre y el tiempo de espera de una aprobación, o un repositorio. Consulta + [Dónde se ejecuta un nodo](#where-a-node-runs). +2. **Cómo se ejecuta**: los ajustes de ejecución: el inventario, las credenciales, el limit, los tags, el nivel de detalle, + los modos check y diff, la condición (**Se ejecuta si**) y **Valores de** (consulta [el archivo del workflow](file.html#run-settings)). +3. **Dónde va**: el nodo tras el que se ejecuta, y en qué caso. + +**Crear** añade el nodo al YAML. + +--- + +## Dónde se ejecuta un nodo + +**Ejecuta** indica qué ejecuta un nodo y dónde, en una sola lista: **Playbook en el RTE**, **Plantilla en AWX**, **Plantilla en +Semaphore**, **Job en Rundeck**, **Job en Jenkins** y, después, **Workflow**, **Aprobación** y **Pull de repositorio**. + +| Ejecuta | El campo de debajo | Runner | +|---|---|---| +| **Playbook en el RTE** | **Playbook**: un playbook del diseñador | **El RTE predeterminado**, o un runner RTE por nombre | +| **Plantilla en AWX** (incluidos AAP y Ascender) | **Plantilla**: el nombre de la plantilla, escrito a mano | los runners de AWX, cada uno con su variante | +| **Plantilla en Semaphore** | **Plantilla**: el nombre de la plantilla, escrito a mano | los runners de Semaphore | +| **Job en Rundeck** o **Job en Jenkins** | **Job**: el nombre del job, escrito a mano | los runners de Rundeck o Jenkins | + +- **Playbook en el RTE** siempre se ofrece; los demás solo cuando la instancia tiene un [runner](../runners.html) de ese tipo. +- **Runner** solo lista los runners de ese tipo, con el predeterminado marcado y preseleccionado (o el primero por nombre). +- Elegir otro sitio borra lo que ejecuta el nodo, para que se vuelva a elegir. +- Un nodo cuyo runner no conoce el diseñador muestra **Plantilla o job en** ese runner. + +El archivo del workflow no cambia: un nodo así es un nodo `job_template` con un `runner:` (consulta +[el archivo del workflow](file.html#run-settings)). + +--- + +## Editar un nodo + +Al hacer clic en un nodo se abre su diálogo, con cuatro pestañas: + +| Pestaña | Qué contiene | +|---|---| +| **Qué ejecuta** | El identificador, **Ejecuta**, lo que ejecuta y, para un nodo de playbook, el runner | +| **Cómo se ejecuta** | Los ajustes de ejecución; las credenciales se eligen de una lista, cada una se añade con `+` | +| **Se ejecuta después de** | Los enlaces que llegan al nodo, y **Todos los padres deben converger** | +| **Ejecuta después** | Los enlaces que salen de él, con **Enlazar a un nodo existente:** y **Enlazar a un nodo nuevo:** | + +**Ver en YAML** abre el YAML en el nodo, y **Quitar nodo** lo elimina junto con sus enlaces. Cada cambio se escribe en el +YAML al instante; **Guardar** guarda el archivo. + +--- + +## Probarlo + +La pestaña **Vista previa** recorre el workflow sin lanzar nada, según las reglas de AWX. + +Todos los nodos empiezan como éxito. Haz clic en un nodo para que falle, y vuelve a hacer clic para que tenga éxito; el grafo muestra entonces +qué nodos se ejecutarían, cuáles no se ejecutarían y si el workflow fallaría. **Restablecer** vuelve a poner todos los nodos en éxito. diff --git a/i18n/es/_build_forms/workflows/file.md b/i18n/es/_build_forms/workflows/file.md new file mode 100644 index 0000000..8ec8e37 --- /dev/null +++ b/i18n/es/_build_forms/workflows/file.md @@ -0,0 +1,165 @@ +--- +layout: default +title: El archivo del workflow +parent: Workflows +nav_order: 1 +translated_from: bb4d3027e33a5bfd11443369b621eb34c4af2f68 +--- + +# El archivo del workflow +{: .no_toc } + +La sintaxis del workflow, los tipos de nodo y los ajustes de ejecución de cada nodo +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Dónde están los archivos de workflow + +Los archivos de workflow son archivos YAML en la carpeta local de workflows y en cada repositorio que se usa para workflows. + +- **La carpeta local** es [`WORKFLOWS_PATH`](../customization/paths.html#env_WORKFLOWS_PATH), por defecto `workflows` dentro de la + carpeta persistente. +- **Un repositorio** con **¿Usar para workflows?** activado se lee entero, así que sus archivos de workflow pueden estar en cualquier carpeta. El + diseñador los guarda de vuelta en el repositorio, y **Push** los envía al remoto. + +Se lee cada archivo YAML que contiene `workflow_job_templates`, hasta seis carpetas de profundidad, y un archivo puede contener varios +workflows. Un formulario indica un workflow por su `name`, así que mantén los nombres únicos: cuando dos workflows comparten nombre, un trabajo que +lo ejecuta falla y pide que renombres uno de ellos. + +--- + +## La sintaxis + +Un archivo de workflow se escribe con la sintaxis de una exportación de AWX, con los nodos bajo `related.workflow_nodes`: + +```yaml +workflow_job_templates: + - name: deploy-app + description: Builds, approves and deploys the application + extra_vars: + app_name: orders-api + related: + workflow_nodes: + - identifier: build + unified_job_template: + name: build.yaml + type: job_template + inventory: + name: staging + related: + credentials: + - name: deploy-ssh + success_nodes: + - identifier: approve + failure_nodes: + - identifier: notify + - identifier: approve + unified_job_template: + name: Approve the deploy + type: workflow_approval_template + timeout: 3600 + related: + success_nodes: + - identifier: deploy + - identifier: deploy + unified_job_template: + name: deploy.yaml + type: job_template + inventory: + name: production + limit: web + job_tags: deploy + - identifier: notify + unified_job_template: + name: notify.yaml + type: job_template +``` + +También se lee la forma de la colección `awx.awx`: `workflow_nodes` directamente bajo el workflow, y nombres escritos como +cadenas simples (`inventory: staging`) en lugar de mapas (`inventory: {name: staging}`). + +### El workflow + +Una entrada de workflow tiene algunas claves propias, junto a sus nodos: + +| Clave | Qué es | +|---|---| +| `name` | El nombre del workflow, que indica el `workflow:` de un formulario | +| `description` | Para qué sirve, se muestra en el diseñador | +| `extra_vars` | Variables que recibe cada nodo; los valores del formulario ganan sobre ellas | +| `related.workflow_nodes` | Los nodos | + +### Un nodo + +Cada nodo tiene un identificador, lo que ejecuta y sus enlaces: + +| Clave | Qué es | +|---|---| +| `identifier` | El id del nodo: letras, dígitos, puntos, guiones, dos puntos y guiones bajos, único en el workflow | +| `unified_job_template.type` | Lo que ejecuta el nodo: consulta [tipos de nodo](#node-types) | +| `unified_job_template.name` | El playbook, workflow, aprobación o repositorio que ejecuta | +| `related.success_nodes` | Los nodos que se ejecutan cuando este tiene éxito | +| `related.failure_nodes` | Los nodos que se ejecutan cuando este falla | +| `related.always_nodes` | Los nodos que se ejecutan cuando este ha terminado, sea como sea | +| `all_parents_must_converge` | `true`: el nodo espera a que se disparen todos los enlaces que llegan a él, no solo uno | + +Los enlaces deben ir hacia delante: un workflow cuyos enlaces forman un bucle no se ejecuta. + +--- + +## Tipos de nodo + +Un nodo ejecuta una de cuatro cosas, con los nombres que les da AWX: + +| Tipo | Lo que ejecuta el nodo | +|---|---| +| `job_template` | Uno de los playbooks del diseñador, como trabajo propio en el RTE; o, con `runner:`, la plantilla o el job de ese runner | +| `workflow_job_template` | Otro workflow, como trabajo de workflow hijo; los workflows se anidan hasta cinco niveles | +| `workflow_approval_template` | Una aprobación: el trabajo del workflow espera hasta que alguien la aprueba o la rechaza | +| `project` | Un pull del repositorio que indica (la actualización de proyecto de AWX) | + +El `unified_job_template.timeout` de un nodo de aprobación se expresa en segundos. Cuando vence sin respuesta, el nodo falla; `0` o sin +tiempo de espera, espera una respuesta el tiempo que haga falta. Una aprobación rechazada hace fallar el nodo, y el workflow continúa por sus +enlaces. + +--- + +## Ajustes de ejecución + +Un nodo `job_template` admite los ajustes de un nodo de AWX, en el propio nodo: + +| Clave | Qué hace | +|---|---| +| `inventory` | El inventario contra el que se ejecuta el playbook, por su nombre, como el `inventory:` de un formulario | +| `related.credentials` | Las credenciales: cada una llega al playbook con su propio nombre, y la primera *SSH* es también el login con el que se conecta ansible. Una de vault solo abre los vaults | +| `limit` | Se ejecuta solo en estos hosts (`--limit`) | +| `job_tags` | Ejecuta solo las tareas con estos tags (`--tags`) | +| `skip_tags` | Omite las tareas con estos tags (`--skip-tags`) | +| `verbosity` | De `0` a `5`: el número de `-v` | +| `diff_mode` | `true`: muestra los cambios (`--diff`) | +| `job_type` | `check`: una simulación (`--check`); `run` o nada: una ejecución real | +| `extra_data` | Variables que recibe este nodo, por debajo de las `extra_vars` del workflow y de los valores del formulario | + +El trabajo del nodo recibe los valores del formulario como extra vars. Los valores del formulario ganan sobre las `extra_vars` del workflow, que ganan +sobre el `extra_data` del nodo, igual que las respuestas de un survey en AWX. + +--- + +## Claves propias de AnsibleForms + +Tres claves de un nodo son propias de AnsibleForms, y AWX las ignora, de modo que un workflow puede hacer lo mismo que un formulario de varios pasos: + +| Clave | Qué hace | +|---|---| +| `runner` | En un nodo `job_template`, ejecuta la plantilla o el job de ese runner en lugar de un playbook del diseñador: AWX, AAP, Ascender, Semaphore, Rundeck o Jenkins. Un runner RTE ejecuta allí el playbook del nodo. Un mismo workflow puede combinarlos. | +| `if_extra_var` | El nodo solo se ejecuta cuando esta extra var es `true`, como el `ifExtraVar` de un paso de multistep. Si no se cumple, el nodo se omite, y lo que va después se ejecuta como tras un éxito. No en un nodo de aprobación, que siempre se ejecuta. | +| `extra_vars_key` | El nodo recibe solo esta clave de las extra vars, con el usuario copiado dentro, como la `key` de un paso de multistep | + +En un runner de plantillas, `unified_job_template.name` es la plantilla o el job que se ejecuta, y el `limit`, los `job_tags`, el modo check +y el modo diff del nodo se pasan igual que los pasaría un formulario del tipo de ese runner. Una `verbosity` mayor que `0` activa el +`verbose` del formulario, y `skip_tags` no se pasa. Solo un runner de AWX (AWX, AAP o Ascender) recibe el inventario del nodo, y las +credenciales como sus credenciales de AWX; Semaphore, Rundeck y Jenkins reciben las credenciales como las `credentials` de un formulario. diff --git a/i18n/es/_build_forms/workflows/index.md b/i18n/es/_build_forms/workflows/index.md new file mode 100644 index 0000000..c7f2fc4 --- /dev/null +++ b/i18n/es/_build_forms/workflows/index.md @@ -0,0 +1,99 @@ +--- +layout: default +title: Workflows +nav_order: 5 +has_children: true +has_toc: false +permalink: /workflows/ +translated_from: e6133a683d5bc1d0ba822dd52b4fed99028ea63c +--- + +# Workflows +{: .no_toc } + +Encadena playbooks, aprobaciones y otros workflows, como hace AWX +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para autores de formularios: qué es un workflow, cómo se ejecutan sus nodos y dónde seguir leyendo. + +Un workflow es un grafo de nodos. Cada nodo ejecuta algo: uno de los playbooks del diseñador, otro workflow, una aprobación +o un pull de repositorio. Los enlaces entre los nodos indican qué se ejecuta después: si tiene éxito, si falla o siempre. Un formulario de +[tipo `workflow`](../forms/workflow.html) lo lanza, y la página de trabajos lo dibuja en directo mientras se ejecuta. + +Los workflows siguen la sintaxis y las reglas de AWX, así que un workflow exportado de AWX se lee igual en AnsibleForms. Se ejecutan en +el RTE, sin AWX: un nodo de playbook es un trabajo propio, con el inventario, las credenciales, el limit y los tags del nodo. + +--- + +## Cómo se ejecuta un workflow + +Los nodos se ejecutan según las reglas de AWX, y las ramas que no dependen unas de otras se ejecutan en paralelo. + +- **Un nodo sin padre arranca enseguida**, cuando arranca el workflow. +- **Un enlace se dispara cuando su nodo ha terminado**: un enlace *si tiene éxito* tras un éxito, un enlace *si falla* tras un fallo, + un enlace *siempre* tras cualquiera de los dos. Un nodo cancelado no dispara ninguno de sus enlaces. +- **Un nodo arranca en cuanto se dispara uno de los enlaces que llegan a él.** Con **Todos los padres deben converger** activado, espera hasta que se hayan + disparado todos los enlaces que llegan a él. +- **Un nodo que ya no puede arrancar se marca como *no se ejecuta***, igual que los nodos que solo él podía arrancar. +- **El workflow falla cuando un nodo ha fallado y nada lo gestiona**: ese nodo no tiene ningún enlace *si falla* ni *siempre*. + Un fallo que gestiona un enlace no hace fallar el workflow. + +Un nodo con una condición (`if_extra_var`) que no se cumple se omite. Lo que va después se ejecuta como tras un éxito. Un nodo de +aprobación no admite condición: siempre espera una respuesta. + +--- + +## Pasar datos entre nodos + +Lo que el playbook de un nodo recoge con `set_stats` llega a los nodos posteriores como extra vars, igual que AWX transmite los +artefactos de un trabajo. Un nodo recibe los datos de cada padre cuyo enlace hacia él se disparó, combinados en el orden en que están +escritos los padres, y transmite lo que recibió más los datos de su propio trabajo; los suyos ganan si ambos definen la misma clave. + +```yaml +# build.yaml, the first node : its version and build id go to the nodes after it +- name: Build + hosts: localhost + gather_facts: false + tasks: + - name: Pass the build on + ansible.builtin.set_stats: + data: + version: "2.4.1" + build_id: 42 +``` + +Un nodo posterior lee entonces {% raw %}`{{ version }}` y `{{ build_id }}`{% endraw %} como cualquier otra extra var. + +Las extra vars de un nodo vienen de cuatro sitios. Uno de ellos, `extra_data`, son las variables propias del nodo: el nombre que les da AWX, +definidas en el nodo en el [archivo del workflow](file.html#run-settings). Cuando dos definen la misma clave, el orden de precedencia es +el de AWX, de menor a mayor: + +| Origen | Gana sobre | +|---|---| +| las variables propias del nodo (`extra_data`) | nada | +| los datos transmitidos con `set_stats` | el `extra_data` del nodo | +| las `extra_vars` del workflow | los datos transmitidos | +| los valores del formulario | todo | + +- Solo se transmiten los datos globales del playbook: `set_stats` con `per_host: false`, su valor predeterminado. +- Un nodo de aprobación, un nodo omitido y un nodo de proyecto transmiten lo que les llegó. +- La salida de un nodo termina con las claves que transmitió, solo por nombre, ya que sus valores pueden ser secretos. +- El trabajo del nodo guarda sus datos como artefactos (**Mostrar artefactos** en su página), y el trabajo del workflow guarda los datos de todos sus + nodos, así que un workflow anidado también los transmite a los nodos posteriores. +- Un nodo que se ejecuta en un runner de AWX transmite del mismo modo los artefactos de su trabajo en AWX. +--- + +## En esta sección + +Las siguientes páginas describen los workflows en detalle: + +| Página | Qué cubre | +|---|---| +| [El archivo del workflow](file.html) | La sintaxis del archivo, los tipos de nodo y los ajustes de ejecución de cada nodo | +| [Editar en el diseñador](designer.html) | La sección Workflows del diseñador: el grafo, los diálogos de nodo y de enlace y la simulación | +| [Ejecutar un workflow](running.html) | El formulario que lo lanza, la página del trabajo, las salidas de los nodos, las aprobaciones y el aborto | diff --git a/i18n/es/_build_forms/workflows/running.md b/i18n/es/_build_forms/workflows/running.md new file mode 100644 index 0000000..9e956b1 --- /dev/null +++ b/i18n/es/_build_forms/workflows/running.md @@ -0,0 +1,103 @@ +--- +layout: default +title: Ejecutar un workflow +parent: Workflows +nav_order: 3 +translated_from: 1883c268cde716170bdb84c7f2df49e9122dc55d +--- + +# Ejecutar un workflow +{: .no_toc } + +Lanza un workflow desde un formulario y síguelo en la página de trabajos +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## El formulario + +Un formulario de [tipo `workflow`](../forms/workflow.html) lanza un workflow, indicado con `workflow:` igual que un playbook: por +su **archivo** en la carpeta de workflows, con sus subcarpetas (`team/deploy-app.yml`), o por el **nombre** del workflow dentro +del archivo (`deploy-app`): + +```yaml +- name: Deploy the app + type: workflow + workflow: team/deploy-app.yml + roles: + - admin + fields: + - name: app_name + type: text + default: orders-api +``` + +Los campos del formulario llegan a cada nodo como extra vars, y también sus [`credentials`](../forms/workflow.html#workflow_credentials), con los nombres +que les da el formulario, igual que los de un formulario de varios pasos llegan a sus pasos: + +```yaml +- name: Provision NAS storage + type: workflow + workflow: nas.yml + credentials: + ontap: ontap_automation # every node's playbook gets `ontap` +``` + +Las credenciales propias de un nodo, definidas en el archivo del workflow, llegan a su playbook con su propio nombre y ganan si ambos usan el mismo nombre. En la pestaña Visual del diseñador de un formulario, **Workflow** es una lista de los +archivos de workflow del diseñador. + +Un nombre de archivo solo también se encuentra en una subcarpeta, cuando solo un archivo lo tiene. Dos workflows con el mismo nombre detienen un formulario que +lo indica por su nombre, y el diseñador avisa al guardar el segundo: renombra uno, o indica su archivo. Un workflow que no se +encuentra termina el trabajo como fallido, con el motivo en su salida. + +--- + +## El trabajo + +Un trabajo de workflow se ejecuta en el servidor de AnsibleForms, y cada nodo de playbook se ejecuta como un trabajo propio, hijo del trabajo +del workflow igual que los pasos de un formulario de varios pasos: un playbook del diseñador en el RTE o, para un nodo con un `runner:`, la +plantilla o el job de ese runner en AWX, AAP, Ascender, Semaphore, Rundeck o Jenkins (consulta [Dónde se ejecuta un nodo](designer.html#where-a-node-runs)). + +La página del trabajo dibuja el workflow en directo: cada nodo coloreado según su estado, los enlaces que se dispararon y los nodos que no +se ejecutarán. Los datos de la parte superior nombran el workflow en lugar de un playbook. + +- **Haz clic en un nodo** que haya terminado para abrir su salida en un diálogo. +- **La salida** contiene la salida de cada nodo bajo un encabezado `WORKFLOW NODE [identifier] (status)`, en el orden en que terminaron + los nodos. +- **Mostrar variables extra** y **Mostrar artefactos**, en el encabezado de cada nodo en la salida y en el diálogo que se abre desde el grafo, + muestran las extravars y los artefactos propios de ese nodo (`{}` si no tiene), en JSON o YAML, con un botón Copiar. + +Las extravars de un nodo contienen lo que le transmitieron los nodos anteriores, y sus artefactos lo que su playbook recogió con +`set_stats`. Los dos botones siguen las [opciones de rol](../config/roles.html#role-options) `showExtravars` y +`showArtifacts`. Los mismos datos se obtienen con `GET /api/v2/job/{id}/node/{nodeId}`, para cualquiera que pueda ver el trabajo. + +El trabajo falla cuando un nodo ha fallado y nada lo ha gestionado, según la regla de AWX; si no, tiene éxito. + +--- + +## Aprobaciones + +Un nodo de aprobación retiene el trabajo del workflow hasta que alguien responde. + +Cuando el workflow llega a un nodo de aprobación, el estado del trabajo pasa a **Esperando aprobación**: se muestra en la campana y en la +página del trabajo, como un [punto de aprobación](../forms/approval.html) de un formulario. El nombre del nodo es el título de la aprobación y su descripción +el mensaje; el resto viene del `approval` del formulario: + +- sus `roles` deciden quién puede aprobarla o rechazarla, y `allowSelf` si quien la lanzó también puede aprobarla; +- a sus `notifications` y `channels` se les envía la solicitud. + +Un formulario de workflow sin `approval` también retiene el trabajo en un nodo de aprobación, pero entonces solo un administrador puede aprobarlo. + +- **Aprobado**: el nodo tiene éxito y el workflow continúa por sus enlaces *si tiene éxito*. +- **Rechazado**: el nodo falla y el workflow continúa por sus enlaces *si falla*. +- **Tiempo agotado**: pasado el tiempo de espera del nodo sin respuesta, el nodo también falla. + +--- + +## Abortar + +**Abortar** en un trabajo de workflow en ejecución lo detiene: los trabajos de nodo en ejecución se abortan, no arranca nada más y se cancela +la aprobación que esté esperando. Un trabajo de workflow que espera una aprobación también se puede abortar. diff --git a/i18n/es/_configure/announcements.md b/i18n/es/_configure/announcements.md new file mode 100644 index 0000000..e154728 --- /dev/null +++ b/i18n/es/_configure/announcements.md @@ -0,0 +1,48 @@ +--- +layout: default +title: Anuncios +nav_order: 1 +has_children: true +has_toc: false +permalink: /announcements.html +translated_from: 9569cbdaa1c1d1d215f521f21023566e3a4df572 +--- + +# Anuncios +{: .no_toc } + +Mensajes bajo la cabecera para todos, como una ventana de mantenimiento, cada uno desde su inicio hasta su fin +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores: informa a todos los usuarios de una ventana de mantenimiento o de un incidente, en **Configuración > Apariencia > Anuncios**. + +La página necesita la opción de rol `showSettings` o `showAppearanceSettings`. Desde la 7.3.0 es una sola lista, y cada anuncio +tiene su propia página y su interruptor Habilitado. + +--- + +## En esta sección + +| Página | Qué cubre | +|---|---| +| [General](announcements/general.html) | El interruptor Habilitado de cada anuncio, que sustituyó al interruptor de todos ellos | +| [Lista](announcements/list.html) | Los anuncios que no han terminado, sus columnas y la página de un anuncio | +| [Añadir uno](announcements/adding-one.html) | El asistente de dos pasos: el mensaje y, después, cuándo se muestra | + + +--- + +## Dónde se muestran + +Los anuncios se muestran como una línea bajo la cabecera de cada página, para todos los usuarios que han iniciado sesión, cada uno con el color de su tipo. + +Se pueden mostrar varios habilitados a la vez, el más reciente primero. La página de inicio de sesión y un formulario incrustado en otro sitio no muestran ninguno. + +Cada uno tiene un botón de cierre. Un anuncio cerrado sigue cerrado en esa pestaña del navegador hasta que se cierra la pestaña, o +hasta que cambia su mensaje o su fecha de fin. Un anuncio guardado llega a la vez a todos los navegadores abiertos; uno que +alcanza su fecha de inicio o de fin se muestra u oculta en ellos en menos de un minuto. diff --git a/i18n/es/_configure/announcements/adding-one.md b/i18n/es/_configure/announcements/adding-one.md new file mode 100644 index 0000000..cd6e85c --- /dev/null +++ b/i18n/es/_configure/announcements/adding-one.md @@ -0,0 +1,29 @@ +--- +layout: default +title: Añadir uno +parent: Anuncios +nav_order: 3 +translated_from: 0391ac4a8ba2f25d94a030831cc1a421d70ea0ba +--- + +# Añadir uno +{: .no_toc } + +El asistente de dos pasos: el mensaje y, después, cuándo se muestra +{: .fs-6 .fw-300 } + +**Añadir anuncio** abre un asistente de dos pasos. **Mensaje** pregunta qué dice: + +| Campo | Significado | +|---|---| +| **Mensaje** | El texto, como máximo 500 caracteres. Obligatorio. | +| **Tipo** | Su color y su icono: azul para Información (el predeterminado), ámbar para Advertencia, rojo para Peligro | + +**Cuándo** pregunta cuándo se muestra: + +| Campo | Significado | +|---|---| +| **Fecha de inicio** | Se muestra a partir de este momento. Déjala vacía para mostrarlo de inmediato. | +| **Fecha de fin** | En este momento se oculta y se elimina. Déjala vacía para mantenerlo hasta que lo elimines. | + +La fecha de fin debe ser posterior a la fecha de inicio y estar en el futuro. diff --git a/i18n/es/_configure/announcements/general.md b/i18n/es/_configure/announcements/general.md new file mode 100644 index 0000000..3e11b8d --- /dev/null +++ b/i18n/es/_configure/announcements/general.md @@ -0,0 +1,33 @@ +--- +layout: default +title: General +parent: Anuncios +nav_order: 1 +translated_from: e5c14d608eacfef0f3aa1a322d0ac0275b55f91e +--- + +# General +{: .no_toc } + +El interruptor Habilitado de cada anuncio, que sustituyó al interruptor de todos ellos +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Activar o desactivar un anuncio + +Desde la 7.3.0, cada anuncio se activa o desactiva por separado, con su interruptor **Habilitado**. + +Un anuncio nuevo está habilitado. **Deshabilitar** en el menú de su fila, o el interruptor **Habilitado** de su página, lo oculta bajo la cabecera y lo mantiene +en la lista, y **Habilitar** lo vuelve a mostrar desde su fecha de inicio hasta su fecha de fin. El interruptor se guarda al instante. + +--- + +## El interruptor Mostrar anuncios de la 7.2 + +Hasta la 7.2, una pestaña **General** tenía un interruptor **Mostrar anuncios** que los activaba o desactivaba todos a la vez. + +Desde la 7.3.0 ese interruptor ya no existe. Si estaba desactivado, la actualización deshabilita todos los anuncios, para que ninguno empiece a mostrarse: vuelve a habilitar los que quieras. diff --git a/i18n/es/_configure/announcements/list.md b/i18n/es/_configure/announcements/list.md new file mode 100644 index 0000000..14f8057 --- /dev/null +++ b/i18n/es/_configure/announcements/list.md @@ -0,0 +1,44 @@ +--- +layout: default +title: Lista +parent: Anuncios +nav_order: 2 +translated_from: 3bbe4ccb28f9305a450ac85b7d99531ce28a84cd +--- + +# Lista +{: .no_toc } + +Los anuncios que no han terminado, sus columnas y la página de un anuncio +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## La lista + +**Configuración > Apariencia > Anuncios** lista todos los anuncios que no han terminado, con estas columnas: + +| Columna | Significado | +|---|---| +| **Habilitado** | Si se muestra bajo la cabecera desde su inicio hasta su fin (desde la 7.3.0) | +| **Mensaje** | El texto bajo la cabecera | +| **Tipo** | Información, Advertencia o Peligro | +| **Fecha de inicio** | Cuándo empieza a mostrarse; `–` cuando se muestra de inmediato | +| **Fecha de fin** | Cuándo termina; `–` cuando se muestra hasta que se elimine | +| **Creado por** | Quién lo añadió | + +Un clic en una fila, o **Editar** en el menú de su fila, abre la página del anuncio. El menú también tiene **Habilitar** o **Deshabilitar**, y **Eliminar**. + +Cuando pasa su fecha de fin, un anuncio se elimina: la lista solo conserva los que están por venir y los que se están mostrando. + +--- + +## La página de un anuncio + +Desde la 7.3.0, cada anuncio se abre en su propia página, en `/settings/announcements/`, con una pestaña, **Detalles**. + +Contiene el mensaje, el tipo, las fechas de inicio y de fin y el interruptor **Habilitado**, que se guarda al instante; los demás campos se guardan +con **Guardar**, y **Eliminar** borra el anuncio. El título de la página lo nombra por los primeros caracteres de su mensaje. diff --git a/i18n/es/_configure/appearance.md b/i18n/es/_configure/appearance.md new file mode 100644 index 0000000..e38dec8 --- /dev/null +++ b/i18n/es/_configure/appearance.md @@ -0,0 +1,40 @@ +--- +layout: default +title: Apariencia +nav_order: 2 +has_children: true +has_toc: false +permalink: /appearance.html +translated_from: 8dacce30ad7f7530858b04514b1edbbf19ea73d2 +--- + +# Apariencia +{: .no_toc } + +Los valores predeterminados, el nombre, el logotipo, el icono del navegador, los enlaces de la cabecera y las etiquetas junto al logotipo +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores: dale a la instancia tu propio aspecto, en **Configuración > Apariencia**, con la opción de rol `showSettings`. +La opción de rol `showAppearanceSettings` por sí sola también abre estas páginas, anuncios incluidos, sin el resto de la configuración. + +Las páginas de este grupo son **Valores predeterminados**, **Nombre**, **Logo**, **Icono**, **Enlaces**, **Etiquetas** y **Anuncios**; +los anuncios tienen una página propia, [Anuncios](announcements.html). Un valor predeterminado, nombre, enlace o etiqueta guardado +se muestra al instante en todos los navegadores abiertos. + +--- + +## En esta sección + +| Página | Qué cubre | +|---|---| +| [Valores predeterminados](appearance/defaults.html) | El tema, el idioma, la zona horaria y el formato de fecha para los usuarios que no han elegido ninguno | +| [Nombre](appearance/name.html) | El nombre de la aplicación en la pestaña del navegador y en la página de inicio de sesión | +| [Logotipo](appearance/logo.html) | Tu propio logotipo en la cabecera | +| [Icono](appearance/icon.html) | Tu propio icono en la pestaña del navegador y en los marcadores | +| [Enlaces](appearance/links.html) | Los enlaces de la cabecera, los integrados y los tuyos | +| [Etiquetas](appearance/labels.html) | Etiquetas junto al logotipo que indican qué instancia es esta; una sola etiqueta Environment hasta la 7.2 | diff --git a/i18n/es/_configure/appearance/defaults.md b/i18n/es/_configure/appearance/defaults.md new file mode 100644 index 0000000..c71d918 --- /dev/null +++ b/i18n/es/_configure/appearance/defaults.md @@ -0,0 +1,46 @@ +--- +layout: default +title: Valores predeterminados +parent: Apariencia +nav_order: 1 +translated_from: 2974af3a3fc29115349fad94498cac9ecd8c6123 +--- + +# Valores predeterminados +{: .no_toc } + +El tema, el idioma, la zona horaria y el formato de fecha para los usuarios que no han elegido ninguno +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Los valores predeterminados + +**Valores predeterminados** define cómo se ve la aplicación para un usuario que no ha elegido nada en sus +[preferencias](../profile/preferences.html); lo que elige el propio usuario siempre tiene prioridad: + +| Campo | Opciones | Sin elección | +|---|---|---| +| **Tema predeterminado** | Seguir el navegador, Claro, Oscuro o Color, con uno de 12 colores para Color | Seguir el navegador (Claro hasta la 7.2) | +| **Idioma predeterminado** | Los idiomas de la aplicación, guardado en [`DEFAULT_LANGUAGE`](../customization/server.html#env_DEFAULT_LANGUAGE) | Inglés | +| **Vista de formularios predeterminada** | Mosaicos o Lista, para la página de formularios | Mosaicos | +| **Zona horaria predeterminada** | UTC, la del navegador de cada usuario o cualquier zona por su nombre | UTC | +| **Formato de fecha predeterminado** | `YYYY-MM-DD`, `DD/MM/YYYY`, `MM/DD/YYYY` o `DD.MM.YYYY`, mostrado con la fecha de hoy | `YYYY-MM-DD` | + +El idioma es una variable de entorno y se aplica sin reiniciar. Con +[`ALLOW_ENV_EDIT`](../customization/configuration.html#env_ALLOW_ENV_EDIT) a `0`, la página lo muestra en solo lectura. + +Desde la 7.3.0 la página está en las dos pestañas de las preferencias del perfil, **Apariencia** (tema, idioma y vista de formularios) y **Fecha y +hora** (zona horaria y formato de fecha, con una vista previa de la hora actual), y su cuadro de búsqueda lista las coincidencias bajo el nombre de cada pestaña. + +--- + +## Seguir el navegador + +Desde la 7.3.0 **Seguir el navegador** muestra a cada usuario el tema claro u oscuro que pide su navegador, y cambia con él. + +Es el valor predeterminado cuando no se elige ningún tema, así que una instalación nueva sigue al navegador. Al pasar el puntero por uno de los colores de +**Color**, la aplicación se previsualiza en ese color; el tema que tenías vuelve cuando el puntero sale, y no se guarda nada hasta pulsar **Guardar**. diff --git a/i18n/es/_configure/appearance/icon.md b/i18n/es/_configure/appearance/icon.md new file mode 100644 index 0000000..b91760e --- /dev/null +++ b/i18n/es/_configure/appearance/icon.md @@ -0,0 +1,23 @@ +--- +layout: default +title: Icono +parent: Apariencia +nav_order: 4 +translated_from: b469d0e284d78450ebfac22e9e596c9808e1fc16 +--- + +# Icono +{: .no_toc } + +Tu propio icono en la pestaña del navegador y en los marcadores +{: .fs-6 .fw-300 } + +**Icono** sustituye el icono de AnsibleForms en la pestaña del navegador y en los marcadores: + +| Regla | Valor | +|---|---| +| Tipos | PNG, ICO, SVG, WEBP, GIF y JPEG | +| Tamaño máximo | 256 KB | +| Mejor resultado | Una imagen cuadrada de al menos 64 × 64 px, o un SVG | + +Elige un archivo en **Icono nuevo** y pulsa **Subir**; **Eliminar el icono personalizado** recupera el icono predeterminado. diff --git a/i18n/es/_configure/appearance/labels.md b/i18n/es/_configure/appearance/labels.md new file mode 100644 index 0000000..1197cd4 --- /dev/null +++ b/i18n/es/_configure/appearance/labels.md @@ -0,0 +1,79 @@ +--- +layout: default +title: Etiquetas +parent: Apariencia +nav_order: 6 +redirect_from: + - /appearance/environment.html +translated_from: 2b439f61976fc2cc161250c82c629ec40713ef2c +--- + +# Etiquetas +{: .no_toc } + +Etiquetas junto al logotipo que indican qué instancia es esta +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## La lista + +Desde la 7.3.0 **Configuración > Apariencia > Etiquetas** contiene varias etiquetas junto al logotipo de la cabecera, como PRODUCTION y EU: + +| Columna | Significado | +|---|---| +| **Habilitado** | Si la etiqueta se muestra junto al logotipo y en la página de inicio de sesión | +| **Etiqueta** | Su texto, mostrado como la píldora de color que muestra la cabecera | + +Las filas siguen el orden de la cabecera. Un clic en una fila, o **Editar** en su menú de fila, abre la página de la etiqueta; el menú de fila también tiene +**Habilitar** o **Deshabilitar**, según corresponda, **Subir** y **Bajar** para cambiar el orden, y **Eliminar**. + +La cabecera y la página de inicio de sesión muestran las etiquetas habilitadas en su orden. A partir de tres, un **+N** gris agrupa las demás en su tooltip, +y un teléfono muestra la primera etiqueta y el **+N**. Una etiqueta guardada aparece al instante en todos los navegadores abiertos. + +--- + +## Añadir una etiqueta + +**Añadir etiqueta** abre un diálogo con los dos campos de la etiqueta, ambos obligatorios: + +| Campo | Significado | +|---|---| +| **Etiqueta** | El texto de la etiqueta, como PRODUCTION | +| **Color** | Azul, verde, ámbar, naranja, rojo o gris, como muestras redondas; rojo al principio | + +Una vista previa muestra la cabecera con todas las etiquetas habilitadas y la nueva tal como se escribe. Una etiqueta nueva está habilitada y va después de las +demás. Su página tiene una pestaña, **Detalles**, con los mismos campos, el interruptor **Habilitado**, que se guarda al instante, y la vista previa. + +--- + +## La etiqueta de entorno de la 7.2 + +Hasta la 7.2, **Configuración > Apariencia > Entorno** definía una sola etiqueta junto al logotipo. + +Con una sola etiqueta, PRODUCTION EU y PRODUCTION US se leen ambas como PRODUCTION cuando hay varias instancias abiertas a la vez. Desde la 7.3.0 +la actualización convierte esa etiqueta en la primera de la lista, con su texto, su color y si se mostraba, y la antigua dirección de **Entorno** +abre la lista. + +--- + +## Etiquetas en la semilla de configuración + +Desde la 7.3.0 la [semilla de configuración](../seed/) declara etiquetas en su sección `labels`, cada una con su texto y, opcionalmente, su color: + +```yaml +labels: + items: + - name: PRODUCTION + color: red + - name: EU + color: blue + enabled: true + position: 2 +``` + +`color` es uno de `blue`, `green`, `amber`, `orange`, `red` o `grey`, y `position` las ordena, la más baja primero. Una etiqueta declarada +en la semilla es de solo lectura en su página, como todo [objeto gestionado](../seed/managed-objects.html). diff --git a/i18n/es/_configure/appearance/links.md b/i18n/es/_configure/appearance/links.md new file mode 100644 index 0000000..aac555c --- /dev/null +++ b/i18n/es/_configure/appearance/links.md @@ -0,0 +1,33 @@ +--- +layout: default +title: Enlaces +parent: Apariencia +nav_order: 5 +translated_from: b17adbc1210baac2cf6786f7b0cef5e18fea8ae1 +--- + +# Enlaces +{: .no_toc } + +Los enlaces de la cabecera, los integrados y los tuyos +{: .fs-6 .fw-300 } + +**Enlaces** define los enlaces de la cabecera, para todos los usuarios con sesión iniciada, en dos pestañas. + +**Integrados** tiene el interruptor **Diseñador**, guardado en [`SHOW_DESIGNER`](../customization/features.html#env_SHOW_DESIGNER) y aplicado +sin reiniciar. Desactivado, el diseñador desaparece para todos: su enlace, su página y su API. + +**Personalizados** contiene tus propios enlaces, como una wiki, un runbook o un portal de soporte. **Añadir enlace** añade una fila con estos campos: + +| Campo | Significado | +|---|---| +| **Icono** | El icono delante de la etiqueta, elegido de una lista | +| **Etiqueta** | El texto del enlace | +| **Dirección** | Un sitio (`https://…`) o una página de la aplicación (`/forms/all`) | +| **Nueva pestaña** | Abre el enlace en una pestaña nueva del navegador | + +Los botones de una fila la mueven: **→** mete el enlace dentro del enlace de encima, que entonces se abre como un menú en la cabecera, +**←** lo vuelve a sacar a la cabecera, y las flechas arriba y abajo lo mueven (un menú con los enlaces que contiene). +Un menú no necesita dirección propia. + +La cabecera contiene como máximo 5 de tus enlaces después de los integrados, y como máximo 10 dentro de cada menú. diff --git a/i18n/es/_configure/appearance/logo.md b/i18n/es/_configure/appearance/logo.md new file mode 100644 index 0000000..bb525fb --- /dev/null +++ b/i18n/es/_configure/appearance/logo.md @@ -0,0 +1,24 @@ +--- +layout: default +title: Logotipo +parent: Apariencia +nav_order: 3 +translated_from: 758fef67f2f704714e58065380b77f000000317f +--- + +# Logotipo +{: .no_toc } + +Tu propio logotipo en la cabecera +{: .fs-6 .fw-300 } + +**Logo** sustituye el logotipo de AnsibleForms de la cabecera por tu propia imagen, mostrada en todos los temas: + +| Regla | Valor | +|---|---| +| Tipos | PNG, JPEG, WEBP, GIF y SVG | +| Tamaño máximo | 900 KB | +| Tamaño en la cabecera | Como máximo 200 × 33 px, manteniendo sus proporciones | + +Elige un archivo en **Nuevo logo** para previsualizarlo y luego pulsa **Subir**. **Logo actual** muestra el logotipo en uso y el tamaño +con el que lo muestra la cabecera. **Eliminar logo personalizado** recupera el logotipo predeterminado, que sigue al tema. diff --git a/i18n/es/_configure/appearance/name.md b/i18n/es/_configure/appearance/name.md new file mode 100644 index 0000000..71b2c8c --- /dev/null +++ b/i18n/es/_configure/appearance/name.md @@ -0,0 +1,16 @@ +--- +layout: default +title: Nombre +parent: Apariencia +nav_order: 2 +translated_from: fa9f4116c202b7a476161e4881208eacfaadbc45 +--- + +# Nombre +{: .no_toc } + +El nombre de la aplicación en la pestaña del navegador y en la página de inicio de sesión +{: .fs-6 .fw-300 } + +**Nombre** contiene el **Nombre de la aplicación**, mostrado en la pestaña del navegador y en la página de inicio de sesión. Vacío, la pestaña del navegador muestra +`AnsibleForms` y la página de inicio de sesión no muestra ningún nombre. diff --git a/i18n/es/_configure/authentication/entra-id.md b/i18n/es/_configure/authentication/entra-id.md new file mode 100644 index 0000000..66cc765 --- /dev/null +++ b/i18n/es/_configure/authentication/entra-id.md @@ -0,0 +1,146 @@ +--- +layout: default +title: Entra ID +parent: Autenticación +nav_order: 3 +translated_from: c5dad595f7df36cfd31a902ad0a44c7b5bb02f4e +--- + +# Entra ID +{: .no_toc } + +Inicio de sesión único con Microsoft Entra ID (Azure AD), con roles según los grupos de Entra ID del usuario +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Cómo funciona + +Con un proveedor Entra ID habilitado, la página de inicio de sesión muestra un botón de Entra ID debajo de **Entrar**. Un inicio de sesión sigue estos pasos: + +1. El botón envía el navegador a `/api/v2/auth/entraid`, que lo redirige a Microsoft. +2. El usuario inicia sesión en Microsoft, que lo redirige de vuelta a la **URI de redirección** con un código de autorización. El servidor + intercambia ese código con Microsoft por el ID token y un access token para Microsoft Graph. +3. El servidor lee los grupos del usuario de Microsoft Graph (`/v1.0/me/transitiveMemberOf`), así que los grupos anidados también cuentan. +4. Los grupos se filtran con el **Filtro de grupo** sobre su nombre para mostrar, y cada grupo que se conserva se guarda dos veces, + como `azuread/` y como `azuread/`. + +El nombre de usuario es el claim `upn` del token, p. ej. `jdoe@example.com`, o el claim `preferred_username` cuando el token +no tiene `upn`; en una cuenta profesional ambos contienen el mismo valor. + +--- + +## Registrar la aplicación + +AnsibleForms necesita un registro de aplicación en el Entra admin center, en **App registrations > New registration**: + +1. Dale un nombre, elige **Accounts in this organizational directory only** como tipos de cuenta admitidos y añade un + URI de redirección de tipo **Web**: `https://af.example.com/api/v2/auth/entraid/callback`. +2. En **Certificates & secrets**, crea un secreto de cliente y copia su valor; solo se muestra una vez. +3. En **API permissions**, añade los permisos **delegated** de Microsoft Graph `User.Read` y `GroupMember.Read.All`, + y concede el consentimiento de administrador para ellos. +4. En la página **Overview**, anota el **Application (client) ID** y el **Directory (tenant) ID**. + +El URI de redirección es la dirección con la que los usuarios llegan a AnsibleForms, seguida de `/api/v2/auth/entraid/callback`. +Con [`BASE_URL`](../customization/server.html#env_BASE_URL) `/forms`, pasa a ser +`https://af.example.com/forms/api/v2/auth/entraid/callback`. Debe coincidir exactamente con la **URI de redirección** de la +credencial del proveedor. + +Hasta la 7.2.0 la ruta era `/api/v2/auth/azureadoauth2/callback`. Sigue funcionando desde la 7.3.0, así que un registro existente +no necesita cambios; una credencial con la **URI de redirección** vacía usa la ruta nueva, así que en ese caso añádela al registro. + +AnsibleForms lee los grupos de Graph y no usa un claim de grupos en el token, así que no hace falta configurar ningún claim de grupos +en el registro. + +--- + +## Añadir el proveedor + +Añade el proveedor en **Acceso > SSO**: un asistente pide sus campos en cuatro pasos, con **Microsoft Entra ID** en el paso **Tipo**: + +| Paso | Campo | Significado | +|---|---|---| +| Detalles | Nombre, Descripción | Un nombre único para el proveedor y una descripción opcional | +| Tipo | Proveedor | **Microsoft Entra ID**; se fija al crearlo y no cambia | +| Credenciales | Credencial | Una credencial **Microsoft Entra ID** de [Conexiones > Credenciales](../credentials.html), ver más abajo | +| Grupos | Filtro de grupo | Expresión regular; solo se conservan los grupos cuyo nombre para mostrar coincide. Vacío conserva todos los grupos | + +La credencial contiene el registro de la aplicación, así que solo un admin puede añadirla o cambiarla: + +| Campo | Significado | +|---|---| +| ID de inquilino | Obligatorio. El Directory (tenant) ID, un GUID, o uno de los dominios del inquilino. Solo los usuarios de ese inquilino pueden iniciar sesión | +| ID de cliente | El Application (client) ID | +| Secreto de cliente | El valor del secreto de cliente, guardado cifrado o leído de un [almacén de secretos](../secret-stores/) | +| URI de redirección | Tal como está registrado, p. ej. `https://af.example.com/api/v2/auth/entraid/callback`. Vacío: la URL de la aplicación y la ruta de retorno | + +Una credencial con el ID de inquilino vacío se rechaza al guardarla. Los endpoints multiinquilino `common`, `organizations` +y `consumers` se rechazan al guardar el proveedor que usa la credencial, porque permitirían iniciar sesión a usuarios de cualquier +inquilino de Microsoft. Al iniciar sesión, el servidor comprueba además que el token viene del inquilino configurado. Un proveedor +guardado antes sin ID de inquilino no deja entrar a nadie: hasta que se rellena el ID de inquilino, el servidor registra +`Could not enable Microsoft Entra ID: set the tenant id to your tenant ('' would let any Microsoft tenant log in)`. + +Hasta la 7.2.0 el inquilino, el ID de cliente, el secreto y la URL de redirección eran campos del proveedor. La actualización a la 7.3.0 los traslada a una +credencial llamada `-sso`, consulta [Direcciones en las credenciales](../credentials/types.html#addresses-in-credentials). +Elige una credencial para cada proveedor nuevo: el servidor sigue aceptando un proveedor con su propio ID de cliente y secreto de +antes de la 7.3.0, que inicia sesión con ellos hasta que se elige una credencial. + +Solo hay un proveedor Entra ID habilitado a la vez, aquel con el que inicia sesión el botón de Entra ID. **Activar** en el menú de fila de otro, +o su interruptor **Activado**, desactiva los demás de su tipo; la columna **Activado** de la lista muestra cuál está en uso. Un proveedor +también puede saltarse la página de inicio de sesión, consulta [Activar y desactivar el inicio de sesión único](./#switching-single-sign-on). Al guardar se aplica al instante, sin reiniciar. + +--- + +## Asignación a roles + +En config.yaml, un grupo de Entra ID es `entraid/` o `entraid/`, y un usuario es `entraid/`. +El object id es único en el inquilino, así que es la opción más segura; un nombre para mostrar es más fácil de leer: + +```yaml +roles: + - name: admin + groups: + - local/admins + - entraid/0b6c8e1a-2f4d-4c7e-9a3b-5d1e7f2a4c6b + - name: operator + groups: + - entraid/AF-Operators + users: + - entraid/jdoe@example.com +``` + +Hasta la 7.2.0 el prefijo era `azuread/`. En un rol sigue coincidiendo con los mismos miembros desde la 7.3.0 y se elimina en la 8.0; el +editor de roles escribe `entraid/`. Solo los roles aceptan `entraid/`: los grupos del usuario se guardan como `azuread/` y +`azuread/`, y así los muestran la página del perfil y la extravar `ansibleforms_user`. Un **Filtro de grupo** como `^AF-` conserva solo los grupos que usan tus roles; consulta [Roles](../config/roles.html). Desde la 7.1.0 +el token de inicio de sesión ya no lleva los grupos, así que su tamaño ya no crece con ellos. El filtro sigue siendo importante para +usuarios con muchos grupos: en cada inicio de sesión se comparan menos grupos con los roles, y el +[registro del directorio](../users-and-groups/directory.html) del usuario lista solo los grupos que cuentan. Antes de la 7.1.0, +un usuario con cien grupos o más lo necesita para ver algún formulario, consulta +[Formularios y trabajos vacíos tras un inicio de sesión único](../troubleshooting.html#empty-forms-and-jobs-after-a-single-sign-on-http-431). + +--- + +## Endpoint de Microsoft Graph + +El servidor inicia la sesión de los usuarios en `https://login.microsoftonline.com` y llama a Microsoft Graph en `https://graph.microsoft.com` por defecto: + +| Variable | Qué cambia | +|---|---| +| [`ENTRA_AUTHORITY_URI`](../customization/security.html#env_ENTRA_AUTHORITY_URI) | La autoridad de inicio de sesión, para una nube nacional como `https://login.microsoftonline.us` | +| [`ENTRA_GRAPH_URI`](../customization/security.html#env_ENTRA_GRAPH_URI) | El endpoint de Graph del que se leen los grupos, como `https://graph.microsoft.us` | + +Ambas reciben un URI base sin barra final y son nuevas en la 7.3.0. `AZURE_AUTHORITY_URI` y `AZURE_GRAPH_URI` se siguen leyendo +cuando no están definidas, con un aviso en el log, y se eliminan en la 8.0. El servidor debe poder llegar a Graph: un inicio de sesión falla cuando no se pueden leer los grupos. + +--- + +## Limitaciones + +Los usuarios de Entra ID inician sesión con Microsoft, así que algunas cosas funcionan de forma distinta que con las cuentas locales y LDAP: + +- No tienen contraseña en AnsibleForms, así que no pueden cambiarla allí; un [token de API](../profile/api-token.html) se crea desde su sesión. +- Los nombres para mostrar no son únicos en Entra ID: dos grupos con el mismo nombre corresponden al mismo rol. Nombra el grupo por su + object id en el rol, `entraid/`, para que coincida solo con ese grupo. diff --git a/i18n/es/_configure/authentication/index.md b/i18n/es/_configure/authentication/index.md new file mode 100644 index 0000000..e9f27e7 --- /dev/null +++ b/i18n/es/_configure/authentication/index.md @@ -0,0 +1,108 @@ +--- +layout: default +title: Autenticación +nav_order: 3 +has_children: true +has_toc: false +permalink: /authentication/ +translated_from: 024cff3760cf611daeacea8aea7d72b85c0a6d17 +--- + +# Autenticación +{: .no_toc } + +Elige cómo inician sesión los usuarios y cómo su cuenta y sus grupos llevan a los roles +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Métodos de inicio de sesión + +AnsibleForms admite cuatro métodos de inicio de sesión, y varios pueden estar activos a la vez: + +| Método | Cuándo usarlo | Página | +|---|---|---| +| Cuentas locales | El admin integrado, cuentas de servicio, equipos pequeños o como respaldo cuando el directorio no está disponible. | [Cuentas locales](local.html) | +| LDAP | Los usuarios y grupos están en Active Directory, OpenLDAP u otro directorio LDAP. | [LDAP](ldap.html) | +| Entra ID | Los usuarios inician sesión con su cuenta de Microsoft 365 / Entra ID (inicio de sesión único). | [Entra ID](entra-id.html) | +| OIDC | Inicio de sesión único a través de otro proveedor OpenID Connect, como Keycloak. | [OIDC](oidc.html) | + +Las cuentas locales y LDAP se configuran en **Acceso > Usuarios**, **Grupos** y **LDAP**; Entra ID y OIDC en +**Acceso > SSO**. + +--- + +## Cómo elige el método un inicio de sesión + +La página de inicio de sesión tiene un formulario de nombre de usuario y contraseña, más un botón por cada proveedor de inicio de sesión único habilitado: + +- **El nombre de usuario y la contraseña** se comprueban primero contra los usuarios locales. Solo cuando ningún usuario local tiene ese nombre, y LDAP + está habilitado, se prueba el directorio. Un usuario local con una contraseña incorrecta se rechaza sin consultar a LDAP. +- **Los botones de Entra ID y OpenID** aparecen debajo de **Entrar** cuando hay un proveedor así habilitado. Envían el navegador al + proveedor y de vuelta; ninguna contraseña llega a AnsibleForms. + +Solo puede haber habilitado a la vez un proveedor Entra ID y un proveedor OIDC: habilitar uno desactiva los demás de su tipo. +Un inicio de sesión fallido con usuario y contraseña muestra siempre el mismo mensaje genérico; el motivo está en el registro del servidor y en el registro de auditoría. + +--- + +## Activar y desactivar el inicio de sesión único + +Desde la 7.3.0 cada proveedor de **Acceso > SSO** tiene dos interruptores propios, que se aplican sin reiniciar: + +| Interruptor | Efecto | +|---|---| +| **Activado** | Muestra el botón del proveedor en la página de inicio de sesión y acepta su inicio de sesión. Activarlo desactiva los demás proveedores de su tipo. | +| **Saltar la página de inicio de sesión** | Los usuarios van directamente a este proveedor, sin la página de inicio de sesión, mientras el proveedor esté habilitado. Activarlo en un proveedor lo desactiva en los demás; aparece en gris en un proveedor desactivado. | + +El formulario de usuario y contraseña sigue accesible en `/login?local`, para el admin local o cuando el proveedor falla, +y se muestra después de cerrar sesión y cuando un inicio de sesión único vuelve con un error. + +Las variables `ENABLE_SSO` y `SSO_AUTO_LOGIN` se eliminan en la 7.3.0. El primer arranque de la 7.3.0 las lee una vez, y +registra un aviso en cada arranque mientras sigan definidas, así que quítalas del entorno después de la actualización: + +| Variable antes de la 7.3.0 | Qué hace la actualización | +|---|---| +| `ENABLE_SSO=0` | Desactiva todos los proveedores: siguen configurados, sin botón en la página de inicio de sesión | +| `SSO_AUTO_LOGIN=1` | Activa **Saltar la página de inicio de sesión** en el proveedor habilitado, cuando hay exactamente uno habilitado y `ENABLE_SSO` no es `0` | + +--- + +## Del usuario a los roles + +Cada inicio de sesión produce un usuario con un **tipo** (`local`, `ldap`, `azuread` u `oidc`), un nombre de usuario y una lista de grupos. +Cada grupo recibe el prefijo de su tipo, así que un grupo de Entra ID se guarda y se muestra como `azuread/...`: + +| Método de inicio de sesión | Nombre de usuario | Grupos | +|---|---|---| +| Local | el nombre de usuario local | `local/`, uno por cada grupo local del usuario | +| LDAP, directorio `main` | el valor del Username Attribute | `ldap/`, el CN de cada grupo que devuelve el directorio | +| LDAP, otro directorio | `/` | `ldap//`, consulta [Varios directorios](ldap.html#several-directories) | +| Entra ID | el claim `upn`, o `preferred_username` cuando el token no tiene `upn` | `azuread/` y `azuread/` de cada grupo, leídos de Microsoft Graph | +| OIDC | el claim `preferred_username` | `oidc/`, del claim `groups` del ID token | + +Los [roles](../config/roles.html) de config.yaml comparan después estos valores: un rol se aplica cuando uno de sus `groups` es uno +de los grupos del usuario, o cuando su `users` contiene `/`. Todo usuario recibe además el rol `default`. Desde la 7.3.0 un +rol nombra a un miembro de Entra ID como `entraid/` o `entraid/`, que coincide con el valor `azuread/` guardado; `azuread/` +en un rol sigue funcionando y se elimina en la 8.0. El perfil y las extravars del trabajo siguen mostrando `azuread/`. + +```yaml +roles: + - name: admin + groups: + - local/admins + - ldap/AF-Admins + - entraid/AF-Admins + - name: operator + groups: + - oidc/af-operators + users: + - ldap/jdoe +``` + +La página del **perfil** de un usuario lista el tipo de inicio de sesión, los grupos y los roles, que es la forma más rápida de comprobar una asignación. +La opción de rol `allowLogin` puede rechazar el inicio de sesión de los usuarios que no coinciden con ningún rol: ponla a `false` en `default` y a +`true` en los roles que pueden iniciar sesión. diff --git a/i18n/es/_configure/authentication/ldap.md b/i18n/es/_configure/authentication/ldap.md new file mode 100644 index 0000000..b3e5f69 --- /dev/null +++ b/i18n/es/_configure/authentication/ldap.md @@ -0,0 +1,240 @@ +--- +layout: default +title: LDAP +parent: Autenticación +nav_order: 2 +translated_from: a4089d681ad976e31e7ddefb29588bd3ca334dab +--- + +# LDAP +{: .no_toc } + +Deja que los usuarios del directorio inicien sesión con su propia contraseña y dales roles a través de sus grupos del directorio +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Cómo funciona + +AnsibleForms se conecta al directorio con una cuenta de vinculación, busca al usuario y después se vincula como ese usuario para comprobar +la contraseña: + +1. El usuario inicia sesión con nombre de usuario y contraseña. Cuando ningún usuario **local** tiene ese nombre, se prueba LDAP. +2. AnsibleForms se vincula con la cuenta de vinculación de la credencial del directorio y busca en la **Base de búsqueda** una entrada cuyo + **Atributo de nombre de usuario** sea igual al nombre de usuario. +3. Se vincula como la entrada encontrada, con la contraseña que se ha escrito. +4. Los grupos se leen de la entrada (o de una búsqueda de grupos), se filtran y se les añade el prefijo `ldap/`. + +Un nombre de usuario en formato UPN (`jdoe@example.com`) que no encuentra ningún usuario se reintenta una vez con la parte anterior a la `@`, así que los usuarios +pueden iniciar sesión con cualquiera de las dos formas aunque el Atributo de nombre de usuario contenga el nombre corto. Una contraseña incorrecta nunca se reintenta. + +Una misma configuración sirve para Active Directory, OpenLDAP y otros; desde la 7.3.0 puede haber [varios directorios](#several-directories). + +--- + +## Configuración + +Desde la 7.3.0 la configuración es por directorio. **Acceso > LDAP** lista los directorios, en el orden en que se busca un nombre de usuario sin directorio: + +![Acceso > LDAP](../assets/screenshots/ldap.jpg) + +| Menú de fila | Qué hace | +|---|---| +| Editar | Abre la página del directorio | +| Probar conexión | Prueba un inicio de sesión con un usuario y una contraseña de prueba, consulta [Probar la conexión](#testing-the-connection) | +| Activar, Desactivar | Activa o desactiva el directorio; `main` se desactiva en lugar de eliminarse | +| Subir, Bajar | Cambia su lugar en el orden de búsqueda | +| Eliminar | Quita el directorio; se muestra pero desactivado en `main` | + +Añadir un directorio pide sus campos en cuatro pasos, las pestañas de su página. **Detalles** contiene **Activado**, el **Nombre** y una **Descripción**: + +| Pestaña | Ajuste | Significado | +|---|---|---| +| Credenciales | Credencial | Obligatorio. Una credencial `ldap` de [Conexiones > Credenciales](../credentials.html), que contiene la conexión, ver más abajo | +| Usuarios | Base de búsqueda | DN donde empieza la búsqueda de usuarios, p. ej. `OU=Users,DC=example,DC=com`. También se buscan los subárboles | +| Usuarios | Atributo de nombre de usuario | Atributo que se compara con el nombre de inicio de sesión: `sAMAccountName` en AD (el predeterminado), `uid` en OpenLDAP | +| Usuarios | Atributo de correo | Atributo que contiene la dirección de correo, normalmente `mail` | +| Usuarios | Dominios de inicio de sesión | Dominios cuyos inicios de sesión `user@domain` van directamente a este directorio, consulta [Varios directorios](#several-directories) | +| Grupos | Atributo de grupos | Atributo de la entrada del usuario que contiene los grupos (`memberOf`, el predeterminado), o `groups` con una búsqueda de grupos | +| Grupos | Base de búsqueda de grupos | DN donde empieza la búsqueda de grupos | +| Grupos | Clase de grupo | objectClass de los grupos, p. ej. `group`, `groupOfNames` o `posixGroup` | +| Grupos | Atributo de miembro de grupo | Atributo de un grupo que lista sus miembros, p. ej. `member`, `uniqueMember` o `memberUid`; predeterminado `member` | +| Grupos | Atributo de Usuario de Miembro de Grupo | Atributo del usuario que contienen esos valores de miembro, p. ej. `dn` o `uid`; predeterminado `dn` | +| Grupos | Filtro de grupo | Expresión regular; solo se conservan los grupos cuyo nombre coincide. Vacío conserva todos los grupos | + +Los valores predeterminados de los tres últimos campos de grupo solo se aplican cuando se usa la búsqueda de grupos, consulta la sección siguiente. + +La credencial `ldap` contiene cómo se llega al directorio y con qué cuenta se busca: + +| Campo | Significado | +|---|---| +| Servidores | Uno por campo, como `host`, `host:port` o `ldaps://host:port`, probados en este orden: cuando no se puede llegar a uno, se prueba el siguiente | +| Puerto | `389` para LDAP sin cifrar, `636` para LDAPS | +| Habilitar TLS | Conectar con LDAPS (`ldaps://`). StartTLS en el puerto 389 no está soportado | +| Ignorar certificados | Omitir la validación del certificado del servidor. Solo se muestra con **Habilitar TLS** | +| Certificado | El certificado de cliente que AnsibleForms presenta a los servidores, en PEM, cuando se comprueban los certificados | +| Paquete CA | El certificado o certificados de CA que firmaron el certificado del servidor, en PEM, cuando se comprueban los certificados | +| DN de usuario de vinculación | El DN de la cuenta de servicio que busca en el directorio | +| Contraseña de usuario de vinculación | Su contraseña, guardada cifrada o leída de un [almacén de secretos](../secret-stores/) | + +Solo un admin puede añadir o cambiar una credencial `ldap`, consulta [Quién puede cambiar una credencial](../credentials.html#who-may-change-a-credential). +Hasta la 7.2.0 los servidores, TLS y la cuenta de vinculación eran campos de la configuración LDAP. La actualización a la 7.3.0 los traslada a una credencial, +consulta [Direcciones en las credenciales](../credentials/types.html#addresses-in-credentials): + +| El directorio antes de la actualización | Adónde van sus servidores | +|---|---| +| Su propio DN y contraseña de vinculación, sin credencial | Una nueva credencial `ldap` llamada `-ldap`, como `main-ldap` | +| Una credencial sin servidores | A esa credencial | +| Una credencial que ya contiene otros servidores | Una copia de la credencial llamada `-`, que pasa a usar el directorio | + +Un nombre ya ocupado recibe `-2`, `-3`, etc. Un directorio de la semilla de configuración se deja como está: la semilla es su dueña. + +--- + +## Grupos + +Los grupos vienen de uno de dos sitios, y el **Atributo de grupos** decide cuál se usa: + +- **Un atributo de la entrada del usuario**, normalmente `memberOf`. Es la configuración habitual para Active Directory, y para + OpenLDAP con el overlay `memberof`. +- **Una búsqueda de grupos**, para directorios sin `memberOf`. Solo se ejecuta cuando **Base de búsqueda de grupos** y **Clase de grupo** + están definidos: AnsibleForms busca entonces grupos de esa clase cuyo **Atributo de miembro de grupo** contenga el + **Atributo de Usuario de Miembro de Grupo** del usuario, y guarda el resultado en el atributo `groups`. Pon **Atributo de grupos** a `groups` + para usarla. + +{: .note } +> Con **Atributo de grupos** `memberOf`, una búsqueda de grupos configurada se ejecuta pero su resultado no se usa para los roles. + +Cada grupo es un DN, y AnsibleForms conserva su primer CN: `CN=AF-Admins,OU=Groups,DC=example,DC=com` pasa a ser `AF-Admins`. +Después se aplica el **Filtro de grupo** a ese nombre, y los grupos que quedan reciben el prefijo `ldap/`: `ldap/AF-Admins`. + +En directorios grandes el filtro mantiene pequeños los tokens y los trabajos; una expresión regular no válida conserva todos los grupos y se registra en el log. + +--- + +## Asignación a roles + +En config.yaml, un grupo LDAP es `ldap/` y un usuario LDAP es `ldap/`, ambos distinguen mayúsculas y minúsculas: + +```yaml +roles: + - name: admin + groups: + - local/admins + - ldap/AF-Admins + - name: operator + groups: + - ldap/AF-Operators + users: + - ldap/jdoe +``` + +El nombre de usuario es el valor del **Atributo de nombre de usuario** tal como lo devuelve el directorio, no como lo escribió el usuario. +Consulta [Roles](../config/roles.html) para las opciones de los roles. + +--- + +## Varios directorios + +Desde la 7.3.0 puede haber varios directorios, cada uno con sus propios servidores, usuarios y grupos. Sus nombres distinguen a sus miembros: + +| Directorio | Nombre de usuario | Grupos | En un rol | +|---|---|---|---| +| `main` | `jdoe` | `ldap/AF-Admins` | `ldap/jdoe`, `ldap/AF-Admins` | +| Cualquier otro, como `corp` | `corp/jdoe` | `ldap/corp/AF-Admins` | `ldap/corp/jdoe`, `ldap/corp/AF-Admins` | + +Un usuario nombra el directorio al iniciar sesión como `corp\jdoe` o `corp/jdoe`, o inicia sesión con una dirección de un dominio que el directorio +posee. Un nombre de usuario sin directorio se prueba en cada directorio habilitado, en su orden, y lo mismo un nombre como `other\jdoe` cuando ningún +directorio se llama `other`: entonces se busca tal como se escribió. + +Decide el primer directorio que conoce al usuario: una contraseña incorrecta allí es la respuesta, y no se prueba el siguiente +directorio, ya que podría contener a otra persona con ese nombre. Un directorio al que no se puede llegar se omite. + +El directorio llamado `default` hasta la 7.2.0 se llama `main` desde la 7.3.0: la actualización lo renombra, y una instalación nueva empieza +con `main`. Sus miembros conservan sus nombres cortos, así que los roles que los nombran no necesitan cambios. El directorio `main` no se puede +renombrar, y el nombre de un directorio solo contiene letras, dígitos, `-` y `_`. + +--- + +## Probar la conexión + +**Probar conexión** pide un usuario y una contraseña de prueba, e intenta un inicio de sesión con la configuración de la pantalla, guardada o no. +Si sale bien, muestra la entrada del usuario en el directorio, que te indica los nombres exactos de los atributos y los DN de los grupos que debes usar. + +Un usuario de prueba que no existe también informa de éxito: la conexión y la cuenta de vinculación funcionan, solo que el usuario no se +encuentra. Errores habituales: + +| Mensaje | Causa | +|---|---| +| `Bad server or port (connection failed)` | El nombre de host no se resuelve o no se puede llegar al puerto. | +| `Wrong binding credentials` | El DN o la contraseña de vinculación de la credencial del directorio son incorrectos. | +| `Unable to verify the certificate` | El certificado del servidor no está firmado por el Paquete CA de la credencial. | +| `Certificate is not valid` | El Certificado o el Paquete CA de la credencial no contienen un certificado PEM válido. | + +--- + +## Ejemplo con Active Directory + +Este ejemplo se conecta a dos controladores de dominio por LDAPS y conserva solo los grupos cuyo nombre empieza por `AF-`: + +| Ajuste | Valor | +|---|---| +| Credencial > Servidores | `dc01.example.com`, y después `dc02.example.com` | +| Credencial > Puerto | `636` | +| Credencial > Habilitar TLS | activado | +| Credencial > Ignorar certificados | desactivado, con un certificado de cliente y la cadena de CA de la CA empresarial | +| Credencial > DN de usuario de vinculación | `CN=svc-ansibleforms,OU=Service Accounts,DC=example,DC=com` | +| Base de búsqueda | `OU=Users,DC=example,DC=com` | +| Atributo de correo | `mail` | +| Atributo de nombre de usuario | `sAMAccountName` | +| Atributo de grupos | `memberOf` | +| Filtro de grupo | `^AF-` | + +Los miembros de `CN=AF-Operators,…` coinciden con los roles que usan `ldap/AF-Operators`, e inician sesión como `jdoe` o `jdoe@example.com`. + +--- + +## Ejemplo con OpenLDAP + +Este ejemplo usa una búsqueda de grupos sobre entradas `groupOfNames`, así que funciona sin el overlay `memberof`: + +| Ajuste | Valor | +|---|---| +| Credencial > Servidores | `ldap.example.com` | +| Credencial > Puerto | `636` | +| Credencial > Habilitar TLS | activado | +| Credencial > DN de usuario de vinculación | `cn=readonly,dc=example,dc=com` | +| Base de búsqueda | `ou=people,dc=example,dc=com` | +| Atributo de correo | `mail` | +| Atributo de nombre de usuario | `uid` | +| Atributo de grupos | `groups` | +| Base de búsqueda de grupos | `ou=groups,dc=example,dc=com` | +| Clase de grupo | `groupOfNames` | +| Atributo de miembro de grupo | `member` | +| Atributo de Usuario de Miembro de Grupo | `dn` | + +Un usuario listado en `cn=af-admins,ou=groups,dc=example,dc=com` recibe el grupo `ldap/af-admins`. Para grupos `posixGroup`, +usa **Atributo de miembro de grupo** `memberUid` y **Atributo de Usuario de Miembro de Grupo** `uid`. + +Con el overlay `memberof`, deja vacíos los campos de búsqueda de grupos y pon **Atributo de grupos** a `memberOf`. + +--- + +## Errores habituales + +La mayoría de los problemas con LDAP se reducen a uno de estos: + +- **Un usuario local con el mismo nombre.** Los usuarios locales se comprueban primero, así que nunca se consulta el directorio para ese nombre. +- **Sin grupos, o con grupos equivocados.** El Atributo de grupos debe existir (`groups` con una búsqueda de grupos); `memberOf` no sigue el anidamiento. +- **Nombres de grupo que no coinciden.** Usa `ldap/` con las mismas mayúsculas y comprueba que el Filtro de grupo conserva el grupo. +- **DN de grupo que no empiezan por un CN.** Solo se usa el primer valor `CN=` como nombre del grupo, así que todo DN de grupo + debe empezar por `CN=` (o `cn=`). +- **Certificados.** TLS necesita los dos campos de certificado de la credencial y un nombre de servidor que coincida; Ignorar certificados es solo para pruebas. +- **Una configuración que no se puede guardar.** Cuando un directorio viene de la [semilla de configuración](../seed/managed-objects.html), su página + es de solo lectura: cambia el archivo de la semilla. + +Un inicio de sesión fallido muestra un mensaje genérico. El servidor registra el motivo y lo guarda en el registro de auditoría, y para Active +Directory se traduce a partir del código del directorio, p. ej. `Wrong password`, `Account locked` o `Password expired`. diff --git a/i18n/es/_configure/authentication/local.md b/i18n/es/_configure/authentication/local.md new file mode 100644 index 0000000..fc6652f --- /dev/null +++ b/i18n/es/_configure/authentication/local.md @@ -0,0 +1,108 @@ +--- +layout: default +title: Cuentas locales +parent: Autenticación +nav_order: 1 +translated_from: 002a371f2b4dbe5cc9c99ee569b8cf17a2a4ac88 +--- + +# Cuentas locales +{: .no_toc } + +El admin integrado, y los usuarios y grupos guardados en la base de datos de AnsibleForms +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## El admin integrado + +Cada arranque crea el grupo local `admins` y el usuario admin si no existen; tres variables controlan el admin: + +| Variable | Predeterminado | Significado | +|---|---|---| +| [`ADMIN_USERNAME`](../customization/server.html#env_ADMIN_USERNAME) | `admin` | Nombre de usuario del admin, creado en el grupo `admins` cuando ningún usuario tiene ese nombre. | +| [`ADMIN_PASSWORD`](../customization/server.html#env_ADMIN_PASSWORD) | `AnsibleForms!123` | Contraseña que recibe el admin cuando se crea (o se restablece). | +| [`REINIT_ADMIN`](../customization/server.html#env_REINIT_ADMIN) | `0` | `1` restablece la contraseña del admin a `ADMIN_PASSWORD` y lo vuelve a poner en `admins`. | + +`ADMIN_PASSWORD` solo se usa cuando el usuario se crea o se restablece: cambiarla después no hace nada por sí solo. + +Un usuario local que inicia sesión con la contraseña predeterminada pública `AnsibleForms!123` solo puede cambiarla. El navegador abre una +página de cambio de contraseña que no se puede cerrar, y el servidor responde a cualquier otra petición con `403` (código +`password_change_required`) hasta que se cambia la contraseña. La nueva contraseña debe ser distinta de la predeterminada; tras el +cambio, el usuario vuelve a iniciar sesión con ella. Define `ADMIN_PASSWORD` con tu propio valor antes del primer arranque para saltarte este paso. + +Cambiar `ADMIN_USERNAME` después del primer arranque crea un segundo usuario admin con ese nombre; el antiguo se mantiene. + +{: .warning } +> `REINIT_ADMIN=1` restablece la contraseña en **cada** arranque mientras esté definida. Úsala para recuperar una contraseña de admin perdida, +> y después quítala y reinicia. + +--- + +## Usuarios y grupos + +Los usuarios y grupos locales se gestionan en **Acceso > Usuarios** y **Acceso > Grupos**. Un usuario tiene estos campos: + +| Campo | Significado | +|---|---| +| Nombre de usuario | El nombre de inicio de sesión. Debe ser único. | +| Descripción | Opcional. | +| Contraseña | Se guarda como hash. Cámbiala después con la acción **Cambiar contraseña**. | +| Correo electrónico | Opcional. | +| Grupo | El primer grupo local del usuario. Añade más en la pestaña **Grupos** de la página del usuario. | + +Los usuarios locales cambian su propia contraseña en su perfil; los demás usuarios lo hacen en su directorio o proveedor de identidad. + +Un usuario puede pertenecer a varios grupos y recibe los roles de todos ellos. La pestaña **Grupos** de la página de un usuario añade y +quita grupos, igual que la pestaña **Usuarios** de la página de un grupo. Un usuario conserva al menos un grupo: el último no se puede +quitar. + +Un grupo que todavía tiene usuarios no se puede eliminar: quita antes sus usuarios. El grupo `admins` nunca se puede eliminar ni +renombrar, y el usuario `admin` nunca se puede eliminar. + +--- + +## Permitir el inicio de sesión + +La pestaña **Detalles** de la página de un usuario tiene un interruptor **Permitir inicio de sesión**, que también se ofrece como **Bloquear inicio de sesión** y **Permitir +inicio de sesión** en el menú de fila de la lista de usuarios. Existe tanto para usuarios locales como para usuarios LDAP y SSO: + +- **Activado**: el usuario inicia sesión con normalidad. +- **Desactivado**: las sesiones del usuario terminan al instante y se rechaza todo inicio de sesión, hasta que un admin lo vuelva a activar. La página + pide confirmación antes. + +El interruptor no se puede desactivar para el usuario `admin`, la vía de vuelta, ni puede desactivarlo un admin para su propia cuenta. La página de +un usuario LDAP o SSO tiene además **Cerrar sesiones**, que cierra la sesión del usuario en todas partes sin bloquearlo. + +--- + +## Asignación a roles + +Los roles nombran los grupos locales como `local/` y los usuarios como `local/`; por defecto `admins` recibe `admin`: + +```yaml +roles: + - name: admin + groups: + - local/admins + - name: operator + groups: + - local/operators + users: + - local/svc-backup +``` + +Mantén `local/admins` en el rol `admin`, para que el admin integrado siempre pueda iniciar sesión, incluso cuando el directorio o el +proveedor de identidad no estén disponibles. Cuando config.yaml no se puede cargar en absoluto, los miembros de `local/admins` siguen recibiendo el +rol `admin`, así que puedes iniciar sesión y corregirlo. Consulta [Roles](../config/roles.html) para las opciones de los roles. + +--- + +## Cuentas locales y LDAP + +Un inicio de sesión con usuario y contraseña comprueba los usuarios locales antes que LDAP. Cuando un usuario local tiene el mismo nombre que un usuario +del directorio, la cuenta local siempre gana y la contraseña del directorio no funciona para ese nombre. Da a las cuentas locales nombres +que no puedan coincidir con cuentas del directorio, por ejemplo con un prefijo `svc-`. diff --git a/i18n/es/_configure/authentication/oidc.md b/i18n/es/_configure/authentication/oidc.md new file mode 100644 index 0000000..e2348df --- /dev/null +++ b/i18n/es/_configure/authentication/oidc.md @@ -0,0 +1,133 @@ +--- +layout: default +title: OIDC +parent: Autenticación +nav_order: 4 +translated_from: 5451df911884021544dea5ffbaa7228a7a4435ef +--- + +# OIDC +{: .no_toc } + +Inicio de sesión único con un proveedor OpenID Connect como Keycloak, con roles según un claim de grupos +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Cómo funciona + +Con un proveedor OIDC habilitado, la página de inicio de sesión muestra un botón de OpenID debajo de **Entrar**. Un inicio de sesión sigue estos pasos: + +1. El botón envía el navegador a `/api/v2/auth/oidc`, que lo redirige al proveedor (flujo de código de autorización + con PKCE). +2. El usuario inicia sesión en el proveedor, que lo redirige de vuelta a la **URI de redirección**. +3. El servidor intercambia el código por tokens y lee los claims del ID token. +4. El nombre de usuario es el claim `preferred_username`, y cada valor del claim `groups` que pasa el + **Filtro de grupo** se convierte en un grupo `oidc/`. + +AnsibleForms solicita los scopes `openid profile email`; los scopes no son configurables. Cerrar sesión en AnsibleForms +también envía el navegador al endpoint de fin de sesión del proveedor, cuando el proveedor publica uno. + +{: .note } +> OIDC se ha probado con Keycloak. Otros proveedores funcionan cuando cumplen los requisitos siguientes. + +--- + +## Requisitos del proveedor + +El proveedor debe ofrecer el descubrimiento de OpenID Connect y un cliente confidencial para AnsibleForms: + +- **Descubrimiento**: AnsibleForms lee `/.well-known/openid-configuration`, así que el emisor debe ser accesible desde + el servidor de AnsibleForms, por HTTPS. +- **Un cliente confidencial** con el flujo de código de autorización, un ID de cliente y un secreto de cliente. +- **El URI de redirección** `https://af.example.com/api/v2/auth/oidc/callback`, es decir, la dirección con la que los usuarios llegan a + AnsibleForms seguida de `/api/v2/auth/oidc/callback`. Con [`BASE_URL`](../customization/server.html#env_BASE_URL) + `/forms`, pasa a ser `https://af.example.com/forms/api/v2/auth/oidc/callback`. +- **Un claim `groups` en el ID token**, con una lista de nombres de grupo. Sin él, el usuario no recibe grupos OIDC. +- **Un claim `preferred_username`**, que se convierte en el nombre de usuario. + +--- + +## Ejemplo con Keycloak + +Este ejemplo usa un realm `example` en `https://keycloak.example.com`; el emisor es entonces +`https://keycloak.example.com/realms/example`. En ese realm: + +1. En **Clients**, crea un cliente OpenID Connect con el client ID `ansibleforms`. Activa **Client authentication**, + mantén **Standard flow** y pon en **Valid redirect URIs** `https://af.example.com/api/v2/auth/oidc/callback`. +2. En la pestaña **Credentials** del cliente, copia el **Client secret**. +3. En la pestaña **Client scopes**, abre `ansibleforms-dedicated` y añade un mapper **By configuration > Group Membership**, + con **Token Claim Name** `groups`, **Full group path** desactivado y **Add to ID token** activado. + +Con **Full group path** activado, el claim contiene rutas como `/af-admins`, y entonces el rol debe usar `oidc//af-admins`. + +--- + +## Añadir el proveedor + +Añade el proveedor en **Acceso > SSO**: un asistente pide sus campos en cuatro pasos, con **Open ID** en el paso **Tipo**: + +| Paso | Campo | Significado | +|---|---|---| +| Detalles | Nombre, Descripción | Un nombre único para el proveedor y una descripción opcional | +| Tipo | Proveedor | **Open ID**; se fija al crearlo y no cambia | +| Credenciales | Credencial | Una credencial **Open ID** de [Conexiones > Credenciales](../credentials.html), ver más abajo | +| Grupos | Filtro de grupo | Expresión regular; solo se conservan los valores del claim `groups` que coinciden. Vacío conserva todos los grupos | + +La credencial contiene el cliente, así que solo un admin puede añadirla o cambiarla: + +| Campo | Significado | +|---|---| +| Emisor | Obligatorio. La URL del emisor, p. ej. `https://keycloak.example.com/realms/example` | +| ID de cliente | El ID de cliente en el proveedor, p. ej. `ansibleforms` | +| Secreto de cliente | El secreto de cliente, guardado cifrado o leído de un [almacén de secretos](../secret-stores/) | +| URI de redirección | Tal como está registrado, p. ej. `https://af.example.com/api/v2/auth/oidc/callback`. Vacío: la URL de la aplicación y la ruta de retorno | + +Hasta la 7.2.0 el emisor, el ID de cliente, el secreto y la URL de redirección eran campos del proveedor. La actualización a la 7.3.0 los traslada a una +credencial llamada `-sso`, consulta [Direcciones en las credenciales](../credentials/types.html#addresses-in-credentials). +Elige una credencial para cada proveedor nuevo: el servidor sigue aceptando un proveedor con su propio ID de cliente y secreto de +antes de la 7.3.0, que inicia sesión con ellos hasta que se elige una credencial. + +Solo hay un proveedor OIDC habilitado a la vez, aquel con el que inicia sesión el botón de OpenID. **Activar** en el menú de fila de otro, +o su interruptor **Activado**, desactiva los demás de su tipo; la columna **Activado** de la lista muestra cuál está en uso. +Un proveedor también puede saltarse la página de inicio de sesión, consulta [Activar y desactivar el inicio de sesión único](./#switching-single-sign-on). + +Guardar el proveedor lo aplica de inmediato. Cuando no se puede llegar al emisor, el servidor registra +`Failed to initialize OIDC strategy` y los inicios de sesión OIDC fallan hasta que se vuelve a guardar el proveedor o se reinicia el servidor. + +--- + +## Asignación a roles + +En config.yaml, un grupo OIDC es `oidc/` y un usuario es `oidc/`: + +```yaml +roles: + - name: admin + groups: + - local/admins + - oidc/af-admins + - name: operator + groups: + - oidc/af-operators + users: + - oidc/jdoe +``` + +Los valores se comparan exactamente, incluidas mayúsculas y minúsculas. Consulta [Roles](../config/roles.html) para las opciones de los roles. + +--- + +## Limitaciones + +Los usuarios OIDC inician sesión en el proveedor, así que algunas cosas funcionan de forma distinta que con las cuentas locales y LDAP: + +- No tienen contraseña en AnsibleForms, así que no pueden cambiarla allí; desde la 7.2.0 un [token de API](../profile/api-token.html) + se crea desde su sesión. +- Mantén corto el claim `groups`: envía solo los grupos que importan a AnsibleForms, por ejemplo con un client scope dedicado + o una estructura de grupos en el proveedor, o fíltralos con el **Filtro de grupo**. Desde la 7.1.0 el token de inicio + de sesión de AnsibleForms ya no lleva los grupos; antes, un usuario con cien grupos o más podía no ver ningún formulario, consulta + [Formularios y trabajos vacíos tras un inicio de sesión único](../troubleshooting.html#empty-forms-and-jobs-after-a-single-sign-on-http-431). diff --git a/i18n/es/_configure/config/categories.md b/i18n/es/_configure/config/categories.md new file mode 100644 index 0000000..412435b --- /dev/null +++ b/i18n/es/_configure/config/categories.md @@ -0,0 +1,32 @@ +--- +layout: default +title: Categorías +parent: config.yaml +nav_order: 1 +translated_from: b85b1c12cd01bdbdaa81c2de4ea9d91dac269e2f +--- + +# Categorías +{: .no_toc } + +Agrupa los formularios en el panel. +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## El atributo categories + +La lista `categories` de nivel superior de config.yaml: + +{% include config_attribute.html name="categories" %} + +--- + +## Objeto Category + +Cada entrada de la lista `categories`: + +{% include config_object.html name="Category" %} diff --git a/i18n/es/_configure/config/constants.md b/i18n/es/_configure/config/constants.md new file mode 100644 index 0000000..e2e96e4 --- /dev/null +++ b/i18n/es/_configure/config/constants.md @@ -0,0 +1,21 @@ +--- +layout: default +title: Constantes +parent: config.yaml +nav_order: 3 +translated_from: 094e6c6c46990b88d06d0f6b1ae8e066b1912e57 +--- + +# Constantes +{: .no_toc } + +Objetos libres que todos los formularios pueden reutilizar. +{: .fs-6 .fw-300 } + +--- + +## El atributo constants + +El objeto `constants` de nivel superior de config.yaml: + +{% include config_attribute.html name="constants" %} diff --git a/i18n/es/_configure/config/index.md b/i18n/es/_configure/config/index.md new file mode 100644 index 0000000..52b6bc8 --- /dev/null +++ b/i18n/es/_configure/config/index.md @@ -0,0 +1,73 @@ +--- +layout: default +title: config.yaml +nav_order: 5 +has_children: true +has_toc: false +permalink: /config/ +translated_from: 6c0e2d282bc1dc386b7392e3af1f60fea593f12b +--- + +# config.yaml +{: .no_toc } + +Categorías, roles y constantes para todos los formularios +{: .fs-6 .fw-300 } + +--- + +Para administradores: el archivo config.yaml que contiene las categorías, los roles y las constantes compartidos por todos los formularios. + +AnsibleForms usa un archivo config.yaml para configurar categorías, roles y constantes. Si no se proporciona ningún archivo, se genera uno con una configuración mínima. La configuración también se puede importar a la base de datos, con lo que deja de hacer falta un archivo config.yaml. + +--- + +## Visión general + +El archivo config.yaml contiene la configuración de las categorías, los roles y las constantes que usa AnsibleForms. + +**VÍDEO**: [Crea tu primer formulario](https://www.youtube.com/watch?v=lIhYZ9Et5Ic) + +El siguiente config.yaml mínimo sirve como punto de partida. Contiene solo la categoría obligatoria `Default` y los roles obligatorios `admin` y `default`. + +```yaml +categories: # a list of categories to group forms + - name: Default + icon: bars +roles: # a list of roles + - name: admin + groups: + - local/admins + - name: default + groups: [] + options: + allowJobRelaunch: true +constants: {} # free objects to re-use over all forms +``` + +--- + +## Prioridad de carga de la configuración + +AnsibleForms carga el archivo config.yaml en el siguiente orden (gana la primera coincidencia): + +1. **DESDE LA BASE DE DATOS** : la configuración importada a la base de datos, solo cuando `ENABLE_CONFIG_IN_DATABASE=1`, o cuando esa + variable no está definida y el origen de configuración guardado en la base de datos es `database` (una copia vacía recurre al archivo) +2. **DESDE UN REPOSITORIO (use_for_config)** : el repositorio con el interruptor "usar para la configuración" activado (nuevo en la 6.1.0) +3. **DESDE UN REPOSITORIO (use_for_forms)** : el repositorio con el interruptor "usar para los formularios" activado (alternativa por compatibilidad con versiones anteriores) +4. **DESDE UN ARCHIVO LOCAL** : el archivo local `config.yaml` (variable de entorno CONFIG_PATH) + +{: .warning } +> **Nota:** Activa "usar para la configuración" en un solo repositorio. Si varios lo tienen activado, se registra un aviso y se usa el primero. + +--- + +## En esta sección + +El archivo tiene tres atributos de nivel superior, y cada uno tiene su propia página: + +| Página | Qué trata | +|---|---| +| [Categorías](categories.html) | Agrupa los formularios del panel en un árbol de categorías con iconos | +| [Roles](roles.html) | Asigna usuarios y grupos a roles, y define qué puede hacer cada rol | +| [Constantes](constants.html) | Objetos libres que todos los formularios pueden reutilizar | diff --git a/i18n/es/_configure/config/roles.md b/i18n/es/_configure/config/roles.md new file mode 100644 index 0000000..654bee2 --- /dev/null +++ b/i18n/es/_configure/config/roles.md @@ -0,0 +1,125 @@ +--- +layout: default +title: Roles +parent: config.yaml +nav_order: 2 +translated_from: c23a8fb9203e85e81cd128d2c129228c0a207f6c +--- + +# Roles +{: .no_toc } + +Asigna usuarios y grupos a roles, y define qué puede hacer cada rol. +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## El atributo roles + +La lista `roles` de nivel superior de config.yaml: + +{% include config_attribute.html name="roles" %} + +--- + +## El rol default + +Todo usuario con sesión iniciada tiene el rol `default`, así que un formulario con `roles: [default]` está abierto a todos los que pueden iniciar sesión. No +hay acceso anónimo: nadie recibe este rol sin iniciar sesión. + +El rol no contiene grupos ni usuarios, ya que se aplica a todos. Sus `options` son los valores predeterminados de todos, como +se describe en [Opciones de rol](#role-options) más abajo. `config.yaml` debe tener un rol `admin` y un rol `default`, y la +página de roles muestra ambos como **Requerido**. + +`default` se llamaba `public` antes de la 7.0.0, y el nombre antiguo sigue funcionando: + +- **En un formulario**, `roles: [public]` sigue coincidiendo con todos los usuarios, así que los formularios de tus repositorios no necesitan cambios. +- **En `config.yaml`**, un rol llamado `public` se renombra a `default` en el archivo al cargar la configuración. Solo cambia el + nombre, antes se hace una copia de seguridad de la configuración, y una configuración guardada en un repositorio queda con un cambio que hacer push. +- **En una plantilla ytt**, la plantilla no se reescribe: se renombra la configuración que produce, y un aviso en el log te pide + que renombres también el rol en la plantilla. La versión 8 rechaza ahí un rol `public`. + +Una configuración con un rol `default` y un rol `public` a la vez se rechaza, porque son el mismo rol. + +--- + +## Objeto Role + +Cada entrada de la lista `roles`: + +{% include config_object.html name="Role" %} + +--- + +## Opciones de rol + +Las `options` de un rol activan o desactivan funciones para sus miembros. Desde la 7.3.0 la página del rol las muestra en cuatro grupos, +cada opción con una (i) que dice lo que da, el mismo texto que muestra la vista **Permisos** del perfil: + +| Opción | Permite al usuario | Predeterminado | +|---|---|---| +| **Configuración** | | | +| `showSettings` | Ver y cambiar todas las páginas de configuración; el registro del servidor y las copias de seguridad tienen sus propias opciones | Solo admins | +| `showAppearanceSettings` | Cambiar la configuración de apariencia, sin el resto de la configuración (desde la 7.3.0) | Desactivado | +| `showConnectionsSettings` | Gestionar la configuración de conexiones (credenciales, runners y más), sin el resto de la configuración (desde la 7.3.0) | Desactivado | +| `showLogs` | Ver el registro del servidor | Solo admins | +| `showAuditLog` | Ver el registro de auditoría, sin el resto de la configuración (desde la 7.3.0) | Solo admins | +| `allowBackupOps` | Hacer copias de seguridad de la base de datos y restaurarla | Solo admins | +| **Diseñador** | | | +| `showDesigner` | Ver y usar el diseñador | Solo admins | +| `allowFormDesign` | Editar la configuración de formularios en el diseñador: categorías, constantes y formularios. Sin ella, solo lectura (desde la 7.3.0) | Activado | +| `allowPlaybookDesign` | Editar los playbooks en el diseñador: guardarlos, crearlos, quitarlos y hacer push. Sin ella, solo lectura | Activado | +| `allowWorkflowDesign` | Editar los workflows en el diseñador: guardarlos, crearlos, quitarlos y hacer push. Sin ella, solo lectura | Activado | +| `allowInventoryDesign` | Editar los inventories en el diseñador: guardarlos, crearlos, quitarlos y hacer push. Sin ella, solo lectura | Activado | +| `allowVarsFileDesign` | Editar los archivos de variables en el diseñador: guardarlos, crearlos, quitarlos y hacer push. Sin ella, solo lectura | Activado | +| **Trabajos** | | | +| `showJobs` | Ver la página de trabajos | Activado | +| `showAllJobLogs` | Ver los trabajos de todos los usuarios, no solo los suyos | Solo admins | +| `manageAllJobs` | Abortar, eliminar y relanzar los trabajos de todos los usuarios; eliminar y relanzar también necesitan las dos opciones siguientes (desde la 7.3.0) | Solo admins | +| `showExtravars` | Ver las extravars de un formulario y de un trabajo | Activado | +| `showDebugButtons` | Ver los botones de depuración de un formulario | Activado | +| `showArtifacts` | Ver los artefactos de un trabajo: lo que devuelve un trabajo en AWX, o lo que un playbook recogió con `set_stats` | Activado | +| `allowVerboseMode` | Ejecutar trabajos en modo detallado | Activado | +| `allowJobRelaunch` | Relanzar un trabajo con los datos del formulario de una ejecución anterior | Solo admins | +| `allowJobDeletion` | Eliminar trabajos: los suyos, o los de todos los usuarios con `manageAllJobs`. Sin ella, el historial de trabajos se conserva completo (desde la 7.3.0) | Activado | +| `allowScheduledJobs` | Crear y gestionar programaciones recurrentes y puntuales, en la página **Programaciones** (consulta el aviso más abajo) | Solo admins | +| `allowPlannedJobs` | Planificar un trabajo para que se ejecute una vez a una hora fijada (**Ejecutar más tarde**) | Activado | +| `allowStoredJobs` | Guardar los datos de un formulario y volver a cargarlos más tarde | Activado | +| **Acceso e integraciones** | | | +| `allowLogin` | Iniciar sesión en AnsibleForms | Activado | +| `allowChat` | Usar el [asistente de chat](../chat/), cuando está habilitado | Activado | +| `allowMcp` | Usar el [servidor MCP](../mcp/) desde un agente de IA, cuando está habilitado | El `allowChat` del rol | +| `allowFileUploads` | Subir archivos mediante los campos de archivo de un formulario (desde la 7.3.0) | Activado | +| `allowApiTokens` | Crear [tokens de API](../profile/api-token.html) para scripts, hasta la duración máxima que permite la instancia (desde la 7.3.0) | Desactivado | +| `extendedTokenExpiration` | Crear también tokens de API que no caducan nunca; necesita `allowApiTokens` | Desactivado | + +Algunas opciones incluyen otras, y la página del rol muestra las incluidas activadas y en gris: + +- **`showSettings`** da también `showAppearanceSettings`, `showConnectionsSettings` y `showAuditLog`. +- **`manageAllJobs`** da también `showAllJobLogs`, ya que actuar sobre los trabajos de todos los usuarios implica verlos. +- **Las opciones de diseño** solo cuentan con `showDesigner`, y `extendedTokenExpiration` solo con `allowApiTokens`. + +Hasta la 7.2.0, `showAllJobLogs` también permitía al usuario abortar y eliminar los trabajos que mostraba; desde la 7.3.0 eso requiere `manageAllJobs`. +Un rol de antes de la 7.3.0 con `extendedTokenExpiration` y sin `allowApiTokens` recibe `allowApiTokens`, así que conserva sus tokens. + +**Solo admins** significa activado para los usuarios que tienen el rol `admin`, y desactivado para todos los demás. Cuando un usuario coincide con varios roles, +las opciones se combinan así: + +- **Entre los roles del usuario**, una opción solo está activada cuando todos los roles que la definen están de acuerdo: un `false` explícito gana. +- **El rol `default`** completa las opciones que no define ninguno de los demás roles del usuario, así que puede contener valores predeterminados para todos. +- **Las opciones que no se definen en ningún sitio** toman el valor predeterminado de la tabla. + +`showExtraVars`, con V mayúscula, se acepta como otra forma de escribir `showExtravars`; cuando cualquiera de las dos es `false`, la +opción está desactivada. + +{: .warning } +> Trata `allowScheduledJobs` como una opción de nivel de admin. Las programaciones no pertenecen al usuario que las creó: todo usuario con +> la opción ve y puede cambiar todas las programaciones. Una programación además se ejecuta con derechos de admin, para cualquier formulario, sea cual sea el +> acceso del creador. Concédela solo a roles en los que confiarías como admins. + +{: .warning } +> `allowLogin: false` en el rol `default` deja fuera a todo usuario cuyos otros roles no definan `allowLogin: true`, incluidos +> los admins. Vuelve a concederla en los roles que deben iniciar sesión antes de guardar. diff --git a/i18n/es/_configure/credentials.md b/i18n/es/_configure/credentials.md new file mode 100644 index 0000000..83c8e51 --- /dev/null +++ b/i18n/es/_configure/credentials.md @@ -0,0 +1,74 @@ +--- +layout: default +title: Credenciales +nav_order: 6 +has_children: true +has_toc: false +permalink: /credentials.html +translated_from: 08110077a1cf59a4a52e078fc2ebc252f7cae48d +--- + +# Credenciales +{: .no_toc } + +Guarda los accesos con los que se conectan los formularios, los repositorios, los almacenes de secretos y los canales de notificación +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores: añade los usuarios, contraseñas y tokens con los que se conecta AnsibleForms, guardados cifrados o leídos de un almacén de secretos. + +--- + +## En esta sección + +| Página | Qué trata | +|---|---| +| [Tipos de credencial](credentials/types.html) | Qué contiene cada tipo y dónde se puede elegir | +| [La página de credenciales](credentials/list.html) | La lista de credenciales y el diálogo que añade una | +| [La página de una credencial](credentials/credential-page.html) | Las pestañas de una credencial y qué la usa | + +--- + +## Cómo funciona + +Una **credencial** es un acceso con nombre, que se añade en **Conexiones > Credenciales**. Los formularios y los demás objetos de administración +se refieren a ella por su nombre, así que la contraseña nunca aparece en un formulario. Su usuario y su contraseña se guardan en la +base de datos de AnsibleForms, cifrados con `ENCRYPTION_SECRET`, o se leen de un [almacén de secretos](secret-stores/) cada vez que se +usa la credencial. + +--- + +## Quién puede cambiar una credencial + +Desde la 7.3.0 una credencial de la que depende una conexión o un inicio de sesión solo la puede añadir, cambiar o eliminar un admin: + +| Credenciales | Por qué | +|---|---| +| Tipos de conexión: `rte`, `awx`, `aap`, `ascender`, `semaphore`, `rundeck`, `jenkins`, `vault` y `cyberark` | Un runner o un almacén de secretos toma de ella su dirección y su secreto | +| Tipos de inicio de sesión: `oidc`, `entraid` y `ldap` | Un proveedor SSO o un directorio LDAP inicia la sesión de los usuarios con ella | +| Cualquier credencial que nombre un runner | El runner inicia sesión con ella, y un cambio del propio runner también necesita un admin | + +Los demás usuarios con acceso a las credenciales las ven en solo lectura. Un usuario que pudiera apuntar una credencial así a otro +servidor recibiría todos los secretos enviados a través de ella, y por eso la regla sigue la de los runners. Otras tres reglas +mantienen a esos usuarios lejos de secretos que de otro modo no podrían leer: + +- **Nombrar una credencial protegida**: solo un admin puede nombrar una de estas credenciales en otro sitio, como en un repositorio, + un canal de notificación o la configuración del asistente de chat. Una credencial que lee su contraseña de un almacén de secretos está + protegida igual, sea cual sea su tipo. +- **Elegir un almacén de secretos**: solo un admin puede apuntar una credencial a un [almacén de secretos](secret-stores/), o cambiar el + almacén o la referencia que lee; otro usuario puede seguir enviándolos sin cambios, o vaciarlos todos. +- **Mover una credencial**: cuando otro usuario cambia dónde inicia sesión una credencial (su host, puerto, emisor, inquilino o + dirección del servidor) sin volver a escribir su contraseña, la contraseña guardada se borra, y también la referencia del almacén de secretos + que lee, de modo que el secreto antiguo nunca va al nuevo destino. + +--- + +## Credenciales en uso + +Una credencial que todavía nombran otros registros no se puede eliminar ni renombrar, ya que quedarían apuntando a nada. +El rechazo indica cuántos registros la usan; la pestaña **Usado por** de [su página](credentials/credential-page.html) los lista. +Apúntalos antes a otra credencial y después elimínala o renómbrala. diff --git a/i18n/es/_configure/credentials/credential-page.md b/i18n/es/_configure/credentials/credential-page.md new file mode 100644 index 0000000..bef5532 --- /dev/null +++ b/i18n/es/_configure/credentials/credential-page.md @@ -0,0 +1,31 @@ +--- +layout: default +title: "La página de una credencial" +parent: Credenciales +nav_order: 3 +translated_from: 0269b5c0d313622bea6c910b96231f41ffd852d7 +--- + +# La página de una credencial +{: .no_toc } + +Las pestañas de una credencial y qué la usa +{: .fs-6 .fw-300 } + +La página de una credencial muestra los pasos de su diálogo como pestañas, más lo que la usa: + +| Pestaña | Qué contiene | +|---|---| +| Detalles | Nombre y descripción. | +| Tipo | Para qué sirve la credencial. | +| Acceso | Todos los campos de su tipo, como en el paso **Acceso** del diálogo: el usuario, la contraseña y dónde inicia sesión (host y puerto, la base de datos, el servidor de correo, los servidores del directorio, el inquilino o el emisor, la dirección del servidor y su TLS). El AppID, el certificado de cliente y la clave de una credencial CyberArk. | +| Almacén | Base de datos o Almacén de secretos, y el almacén y la referencia. No para CyberArk ni HashiCorp Vault. | +| Usado por | Todo lo que nombra esta credencial: almacenes de secretos, canales de notificación, repositorios, directorios LDAP, proveedores SSO, runners, el asistente de chat, formularios y los workflows del diseñador. Una fila lo abre. | + +La contraseña se escribe en la pestaña **Acceso** y nunca se vuelve a mostrar una vez guardada: si se deja vacía, se mantiene la guardada. En una credencial +CyberArk, deja vacía la clave de cliente para mantener la guardada. Arriba a la derecha están **Probar conexión** (solo credenciales de base de datos), +**Eliminar** y **Guardar**. + +Desde la 7.3.0, **Eliminar** y un nombre nuevo se rechazan mientras **Usado por** liste algo, consulta [Credenciales en uso](../credentials.html#credentials-in-use). + +Una credencial declarada en la [semilla de configuración](../seed/) es de solo lectura en esta página: cámbiala en el archivo de la semilla. diff --git a/i18n/es/_configure/credentials/list.md b/i18n/es/_configure/credentials/list.md new file mode 100644 index 0000000..efe4eda --- /dev/null +++ b/i18n/es/_configure/credentials/list.md @@ -0,0 +1,45 @@ +--- +layout: default +title: La página de credenciales +parent: Credenciales +nav_order: 2 +translated_from: 986d768c15d0b91bdc3760026bd904d3f89ff3e0 +--- + +# La página de credenciales +{: .no_toc } + +La lista de credenciales y el diálogo que añade una +{: .fs-6 .fw-300 } + +La página de **Conexiones > Credenciales** lista todas las credenciales con su **Nombre**, **Tipo**, **Descripción** y **Usado por**, el +número de registros, formularios y workflows que la nombran (consulta [Credenciales en uso](../credentials.html#credentials-in-use)). + +**Añadir credencial** abre un diálogo por pasos: + +| Paso | Campos | +|---|---| +| Credencial | Nombre y descripción, ambos obligatorios. | +| Tipo | Uno de 18 tipos, ver más abajo. | +| Acceso | Con qué y dónde inicia sesión el tipo, ver más abajo. | +| Almacén | Dónde se guardan el usuario y la contraseña: **Base de datos** (cifrados en la base de datos de AnsibleForms) o **Almacén de secretos**, con el almacén y la **Referencia** dentro de él. No para CyberArk ni HashiCorp Vault, con los que inicia sesión un almacén de secretos. | + +El paso **Tipo** lista los tipos por su nombre: AAP, API, Ascender, AWX, CyberArk, Base de datos, Git, HashiCorp Vault, Jenkins, LDAP, +Microsoft Entra ID, Open ID, RTE, Rundeck, Semaphore, SMTP, SSH y Usuario y contraseña (consulta [Tipos de credencial](types.html)). + +El paso **Acceso** contiene todos los campos del tipo, la dirección incluida; no hay un paso de conexión aparte: + +| Tipos | Campos del paso Acceso | +|---|---| +| Todos los tipos salvo CyberArk | La contraseña, o lo que la sustituye: la contraseña de vinculación de LDAP, el secreto de cliente de Entra ID y Open ID, el token de HashiCorp Vault, el token del RTE, el token de API de Semaphore, Rundeck y Jenkins, un token o una contraseña para AWX, AAP y Ascender | +| Todos los tipos salvo HashiCorp Vault, RTE, Semaphore y Rundeck | El usuario: el AppID de CyberArk, el DN de vinculación de LDAP, el ID de cliente de Entra ID y Open ID, opcional para AWX, AAP y Ascender | +| SSH, Base de datos | **Servidor** y **Puerto**; para una base de datos también el **Tipo de base de datos** y la **Base de datos** | +| SMTP | **Servidor de correo** (obligatorio), su **Puerto** y **Usar TLS** | +| LDAP | Los **Servidores** del directorio (obligatorio), su **Puerto** y **Habilitar TLS**; con TLS, **Ignorar certificados**, el **Paquete CA** y un **Certificado de cliente** | +| Microsoft Entra ID | **ID de inquilino** (obligatorio) y **URI de redirección** | +| Open ID | **Emisor** (obligatorio) y **URI de redirección** | +| HashiCorp Vault, CyberArk, RTE, AWX, AAP, Ascender, Semaphore, Rundeck, Jenkins | **Servidor**, la dirección en la que inicia sesión, obligatorio, con **Omitir la verificación del certificado** y el **Paquete CA** | +| CyberArk | **Certificado de cliente** y **Clave de cliente** | + +Una fila abre la página propia de la credencial. Su menú ofrece **Editar**, **Probar conexión** (solo credenciales de base de datos) y **Eliminar**. La +contraseña se cambia en la página de la credencial, en su pestaña **Acceso**. diff --git a/i18n/es/_configure/credentials/types.md b/i18n/es/_configure/credentials/types.md new file mode 100644 index 0000000..269b1d2 --- /dev/null +++ b/i18n/es/_configure/credentials/types.md @@ -0,0 +1,65 @@ +--- +layout: default +title: Tipos de credencial +parent: Credenciales +nav_order: 1 +translated_from: 1f21f8fc1ae73953922d8c9f21de6db05b3b1a87 +--- + +# Tipos de credencial +{: .no_toc } + +Qué contiene cada tipo y dónde se puede elegir +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Los tipos + +El tipo indica para qué sirve una credencial. Decide qué campos pide el diálogo y dónde se puede elegir: + +| Tipo | Qué contiene | Usado por | +|---|---|---| +| Usuario y contraseña | Un usuario y una contraseña para cualquier acceso: una API REST con autenticación básica, un servicio y más. El predeterminado | Playbooks, mediante las `credentials` de un formulario o un campo de credencial; [expresiones REST](../expressions/credentials.html) | +| SSH | Un usuario y una contraseña para SSH a un servidor, con su host y su puerto | Playbooks, mediante las `credentials` o `ansibleCredentials` de un formulario o un campo de credencial, y el acceso de un nodo de workflow | +| Git | Un usuario y una contraseña o un token para repositorios git | [Repositorios](../repositories/authentication.html) | +| API | Un usuario y una contraseña o un token para una API REST | [Expresiones REST](../expressions/credentials.html), el token de un bot de Telegram, el bearer token de un webhook, la clave de API del asistente de chat | +| Base de datos | Un acceso a base de datos, con su tipo, host, puerto y nombre de base de datos | Consultas de formularios (`dbConfig`) y `fnCredentials`; se puede probar | +| CyberArk | La dirección del CyberArk Central Credential Provider (CCP) y su TLS, y el AppID, el certificado de cliente y la clave con los que accede un almacén CyberArk | Un almacén de secretos [CyberArk](../secret-stores/cyberark.html) | +| SMTP | Un servidor de correo, su puerto y TLS, y su acceso (ninguno para un relay) | Un [canal de notificación](../notification-channels.html) de correo | +| LDAP | Los servidores, el puerto, el TLS y los certificados de un directorio, y su DN y contraseña de vinculación | Un [directorio LDAP](../authentication/ldap.html) | +| Microsoft Entra ID | El registro de aplicación de un proveedor: su ID de inquilino, ID de cliente, secreto de cliente y URI de redirección | Un [proveedor Entra ID](../authentication/entra-id.html) | +| Open ID | El cliente de un proveedor: su emisor, ID de cliente, secreto de cliente y URI de redirección | Un [proveedor OIDC](../authentication/oidc.html) | +| RTE | El token (`RTE_TOKEN`) y la dirección de un RTE | Un [runner](../runners.html) RTE | +| AWX, AAP, Ascender | El token o el acceso, y la dirección, de esa plataforma | Un runner de ese tipo | +| Semaphore, Rundeck | El token de API y la dirección de ese producto | Un runner de ese tipo | +| Jenkins | Un usuario, su token de API y la dirección de un Jenkins | Un runner Jenkins | +| HashiCorp Vault | El token y la dirección con los que accede un almacén Vault | Un almacén de secretos [HashiCorp Vault](../secret-stores/hashicorp-vault.html) | + +Una credencial de base de datos puede ser de tipo MySQL, MSSQL, PostgreSQL, Oracle o MongoDB. + +En la API y en la [semilla de configuración](../seed/), los tipos son `userpass`, `ssh`, `git`, `api`, `database`, `cyberark`, `smtp`, +`ldap`, `entraid`, `oidc`, `rte`, `awx`, `aap`, `ascender`, `semaphore`, `rundeck`, `jenkins` y `vault`. La actualización desde la 6 +da a cada credencial el tipo `userpass`, o `database` a una de base de datos: un formulario la usa como en la 6. Cámbiala a SSH para un +acceso con el que se conecta el playbook de un nodo de workflow, o a API para una API REST. + +--- + +## Direcciones en las credenciales + +Desde la 7.3.0 un acceso que funciona en un servidor lleva consigo ese servidor, así que el registro que lo usa solo nombra la credencial: + +| Registro | Qué pasó a su credencial | +|---|---| +| Un directorio LDAP | Sus servidores, puerto, TLS, certificados y cuenta de vinculación, a una credencial `ldap` llamada `-ldap` | +| Un proveedor SSO | Su ID de cliente y secreto, inquilino o emisor, y URI de redirección, a una credencial `entraid` u `oidc` llamada `-sso` | +| Un runner | Su dirección y TLS, y su token o acceso, a una credencial de su tipo, `-token` o `-login` | +| Un almacén de secretos | Su dirección y TLS, y su token (`-token`) o el AppID y el certificado de CyberArk (`-appid`) | +| Un canal de correo | Su servidor, puerto y TLS, a su credencial `smtp`, o a una nueva llamada `-smtp` | + +La actualización a la 7.3.0 hace estos traslados una vez. Un registro que ya nombraba una credencial la conserva, y recibe una copia de ella cuando +otro registro usaba el mismo acceso con otro servidor. Los registros declarados en la [semilla de configuración](../seed/) se dejan como los tiene la semilla. +Las credenciales de las conexiones y de los inicios de sesión solo las puede cambiar un admin, consulta [Quién puede cambiar una credencial](../credentials.html#who-may-change-a-credential). diff --git a/i18n/es/_configure/customization/configuration.md b/i18n/es/_configure/customization/configuration.md new file mode 100644 index 0000000..8a71ed9 --- /dev/null +++ b/i18n/es/_configure/customization/configuration.md @@ -0,0 +1,62 @@ +--- +layout: default +title: Configuración +parent: Variables de entorno +nav_order: 5 +translated_from: f460336cad995f3fd6cb5a2e4667e4c1a3b67a2b +--- + +# Configuración +{: .no_toc } + +La semilla de configuración, dónde se guarda la configuración, git e ytt +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para decidir de dónde sale la configuración: una semilla de configuración, la base de datos, git o plantillas ytt. + +--- + +## Ejemplo + +Aplica una semilla de configuración montada, compruébala cada cinco minutos y guarda `config.yaml` en la base de datos. En `docker-compose.yml`: + +```yaml + environment: + - CONFIG_SEED_PATH=/seed/seed.yaml + - CONFIG_SEED_RELOAD_SECONDS=300 + - ENABLE_CONFIG_IN_DATABASE=1 + - ALLOW_ENV_EDIT=0 # the environment is declared here, not on the settings pages + volumes: + - ./seed:/seed:ro +``` + +Una semilla no válida impide que el servidor arranque. Consulta [Semilla de configuración](../seed/) para el formato del archivo. + +--- + +## De dónde se lee config.yaml + +Las categorías, los roles y las constantes salen de `config.yaml` en disco o de su copia en la base de datos, decidido en este orden: + +1. [`ENABLE_CONFIG_IN_DATABASE`](#env_ENABLE_CONFIG_IN_DATABASE), cuando está definida, en el entorno o en la + pestaña **Configuración** de la página de configuración: `1` lee la base de datos, `0` lee el archivo. +2. Si no, el origen de configuración guardado en la base de datos (`file` o `database`), que las instancias configuradas + antes de la 6.3.0 eligieron en la página de configuración. +3. Si no, el archivo: el `config.yaml` del repositorio con **¿Usar para configuración?** activado, si no el de un repositorio con + **¿Usar para formularios?** activado, y si no el de [`CONFIG_PATH`](paths.html#env_CONFIG_PATH). + +Un `ENABLE_CONFIG_IN_DATABASE` definido explícitamente siempre gana, incluso `0` frente a un `database` guardado. Se lee cada vez que +se carga la configuración, así que un cambio se aplica sin reiniciar. La página **Estado** muestra el origen en uso en su pestaña **Información**. + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="configuration" %} diff --git a/i18n/es/_configure/customization/features.md b/i18n/es/_configure/customization/features.md new file mode 100644 index 0000000..ba59cc1 --- /dev/null +++ b/i18n/es/_configure/customization/features.md @@ -0,0 +1,45 @@ +--- +layout: default +title: Funciones +parent: Variables de entorno +nav_order: 4 +translated_from: dbd2d865ae9b177e389409777bdfffc8f199f9da +--- + +# Funciones +{: .no_toc } + +El diseñador, MCP, el chat, la validación del lanzamiento, la lista de trabajos, las métricas, la API de AWX y el registro de consultas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para activar o desactivar partes opcionales de la aplicación. + +--- + +## Ejemplo + +Sirve el servidor MCP para agentes de IA e impone las reglas de validación del formulario en cada lanzamiento. En el `.env` del proyecto compose: + +```bash +ENABLE_MCP=1 +LAUNCH_VALIDATION=enforce +``` + +`ENABLE_MCP` y `ENABLE_CHAT` se aplican sin reiniciar: se leen en cada petición, así que puedes activarlas en la +página de configuración. Mientras una está desactivada, su endpoint (`/api/v2/mcp` o `/api/v2/chat`) responde `404`. + +Ejecuta primero `LAUNCH_VALIDATION=log` durante un tiempo: registra lo que `enforce` rechazaría, sin rechazarlo. Consulta [MCP](../mcp/) +y [Registrar e imponer](../launch-validation/log-and-enforce.html). + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="features" %} diff --git a/i18n/es/_configure/customization/index.md b/i18n/es/_configure/customization/index.md new file mode 100644 index 0000000..8d90e7c --- /dev/null +++ b/i18n/es/_configure/customization/index.md @@ -0,0 +1,81 @@ +--- +layout: default +title: Variables de entorno +nav_order: 8 +has_children: true +has_toc: false +permalink: /customization/ +translated_from: fd6e83f3550f59aa576c8addaf28bfa6115b96a0 +--- + +# Variables de entorno +{: .no_toc } + +Configura AnsibleForms con variables de entorno +{: .fs-6 .fw-300 } + +--- + +Para administradores que despliegan AnsibleForms: las variables de entorno que lee el servidor, agrupadas por tema. + +AnsibleForms se ajusta con variables de entorno, tanto si se ejecuta con Docker Compose, en Kubernetes o desde el código fuente. + +--- + +## De dónde salen los valores + +El servidor lee cada variable de la primera de estas fuentes que la define, así que un valor más arriba en la lista siempre gana: + +1. **El entorno del proceso**: `environment:` y `env_file:` en docker-compose (el `.env` del proyecto se pasa así), + `env` y `envFrom` en Kubernetes, o la shell que arranca `node`. +2. **`persistent/.env`**, el archivo que escriben las páginas de configuración (`data/.env` en el proyecto docker-compose, otra ruta con + `MANAGED_ENV_PATH`). Se lee en cada arranque, también en producción, y nunca sobrescribe una variable que ya está definida. +3. **`server/.env.`**, por ejemplo `.env.development`. Solo se lee cuando `NODE_ENV` no es `production` (o con + `FORCE_DOTENV=1`), en la práctica al ejecutar [desde el código fuente](../installation/from-source.html) en modo de desarrollo. +4. **El valor predeterminado integrado**, que aparece en las tablas de cada grupo. + +Las páginas de configuración, en **Configuración > General**, muestran el valor vigente y guardan los cambios en `persistent/.env`. Muchas variables +se aplican en cuanto se guardan; las demás se guardan pero necesitan un reinicio, y la página las lista tras guardar. Una variable definida en +el entorno del proceso no se puede cambiar ahí: la página la muestra como *Definida en el entorno, no se puede cambiar aquí.* +Algunas variables, como `ENCRYPTION_SECRET` y `ACCESS_TOKEN_SECRET`, no se pueden cambiar nunca desde la página. + +Las variables que lee un RTE, como `AF_ROLE`, `RTE_MODE`, `APP_URL` o `RTE_MAX_JOBS`, no están en las páginas de configuración: la +aplicación no las usa, así que defínelas en el entorno donde se ejecuta el RTE. Las variables `RTE_*` que lee la propia aplicación, +`RTE_QUEUE_MINUTES` y `RTE_FILES_MAX_MB`, son la excepción y siguen en las páginas. Cuatro variables del RTE, `RTE_MAX_JOBS`, +`RTE_CACHE_MB`, `RTE_FILES` y `RTE_DRAIN_SECONDS`, se pueden definir por RTE en su página de runner, donde un valor gana a la +variable (consulta [La configuración de un RTE](../runners/runner-page.html#an-rtes-settings)). + +Con [`ALLOW_ENV_EDIT`](configuration.html#env_ALLOW_ENV_EDIT)`=0` las páginas solo muestran los valores y se rechaza el guardado. Úsalo +cuando el entorno se declara en un manifiesto, como se describe en [Edición y auditoría](../seed/environment-and-audit.html). + +La [semilla de configuración](../seed/) no define variables de entorno. Declara objetos que se guardan en la base de datos, y las +referencias `${ENV_VAR}` que contiene se resuelven a partir del entorno del servidor en ejecución. + +Algunas variables solo se leen del entorno y no están en las tablas: `MANAGED_ENV_PATH` y `FORCE_DOTENV`, descritas +arriba, y las variables de proxy del sistema `HTTPS_PROXY` y `NO_PROXY` (también se leen en minúsculas). Un RTE outbound llama a la aplicación a través de +`HTTPS_PROXY`, y un RTE inbound envía su llamada de registro a través de él, salvo que el host de la aplicación esté en `NO_PROXY` (nombres de host, +sufijos `.domain`, o `*` para todos los hosts). + +Las variables marcadas como Secreto contienen una contraseña, un token o una clave: mantenlas fuera del control +de versiones, por ejemplo en un Secret de Kubernetes o en un archivo `.env` que no se haga commit. + +--- + +## En esta sección + +Las variables se agrupan por tema, con una página por grupo: + +| Página | Qué trata | +|---|---| +{% for g in site.data.env_groups %}| [{{ g.title }}]({{ g.slug }}.html) | {{ g.lead | slice: 0 | upcase }}{{ g.lead | slice: 1, 500 }} | +{% endfor %} +{% capture other %}{% include env_vars_table.html rest=true %}{% endcapture %} +{% if other contains "" %} +--- + +## Otras variables + +Las siguientes variables todavía no están asignadas a ningún grupo: + +{{ other }} +{% endif %} diff --git a/i18n/es/_configure/customization/logging.md b/i18n/es/_configure/customization/logging.md new file mode 100644 index 0000000..a48bff7 --- /dev/null +++ b/i18n/es/_configure/customization/logging.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Registro +parent: Variables de entorno +nav_order: 7 +translated_from: 81439c2c8adc69d31191b0a07cd9e444ff5fb2be +--- + +# Registro +{: .no_toc } + +Niveles de log, archivos, syslog y colores de la consola +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para ajustar cuánto se registra, adónde van los archivos de log o para mostrar las marcas de tiempo en tu propia zona horaria. + +--- + +## Ejemplo + +Registra más en los archivos que en la consola, y muestra las marcas de tiempo del log y de los trabajos en hora de Ámsterdam. +En el `.env` del proyecto compose: + +```bash +LOG_LEVEL=info +LOG_CONSOLE_LEVEL=notice +LOG_TZ=Europe/Amsterdam +``` + +--- + +## Syslog + +Los servidores syslog ya no se configuran aquí: añádelos en **Configuración > Conexiones > Syslog**, consulta [Logs](../production/logs.html#syslog). +Las variables `LOG_SYSLOG_*` de abajo están obsoletas desde la 7. El primer arranque que encuentra `LOG_SYSLOG_HOST` definida las importa +una vez como el servidor syslog `syslog`, que recibe el registro del servidor, y a partir de entonces las ignora. + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="logging" %} diff --git a/i18n/es/_configure/customization/paths.md b/i18n/es/_configure/customization/paths.md new file mode 100644 index 0000000..3ebed97 --- /dev/null +++ b/i18n/es/_configure/customization/paths.md @@ -0,0 +1,45 @@ +--- +layout: default +title: Rutas +parent: Variables de entorno +nav_order: 2 +translated_from: 232fd91a41c511e9f46867101d74d2313dc7ee3c +--- + +# Rutas +{: .no_toc } + +Dónde están la configuración, los formularios, los playbooks, los archivos subidos y las copias de seguridad +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para guardar parte de los datos en otro volumen, por ejemplo las copias de seguridad y los archivos subidos en un almacenamiento mayor o más barato. + +--- + +## Ejemplo + +Copias de seguridad y archivos subidos en un volumen aparte, y el resto de los datos en la carpeta persistente habitual. En `docker-compose.yml`: + +```yaml + environment: + - BACKUP_PATH=/data/backups + - FORMS_BACKUP_PATH=/data/forms_backups + - UPLOAD_PATH=/data/uploads + volumes: + - /srv/ansibleforms-data:/data +``` + +Una ruta nueva solo se aplica a los archivos nuevos: lo que ya está en la carpeta antigua no se mueve, así que cópialo tú. + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="paths" %} diff --git a/i18n/es/_configure/customization/retention.md b/i18n/es/_configure/customization/retention.md new file mode 100644 index 0000000..312aafb --- /dev/null +++ b/i18n/es/_configure/customization/retention.md @@ -0,0 +1,42 @@ +--- +layout: default +title: Copias de seguridad y retención +parent: Variables de entorno +nav_order: 3 +translated_from: 76f81cfa15eaa98199a3d5963f7adc9a6f8b01dc +--- + +# Copias de seguridad y retención +{: .no_toc } + +Los comandos de copia de seguridad, y cuánto tiempo se conservan las copias, los trabajos y los registros de auditoría +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para controlar cuánto historial conserva la base de datos y cuántas copias de seguridad se quedan en disco. + +--- + +## Ejemplo + +Conserva los trabajos terminados durante 90 días y el registro de auditoría durante dos años. En el `.env` del proyecto compose: + +```bash +JOB_RETENTION_DAYS=90 +AUDIT_RETENTION_DAYS=730 +``` + +Una tarea diaria elimina los trabajos terminados más antiguos que esto a las 2:30 y las entradas de auditoría a las 3:30. Un trabajo en ejecución, o uno que espera +aprobación, nunca se elimina. Ambas variables se aplican sin reiniciar cuando se cambian en la página de configuración. + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="retention" %} diff --git a/i18n/es/_configure/customization/security.md b/i18n/es/_configure/customization/security.md new file mode 100644 index 0000000..1582910 --- /dev/null +++ b/i18n/es/_configure/customization/security.md @@ -0,0 +1,61 @@ +--- +layout: default +title: Variables de seguridad +parent: Variables de entorno +nav_order: 6 +translated_from: af00832e0e69ea667004fbe5ebe635dd70ff3684 +--- + +# Variables de seguridad +{: .no_toc } + +Tokens, cifrado, hosts REST salientes, enmascaramiento y los antiguos ajustes VAULT_* +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables para definir tus propios secretos de token y de cifrado, y para limitar lo que los formularios pueden llamar y mostrar. + +--- + +## Ejemplo + +Tus propios secretos, llamadas REST limitadas a una API y a una red interna, y una clave enmascarada más. En el `.env` del proyecto compose: + +```bash +ACCESS_TOKEN_SECRET=change-me-to-a-long-random-string +ENCRYPTION_SECRET=change-me-to-32-random-characters +REST_ALLOWED_HOSTS=api.example.com,10.20.0.0/16 +MASK_EXTRAVARS_REGEX=password|secret|token|apikey +``` + +Define `ENCRYPTION_SECRET` antes de guardar la primera credencial. Desde la 7.3.0, definirla o cambiarla vuelve a cifrar todos los secretos +guardados en el siguiente arranque, con `ENCRYPTION_SECRET_PREVIOUS` indicando la clave antigua cuando era tuya, consulta +[Cambiar el secreto de cifrado](../security/secrets.html#changing-the-encryption-secret). + +--- + +## Llamadas REST y jq + +Desde la 7.3.0 las funciones auxiliares REST (`fn.fnRestBasic`, `fn.fnRestAdvanced`, `fn.fnRestJwt`, `fn.fnRestJwtSecure`) comprueban el +certificado del endpoint al que llaman, como cualquier otra conexión; hasta la 7.2.0 no lo hacían nunca. Se les aplican tres ajustes: + +| Variable | Predeterminado | Efecto | +|---|---|---| +| [`REST_IGNORE_CERTS`](#env_REST_IGNORE_CERTS) | `0` | `1` omite la comprobación del certificado: solo para endpoints con un certificado autofirmado | +| [`REST_TIMEOUT_SECONDS`](#env_REST_TIMEOUT_SECONDS) | `60` | Cuánto espera una llamada REST una respuesta antes de fallar por tiempo de espera | +| [`JQ_TIMEOUT_SECONDS`](#env_JQ_TIMEOUT_SECONDS) | `30` | Cuánto puede ejecutarse un filtro jq antes de detenerse; la salida de jq también está limitada a 64 MB | + +Las tres se aplican al guardarlas, sin reiniciar. Para un endpoint cuyo certificado está firmado por una CA privada, mantén la comprobación activada +y dale esa CA a Node.js: define `NODE_EXTRA_CA_CERTS` con un archivo PEM que la contenga en el entorno de la aplicación y reinicia la aplicación. + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="security" %} diff --git a/i18n/es/_configure/customization/server.md b/i18n/es/_configure/customization/server.md new file mode 100644 index 0000000..3b03654 --- /dev/null +++ b/i18n/es/_configure/customization/server.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Servidor y base de datos +parent: Variables de entorno +nav_order: 1 +translated_from: 0e4bba71dd8dd8efdf5ed7ff9c9644b1c0a89185 +--- + +# Servidor y base de datos +{: .no_toc } + +El servidor web, la conexión a MySQL, la cuenta de admin y lo que ejecuta cada proceso (AF_ROLE y las variables RTE_*) +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Cambia estas variables cuando la aplicación se ejecuta detrás de un proxy inverso, en otro puerto o contra una base de datos fuera del proyecto compose. + +--- + +## Ejemplo + +Detrás de un proxy inverso que termina TLS, bajo `/ansibleforms`, con un MySQL externo. En `docker-compose.yml`: + +```yaml + environment: + - BASE_URL=/ansibleforms # the proxy forwards /ansibleforms/ unchanged + - HTTPS=0 # the proxy terminates TLS + - PORT=8000 + - DB_HOST=mysql.example.com + - DB_PORT=3306 + - DB_USER=ansibleforms + - DB_PASSWORD=change-me +``` + +Estas líneas sustituyen a las líneas `DB_*` y `PORT` que el archivo compose ya tiene ahí, que ganan a los mismos nombres en `.env`. Quita +el servicio `mysqldb` y el `depends_on` que lo espera, y mapea el puerto 8000 en `ports`. La parte del proxy se describe en la +página [Proxy inverso](../production/reverse-proxy.html). + +--- + +## Variables + +Todas las variables de este grupo, con su tipo, su valor predeterminado y su descripción: + +{% include env_vars_table.html group="server" %} diff --git a/i18n/es/_configure/disclaimer.md b/i18n/es/_configure/disclaimer.md new file mode 100644 index 0000000..2621447 --- /dev/null +++ b/i18n/es/_configure/disclaimer.md @@ -0,0 +1,55 @@ +--- +layout: default +title: Aviso legal +nav_order: 7 +has_children: true +has_toc: false +permalink: /disclaimer.html +translated_from: a22b56db001904583c51e6bd28f1281737f32a32 +--- + +# Aviso legal +{: .no_toc } + +Condiciones que cada usuario acepta una vez antes de usar la aplicación, registrando quién aceptó qué versión y cuándo +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores: pide a todos los usuarios que acepten unas condiciones de uso, en **Configuración > Acceso > Aviso legal**, con la opción de rol `showSettings`. + +Desde la 7.3.0 la página lista todas las versiones del aviso legal, y cada versión se abre en una página propia con sus aceptaciones. +Hasta la 7.2, la página tenía en su lugar tres pestañas (**General**, **Versiones** y **Aceptaciones**); la actualización conserva todas las versiones. + +--- + +## En esta sección + +| Página | Qué trata | +|---|---| +| [General](disclaimer/general.html) | La lista de versiones, añadir una y habilitar la que aceptan los usuarios | +| [Versiones](disclaimer/versions.html) | La página de una versión, y cambiar su texto como corrección o para todos de nuevo | +| [Aceptaciones](disclaimer/acceptances.html) | Quién aceptó qué versión, y volver a pedirlo a usuarios elegidos | + + +--- + +## Qué ven los usuarios + +Con una versión habilitada, todo usuario que inicia sesión la recibe en un diálogo antes que nada, admins incluidos: + +1. Leen su título y su texto. +2. Marcan la casilla y pulsan **Aceptar**. **Rechazar**, en rojo, cierra su sesión en su lugar. + +Desde la 7.3.0 la página detrás del diálogo se ve desenfocada hasta que responden. + +Un usuario acepta cada versión una vez: no se le vuelve a preguntar en su siguiente inicio de sesión, solo cuando llega una versión nueva, cuando +un admin guarda un texto cambiado con [Pedirlo a todos de nuevo](disclaimer/versions.html#changing-the-text), o cuando un admin usa +[Volver a pedir](disclaimer/acceptances.html#asking-users-again) sobre su aceptación. + +Hasta que aceptan, la API responde a cada llamada de su sesión con el estado `403` y el código `disclaimer_required`, +salvo las llamadas bajo `/api/v2/disclaimer` y `/api/v2/profile`, y los eventos en directo que sigue el navegador. +A un [token de API](profile/api-token.html) nunca se le pregunta, ya que un script no tiene a nadie que haga clic. diff --git a/i18n/es/_configure/disclaimer/acceptances.md b/i18n/es/_configure/disclaimer/acceptances.md new file mode 100644 index 0000000..261458f --- /dev/null +++ b/i18n/es/_configure/disclaimer/acceptances.md @@ -0,0 +1,41 @@ +--- +layout: default +title: Aceptaciones +parent: Aviso legal +nav_order: 3 +translated_from: 952000db809a111ac98ba2850a5eb8b31e7a96f5 +--- + +# Aceptaciones +{: .no_toc } + +Quién aceptó cada versión, y cómo volver a pedírsela a los usuarios que elijas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Quién la aceptó + +Cada aceptación se registra con el usuario y la versión, y la pestaña **Aceptaciones** de la página de una versión las lista, la más reciente primero: + +| Columna | Significado | +|---|---| +| **Usuario** | El nombre de usuario | +| **Inicio de sesión** | Cómo inició sesión: Local, LDAP, Microsoft Entra ID u OpenID Connect | +| **Aceptado** | Cuándo | +| **Dirección IP** | Desde dónde | +| **Navegador** | Su navegador | + +--- + +## Volver a pedirla + +Desde la 7.3.0 se puede pedir a los usuarios que elijas que vuelvan a aceptar una versión, sin pedírselo a todos. + +**Volver a pedir**, en el menú de una fila o sobre las filas marcadas en la lista, borra esas aceptaciones tras una confirmación: esos +usuarios vuelven a ver el aviso legal en su siguiente petición, sin tener que iniciar sesión de nuevo. La lista también se puede exportar, como cualquier tabla. + +El [registro de auditoría](../production/logs.html#audit-trail) también guarda cada aceptación, cada rechazo y cada cambio del aviso legal. diff --git a/i18n/es/_configure/disclaimer/general.md b/i18n/es/_configure/disclaimer/general.md new file mode 100644 index 0000000..b3daea8 --- /dev/null +++ b/i18n/es/_configure/disclaimer/general.md @@ -0,0 +1,58 @@ +--- +layout: default +title: General +parent: Aviso legal +nav_order: 1 +translated_from: 5545e95acf978130082fcb459c917894e7a5286a +--- + +# General +{: .no_toc } + +La lista de versiones, cómo añadir una y cómo habilitar la que aceptan los usuarios +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## La lista + +Desde la 7.3.0, **Configuración > Acceso > Aviso legal** es una lista de las versiones del aviso legal, una por cada texto tal como se aceptó: + +| Columna | Significado | +|---|---| +| **Habilitado** | Si es la versión que aceptan los usuarios; como mucho hay una habilitada | +| **Versión** | Su número, contando desde 1 | +| **Título** | El título de su diálogo | +| **Publicado por** | Quién la añadió | +| **Publicado** | Cuándo | +| **Aceptaciones** | Cuántos usuarios la aceptaron | + +Un clic en una fila, o **Editar** en su menú, abre la [página de la versión](versions.html). El menú también ofrece **Activar** o **Desactivar**, y **Eliminar**. + +--- + +## Añadir una versión + +**Añadir aviso** abre un diálogo para una versión nueva, con el número siguiente, que no queda habilitada hasta que la habilites: + +| Campo | Significado | +|---|---| +| **Título** | El título del diálogo, como Condiciones de uso | +| **Texto** | Lo que aceptan los usuarios, como mucho 20000 caracteres. Texto plano: se conservan los saltos de línea. | +| **Casilla** | Lo que marcan los usuarios antes de poder aceptar, al principio "He leído y acepto estas condiciones." | + +Los tres son obligatorios. La versión habilitada hasta entonces sigue siendo la que aceptan los usuarios, así que puedes preparar un texto nuevo con antelación. + +--- + +## Habilitar una versión + +**Activar** en el menú de una fila, o el interruptor **Habilitado** en la página de la versión, convierte esa versión en la que aceptan +los usuarios y deshabilita las demás. + +A los usuarios que ya aceptaron esa versión no se les vuelve a pedir, así que puedes volver a habilitar una versión anterior sin pedírsela a todos. +**Desactivar** en la versión habilitada desactiva el aviso legal hasta que se habilite otra versión; las versiones y las aceptaciones se conservan. +Eliminar una versión elimina también sus aceptaciones, y eliminar la habilitada desactiva el aviso legal. diff --git a/i18n/es/_configure/disclaimer/versions.md b/i18n/es/_configure/disclaimer/versions.md new file mode 100644 index 0000000..272694c --- /dev/null +++ b/i18n/es/_configure/disclaimer/versions.md @@ -0,0 +1,43 @@ +--- +layout: default +title: Versiones +parent: Aviso legal +nav_order: 2 +translated_from: dcab4535a8f7e4aeaff321136564ab14d61dc3fa +--- + +# Versiones +{: .no_toc } + +La página de una versión, y cómo cambiar su texto como corrección o volver a pedírselo a todos +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## La página de una versión + +Desde la 7.3.0 cada versión tiene su propia página, en `/settings/disclaimer/`, con dos pestañas: + +| Pestaña | Qué contiene | +|---|---| +| **Detalles** | El título, el texto y el texto de la casilla, y el interruptor **Habilitado**, que se guarda al momento | +| **Aceptaciones** | Quién aceptó esta versión, consulta [Aceptaciones](acceptances.html) | + +**Vista previa** muestra el diálogo tal como lo verán los usuarios, y **Eliminar** borra la versión junto con sus aceptaciones. El cuadro +de búsqueda sobre las pestañas busca en ambas a la vez: los campos que coinciden en **Detalles** y las filas que coinciden en **Aceptaciones**. + +--- + +## Cambiar el texto + +**Guardar** sobre un título, un texto o un texto de la casilla modificados que los usuarios ya aceptaron pregunta **¿Volver a pedirlo a todos?**: + +| Respuesta | Qué ocurre | +|---|---| +| **Pedirlo a todos de nuevo** | Se borran las aceptaciones de esta versión, y cada usuario vuelve a aceptarla en su siguiente petición | +| **Mantener como corrección** | El texto se reescribe, por ejemplo para corregir una errata, y las aceptaciones siguen contando: no se vuelve a pedir a nadie | + +Una versión que nadie ha aceptado todavía se guarda sin preguntar. Para conservar el texto aceptado, [añade una versión nueva](general.html#adding-a-version) y habilítala. diff --git a/i18n/es/_configure/notification-channels.md b/i18n/es/_configure/notification-channels.md new file mode 100644 index 0000000..9f4a90d --- /dev/null +++ b/i18n/es/_configure/notification-channels.md @@ -0,0 +1,83 @@ +--- +layout: default +title: Canales de notificación +nav_order: 9 +has_children: true +has_toc: false +permalink: /notification-channels.html +translated_from: 1488a67f6f281b0236e4bad3a8b3d0c3bb32326c +--- + +# Canales de notificación +{: .no_toc } + +Envía solicitudes de aprobación y notificaciones de trabajos por correo, a Slack, Microsoft Teams, Telegram o a cualquier webhook +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +Para administradores: añade los servidores de correo y los canales de chat a los que los formularios envían sus notificaciones y aprobaciones. + +--- + +## En esta sección + +| Página | Qué trata | +|---|---| +| [Correo](notification-channels/mail.html) | Los servidores SMTP, el predeterminado y su inicio de sesión | +| [Slack y Microsoft Teams](notification-channels/slack-and-teams.html) | El webhook de un canal de Slack o de Teams | +| [Telegram](notification-channels/telegram.html) | Un bot que publica en un chat de Telegram | +| [Webhook](notification-channels/webhook.html) | Una petición JSON a cualquier endpoint HTTP | +| [Usar canales en un formulario](notification-channels/in-a-form.html) | Cómo nombrar canales en las notificaciones y aprobaciones | +| [Probar un canal](notification-channels/testing.html) | Enviar prueba, y qué demuestra | + + +--- + +## Cómo funciona + +Un **canal de notificación** es un destino de las notificaciones, que se añade en **Conexiones > Notificaciones**. Cada canal es +de uno de estos tipos: + +| Tipo | Qué envía | +|---|---| +| Correo | Correo a través de un servidor SMTP: solicitudes de aprobación y notificaciones de trabajos. | +| Slack | Un mensaje en un canal de Slack, mediante su webhook entrante, con un botón que lleva al trabajo. | +| Microsoft Teams | Una tarjeta en un canal de Teams, mediante un webhook de Workflows, con un botón que lleva al trabajo. | +| Telegram | Un mensaje de un bot en un chat de Telegram, con un enlace al trabajo. | +| Webhook | Una petición JSON a cualquier endpoint HTTP, construida a partir de una plantilla. | + +Un formulario nombra sus canales en sus [notificaciones](forms/notifications.html) y en su [aprobación](forms/approval.html), +con `channels:`. Una notificación que no se puede enviar se escribe en el log; nunca hace fallar el trabajo ni la aprobación. + +--- + +## La página de notificaciones + +La página de **Conexiones > Notificaciones** lista todos los canales con estas columnas: + +| Columna | Significado | +|---|---| +| Activado | Si se le envían notificaciones, consulta [Usar canales en un formulario](notification-channels/in-a-form.html). | +| Nombre | Cómo lo nombran los formularios en `channels:`. | +| Tipo | Correo, Slack, Microsoft Teams, Telegram o Webhook. El canal de correo predeterminado muestra una etiqueta **Predeterminado** tras su tipo. | +| Descripción | Para qué sirve el canal. | + +**Añadir canal de notificación** abre un diálogo: **Canal** (nombre y descripción), **Tipo** y después los pasos del tipo +elegido. El tipo se fija al crear el canal y ya no cambia. Una fila abre la página del canal, con los mismos pasos como +pestañas tras **Detalles**, y **Enviar prueba**, **Eliminar** y **Guardar** arriba a la derecha. El menú de la fila ofrece +**Editar**, **Activar** o **Desactivar** (según corresponda), **Usar como predeterminado** (correo), **Enviar prueba** y **Eliminar**. + +La pestaña **Detalles** de la página de un canal contiene el interruptor **Activado**, que se guarda al momento, y para el correo el interruptor +**Predeterminado**, que se guarda con **Guardar**. + +Un usuario que abre estas páginas con `showConnectionsSettings` pero sin el rol de administrador no puede elegir una credencial que solo +un administrador puede nombrar, consulta [Quién puede cambiar una credencial](credentials.html#who-may-change-a-credential). Cuando ese usuario cambia +adónde envía un canal (la url de un webhook o su comprobación del certificado, la dirección de la Bot API de Telegram), el canal pierde la +credencial que nombraba, salvo que elija una en el mismo cambio. + +Un canal declarado en la [semilla de configuración](seed/) es de solo lectura en esta página, y aun así se puede probar. + diff --git a/i18n/es/_configure/notification-channels/in-a-form.md b/i18n/es/_configure/notification-channels/in-a-form.md new file mode 100644 index 0000000..0ea7ff9 --- /dev/null +++ b/i18n/es/_configure/notification-channels/in-a-form.md @@ -0,0 +1,41 @@ +--- +layout: default +title: Usar canales en un formulario +parent: Canales de notificación +nav_order: 5 +translated_from: ab807b79469429faf3c3838f268af1ba6c29805a +--- + +# Usar canales en un formulario +{: .no_toc } + +Cómo nombrar canales en las notificaciones y aprobaciones +{: .fs-6 .fw-300 } + +Los formularios nombran los canales por su nombre, en `notifications` para el estado y los eventos del trabajo, y en `approval`: + +```yaml +notifications: + recipients: + - ops@example.com + channels: + - ops-slack + - ops-teams + onStatus: + - failed + +approval: + title: Remove host $(vm.name) ? + roles: + - approvers + channels: + - approvers-teams +``` + +- Los canales de chat y de webhook reciben un mensaje breve (el formulario, el trabajo, qué ha pasado) con un enlace al trabajo; en una + aprobación, la página del trabajo es donde se aprueba o se rechaza. +- Un canal de **correo** de la lista envía el correo habitual a `recipients` (las direcciones de `notifications` de una aprobación) a través + de ese servidor en lugar del predeterminado. +- Un nombre que no existe, o un canal de chat o de webhook desactivado, se omite y se indica en el log. +- Un canal de **correo** desactivado no se omite: el correo sale por el canal de correo predeterminado, y el + log lo indica. diff --git a/i18n/es/_configure/notification-channels/mail.md b/i18n/es/_configure/notification-channels/mail.md new file mode 100644 index 0000000..450db3f --- /dev/null +++ b/i18n/es/_configure/notification-channels/mail.md @@ -0,0 +1,24 @@ +--- +layout: default +title: Correo +parent: Canales de notificación +nav_order: 1 +translated_from: 3ab21a1f2987744527863f5f25ce347dbdf2cd3d +--- + +# Correo +{: .no_toc } + +Los servidores SMTP, el predeterminado y su inicio de sesión +{: .fs-6 .fw-300 } + +Un canal de correo es un servidor SMTP. Puedes tener varios; el marcado como **Predeterminado** es el servidor con el que la aplicación envía su +correo cuando un formulario no nombra ningún canal de correo. El primer canal de correo pasa a ser el predeterminado, y **Usar como predeterminado** lo cambia. + +| Paso | Campo | Significado | +|---|---|---| +| Credenciales | Credencial | Obligatorio. Una credencial **smtp** de [Conexiones > Credenciales](../credentials.html): el servidor de correo, su puerto y TLS, y el inicio de sesión. Para un relay sin inicio de sesión, una credencial smtp sin usuario. | +| Remitente | Correo de | La dirección del remitente que ven los destinatarios. | + +El interruptor **Predeterminado** de la pestaña **Detalles** de la página de un canal de correo lo convierte en el predeterminado, igual que **Usar como predeterminado**. +Los ajustes de correo de la semilla de configuración se usan mientras ningún canal de correo sea el predeterminado, o mientras el predeterminado esté desactivado. diff --git a/i18n/es/_configure/notification-channels/slack-and-teams.md b/i18n/es/_configure/notification-channels/slack-and-teams.md new file mode 100644 index 0000000..b4e5c7e --- /dev/null +++ b/i18n/es/_configure/notification-channels/slack-and-teams.md @@ -0,0 +1,22 @@ +--- +layout: default +title: Slack y Microsoft Teams +parent: Canales de notificación +nav_order: 2 +translated_from: d0c548d54213641c8b6b142e1d7cd1764b6d02b9 +--- + +# Slack y Microsoft Teams +{: .no_toc } + +El webhook de un canal de Slack o de Teams +{: .fs-6 .fw-300 } + +Los canales de Slack y de Teams solo necesitan la dirección del webhook del canal: + +| Tipo | Dirección del webhook | +|---|---| +| Slack | El webhook entrante del canal (Slack app > Incoming Webhooks). | +| Microsoft Teams | El webhook de Workflows del canal ("post to a channel when a webhook request is received"). | + +La dirección es el secreto que permite a cualquiera publicar en el canal: se guarda cifrada y no se vuelve a mostrar una vez guardada. diff --git a/i18n/es/_configure/notification-channels/telegram.md b/i18n/es/_configure/notification-channels/telegram.md new file mode 100644 index 0000000..6df9e79 --- /dev/null +++ b/i18n/es/_configure/notification-channels/telegram.md @@ -0,0 +1,21 @@ +--- +layout: default +title: Telegram +parent: Canales de notificación +nav_order: 3 +translated_from: c268bd1ed02b399d229ecec8e49d7ba4956ee131 +--- + +# Telegram +{: .no_toc } + +Un bot que publica en un chat de Telegram +{: .fs-6 .fw-300 } + +Un canal de Telegram es un bot que publica en un chat. Su paso **Credenciales** contiene el **Token del bot**, y su paso **Bot** el resto: + +| Campo | Significado | +|---|---| +| Token del bot | Una credencial **api** de [Conexiones > Credenciales](../credentials.html) cuya contraseña es el token del bot que da BotFather. | +| Id del chat | El chat, grupo o canal en el que publica el bot; un número negativo para un grupo o un canal. | +| Dirección de la Bot API | Un servidor Bot API propio; vacío para `api.telegram.org`. | diff --git a/i18n/es/_configure/notification-channels/testing.md b/i18n/es/_configure/notification-channels/testing.md new file mode 100644 index 0000000..dca4f6f --- /dev/null +++ b/i18n/es/_configure/notification-channels/testing.md @@ -0,0 +1,18 @@ +--- +layout: default +title: Probar un canal +parent: Canales de notificación +nav_order: 6 +translated_from: ae699fb758338a45780631ffec3f85ef3401280f +--- + +# Probar un canal +{: .no_toc } + +Enviar prueba, y qué demuestra +{: .fs-6 .fw-300 } + +**Enviar prueba** envía una notificación de prueba a través del canal tal como está guardado, así que guarda antes tus cambios. También funciona +con un canal desactivado. Para un canal de correo +el diálogo pide la dirección a la que enviarla; los demás publican en su propio canal. El diálogo muestra si ha funcionado +y, si no, el motivo. diff --git a/i18n/es/_configure/notification-channels/webhook.md b/i18n/es/_configure/notification-channels/webhook.md new file mode 100644 index 0000000..2ecc201 --- /dev/null +++ b/i18n/es/_configure/notification-channels/webhook.md @@ -0,0 +1,53 @@ +--- +layout: default +title: Webhook +parent: Canales de notificación +nav_order: 4 +translated_from: 99bafedd6f37d6afe3104cd7d8819c3891b518d0 +--- + +# Webhook +{: .no_toc } + +Una petición JSON a cualquier endpoint HTTP +{: .fs-6 .fw-300 } + +Un canal webhook envía una petición JSON a cualquier endpoint HTTP. Su paso **Endpoint** indica adónde y cómo, y su paso +**Credenciales** contiene el **Token bearer**: + +| Campo | Significado | +|---|---| +| Url | La dirección a la que se envía la petición. | +| Método | `POST` (predeterminado) o `PUT`. | +| Token bearer | Una credencial **api** de [Conexiones > Credenciales](../credentials.html) cuya contraseña se envía como `Authorization: Bearer `. Vacío: sin token. | +| Comprobar el certificado TLS | Activado por defecto; desactívalo solo para un endpoint con un certificado autofirmado. | + +{% raw %} +Su paso **Contenido** contiene las **Cabeceras**, cabeceras adicionales como un objeto JSON, por ejemplo `{ "X-Source": "ansibleforms" }`, y el +**Contenido**, la plantilla JSON del cuerpo. Se rellenan estos marcadores: + +| Marcador | Valor | +|---|---| +| `{{title}}` | El título de la notificación | +| `{{text}}` | Su texto | +| `{{status}}` | El estado del trabajo | +| `{{event}}` | El evento del trabajo | +| `{{jobId}}` | El id del trabajo | +| `{{form}}` | El nombre del formulario | +| `{{user}}` | El usuario que lanzó el trabajo | +| `{{link}}` | El enlace al trabajo | + +Un marcador que ocupa una cadena JSON entera (`"{{text}}"`) se sustituye por el valor como JSON; dentro de una cadena más larga, su +texto se escapa para JSON. Un marcador desconocido se deja tal cual. Un contenido vacío envía esta plantilla: + +```json +{ + "title": "{{title}}", + "text": "{{text}}", + "status": "{{status}}", + "event": "{{event}}", + "job": { "id": "{{jobId}}", "form": "{{form}}", "user": "{{user}}" }, + "link": "{{link}}" +} +``` +{% endraw %} diff --git a/i18n/es/_configure/production/backups.md b/i18n/es/_configure/production/backups.md new file mode 100644 index 0000000..6734909 --- /dev/null +++ b/i18n/es/_configure/production/backups.md @@ -0,0 +1,111 @@ +--- +layout: default +title: Copias de seguridad +parent: En producción +nav_order: 2 +redirect_from: + - /production/backups-and-logs.html +translated_from: 85cd29062adaaa52a93b688dcaddda94ef40a7bd +--- + +# Copias de seguridad +{: .no_toc } + +Copias nocturnas, qué contienen, cómo restaurarlas y qué más conviene guardar +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Copias nocturnas + +AnsibleForms hace una copia de seguridad de sí mismo cada noche a medianoche, en la zona horaria de [`LOG_TZ`](../customization/logging.html#env_LOG_TZ) +(UTC por defecto). Estos ajustes la controlan: + +| Variable | Valor predeterminado | Significado | +|----------|---------|---------| +| [`BACKUP_PATH`](../customization/paths.html#env_BACKUP_PATH) | `%PERSISTENT_FOLDER%/backups` | Dónde se escriben las copias, cada una en una carpeta con su marca de tiempo | +| [`NIGHTLY_BACKUP_RETENTION`](../customization/retention.html#env_NIGHTLY_BACKUP_RETENTION) | `7` | Cuántas copias nocturnas se conservan; `0` las conserva todas | +| [`MYSQLDUMP_COMMAND`](../customization/retention.html#env_MYSQLDUMP_COMMAND) | `mariadb-dump --ssl-verify-server-cert=OFF` | El comando que vuelca la base de datos | +| [`MYSQL_COMMAND`](../customization/retention.html#env_MYSQL_COMMAND) | `mariadb --ssl-verify-server-cert=OFF` | El comando que reproduce un volcado al restaurar | +| [`BACKUP_COMMAND_TIMEOUT_SECONDS`](../customization/retention.html#env_BACKUP_COMMAND_TIMEOUT_SECONDS) | `3600` | Cuánto tiempo pueden durar el volcado y la restauración | + +Los usuarios con la opción de rol `allowBackupOps` también pueden hacer una copia en cualquier momento en la página **Copias de seguridad**. La retención +solo elimina copias nocturnas, y solo cuentan para ella las copias con un volcado utilizable, así que una serie de noches fallidas +nunca desplaza la última copia buena. + +Antes de que una actualización cambie la base de datos, el worker hace primero una copia normal, una vez por versión, llamada *Before the upgrade to +<version>*. Aparece en la página **Copias de seguridad** y la retención nocturna nunca la elimina. Si falla (no hay herramienta de volcado, +no se puede escribir en `BACKUP_PATH`), la actualización se detiene e indica el motivo, de modo que la aplicación nunca arranca sobre una base de datos a medio actualizar. +[`UPGRADE_BACKUP`](../customization/retention.html#env_UPGRADE_BACKUP)`=0` actualiza sin copia. + +Los comandos pueden ser envoltorios como `docker exec mysqldump`, siempre que su primera palabra exista localmente. Desde la 7.3.0 se ejecutan +sin shell: las comillas y `$NAME` del entorno del comando funcionan, pero `;`, `|`, `&&`, `>` y `<` se rechazan. + +--- + +## Permisos en la base de datos + +El volcado se ejecuta como [`DB_USER`](../customization/server.html#env_DB_USER), y no necesita más permisos que los que necesita la propia aplicación: + +```sql +CREATE USER 'ansibleforms'@'%' IDENTIFIED BY ''; +GRANT ALL PRIVILEGES ON `AnsibleForms`.* TO 'ansibleforms'@'%'; +``` + +El volcado se hace con `--no-tablespaces`, así que no hace falta el permiso `PROCESS` de todo el servidor, y omite las tablas +que describen los procesos en ejecución (`nodes`, `cache_epochs`, `designer_lock`, `runner_leases`). La restauración lo reproduce con el mismo usuario. +La página **Estado** muestra un aviso en su comprobación **Usuario de la base de datos** cuando el usuario tiene permisos sobre todas las bases de datos, como `root`. + +--- + +## Qué contiene una copia + +Cada copia es una carpeta dentro de `BACKUP_PATH`, con el nombre de su marca de tiempo UTC (`YYYYMMDDHHmmss`), que contiene: + +| Archivo | Contenido | +|------|---------| +| `ansibleforms.sql` | El volcado de la base de datos `AnsibleForms`: usuarios, grupos, credenciales, trabajos, ajustes, el registro de auditoría | +| `config.yaml` | El archivo de [`CONFIG_PATH`](../customization/paths.html#env_CONFIG_PATH) | +| `forms/` | La carpeta de [`FORMS_FOLDER_PATH`](../customization/paths.html#env_FORMS_FOLDER_PATH) | +| `managed.env` | El archivo de entorno que escriben las páginas de configuración, sin `ENCRYPTION_SECRET`, `ENCRYPTION_SECRET_PREVIOUS` ni `ACCESS_TOKEN_SECRET` | +| `meta.yaml` | La descripción de la copia | + +Una copia cuyo volcado falla se vuelve a eliminar y se registra como error. La página **Estado** avisa cuando no hay ninguna copia, +cuando la más reciente tiene más de dos días o cuando no tiene un volcado utilizable, y comprueba que la herramienta de volcado existe. + +[`OLD_BACKUP_DAYS`](../customization/retention.html#env_OLD_BACKUP_DAYS), en cambio, conserva los puntos de restauración del diseñador (60 días por defecto). + +--- + +## Restaurar una copia + +Restaura desde la [página **Copias de seguridad**](../gui/administration.html#restore), con **Crear una nueva copia de seguridad antes de restaurar** marcado. Una restauración: + +* reproduce `ansibleforms.sql`, que sustituye toda la base de datos; desde la 7.3.0, una copia de una versión anterior se vuelve a + actualizar después, como al arrancar una versión nueva, y se informa de cualquier parche que falle +* vuelve a copiar `config.yaml` y sustituye la carpeta `forms/` por la de la copia: desde la 7.3.0 los formularios que no están en la copia se + eliminan en lugar de conservarse, los enlaces y el modo de la carpeta se mantienen, y una copia fallida devuelve la carpeta a su estado anterior +* **no** restaura `managed.env`, porque describe el host del que procede la copia. El botón **Restaurar entorno** + lo recupera si se le pide, guarda el archivo actual como `.env.bak` y requiere un reinicio. + +Una copia sin un volcado utilizable se rechaza; tras una restauración interrumpida, corrige la causa y vuelve a restaurar. + +En un host nuevo, define el mismo `ENCRYPTION_SECRET` antes de restaurar, o las credenciales guardadas no se podrán descifrar. + +--- + +## Qué más hay que guardar + +La copia nocturna cubre la base de datos, la configuración y los formularios, pero nada más de la carpeta persistente: + +* **`ENCRYPTION_SECRET` y `ACCESS_TOKEN_SECRET`** : nunca están en una copia, por diseño. Guárdalos en tu gestor de secretos; + sin el secreto de cifrado, un volcado de la base de datos no sirve para las credenciales. +* **las propias copias** : están en el mismo volumen que la instancia. Copia `BACKUP_PATH` a otro host. +* **el resto de la carpeta persistente** : playbooks, archivos subidos, archivos de variables, repositorios clonados, certificados y logs. + Con el proyecto docker-compose, haz copia de toda la carpeta `data/`, que también contiene la clave SSH, las funciones + personalizadas y los roles y colecciones de Ansible. + +El contenido que procede de git (repositorios, formularios de un repositorio) se puede volver a clonar, así que su copia importa menos. diff --git a/i18n/es/_configure/production/index.md b/i18n/es/_configure/production/index.md new file mode 100644 index 0000000..67e4fa4 --- /dev/null +++ b/i18n/es/_configure/production/index.md @@ -0,0 +1,59 @@ +--- +layout: default +title: En producción +nav_order: 12 +has_children: true +has_toc: false +permalink: /production/ +translated_from: 2d7f4be84a76837fae34c1b414a229448bfae4ea +--- + +# En producción +{: .no_toc } + +Publica, haz copias de seguridad y monitoriza una instancia de la que dependen otras personas +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Antes de pasar a producción + +Los valores predeterminados ponen una instancia en marcha rápidamente, pero varios de ellos solo sirven para un primer vistazo. AnsibleForms +escribe un aviso `[SECURITY]` en el log al arrancar cuando `ENCRYPTION_SECRET` o `ACCESS_TOKEN_SECRET` no están definidos, y la +página **Estado** informa de las copias de seguridad, de los ajustes de retención y del saneador de expresiones. + +--- + +## Lista de comprobación para producción + +Repasa estos puntos una vez antes de que los usuarios dependan de la instancia, y de nuevo tras una actualización mayor: + +| Punto | Qué hacer | Detalles | +|------|------------|---------| +| Contraseña del admin | Cambia la contraseña de la cuenta local de administrador | [Bastionado](../security/hardening.html#the-admin-account) | +| Secreto de cifrado | Define [`ENCRYPTION_SECRET`](../customization/security.html#env_ENCRYPTION_SECRET) antes de guardar ninguna credencial | [Secretos y cifrado](../security/secrets.html#the-two-master-secrets) | +| Secreto de los tokens | Define [`ACCESS_TOKEN_SECRET`](../customization/security.html#env_ACCESS_TOKEN_SECRET), para que las sesiones sobrevivan a un reinicio | [Secretos y cifrado](../security/secrets.html#the-two-master-secrets) | +| HTTPS | Termina TLS con tu propio certificado, no con el de ejemplo | [HTTPS](../security/https.html) | +| Proxy inverso | Publica la instancia detrás de un proxy, opcionalmente bajo [`BASE_URL`](../customization/server.html#env_BASE_URL) | [Proxy inverso](reverse-proxy.html) | +| Endpoint del esquema | Pon [`ALLOW_SCHEMA_CREATION`](../customization/server.html#env_ALLOW_SCHEMA_CREATION) a `0` una vez creado el esquema | [Bastionado](../security/hardening.html#the-database) | +| Llamadas salientes | Restringe las expresiones REST con [`REST_DENIED_HOSTS`](../customization/security.html#env_REST_DENIED_HOSTS) | [Bastionado](../security/hardening.html#expressions-and-outbound-calls) | +| Funciones opcionales | Deja MCP y el asistente de chat desactivados salvo que los uses | [Bastionado](../security/hardening.html#optional-features) | +| Copias de seguridad | Comprueba que la copia nocturna funciona, y cópiala fuera del host | [Copias de seguridad](backups.html) | +| Retención | Decide cuánto tiempo se conservan los trabajos y las entradas de auditoría | [Logs](logs.html#retention) | + +--- + +## En esta sección + +Cada tema tiene su propia página; la protección de la instancia se trata en [Seguridad](../security/): + +| Página | Qué trata | +|---|---| +| [Proxy inverso](reverse-proxy.html) | Publicar AnsibleForms detrás de Nginx, Traefik o Apache, en la raíz o bajo una subruta | +| [Copias de seguridad](backups.html) | Copias nocturnas, qué contienen, cómo restaurarlas y qué más conviene guardar | +| [Logs](logs.html) | Archivos de log, syslog, el registro de auditoría y la retención de trabajos y entradas de auditoría | +| [Escalado](scaling.html) | Varios nodos de aplicación con un worker; el RTE tiene su propia sección, [Entornos de ejecución](../rte/) | +| [Monitorización](monitoring.html) | Sondas de salud, métricas de Prometheus y la página Estado | diff --git a/i18n/es/_configure/production/logs.md b/i18n/es/_configure/production/logs.md new file mode 100644 index 0000000..25e795d --- /dev/null +++ b/i18n/es/_configure/production/logs.md @@ -0,0 +1,126 @@ +--- +layout: default +title: Logs +parent: En producción +nav_order: 3 +translated_from: 17d3894c528610d4916fbdcf4beb5bbbacd2b266 +--- + +# Logs +{: .no_toc } + +Archivos de log, syslog, el registro de auditoría y cuánto tiempo se conservan los trabajos y las entradas de auditoría +{: .fs-6 .fw-300 } + +1. TOC +{:toc} + +--- + +## Archivos de log + +AnsibleForms escribe dos logs con rotación diaria en [`LOG_PATH`](../customization/logging.html#env_LOG_PATH) +(`%PERSISTENT_FOLDER%/logs`), comprime los días anteriores y los conserva durante [`LOG_RETENTION_DAYS`](../customization/logging.html#env_LOG_RETENTION_DAYS) +(30 por defecto, `0` conserva todos los archivos; un cambio en la página de configuración se aplica al momento): + +* `ansibleforms..log` : todo lo que tenga nivel [`LOG_LEVEL`](../customization/logging.html#env_LOG_LEVEL) o superior +* `ansibleforms.errors..log` : solo los errores + +La consola, que se lee con `docker logs` o `kubectl logs`, tiene su propio nivel, +[`LOG_CONSOLE_LEVEL`](../customization/logging.html#env_LOG_CONSOLE_LEVEL). Ambos valen `notice` por defecto, lo adecuado para +producción; usa `info` o `debug` solo mientras investigas un problema, y deja +[`ENABLE_DB_QUERY_LOGGING`](../customization/features.html#env_ENABLE_DB_QUERY_LOGGING) desactivado. Los niveles se pueden cambiar en +las páginas de configuración sin reiniciar, y la página **Registro del servidor** muestra el log a los usuarios con la opción `showLogs`. + +--- + +## Líneas JSON + +Para un recolector de logs como Loki, ELK o Splunk, pon [`LOG_FORMAT`](../customization/logging.html#env_LOG_FORMAT) a `json`. Cada +línea, en la consola y en los archivos, pasa a ser un objeto JSON en lugar de `