From 5d6e43e847e5b8f712406c23e610644c4dcd8d43 Mon Sep 17 00:00:00 2001 From: Sharon Gratch Date: Mon, 29 Jan 2024 17:20:09 +0200 Subject: [PATCH] Enhance vSphere provider help text fields Enhance and rephrase the help text fields and validations for following vSphere dialogs: 1. Create vSphere provider - URL, VDDK init image, username, password. 2. vSphere Credentials view/edit modes - username, password. 3. vSphere details - URL and VDDK edit fields 4. vSphere details - URL and VDDK titles icon help text Changes include supporting formatted help text. Note that handling the fingerprint and skip ca validation fields will be done as part of https://github.com/kubev2v/forklift-console-plugin/issues/827. Signed-off-by: Sharon Gratch --- .../en/plugin__forklift-console-plugin.json | 30 ++--- .../modals/EditModal/EditModal.style.css | 6 + .../EditProviderURL/VSphereEditURLModal.tsx | 44 +++++-- .../EditProviderVDDKImage.tsx | 45 +++++-- .../vsphereSecretFieldValidator.ts | 11 +- .../create/ProvidersCreatePage.style.css | 6 + .../components/VSphereProviderCreateForm.tsx | 114 +++++++++++++++--- .../details/ProviderDetailsPage.style.css | 6 + .../edit/VSphereCredentialsEdit.tsx | 71 ++++++++++- .../list/VSphereCredentialsList.tsx | 19 ++- .../DetailsSection/VSphereDetailsSection.tsx | 21 +++- 11 files changed, 316 insertions(+), 57 deletions(-) diff --git a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json index 357e6b7f4..e6b6931e1 100644 --- a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json +++ b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json @@ -16,7 +16,9 @@ "A Secret containing credentials and other confidential information. Empty may be used for the host provider.": "A Secret containing credentials and other confidential information. Empty may be used for the host provider.", "A service account token with cluster admin privileges, required for authenticating the connection to the API server.": "A service account token with cluster admin privileges, required for authenticating the connection to the API server.", "A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.": "A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.", + "A user name for connecting to the vCenter API endpoint. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .": "A user name for connecting to the vCenter API endpoint. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .", "A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.": "A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.", + "A user password for connecting to the vCenter API endpoint.": "A user password for connecting to the vCenter API endpoint.", "Actions": "Actions", "Add source and target providers for the migration.": "Add source and target providers for the migration.", "Application credential ID": "Application credential ID", @@ -117,26 +119,24 @@ "Empty": "Empty", "Endpoint": "Endpoint", "Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.", - "Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.": "Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.", + "Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk .": "Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk .", "Error": "Error", "Error: CA Certificate must be valid.": "Error: CA Certificate must be valid.", "Error: Fingerprint is required and must be valid.": "Error: Fingerprint is required and must be valid.", "Error: Insecure Skip Verify must be a boolean value.": "Error: Insecure Skip Verify must be a boolean value.", "Error: Name is required and must be a unique within a namespace and valid Kubernetes name (i.e., must contain no more than 253 characters, consists of lower case alphanumeric characters , '-' or '.' and starts and ends with an alphanumeric character).": "Error: Name is required and must be a unique within a namespace and valid Kubernetes name (i.e., must contain no more than 253 characters, consists of lower case alphanumeric characters , '-' or '.' and starts and ends with an alphanumeric character).", - "Error: Password is required and must be valid.": "Error: Password is required and must be valid.", "Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.": "Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.", "Error: The format of the provided token is invalid. Ensure the token is a valid Kubernetes service account token.": "Error: The format of the provided token is invalid. Ensure the token is a valid Kubernetes service account token.", + "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk .": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk .", "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.", - "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.", "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and, optionally, a port. For example: https://api.‹your-openshift-domain›:6443.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and, optionally, a port. For example: https://api.‹your-openshift-domain›:6443.", "Error: The format of the provided URL is invalid. Ensure the URL is in the following format: ip_or_hostname_of_nfs_server:/nfs_path. For example: 10.10.0.10:/ova .": "Error: The format of the provided URL is invalid. Ensure the URL is in the following format: ip_or_hostname_of_nfs_server:/nfs_path. For example: 10.10.0.10:/ova .", "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.": "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.", "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.": "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.", + "Error: The format of the provided VDDK init image is invalid. Ensure the path is a valid container image path. For example: quay.io/kubev2v/vddk:latest.": "Error: The format of the provided VDDK init image is invalid. Ensure the path is a valid container image path. For example: quay.io/kubev2v/vddk:latest.", "Error: This field must be a boolean.": "Error: This field must be a boolean.", "Error: this field must be set to a boolean value.": "Error: this field must be set to a boolean value.", "Error: URL is required and must be valid.": "Error: URL is required and must be valid.", - "Error: Username is required and must be valid.": "Error: Username is required and must be valid.", - "Error: VDDK init image must be valid.": "Error: VDDK init image must be valid.", "Failed": "Failed", "False": "False", "Features": "Features", @@ -248,6 +248,7 @@ "No StorageMaps found.": "No StorageMaps found.", "Not Ready": "Not Ready", "Note: If 'Skip certificate validation' is selected, migrations from this provider will not be secure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.": "Note: If 'Skip certificate validation' is selected, migrations from this provider will not be secure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.", + "Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.": "Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.", "Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
<1>https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA .": "Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
<1>https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA .", "NUMA": "NUMA", "Number of cluster in provider": "Number of cluster in provider", @@ -354,8 +355,6 @@ "Specifies the duration for retaining 'must gather' reports before they are automatically deleted. The default value is -1, which implies automatic cleanup is disabled.": "Specifies the duration for retaining 'must gather' reports before they are automatically deleted. The default value is -1, which implies automatic cleanup is disabled.", "Specify OpenStack Identity (Keystone) endpoint, for example, http://controller:5000/v3.": "Specify OpenStack Identity (Keystone) endpoint, for example, http://controller:5000/v3.", "Specify the type of source provider. Allowed values are ova, ovirt, vsphere, openshift, and openstack. This label is needed to verify the credentials are correct when the remote system is accessible and, for RHV, to retrieve the Manager CA certificate when a third-party certificate is specified.": "Specify the type of source provider. Allowed values are ova, ovirt, vsphere, openshift, and openstack. This label is needed to verify the credentials are correct when the remote system is accessible and, for RHV, to retrieve the Manager CA certificate when a third-party certificate is specified.", - "Specify the VDDK image that you created.": "Specify the VDDK image that you created.", - "Specify the VDDK image that you created. VDDK accelerates migrations significantly.": "Specify the VDDK image that you created. VDDK accelerates migrations significantly.", "SSHA-1 fingerprint": "SSHA-1 fingerprint", "Staging": "Staging", "Status": "Status", @@ -376,6 +375,7 @@ "The limit for memory usage by the controller, specified in Megabytes (Mi). Default value is 800Mi.": "The limit for memory usage by the controller, specified in Megabytes (Mi). Default value is 800Mi.", "The maximum age in hours for must gather cleanup. Default value is -1, which implies never.": "The maximum age in hours for must gather cleanup. Default value is -1, which implies never.", "The password for the ESXi host admin": "The password for the ESXi host admin", + "The path must be empty or a valid container image path. For example: quay.io/kubev2v/vddk:latest.": "The path must be empty or a valid container image path. For example: quay.io/kubev2v/vddk:latest.", "The provider currently requires the SHA-1 fingerprint of the vCenter Server's TLS certificate in all circumstances. vSphere calls this the server's thumbprint.": "The provider currently requires the SHA-1 fingerprint of the vCenter Server's TLS certificate in all circumstances. vSphere calls this the server's thumbprint.", "The provider is not ready - ": "The provider is not ready - ", "The provider is not ready.": "The provider is not ready.", @@ -416,6 +416,7 @@ "URL": "URL", "URL must start with https:// or http:// and contain valid hostname and path": "URL must start with https:// or http:// and contain valid hostname and path", "URL of the API endpoint of the Red Hat Virtualization Manager (RHVM) on which the source VM is mounted. Ensure that the URL includes the path leading to the RHVM API server, usually /ovirt-engine/api. For example, https://rhv-host-example.com/ovirt-engine/api.": "URL of the API endpoint of the Red Hat Virtualization Manager (RHVM) on which the source VM is mounted. Ensure that the URL includes the path leading to the RHVM API server, usually /ovirt-engine/api. For example, https://rhv-host-example.com/ovirt-engine/api.", + "URL of the API endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.": "URL of the API endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.", "URL of the NFS file share that serves the OVA. Ensure the URL is in the following format: ip_or_hostname_of_nfs_server:/nfs_path. For example: 10.10.0.10:/ova .": "URL of the NFS file share that serves the OVA. Ensure the URL is in the following format: ip_or_hostname_of_nfs_server:/nfs_path. For example: 10.10.0.10:/ova .", "URL of the NFS file share that serves the OVA.

