Skip to content

Commit 96978ff

Browse files
committed
Resolves: MTV-3738 | Provider create form - Ovirt
Signed-off-by: Jeff Puzzo <[email protected]>
1 parent 3c5ce02 commit 96978ff

File tree

11 files changed

+390
-63
lines changed

11 files changed

+390
-63
lines changed

locales/en/plugin__forklift-console-plugin.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
"A username and domain for the vCenter API endpoint, for example: [email protected]. [required]": "A username and domain for the vCenter API endpoint, for example: [email protected]. [required]",
114114
"A username for connecting to the OpenStack Identity (Keystone) endpoint.": "A username for connecting to the OpenStack Identity (Keystone) endpoint.",
115115
"A username for connecting to the OpenStack Identity (Keystone) endpoint. [required]": "A username for connecting to the OpenStack Identity (Keystone) endpoint. [required]",
116+
"A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint, for example: name@internal": "A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint, for example: name@internal",
116117
"A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint, for example: name@internal . [required]": "A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint, for example: name@internal . [required]",
117118
"A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the username is in the format of username@user-domain. For example: admin@internal.": "A username for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the username is in the format of username@user-domain. For example: admin@internal.",
118119
"A username for connecting to the vCenter API endpoint. Ensure the username includes the user domain. For example: <1>[email protected]</1>.": "A username for connecting to the vCenter API endpoint. Ensure the username includes the user domain. For example: <1>[email protected]</1>.",
@@ -451,6 +452,7 @@
451452
"Guest conversion uses the virt-v2v tool to modify all of the internal configurations of the VMs in the plan to make them compatible with Red Hat OpenShift Virtualization.": "Guest conversion uses the virt-v2v tool to modify all of the internal configurations of the VMs in the plan to make them compatible with Red Hat OpenShift Virtualization.",
452453
"Health": "Health",
453454
"Health indicates the current status of the pods related to the migration toolkit for virtualization, including whether any have failed. For more details, refer to the logs.": "Health indicates the current status of the pods related to the migration toolkit for virtualization, including whether any have failed. For more details, refer to the logs.",
455+
"Hide password": "Hide password",
454456
"Hide token": "Hide token",
455457
"Hide values": "Hide values",
456458
"Hide variables": "Hide variables",
@@ -935,6 +937,7 @@
935937
"Short snapshot polling interval": "Short snapshot polling interval",
936938
"Show archived": "Show archived",
937939
"Show default projects": "Show default projects",
940+
"Show password": "Show password",
938941
"Show token": "Show token",
939942
"Show variables": "Show variables",
940943
"Since the VM is offline, there are no live changes to track, making the data copy a one-time, full-disk transfer.": "Since the VM is offline, there are no live changes to track, making the data copy a one-time, full-disk transfer.",
@@ -1044,9 +1047,11 @@
10441047
"The target storage mapping data from the inventory is not available: {{resourcesError}}.": "The target storage mapping data from the inventory is not available: {{resourcesError}}.",
10451048
"The uploaded file name extension should be .ova": "The uploaded file name extension should be .ova",
10461049
"The URL is invalid. URL should include the schema, for example: https://example.com:6443.": "The URL is invalid. URL should include the schema, for example: https://example.com:6443.",
1050+
"The URL is required. URL should include the schema and path, for example: https://rhv-host-example.com/ovirt-engine/api": "The URL is required. URL should include the schema and path, for example: https://rhv-host-example.com/ovirt-engine/api",
10471051
"The URL of the API endpoint, for example: https://example.com:6443.": "The URL of the API endpoint, for example: https://example.com:6443.",
10481052
"The URL of the OpenStack Identity (Keystone) API endpoint, for example: https://identity_service.com:5000/v3.": "The URL of the OpenStack Identity (Keystone) API endpoint, for example: https://identity_service.com:5000/v3.",
10491053
"The URL of the OpenStack Identity (Keystone) endpoint, for example: https://identity_service.com:5000/v3": "The URL of the OpenStack Identity (Keystone) endpoint, for example: https://identity_service.com:5000/v3",
1054+
"The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api": "The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api",
10501055
"The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .": "The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .",
10511056
"The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk.": "The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk.",
10521057
"The username for the ESXi host admin": "The username for the ESXi host admin",
@@ -1135,8 +1140,11 @@
11351140
"User name password is required, user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.": "User name password is required, user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.",
11361141
"User name usually include `@` char, for example: name@internal .": "User name usually include `@` char, for example: name@internal .",
11371142
"User name usually include the domain, for example: [email protected]": "User name usually include the domain, for example: [email protected]",
1143+
"User password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint": "User password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint",
11381144
"Username": "Username",
11391145
"Username for connecting to OpenStack Identity (Keystone)": "Username for connecting to OpenStack Identity (Keystone)",
1146+
"Username is required": "Username is required",
1147+
"Username is required. The username usually includes @ character, for example: name@internal": "Username is required. The username usually includes @ character, for example: name@internal",
11401148
"Validation Failed": "Validation Failed",
11411149
"Value": "Value",
11421150
"Values": "Values",

