Skip to content

Commit 745801e

Browse files
Merge pull request #7734 from DavidResende0/react-cloud-subnet
Convert the subnet form to DDF
2 parents 83564ee + 9f53567 commit 745801e

File tree

15 files changed

+374
-656
lines changed

15 files changed

+374
-656
lines changed

app/controllers/cloud_subnet_controller.rb

Lines changed: 0 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -44,64 +44,6 @@ def new
4444
drop_breadcrumb(:name => _("Add New Subnet"), :url => "/cloud_subnet/new")
4545
end
4646

47-
def create
48-
assert_privileges("cloud_subnet_new")
49-
case params[:button]
50-
when "cancel"
51-
javascript_redirect(:action => 'show_list',
52-
:flash_msg => _("Creation of a Cloud Subnet was cancelled by the user"))
53-
54-
when "add"
55-
@subnet = CloudSubnet.new
56-
begin
57-
options = new_form_params
58-
ems = ExtManagementSystem.find(options[:ems_id])
59-
if CloudSubnet.class_by_ems(ems).supports_create?
60-
options.delete(:ems_id)
61-
task_id = ems.create_cloud_subnet_queue(session[:userid], options)
62-
63-
if task_id.kind_of?(Integer)
64-
initiate_wait_for_task(:task_id => task_id, :action => "create_finished")
65-
else
66-
javascript_flash(
67-
:text => _("Cloud Subnet creation: Task start failed"),
68-
:severity => :error,
69-
:spinner_off => true
70-
)
71-
end
72-
else
73-
@in_a_form = true
74-
add_flash(_(CloudSubnet.unsupported_reason(:create)), :error)
75-
drop_breadcrumb(:name => _("Add new Cloud Subnet "), :url => "/subnet/new")
76-
javascript_flash
77-
end
78-
rescue ArgumentError => err
79-
javascript_flash(
80-
:text => _("Parameter Error: %{error_message}") % {:error_message => err.message},
81-
:severity => :error,
82-
:spinner_off => true
83-
)
84-
end
85-
end
86-
end
87-
88-
def create_finished
89-
task_id = session[:async][:params][:task_id]
90-
subnet_name = session[:async][:params][:name]
91-
task = MiqTask.find(task_id)
92-
if MiqTask.status_ok?(task.status)
93-
add_flash(_("Cloud Subnet \"%{name}\" created") % {:name => subnet_name})
94-
else
95-
add_flash(_("Unable to create Cloud Subnet: %{details}") %
96-
{ :name => subnet_name, :details => task.message }, :error)
97-
end
98-
99-
@breadcrumbs&.pop
100-
session[:edit] = nil
101-
flash_to_session
102-
javascript_redirect(:action => "show_list")
103-
end
104-
10547
def delete_subnets
10648
assert_privileges("cloud_subnet_delete")
10749
subnets = find_records_with_rbac(CloudSubnet, checked_or_params)
@@ -147,56 +89,6 @@ def edit
14789
)
14890
end
14991

150-
def update
151-
assert_privileges("cloud_subnet_edit")
152-
@subnet = find_record_with_rbac(CloudSubnet, params[:id])
153-
case params[:button]
154-
when "cancel"
155-
flash_and_redirect(_("Edit of Subnet \"%{name}\" was cancelled by the user") % {:name => @subnet.name})
156-
157-
when "save"
158-
if @subnet.supports_create?
159-
begin
160-
options = changed_form_params
161-
task_id = @subnet.update_cloud_subnet_queue(session[:userid], options)
162-
163-
if task_id.kind_of?(Integer)
164-
initiate_wait_for_task(:task_id => task_id, :action => "update_finished")
165-
else
166-
javascript_flash(
167-
:text => _("Cloud Subnet update failed: Task start failed"),
168-
:severity => :error,
169-
:spinner_off => true
170-
)
171-
end
172-
rescue ArgumentError => err
173-
javascript_flash(
174-
:text => _("Parameter Error: %{error_message}") % {:error_message => err.message},
175-
:severity => :error,
176-
:spinner_off => true
177-
)
178-
end
179-
else
180-
add_flash(_("Couldn't initiate update of Cloud Subnet \"%{name}\": %{details}") % {
181-
:name => @subnet.name,
182-
:details => @subnet.unsupported_reason(:update)
183-
}, :error)
184-
end
185-
end
186-
end
187-
188-
def update_finished
189-
task_id = session[:async][:params][:task_id]
190-
subnet_name = session[:async][:params][:name]
191-
task = MiqTask.find(task_id)
192-
if MiqTask.status_ok?(task.status)
193-
flash_and_redirect(_("Cloud Subnet \"%{name}\" updated") % {:name => subnet_name})
194-
else
195-
flash_and_redirect(_("Unable to update Cloud Subnet \"%{name}\": %{details}") % {:name => subnet_name,
196-
:details => task.message}, :error)
197-
end
198-
end
199-
20092
def download_data
20193
assert_privileges('cloud_subnet_view')
20294
super
@@ -222,85 +114,6 @@ def switch_to_bol(option)
222114
end
223115
end
224116