The URL must be in the following format: nfs_server:/nfs_path, where:
nfs_server: An IP or hostname of the server where the share was created.
nfs_path: The path on the server where the OVA files are stored.
For example: 10.10.0.10:/ova .

Note:
This URL field is not editable.": "URL of the NFS file share that serves the OVA.

The URL must be in the following format: nfs_server:/nfs_path, where:
nfs_server: An IP or hostname of the server where the share was created.
nfs_path: The path on the server where the OVA files are stored.
For example: 10.10.0.10:/ova .

Note:
This URL field is not editable.", "URL of the Openshift Virtualization API endpoint.": "URL of the Openshift Virtualization API endpoint.", @@ -423,17 +424,17 @@ "URL of the provider": "URL of the provider", "URL of the Red Hat Virtualization Manager (RHVM) API endpoint.": "URL of the Red Hat Virtualization Manager (RHVM) API endpoint.", "URL of the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.": "URL of the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.", - "URL of the SDK endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.": "URL of the SDK endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.", - "URL of the vCenter SDK endpoint.": "URL of the vCenter SDK endpoint.", - "URL of the vCenter SDK endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.": "URL of the vCenter SDK endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.", + "URL of the vCenter API endpoint.": "URL of the vCenter API endpoint.", + "URL of the vCenter API endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk .": "URL of the vCenter API endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk .", "User ID": "User ID", "Username": "Username", "Validation Failed": "Validation Failed", - "VDDK container image. It is strongly recommended to specify a VDDK image to accelerate migrations.": "VDDK container image. It is strongly recommended to specify a VDDK image to accelerate migrations.", "VDDK init image": "VDDK init image", - "VDDK init image must be a valid container image, for example quay.io/kubev2v/example:latest": "VDDK init image must be a valid container image, for example quay.io/kubev2v/example:latest", "View details": "View details", "View provider details": "View provider details", + "Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a valid container image path in the format of registry_route_or_server_path/vddk:‹tag›. For example: quay.io/kubev2v/example:latest. It is strongly recommended to specify a VDDK init image to accelerate migrations.": "Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a valid container image path in the format of registry_route_or_server_path/vddk:‹tag›. For example: quay.io/kubev2v/example:latest. It is strongly recommended to specify a VDDK init image to accelerate migrations.", + "Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a valid container image path. For example: quay.io/kubev2v/vddk:latest.": "Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a valid container image path. For example: quay.io/kubev2v/vddk:latest.", + "Virtual Disk Development Kit (VDDK) container init image path.

Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.": "Virtual Disk Development Kit (VDDK) container init image path.

Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.", "Virtual Machine Migrations": "Virtual Machine Migrations", "Virtual machines": "Virtual machines", "Virtual Machines": "Virtual Machines", @@ -441,11 +442,10 @@ "Volume Types": "Volume Types", "Volumes": "Volumes", "vSphere product name": "vSphere product name", - "vSphere REST API password credentials.": "vSphere REST API password credentials.", - "vSphere REST API user name.": "vSphere REST API user name.", "Warning concerns": "Warning concerns", "Warning: The provided URL does not end with the RHVM API endpoint path: \"/ovirt-engine/api\". Ensure the URL includes the correct path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Warning: The provided URL does not end with the RHVM API endpoint path: \"/ovirt-engine/api\". Ensure the URL includes the correct path. For example: https://rhv-host-example.com/ovirt-engine/api.", - "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.": "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.", + "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk .": "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk .", + "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .": "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .", "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: admin@internal.": "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: admin@internal.", "Welcome": "Welcome", "When a plan is archived, its history, metadata, and logs are deleted. The plan cannot be edited or restarted but it can be viewed.": "When a plan is archived, its history, metadata, and logs are deleted. The plan cannot be edited or restarted but it can be viewed.", diff --git a/packages/forklift-console-plugin/src/modules/Providers/modals/EditModal/EditModal.style.css b/packages/forklift-console-plugin/src/modules/Providers/modals/EditModal/EditModal.style.css index bacda1c14..7aeae867f 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/modals/EditModal/EditModal.style.css +++ b/packages/forklift-console-plugin/src/modules/Providers/modals/EditModal/EditModal.style.css @@ -9,6 +9,12 @@ padding-top: var(--pf-c-form__helper-text--MarginTop); } +.forklift--edit-modal-field-warning-validation { + color: var(--pf-c-form__helper-text--m-warning--Color); + font-size: small; + padding-top: var(--pf-c-form__helper-text--MarginTop); +} + .forklift-edit-modal-field-success-validation { color: var(--pf-c-form__helper-text--m-success--Color); font-size: small; diff --git a/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/VSphereEditURLModal.tsx b/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/VSphereEditURLModal.tsx index c5b43842e..267b6e39b 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/VSphereEditURLModal.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/VSphereEditURLModal.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Trans } from 'react-i18next'; import { useForkliftTranslation } from 'src/utils/i18n'; import { ProviderModel } from '@kubev2v/types'; @@ -14,14 +15,41 @@ export const VSphereEditURLModal: React.FC = (props) const { t } = useForkliftTranslation(); const helperTextMsgs = { - error: t( - 'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.', + error: ( +
+ + { + 'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk .' + } + +
), - warning: t( - 'Warning: The provided URL does not end with the SDK endpoint path: "/sdk". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.', + warning: ( +
+ + { + 'Warning: The provided URL does not end with the SDK endpoint path: "/sdk". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk .' + } + +
), - success: t( - 'Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk.', + success: ( +
+ + { + 'Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk .' + } + +
+ ), + default: ( +
+ + { + 'Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk .' + } + +
), }; @@ -58,8 +86,8 @@ export const VSphereEditURLModal: React.FC = (props) label={props?.label || t('URL')} model={ProviderModel} variant={ModalVariant.large} - body={t('URL of the vCenter SDK endpoint.')} - helperText={helperTextMsgs.success} + body={t('URL of the vCenter API endpoint.')} + helperText={helperTextMsgs.default} onConfirmHook={patchProviderURL} validationHook={urlValidationHook} /> diff --git a/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderVDDKImage/EditProviderVDDKImage.tsx b/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderVDDKImage/EditProviderVDDKImage.tsx index 0c3819180..a63e47f09 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderVDDKImage/EditProviderVDDKImage.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderVDDKImage/EditProviderVDDKImage.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Trans } from 'react-i18next'; import { useForkliftTranslation } from 'src/utils/i18n'; import { Modify, ProviderModel, V1beta1Provider } from '@kubev2v/types'; @@ -56,19 +57,44 @@ export type EditProviderVDDKImageProps = Modify< const EditProviderVDDKImage_: React.FC = (props) => { const { t } = useForkliftTranslation(); + const vddkHelperTextMsgs = { + error: ( +
+ + Error: The format of the provided VDDK init image is invalid. Ensure the path is a valid + container image path. For example: {''}quay.io/kubev2v/vddk:latest{''}. + +
+ ), + success: ( +
+ + The path must be empty or a valid container image path. For example: {''} + quay.io/kubev2v/vddk:latest{''}. + +
+ ), + default: ( +
+ + The path must be empty or a valid container image path. For example: {''} + quay.io/kubev2v/vddk:latest{''}. + +
+ ), + }; + const imageValidationHook: ValidationHookType = (value) => { const trimmedValue = value.toString().trim(); const isValidImage = trimmedValue === '' || validateContainerImage(value.toString().trim()); return isValidImage ? { - validationHelpText: undefined, + validationHelpText: vddkHelperTextMsgs.success, validated: 'success', } : { - validationHelpText: t( - 'VDDK init image must be a valid container image, for example quay.io/kubev2v/example:latest', - ), + validationHelpText: vddkHelperTextMsgs.error, validated: 'error', }; }; @@ -80,9 +106,14 @@ const EditProviderVDDKImage_: React.FC = (props) => title={props?.title || t('Edit VDDK init image')} label={props?.label || t('VDDK init image')} model={ProviderModel} - body={t( - 'Specify the VDDK image that you created. VDDK accelerates migrations significantly.', - )} + body={ + + { + 'Virtual Disk Development Kit (VDDK) container init image path.

Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.' + } +
+ } + helperText={vddkHelperTextMsgs.default} validationHook={imageValidationHook} onConfirmHook={onConfirm} /> diff --git a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/vsphereSecretFieldValidator.ts b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/vsphereSecretFieldValidator.ts index 30894fda3..d6225d888 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/vsphereSecretFieldValidator.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/vsphereSecretFieldValidator.ts @@ -1,5 +1,5 @@ import { Validation } from '../../types'; -import { validateFingerprint, validateNoSpaces } from '../common'; +import { validateFingerprint, validateNoSpaces, validateUsernameAndDomain } from '../common'; /** * Validates form input fields based on their id. @@ -11,6 +11,7 @@ import { validateFingerprint, validateNoSpaces } from '../common'; * 'default' - The default state of the form field, used when the field is empty or a value hasn't been entered yet. * 'success' - The field's value has passed validation. * 'error' - The field's value has failed validation. + * 'warning' - The field's value might fail the validation, but it's not mandatory and not disabling the form saving. */ export const vsphereSecretFieldValidator = (id: string, value: string) => { const trimmedValue = value.trim(); @@ -19,7 +20,7 @@ export const vsphereSecretFieldValidator = (id: string, value: string) => { switch (id) { case 'user': - validationState = validateUser(trimmedValue) ? 'success' : 'error'; + validationState = validateUser(trimmedValue); break; case 'password': validationState = validatePassword(trimmedValue) ? 'success' : 'error'; @@ -39,7 +40,11 @@ export const vsphereSecretFieldValidator = (id: string, value: string) => { }; const validateUser = (value: string) => { - return validateNoSpaces(value); + return validateNoSpaces(value) + ? validateUsernameAndDomain(value) + ? 'success' + : 'warning' + : 'error'; }; const validatePassword = (value: string) => { diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/create/ProvidersCreatePage.style.css b/packages/forklift-console-plugin/src/modules/Providers/views/create/ProvidersCreatePage.style.css index 9ab66e134..e7e18636e 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/create/ProvidersCreatePage.style.css +++ b/packages/forklift-console-plugin/src/modules/Providers/views/create/ProvidersCreatePage.style.css @@ -20,6 +20,12 @@ padding-top: var(--pf-c-form__helper-text--MarginTop); } +.forklift--create-provider-field-warning-validation { + color: var(--pf-c-form__helper-text--m-warning--Color); + font-size: small; + padding-top: var(--pf-c-form__helper-text--MarginTop); +} + .forklift--create-provider-field-success-validation { color: var(--pf-c-form__helper-text--m-success--Color); font-size: small; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/create/components/VSphereProviderCreateForm.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/create/components/VSphereProviderCreateForm.tsx index 478efe05e..687505a6f 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/create/components/VSphereProviderCreateForm.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/create/components/VSphereProviderCreateForm.tsx @@ -1,9 +1,11 @@ import React, { useCallback, useReducer } from 'react'; +import { Trans } from 'react-i18next'; import { validateContainerImage, validateURL, Validation } from 'src/modules/Providers/utils'; import { useForkliftTranslation } from 'src/utils/i18n'; import { V1beta1Provider } from '@kubev2v/types'; -import { Form, FormGroup, TextInput } from '@patternfly/react-core'; +import { Form, FormGroup, Popover, TextInput } from '@patternfly/react-core'; +import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon'; export interface VSphereProviderCreateFormProps { provider: V1beta1Provider; @@ -19,23 +21,86 @@ export const VSphereProviderCreateForm: React.FC const url = provider?.spec?.url || ''; const vddkInitImage = provider?.spec?.settings?.['vddkInitImage'] || ''; - const helperTextMsgs = { - error: t( - 'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.', + const urlHelperTextMsgs = { + error: ( +
+ + { + 'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk .' + } + +
), - warning: t( - 'Warning: The provided URL does not end with the SDK endpoint path: "/sdk". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.', + warning: ( +
+ + { + 'Warning: The provided URL does not end with the SDK endpoint path: "/sdk". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk .' + } + +
), - success: t( - 'URL of the vCenter SDK endpoint. Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk.', + success: ( +
+ + { + 'URL of the vCenter API endpoint. Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk .' + } + +
+ ), + default: ( +
+ + { + 'URL of the vCenter API endpoint. Ensure the URL includes the "/sdk" path. For example: https://vCenter-host-example.com/sdk .' + } + +
), }; + const vddkHelperTextMsgs = { + error: ( +
+ + Error: The format of the provided VDDK init image is invalid. Ensure the path is a valid + container image path. For example: {''}quay.io/kubev2v/vddk:latest{''}. + +
+ ), + success: ( +
+ + Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a + valid container image path. For example: {''}quay.io/kubev2v/vddk:latest + {''}. + +
+ ), + default: ( +
+ + Virtual Disk Development Kit (VDDK) container init image path. The path must be empty or a + valid container image path. For example: {''}quay.io/kubev2v/vddk:latest + {''}. + +
+ ), + }; + + const vddkHelperTextPopover = ( + + {'Note: It is strongly recommended to specify a VDDK init image to accelerate migrations.'} + + ); + const initialState = { validation: { url: 'default' as Validation, - urlHelperText: helperTextMsgs.success, + urlHelperText: urlHelperTextMsgs.default, vddkInitImage: 'default' as Validation, + vddkHelperText: vddkHelperTextMsgs.default, }, }; @@ -65,6 +130,14 @@ export const VSphereProviderCreateForm: React.FC trimmedValue == '' || validateContainerImage(trimmedValue) ? 'success' : 'error'; dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } }); + dispatch({ + type: 'SET_FIELD_VALIDATED', + payload: { + field: 'vddkHelperText', + validationState: vddkHelperTextMsgs[validationState], + }, + }); + onChange({ ...provider, spec: { @@ -88,7 +161,7 @@ export const VSphereProviderCreateForm: React.FC type: 'SET_FIELD_VALIDATED', payload: { field: 'urlHelperText', - validationState: helperTextMsgs[validationState], + validationState: urlHelperTextMsgs[validationState], }, }); @@ -128,11 +201,24 @@ export const VSphereProviderCreateForm: React.FC VDDK init image} + bodyContent={
{vddkHelperTextPopover}
} + alertSeverityVariant="info" + > + + + } > = ({ secret, o const thumbprint = safeBase64Decode(secret?.data?.thumbprint || ''); const insecureSkipVerify = safeBase64Decode(secret?.data?.insecureSkipVerify || '') === 'true'; + const usernameHelperTextMsgs = { + error: ( +
+ + { + "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters." + } + +
+ ), + warning: ( +
+ + { + 'Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .' + } + +
+ ), + success: ( +
+ + { + 'A user name for connecting to the vCenter API endpoint. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .' + } + +
+ ), + default: ( +
+ + { + 'A user name for connecting to the vCenter API endpoint. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .' + } + +
+ ), + }; + + const passwordHelperTextMsgs = { + error: t( + "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.", + ), + success: t('A user password for connecting to the vCenter API endpoint.'), + }; + const initialState = { passwordHidden: true, validation: { user: 'default' as Validation, + userHelperText: usernameHelperTextMsgs.default, password: 'default' as Validation, thumbprint: 'default' as Validation, }, @@ -54,6 +102,21 @@ export const VSphereCredentialsEdit: React.FC = ({ secret, o const validationState = vsphereSecretFieldValidator(id, value); dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } }); + // The 'warning' validation state is currently supported only for the 'username' field. + switch (id) { + case 'user': + dispatch({ + type: 'SET_FIELD_VALIDATED', + payload: { + field: 'userHelperText', + validationState: usernameHelperTextMsgs[validationState], + }, + }); + break; + default: + break; + } + // don't trim fields that allow spaces const encodedValue = ['cacert'].includes(id) ? Base64.encode(value) @@ -74,8 +137,8 @@ export const VSphereCredentialsEdit: React.FC = ({ secret, o label={t('Username')} isRequired fieldId="username" - helperText={t('vSphere REST API user name.')} - helperTextInvalid={t('Error: Username is required and must be valid.')} + helperText={state.validation.userHelperText} + helperTextInvalid={state.validation.userHelperText} validated={state.validation.user} > = ({ secret, o label={t('Password')} isRequired fieldId="password" - helperText={t('vSphere REST API password credentials.')} - helperTextInvalid={t('Error: Password is required and must be valid.')} + helperText={passwordHelperTextMsgs.success} + helperTextInvalid={passwordHelperTextMsgs.error} validated={state.validation.password} > = ({ secret, r const items = []; const fields = { - user: { label: t('Username'), description: t('vSphere REST API user name.') }, - password: { label: t('Password'), description: t('vSphere REST API password credentials.') }, + user: { + label: t('Username'), + description: ( +
+ + { + 'A user name for connecting to the vCenter API endpoint. Ensure the user name is in the format of username@user-domain. For example: user@vsphere.local .' + } + +
+ ), + }, + password: { + label: t('Password'), + description: 'A user password for connecting to the vCenter API endpoint.', + }, thumbprint: { label: t('SSHA-1 fingerprint'), description: t( diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/VSphereDetailsSection.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/VSphereDetailsSection.tsx index 6d8f72888..d657ba4b6 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/VSphereDetailsSection.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/VSphereDetailsSection.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Trans } from 'react-i18next'; import { EditProviderURLModal, EditProviderVDDKImage, @@ -88,9 +89,11 @@ export const VSphereDetailsSection: React.FC = ({ data }) = } helpContent={ - {t( - `URL of the SDK endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.`, - )} + + { + 'URL of the API endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.' + } + } crumbs={['Provider', 'spec', 'url']} @@ -141,7 +144,17 @@ export const VSphereDetailsSection: React.FC = ({ data }) = moreInfoLink={ 'https://access.redhat.com/documentation/en-us/migration_toolkit_for_virtualization/2.5/html-single/installing_and_using_the_migration_toolkit_for_virtualization/index#creating-vddk-image_mtv' } - helpContent={{t(`Specify the VDDK image that you created.`)}} + helpContent={ + + + Virtual Disk Development Kit (VDDK) container init image path. The path must be empty + or a valid container image path in the format of {''} + registry_route_or_server_path/vddk:‹tag›{''}. For example:{' '} + {''}quay.io/kubev2v/example:latest{''}. It is strongly recommended to + specify a VDDK init image to accelerate migrations. + + + } crumbs={['Provider', 'spec', 'settings', 'vddkInitImage']} onEdit={canEdit && (() => showModal())} />