src/providers/create-new/ProviderTypeFields.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import ServiceAccountTokenField from './fields/openshift/ServiceAccountTokenFiel
1212
import OpenStackAuthenticationTypeField from './fields/openstack/OpenStackAuthenticationTypeField';
1313
import OpenStackUrlField from './fields/openstack/OpenStackUrlField';
1414
import NfsDirectoryField from './fields/ova/NfsDirectoryField';
15+
import OvirtCredentialsFields from './fields/ovirt/OvirtCredentialsFields';
16+
import OvirtUrlField from './fields/ovirt/OvirtUrlField';
1517
import ProviderNameField from './fields/ProviderNameField';
1618
import ProviderProjectField from './fields/ProviderProjectField';
1719
import ProviderTypeField from './fields/ProviderTypeField';
@@ -21,9 +23,13 @@ const ProviderTypeFields: FC = () => {
2123
const { t } = useForkliftTranslation();
2224
const { control } = useFormContext<CreateProviderFormData>();
2325

24-
const [selectedProviderType, openshiftUrl] = useWatch({
26+
const [selectedProviderType, openshiftUrl, ovirtUrl] = useWatch({
2527
control,
26-
name: [ProviderFormFieldId.ProviderType, ProviderFormFieldId.OpenshiftUrl],
28+
name: [
29+
ProviderFormFieldId.ProviderType,
30+
ProviderFormFieldId.OpenshiftUrl,
31+
ProviderFormFieldId.OvirtUrl,
32+
],
2733
});
2834

2935
return (
@@ -48,6 +54,14 @@ const ProviderTypeFields: FC = () => {
4854
<CertificateValidationField />
4955
</>
5056
)}
57+
{selectedProviderType === PROVIDER_TYPES.ovirt && (
58+
<>
59+
<OvirtUrlField />
60+
<SectionHeading text={t('Provider credentials')} />
61+
{ovirtUrl?.trim() && <OvirtCredentialsFields />}
62+
<CertificateValidationField />
63+
</>
64+
)}
5165
</>
5266
);
5367
};

src/providers/create-new/__tests__/CreateProviderForm.test.tsx

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ jest.mock('@utils/hooks/useDefaultProject', () => ({
7070
}));
7171

7272
jest.mock('../fields/ProviderTypeField', () => {
73-
const React = jest.requireActual('react');
7473
const { useController } = jest.requireActual('react-hook-form');
7574
const { useCreateProviderFormContext } = jest.requireActual(
7675
'../hooks/useCreateProviderFormContext',
@@ -85,30 +84,17 @@ jest.mock('../fields/ProviderTypeField', () => {
8584
name: 'providerType',
8685
});
8786

88-
return React.createElement(
89-
'div',
90-
null,
91-
React.createElement('label', { htmlFor: 'provider-type-select' }, 'Provider type'),
92-
React.createElement(
93-
'select',
94-
{ id: 'provider-type-select', 'data-testid': 'provider-type-select', ...field },
95-
React.createElement('option', { value: '' }, 'Select a provider type'),
96-
React.createElement(
97-
'option',
98-
{ value: 'openshift', 'data-testid': 'provider-type-option-openshift' },
99-
'OpenShift Virtualization',
100-
),
101-
React.createElement(
102-
'option',
103-
{ value: 'openstack', 'data-testid': 'provider-type-option-openstack' },
104-
'OpenStack',
105-
),
106-
React.createElement(
107-
'option',
108-
{ value: 'ova', 'data-testid': 'provider-type-option-ova' },
109-
'OVA',
110-
),
111-
),
87+
return (
88+
<div>
89+
<label htmlFor="provider-type-select">Provider type</label>
90+
<select id="provider-type-select" data-testid="provider-type-select" {...field}>
91+
<option value="">Select a provider type</option>
92+
<option value="openshift">OpenShift Virtualization</option>
93+
<option value="openstack">OpenStack</option>
94+
<option value="ova">Open Virtual Appliance</option>
95+
<option value="ovirt">Red Hat Virtualization</option>
96+
</select>
97+
</div>
11298
);
11399
},
114100
};
@@ -146,8 +132,7 @@ describe('CreateProviderForm', () => {
146132
const user = userEvent.setup();
147133
render(<CreateProviderForm />);
148134

149-
const typeSelect = screen.getByLabelText(/provider type/i);
150-
await user.selectOptions(typeSelect, 'openshift');
135+
await user.selectOptions(screen.getByLabelText(/provider type/i), 'openshift');
151136

152137
await waitFor(() => {
153138
expect(screen.getByRole('textbox', { name: /provider name/i })).toBeInTheDocument();
@@ -277,4 +262,60 @@ describe('CreateProviderForm', () => {
277262
).not.toBeInTheDocument();
278263
});
279264
});
265+
266+
describe('oVirt provider', () => {
267+
it('shows oVirt-specific fields after selecting oVirt type', async () => {
268+
const user = userEvent.setup();
269+
render(<CreateProviderForm />);
270+
271+
await user.selectOptions(screen.getByLabelText(/provider type/i), 'ovirt');
272+
273+
await waitFor(() => {
274+
expect(screen.getByRole('textbox', { name: /provider name/i })).toBeInTheDocument();
275+
expect(screen.getByTestId('ovirt-url-input')).toBeInTheDocument();
276+
expect(
277+
screen.getByRole('radio', { name: /skip certificate validation/i }),
278+
).toBeInTheDocument();
279+
});
280+
281+
expect(screen.queryByTestId('ovirt-username-input')).not.toBeInTheDocument();
282+
expect(screen.queryByTestId('ovirt-password-input')).not.toBeInTheDocument();
283+
});
284+
285+
it('shows credentials fields when URL is provided', async () => {
286+
const user = userEvent.setup();
287+
render(<CreateProviderForm />);
288+
289+
await user.selectOptions(screen.getByLabelText(/provider type/i), 'ovirt');
290+
291+
await waitFor(() => {
292+
expect(screen.getByTestId('ovirt-url-input')).toBeInTheDocument();
293+
});
294+
295+
const urlInput = screen.getByTestId('ovirt-url-input');
296+
await user.type(urlInput, 'https://rhv.example.com/ovirt-engine/api');
297+
298+
await waitFor(() => {
299+
expect(screen.getByTestId('ovirt-username-input')).toBeInTheDocument();
300+
expect(screen.getByTestId('ovirt-password-input')).toBeInTheDocument();
301+
});
302+
});
303+
304+
it('hides other provider fields when oVirt provider is selected', async () => {
305+
const user = userEvent.setup();
306+
render(<CreateProviderForm />);
307+
308+
await user.selectOptions(screen.getByLabelText(/provider type/i), 'ovirt');
309+
310+
await waitFor(() => {
311+
expect(screen.getByRole('textbox', { name: /provider name/i })).toBeInTheDocument();
312+
});
313+
314+
expect(
315+
screen.queryByRole('textbox', { name: /service account bearer token/i }),
316+
).not.toBeInTheDocument();
317+
expect(screen.queryByLabelText(/nfs shared directory/i)).not.toBeInTheDocument();
318+
expect(screen.queryByTestId('openstack-url-input')).not.toBeInTheDocument();
319+
});
320+
});
280321
});

src/providers/create-new/fields/CACertificateField.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ const CACertificateField: FC = () => {
1515
const { t } = useForkliftTranslation();
1616
const { control } = useFormContext();
1717

18-
const [certificateValidation, openshiftUrl, openstackUrl] = useWatch({
18+
const [certificateValidation, openshiftUrl, openstackUrl, ovirtUrl] = useWatch({
1919
control,
2020
name: [
2121
ProviderFormFieldId.CertificateValidation,
2222
ProviderFormFieldId.OpenshiftUrl,
2323
ProviderFormFieldId.OpenstackUrl,
24+
ProviderFormFieldId.OvirtUrl,
2425
],
2526
});
2627

27-
const url = openshiftUrl ?? openstackUrl;
28+
const url = openshiftUrl ?? openstackUrl ?? ovirtUrl;
2829

2930
const {
3031
field: { onChange, value },

src/providers/create-new/fields/constants.ts

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,57 +18,52 @@ enum CertificateFormFieldId {
1818
}
1919

2020
enum OpenshiftProviderFormFieldId {
21-
Url = 'openshiftUrl',
21+
OpenshiftUrl = 'openshiftUrl',
2222
ServiceAccountToken = 'serviceAccountToken',
2323
}
2424

2525
enum OpenstackProviderFormFieldId {
26-
Url = 'openstackUrl',
27-
AuthType = 'openstackAuthType',
28-
Username = 'openstackUsername',
29-
Password = 'openstackPassword',
30-
Token = 'openstackToken',
31-
RegionName = 'openstackRegionName',
32-
ProjectName = 'openstackProjectName',
33-
DomainName = 'openstackDomainName',
34-
UserId = 'openstackUserId',
35-
ProjectId = 'openstackProjectId',
36-
ApplicationCredentialName = 'openstackApplicationCredentialName',
37-
ApplicationCredentialSecret = 'openstackApplicationCredentialSecret',
38-
ApplicationCredentialId = 'openstackApplicationCredentialId',
26+
OpenstackApplicationCredentialId = 'openstackApplicationCredentialId',
27+
OpenstackApplicationCredentialName = 'openstackApplicationCredentialName',
28+
OpenstackApplicationCredentialSecret = 'openstackApplicationCredentialSecret',
29+
OpenstackAuthType = 'openstackAuthType',
30+
OpenstackDomainName = 'openstackDomainName',
31+
OpenstackPassword = 'openstackPassword',
32+
OpenstackProjectId = 'openstackProjectId',
33+
OpenstackProjectName = 'openstackProjectName',
34+
OpenstackRegionName = 'openstackRegionName',
35+
OpenstackToken = 'openstackToken',
36+
OpenstackUrl = 'openstackUrl',
37+
OpenstackUserId = 'openstackUserId',
38+
OpenstackUsername = 'openstackUsername',
3939
}
4040

4141
enum OvaProviderFormFieldId {
4242
NfsDirectory = 'nfsDirectory',
4343
}
4444

45+
enum OvirtProviderFormFieldId {
46+
OvirtPassword = 'ovirtPassword',
47+
OvirtUrl = 'ovirtUrl',
48+
OvirtUsername = 'ovirtUsername',
49+
}
50+
4551
export const ProviderFormFieldId = {
4652
...CommonProviderFormFieldId,
4753
...CertificateFormFieldId,
48-
NfsDirectory: OvaProviderFormFieldId.NfsDirectory,
49-
OpenshiftUrl: OpenshiftProviderFormFieldId.Url,
50-
OpenstackApplicationCredentialId: OpenstackProviderFormFieldId.ApplicationCredentialId,
51-
OpenstackApplicationCredentialName: OpenstackProviderFormFieldId.ApplicationCredentialName,
52-
OpenstackApplicationCredentialSecret: OpenstackProviderFormFieldId.ApplicationCredentialSecret,
53-
OpenstackAuthType: OpenstackProviderFormFieldId.AuthType,
54-
OpenstackDomainName: OpenstackProviderFormFieldId.DomainName,
55-
OpenstackPassword: OpenstackProviderFormFieldId.Password,
56-
OpenstackProjectId: OpenstackProviderFormFieldId.ProjectId,
57-
OpenstackProjectName: OpenstackProviderFormFieldId.ProjectName,
58-
OpenstackRegionName: OpenstackProviderFormFieldId.RegionName,
59-
OpenstackToken: OpenstackProviderFormFieldId.Token,
60-
OpenstackUrl: OpenstackProviderFormFieldId.Url,
61-
OpenstackUserId: OpenstackProviderFormFieldId.UserId,
62-
OpenstackUsername: OpenstackProviderFormFieldId.Username,
63-
ServiceAccountToken: OpenshiftProviderFormFieldId.ServiceAccountToken,
54+
...OvaProviderFormFieldId,
55+
...OpenshiftProviderFormFieldId,
56+
...OpenstackProviderFormFieldId,
57+
...OvirtProviderFormFieldId,
6458
} as const;
6559

6660
export type ProviderFormFieldIdType =
6761
| CommonProviderFormFieldId
6862
| CertificateFormFieldId
6963
| OpenshiftProviderFormFieldId
7064
| OpenstackProviderFormFieldId
71-
| OvaProviderFormFieldId;
65+
| OvaProviderFormFieldId
66+
| OvirtProviderFormFieldId;
7267

7368
export const providerFormFieldLabels = {
7469
[ProviderFormFieldId.CaCertificate]: t('CA certificate'),

0 commit comments

Comments
 (0)