225-
def parse_allocation_pools(option)
226-
return [] unless option
227-
228-
option.lines.map do |pool|
229-
start_addr, end_addr, extra_entry = pool.split(",")
230-
raise ArgumentError, _("Too few addresses in line. Proper format is start_ip_address,end_ip_address (one Allocation Pool per line)") unless end_addr
231-
raise ArgumentError, _("Too many addresses in line. Proper format is start_ip_address,end_ip_address (one Allocation Pool per line)") if extra_entry
232-
233-
{"start" => start_addr.strip, "end" => end_addr.strip}
234-
end
235-
end
236-
237-
def parse_host_routes(option)
238-
return [] unless option
239-
240-
option.lines.map do |route|
241-
dest_addr, nexthop_addr, extra_entry = route.split(",")
242-
raise ArgumentError, _("Too few entries in line. Proper format is destination_cidr,nexthop (one Host Route per line)") unless nexthop_addr
243-
raise ArgumentError, _("Too many entries in line. Proper format is destination_cidr,nexthop (one Host Route per line)") if extra_entry
244-
245-
{"destination" => dest_addr.strip, "nexthop" => nexthop_addr.strip}
246-
end
247-
end
248-
249-
def parse_dns_nameservers(option)
250-
return [] unless option
251-
252-
option.lines.map do |nameserver|
253-
one_nameserver, extra_entry = nameserver.strip.split(/\s+|,/)
254-
raise ArgumentError, _("One DNS Name Server per line is required.") if !one_nameserver || extra_entry
255-
256-
one_nameserver
257-
end
258-
end
259-
260-
def changed_form_params
261-
# Allowed fields for update: name, enable_dhcp, dns_nameservers, allocation_pools, host_routes, gateway_ip
262-
options = {}
263-
options[:name] = params[:name] unless @subnet.name == params[:name]
264-
265-
# Provider to automatically assign gateway address unless provided
266-
unless @subnet.gateway == params[:gateway]
267-
options[:gateway_ip] = params[:gateway].presence
268-
end
269-
270-
unless @subnet.dhcp_enabled == switch_to_bol(params[:dhcp_enabled])
271-
options[:enable_dhcp] = switch_to_bol(params[:dhcp_enabled])
272-
end
273-
unless @subnet.allocation_pools == (pools = parse_allocation_pools(params[:allocation_pools])) || (@subnet.allocation_pools.blank? && pools.blank?)
274-
options[:allocation_pools] = pools
275-
end
276-
unless @subnet.host_routes == (routes = parse_host_routes(params[:host_routes])) || (@subnet.host_routes.blank? && routes.blank?)
277-
options[:host_routes] = routes
278-
end
279-
unless @subnet.dns_nameservers == (nameservers = parse_dns_nameservers(params[:dns_nameservers])) || (@subnet.dns_nameservers.blank? && nameservers.blank?)
280-
options[:dns_nameservers] = nameservers
281-
end
282-
options
283-
end
284-
285-
def new_form_params
286-
params[:network_protocol] ||= "ipv4"
287-
params[:dhcp_enabled] ||= false
288-
options = {}
289-
copy_params_if_present(options, params, %i[name ems_id cidr network_id availability_zone_id ipv6_router_advertisement_mode ipv6_address_mode network_group_id parent_cloud_subnet_id])
290-
# Provider to automatically assign gateway address unless provided
291-
if params[:gateway]
292-
options[:gateway_ip] = params[:gateway].presence
293-
end
294-
options[:ip_version] = /4/.match?(params[:network_protocol]) ? 4 : 6
295-
options[:cloud_tenant] = find_record_with_rbac(CloudTenant, params[:cloud_tenant][:id]) if params.fetch_path(:cloud_tenant, :id)
296-
options[:enable_dhcp] = params[:dhcp_enabled]
297-
# TODO: Add dns_nameservers, allocation_pools, host_routes
298-
options[:allocation_pools] = parse_allocation_pools(params[:allocation_pools]) if params[:allocation_pools].present?
299-
options[:dns_nameservers] = parse_dns_nameservers(params[:dns_nameservers]) if params[:dns_nameservers].present?
300-
options[:host_routes] = parse_host_routes(params[:host_routes]) if params[:host_routes].present?
301-
options
302-
end
303-
304117
# dispatches operations to multiple subnets
305118
def process_cloud_subnets(subnets, operation)
306119
return if subnets.empty?
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React, { useState, useEffect } from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import MiqFormRenderer from '@@ddf';
5+
import createSchema from './subnet-form.schema';
6+
import miqRedirectBack from '../../helpers/miq-redirect-back';
7+
import { API } from '../../http_api';
8+
import { Loading } from 'carbon-components-react';
9+
10+
const SubnetForm = ({ recordId }) => {
11+
const [{ initialValues, isLoading, fields }, setState] = useState({ isLoading: !!recordId, fields: [] });
12+
const submitLabel = !!recordId ? __('Save') : __('Add');
13+
14+
const loadSchema = (appendState = {}) => ({ data: { form_schema: { fields } } }) => {
15+
if (!!recordId && appendState.initialValues.type === 'ManageIQ::Providers::Openstack::NetworkManager::CloudSubnet') {
16+
Object.assign(fields[0], {isDisabled: true});
17+
Object.assign(fields[1], {isDisabled: true});
18+
Object.assign(fields[4], {isDisabled: true});
19+
}
20+
setState((state) => ({
21+
...state,
22+
...appendState,
23+
fields,
24+
}));
25+
};
26+
27+
useEffect(() => {
28+
if (recordId) {
29+
API.get(`/api/cloud_subnets/${recordId}`).then((initialValues) => {
30+
if(typeof initialValues.cloud_network_id ==="string") {
31+
initialValues.cloud_network_id=Number(initialValues.cloud_network_id);
32+
}
33+
if(typeof initialValues.cloud_tenant_id ==="string") {
34+
initialValues.cloud_tenant_id=Number(initialValues.cloud_tenant_id);
35+
}
36+
API.options(`/api/cloud_subnets?ems_id=${initialValues.ems_id}`).then(loadSchema({ initialValues, isLoading: false }));
37+
});
38+
}
39+
}, [recordId]);
40+
41+
const onSubmit = (values) => {
42+
API.get(`/api/providers/${values.ems_id}`).then(({ type }) => {
43+
if (type === 'ManageIQ::Providers::Openstack::NetworkManager') {
44+
if (values.ip_version === undefined) {
45+
values.ip_version = '4';
46+
}
47+
if (values.dhcp_enabled === undefined) {
48+
values.dhcp_enabled = false;
49+
}
50+
values.enable_dhcp = values.dhcp_enabled;
51+
delete values.dhcp_enabled;
52+
delete Object.assign(values, values.extra_attributes).extra_attributes;
53+
}
54+
55+
miqSparkleOn();
56+
const request = recordId ? API.patch(`/api/cloud_subnets/${recordId}`, values) : API.post('/api/cloud_subnets', values);
57+
58+
request.then(() => {
59+
const message = sprintf(recordId
60+
? __('Modification of Cloud Subnet %s has been successfully queued')
61+
: __('Add of Cloud Subnet "%s" has been successfully queued.'),
62+
values.name);
63+
64+
miqRedirectBack(message, 'success', '/cloud_subnet/show_list');
65+
}).catch(miqSparkleOff);
66+
});
67+
};
68+
69+
const onCancel = () => {
70+
const message = sprintf(
71+
recordId
72+
? __('Edit of Cloud Subnet "%s" was canceled by the user.')
73+
: __('Creation of new Cloud Subnet was canceled by the user.'),
74+
initialValues && initialValues.name,
75+
);
76+
miqRedirectBack(message, 'warning', '/cloud_subnet/show_list');
77+
};
78+
79+
if (isLoading) return <Loading className='export-spinner' withOverlay={false} small />;
80+
return !isLoading && (
81+
<MiqFormRenderer
82+
schema={createSchema(!!recordId, fields, loadSchema)}
83+
initialValues={initialValues}
84+
canReset={!!recordId}
85+
onSubmit={onSubmit}
86+
onCancel={onCancel}
87+
buttonsLabels={{ submitLabel }}
88+
/>
89+
);
90+
};
91+
92+
SubnetForm.propTypes = {
93+
recordId: PropTypes.string,
94+
};
95+
SubnetForm.defaultProps = {
96+
recordId: undefined,
97+
};
98+
99+
export default SubnetForm;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { componentTypes, validatorTypes } from '@@ddf';
2+
import { API } from '../../http_api';
3+
4+
const emsUrl = '/api/providers?expand=resources&attributes=id,name,supports_cloud_subnet_create&filter[]=supports_cloud_subnet_create=true';
5+
6+
const createSchema = (edit, fields = [], loadSchema) => ({
7+
fields: [
8+
{
9+
component: componentTypes.SELECT,
10+
name: 'ems_id',
11+
id: 'ems_id',
12+
label: __('Network Manager'),
13+
onChange: (value) => API.options(`/api/cloud_subnets?ems_id=${value}`).then(loadSchema()),
14+
loadOptions: () => API.get(emsUrl).then(({ resources }) => resources.map(({ id, name }) => ({ label: name, value: id }))),
15+
includeEmpty: true,
16+
isDisabled: edit,
17+
isRequired: true,
18+
validate: [{ type: validatorTypes.REQUIRED }],
19+
},
20+
{
21+
component: componentTypes.TEXT_FIELD,
22+
name: 'name',
23+
id: 'name',
24+
label: __('Name'),
25+
isRequired: true,
26+
validate: [{ type: validatorTypes.REQUIRED }],
27+
condition: {
28+
when: 'ems_id',
29+
isNotEmpty: true,
30+
},
31+
},
32+
{
33+
component: componentTypes.TEXT_FIELD,
34+
name: 'cidr',
35+
id: 'cidr',
36+
label: __('CIDR'),
37+
isRequired: true,
38+
isDisabled: edit,
39+
validate: [{ type: validatorTypes.REQUIRED }], // TODO: pattern for validating IPv4/mask or IPv6/mask
40+
condition: {
41+
when: 'ems_id',
42+
isNotEmpty: true,
43+
},
44+
},
45+
{
46+
component: componentTypes.FIELD_ARRAY,
47+
name: 'dns_nameservers',
48+
id: 'dns_nameservers',
49+
label: __('DNS Servers'),
50+
noItemsMessage: __('None'),
51+
buttonLabels: {
52+
add: __('Add'),
53+
remove: __('Remove'),
54+
},
55+
AddButtonProps: {
56+
size: 'small',
57+
},
58+
RemoveButtonProps: {
59+
size: 'small',
60+
},
61+
fields: [{ // TODO: pattern for validating IPv4 or IPv6
62+
component: componentTypes.TEXT_FIELD,
63+
}],
64+
condition: {
65+
when: 'ems_id',
66+
isNotEmpty: true,
67+
},
68+
},
69+
...fields,
70+
],
71+
});
72+
73+
export default createSchema;

0 commit comments

Comments
 (0)