diff --git a/app/controllers/cloud_subnet_controller.rb b/app/controllers/cloud_subnet_controller.rb
index 4fdc4edfa6a0..5d4262239f5c 100644
--- a/app/controllers/cloud_subnet_controller.rb
+++ b/app/controllers/cloud_subnet_controller.rb
@@ -35,73 +35,10 @@ def button
end
def new
- assert_privileges("cloud_subnet_new")
- assert_privileges("ems_network_show_list")
- assert_privileges("cloud_tenant_show_list")
- assert_privileges("cloud_network_show_list")
-
@in_a_form = true
drop_breadcrumb(:name => _("Add New Subnet"), :url => "/cloud_subnet/new")
end
- def create
- assert_privileges("cloud_subnet_new")
- case params[:button]
- when "cancel"
- javascript_redirect(:action => 'show_list',
- :flash_msg => _("Creation of a Cloud Subnet was cancelled by the user"))
-
- when "add"
- @subnet = CloudSubnet.new
- begin
- options = new_form_params
- ems = ExtManagementSystem.find(options[:ems_id])
- if CloudSubnet.class_by_ems(ems).supports_create?
- options.delete(:ems_id)
- task_id = ems.create_cloud_subnet_queue(session[:userid], options)
-
- if task_id.kind_of?(Integer)
- initiate_wait_for_task(:task_id => task_id, :action => "create_finished")
- else
- javascript_flash(
- :text => _("Cloud Subnet creation: Task start failed"),
- :severity => :error,
- :spinner_off => true
- )
- end
- else
- @in_a_form = true
- add_flash(_(CloudSubnet.unsupported_reason(:create)), :error)
- drop_breadcrumb(:name => _("Add new Cloud Subnet "), :url => "/subnet/new")
- javascript_flash
- end
- rescue ArgumentError => err
- javascript_flash(
- :text => _("Parameter Error: %{error_message}") % {:error_message => err.message},
- :severity => :error,
- :spinner_off => true
- )
- end
- end
- end
-
- def create_finished
- task_id = session[:async][:params][:task_id]
- subnet_name = session[:async][:params][:name]
- task = MiqTask.find(task_id)
- if MiqTask.status_ok?(task.status)
- add_flash(_("Cloud Subnet \"%{name}\" created") % {:name => subnet_name})
- else
- add_flash(_("Unable to create Cloud Subnet: %{details}") %
- { :name => subnet_name, :details => task.message }, :error)
- end
-
- @breadcrumbs&.pop
- session[:edit] = nil
- flash_to_session
- javascript_redirect(:action => "show_list")
- end
-
def delete_subnets
assert_privileges("cloud_subnet_delete")
subnets = find_records_with_rbac(CloudSubnet, checked_or_params)
@@ -138,7 +75,6 @@ def delete_subnets
def edit
params[:id] = checked_item_id if params[:id].blank?
- assert_privileges("cloud_subnet_edit")
@subnet = find_record_with_rbac(CloudSubnet, params[:id])
@in_a_form = true
drop_breadcrumb(
@@ -147,56 +83,6 @@ def edit
)
end
- def update
- assert_privileges("cloud_subnet_edit")
- @subnet = find_record_with_rbac(CloudSubnet, params[:id])
- case params[:button]
- when "cancel"
- flash_and_redirect(_("Edit of Subnet \"%{name}\" was cancelled by the user") % {:name => @subnet.name})
-
- when "save"
- if @subnet.supports_create?
- begin
- options = changed_form_params
- task_id = @subnet.update_cloud_subnet_queue(session[:userid], options)
-
- if task_id.kind_of?(Integer)
- initiate_wait_for_task(:task_id => task_id, :action => "update_finished")
- else
- javascript_flash(
- :text => _("Cloud Subnet update failed: Task start failed"),
- :severity => :error,
- :spinner_off => true
- )
- end
- rescue ArgumentError => err
- javascript_flash(
- :text => _("Parameter Error: %{error_message}") % {:error_message => err.message},
- :severity => :error,
- :spinner_off => true
- )
- end
- else
- add_flash(_("Couldn't initiate update of Cloud Subnet \"%{name}\": %{details}") % {
- :name => @subnet.name,
- :details => @subnet.unsupported_reason(:update)
- }, :error)
- end
- end
- end
-
- def update_finished
- task_id = session[:async][:params][:task_id]
- subnet_name = session[:async][:params][:name]
- task = MiqTask.find(task_id)
- if MiqTask.status_ok?(task.status)
- flash_and_redirect(_("Cloud Subnet \"%{name}\" updated") % {:name => subnet_name})
- else
- flash_and_redirect(_("Unable to update Cloud Subnet \"%{name}\": %{details}") % {:name => subnet_name,
- :details => task.message}, :error)
- end
- end
-
def download_data
assert_privileges('cloud_subnet_view')
super
@@ -222,85 +108,6 @@ def switch_to_bol(option)
end
end
- def parse_allocation_pools(option)
- return [] unless option
-
- option.lines.map do |pool|
- start_addr, end_addr, extra_entry = pool.split(",")
- raise ArgumentError, _("Too few addresses in line. Proper format is start_ip_address,end_ip_address (one Allocation Pool per line)") unless end_addr
- raise ArgumentError, _("Too many addresses in line. Proper format is start_ip_address,end_ip_address (one Allocation Pool per line)") if extra_entry
-
- {"start" => start_addr.strip, "end" => end_addr.strip}
- end
- end
-
- def parse_host_routes(option)
- return [] unless option
-
- option.lines.map do |route|
- dest_addr, nexthop_addr, extra_entry = route.split(",")
- raise ArgumentError, _("Too few entries in line. Proper format is destination_cidr,nexthop (one Host Route per line)") unless nexthop_addr
- raise ArgumentError, _("Too many entries in line. Proper format is destination_cidr,nexthop (one Host Route per line)") if extra_entry
-
- {"destination" => dest_addr.strip, "nexthop" => nexthop_addr.strip}
- end
- end
-
- def parse_dns_nameservers(option)
- return [] unless option
-
- option.lines.map do |nameserver|
- one_nameserver, extra_entry = nameserver.strip.split(/\s+|,/)
- raise ArgumentError, _("One DNS Name Server per line is required.") if !one_nameserver || extra_entry
-
- one_nameserver
- end
- end
-
- def changed_form_params
- # Allowed fields for update: name, enable_dhcp, dns_nameservers, allocation_pools, host_routes, gateway_ip
- options = {}
- options[:name] = params[:name] unless @subnet.name == params[:name]
-
- # Provider to automatically assign gateway address unless provided
- unless @subnet.gateway == params[:gateway]
- options[:gateway_ip] = params[:gateway].presence
- end
-
- unless @subnet.dhcp_enabled == switch_to_bol(params[:dhcp_enabled])
- options[:enable_dhcp] = switch_to_bol(params[:dhcp_enabled])
- end
- unless @subnet.allocation_pools == (pools = parse_allocation_pools(params[:allocation_pools])) || (@subnet.allocation_pools.blank? && pools.blank?)
- options[:allocation_pools] = pools
- end
- unless @subnet.host_routes == (routes = parse_host_routes(params[:host_routes])) || (@subnet.host_routes.blank? && routes.blank?)
- options[:host_routes] = routes
- end
- unless @subnet.dns_nameservers == (nameservers = parse_dns_nameservers(params[:dns_nameservers])) || (@subnet.dns_nameservers.blank? && nameservers.blank?)
- options[:dns_nameservers] = nameservers
- end
- options
- end
-
- def new_form_params
- params[:network_protocol] ||= "ipv4"
- params[:dhcp_enabled] ||= false
- options = {}
- 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])
- # Provider to automatically assign gateway address unless provided
- if params[:gateway]
- options[:gateway_ip] = params[:gateway].presence
- end
- options[:ip_version] = /4/.match?(params[:network_protocol]) ? 4 : 6
- options[:cloud_tenant] = find_record_with_rbac(CloudTenant, params[:cloud_tenant][:id]) if params.fetch_path(:cloud_tenant, :id)
- options[:enable_dhcp] = params[:dhcp_enabled]
- # TODO: Add dns_nameservers, allocation_pools, host_routes
- options[:allocation_pools] = parse_allocation_pools(params[:allocation_pools]) if params[:allocation_pools].present?
- options[:dns_nameservers] = parse_dns_nameservers(params[:dns_nameservers]) if params[:dns_nameservers].present?
- options[:host_routes] = parse_host_routes(params[:host_routes]) if params[:host_routes].present?
- options
- end
-
# dispatches operations to multiple subnets
def process_cloud_subnets(subnets, operation)
return if subnets.empty?
diff --git a/app/javascript/components/subnet-form/index.jsx b/app/javascript/components/subnet-form/index.jsx
new file mode 100644
index 000000000000..ff742511313e
--- /dev/null
+++ b/app/javascript/components/subnet-form/index.jsx
@@ -0,0 +1,99 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+
+import MiqFormRenderer from '@@ddf';
+import createSchema from './subnet-form.schema';
+import miqRedirectBack from '../../helpers/miq-redirect-back';
+import { API } from '../../http_api';
+import { Loading } from 'carbon-components-react';
+
+const SubnetForm = ({ recordId }) => {
+ const [{ initialValues, isLoading, fields }, setState] = useState({ isLoading: !!recordId, fields: [] });
+ const submitLabel = !!recordId ? __('Save') : __('Add');
+
+ const loadSchema = (appendState = {}) => ({ data: { form_schema: { fields } } }) => {
+ if (!!recordId && appendState.initialValues.type === 'ManageIQ::Providers::Openstack::NetworkManager::CloudSubnet') {
+ Object.assign(fields[0], {isDisabled: true});
+ Object.assign(fields[1], {isDisabled: true});
+ Object.assign(fields[4], {isDisabled: true});
+ }
+ setState((state) => ({
+ ...state,
+ ...appendState,
+ fields,
+ }));
+ };
+
+ useEffect(() => {
+ if (recordId) {
+ API.get(`/api/cloud_subnets/${recordId}`).then((initialValues) => {
+ if(typeof initialValues.cloud_network_id ==="string") {
+ initialValues.cloud_network_id=Number(initialValues.cloud_network_id);
+ }
+ if(typeof initialValues.cloud_tenant_id ==="string") {
+ initialValues.cloud_tenant_id=Number(initialValues.cloud_tenant_id);
+ }
+ API.options(`/api/cloud_subnets?ems_id=${initialValues.ems_id}`).then(loadSchema({ initialValues, isLoading: false }));
+ });
+ }
+ }, [recordId]);
+
+ const onSubmit = (values) => {
+ API.get(`/api/providers/${values.ems_id}`).then(({ type }) => {
+ if (type === 'ManageIQ::Providers::Openstack::NetworkManager') {
+ if (values.ip_version === undefined) {
+ values.ip_version = '4';
+ }
+ if (values.dhcp_enabled === undefined) {
+ values.dhcp_enabled = false;
+ }
+ values.enable_dhcp = values.dhcp_enabled;
+ delete values.dhcp_enabled;
+ delete Object.assign(values, values.extra_attributes).extra_attributes;
+ }
+
+ miqSparkleOn();
+ const request = recordId ? API.patch(`/api/cloud_subnets/${recordId}`, values) : API.post('/api/cloud_subnets', values);
+
+ request.then(() => {
+ const message = sprintf(recordId
+ ? __('Modification of Cloud Subnet %s has been successfully queued')
+ : __('Add of Cloud Subnet "%s" has been successfully queued.'),
+ values.name);
+
+ miqRedirectBack(message, 'success', '/cloud_subnet/show_list');
+ }).catch(miqSparkleOff);
+ });
+ };
+
+ const onCancel = () => {
+ const message = sprintf(
+ recordId
+ ? __('Edit of Cloud Subnet "%s" was canceled by the user.')
+ : __('Creation of new Cloud Subnet was canceled by the user.'),
+ initialValues && initialValues.name,
+ );
+ miqRedirectBack(message, 'warning', '/cloud_subnet/show_list');
+ };
+
+ if (isLoading) return ;
+ return !isLoading && (
+
+ );
+};
+
+SubnetForm.propTypes = {
+ recordId: PropTypes.string,
+};
+SubnetForm.defaultProps = {
+ recordId: undefined,
+};
+
+export default SubnetForm;
diff --git a/app/javascript/components/subnet-form/subnet-form.schema.js b/app/javascript/components/subnet-form/subnet-form.schema.js
new file mode 100644
index 000000000000..c15b88db7a83
--- /dev/null
+++ b/app/javascript/components/subnet-form/subnet-form.schema.js
@@ -0,0 +1,73 @@
+import { componentTypes, validatorTypes } from '@@ddf';
+import { API } from '../../http_api';
+
+const emsUrl = '/api/providers?expand=resources&attributes=id,name,supports_cloud_subnet_create&filter[]=supports_cloud_subnet_create=true';
+
+const createSchema = (edit, fields = [], loadSchema) => ({
+ fields: [
+ {
+ component: componentTypes.SELECT,
+ name: 'ems_id',
+ id: 'ems_id',
+ label: __('Network Manager'),
+ onChange: (value) => API.options(`/api/cloud_subnets?ems_id=${value}`).then(loadSchema()),
+ loadOptions: () => API.get(emsUrl).then(({ resources }) => resources.map(({ id, name }) => ({ label: name, value: id }))),
+ includeEmpty: true,
+ isDisabled: edit,
+ isRequired: true,
+ validate: [{ type: validatorTypes.REQUIRED }],
+ },
+ {
+ component: componentTypes.TEXT_FIELD,
+ name: 'name',
+ id: 'name',
+ label: __('Name'),
+ isRequired: true,
+ validate: [{ type: validatorTypes.REQUIRED }],
+ condition: {
+ when: 'ems_id',
+ isNotEmpty: true,
+ },
+ },
+ {
+ component: componentTypes.TEXT_FIELD,
+ name: 'cidr',
+ id: 'cidr',
+ label: __('CIDR'),
+ isRequired: true,
+ isDisabled: edit,
+ validate: [{ type: validatorTypes.REQUIRED }], // TODO: pattern for validating IPv4/mask or IPv6/mask
+ condition: {
+ when: 'ems_id',
+ isNotEmpty: true,
+ },
+ },
+ {
+ component: componentTypes.FIELD_ARRAY,
+ name: 'dns_nameservers',
+ id: 'dns_nameservers',
+ label: __('DNS Servers'),
+ noItemsMessage: __('None'),
+ buttonLabels: {
+ add: __('Add'),
+ remove: __('Remove'),
+ },
+ AddButtonProps: {
+ size: 'small',
+ },
+ RemoveButtonProps: {
+ size: 'small',
+ },
+ fields: [{ // TODO: pattern for validating IPv4 or IPv6
+ component: componentTypes.TEXT_FIELD,
+ }],
+ condition: {
+ when: 'ems_id',
+ isNotEmpty: true,
+ },
+ },
+ ...fields,
+ ],
+});
+
+export default createSchema;
diff --git a/app/javascript/oldjs/components/cloud_subnet/cloud-subnet-form.js b/app/javascript/oldjs/components/cloud_subnet/cloud-subnet-form.js
deleted file mode 100644
index a0c3fcaf99d7..000000000000
--- a/app/javascript/oldjs/components/cloud_subnet/cloud-subnet-form.js
+++ /dev/null
@@ -1,111 +0,0 @@
-ManageIQ.angular.app.component('cloudSubnetForm', {
- bindings: {
- cloudSubnetFormId: '@',
- },
- controllerAs: 'vm',
- controller: cloudSubnetFormController,
- templateUrl: '/static/cloud_subnet/cloud-subnet-form.html.haml',
-});
-
-cloudSubnetFormController.$inject = ['API', 'miqService', '$scope'];
-
-function cloudSubnetFormController(API, miqService, $scope) {
- var vm = this;
-
- this.$onInit = function() {
- vm.afterGet = false;
-
- vm.cloudSubnetModel = { name: '' };
- vm.networkProtocols = ['ipv4', 'ipv6'];
-
- vm.formId = vm.cloudSubnetFormId;
- vm.model = 'cloudSubnetModel';
- ManageIQ.angular.scope = $scope;
- vm.saveable = miqService.saveable;
-
- vm.newRecord = vm.cloudSubnetFormId === 'new';
-
- miqService.sparkleOn();
- if (vm.newRecord) {
- vm.cloudSubnetModel.ems_id = '';
- vm.cloudSubnetModel.network_id = '';
- vm.cloudSubnetModel.dhcp_enabled = true;
- vm.cloudSubnetModel.network_protocol = 'ipv4';
- API.get("/api/providers?expand=resources&attributes=name&filter[]=type='*NetworkManager'").then(function(data) {
- vm.available_ems = data.resources;
- vm.afterGet = true;
- vm.modelCopy = angular.copy( vm.cloudSubnetModel );
- miqService.sparkleOff();
- }).catch(miqService.handleFailure);
- } else {
- API.get('/api/cloud_subnets/' + vm.cloudSubnetFormId + '?expand=resources&attributes=ext_management_system.name,cloud_tenant.name,cloud_network.name').then(function(data) {
- Object.assign(vm.cloudSubnetModel, _.pick(data, 'name', 'ext_management_system', 'cloud_network', 'cloud_tenant', 'network_protocol', 'cidr', 'dhcp_enabled', 'gateway', 'extra_attributes', 'dns_nameservers'));
- vm.cloudSubnetModel.allocation_pools = "";
- vm.cloudSubnetModel.host_routes = "";
- if (vm.cloudSubnetModel.extra_attributes !== undefined) {
- if (vm.cloudSubnetModel.extra_attributes.allocation_pools !== undefined) {
- vm.cloudSubnetModel.extra_attributes.allocation_pools.forEach(function(val, i) {
- if (i > 0) {
- vm.cloudSubnetModel.allocation_pools += "\n";
- }
- vm.cloudSubnetModel.allocation_pools += val["start"] + "," + val["end"];
- });
- }
- if (vm.cloudSubnetModel.extra_attributes.host_routes !== undefined) {
- vm.cloudSubnetModel.extra_attributes.host_routes.forEach(function(val, i) {
- if (i > 0) {
- vm.cloudSubnetModel.host_routes += "\n";
- }
- vm.cloudSubnetModel.host_routes += val["destination"] + "," + val["nexthop"];
- });
- }
- }
- if (vm.cloudSubnetModel.dns_nameservers !== undefined) {
- vm.cloudSubnetModel.dns_nameservers = vm.cloudSubnetModel.dns_nameservers.join("\n");
- } else {
- vm.cloudSubnetModel.dns_nameservers = "";
- }
- vm.afterGet = true;
- vm.modelCopy = angular.copy( vm.cloudSubnetModel );
- miqService.sparkleOff();
- }).catch(miqService.handleFailure);
- }
- };
-
- vm.addClicked = function() {
- var url = 'create/new?button=add';
- miqService.miqAjaxButton(url, vm.cloudSubnetModel, { complete: false });
- };
-
- vm.cancelClicked = function() {
- var url = '';
- if (vm.newRecord) {
- url = '/cloud_subnet/create/new?button=cancel';
- } else {
- url = '/cloud_subnet/update/' + vm.cloudSubnetFormId + '?button=cancel';
- }
- miqService.miqAjaxButton(url);
- };
-
- vm.saveClicked = function() {
- var url = '/cloud_subnet/update/' + vm.cloudSubnetFormId + '?button=save';
- miqService.miqAjaxButton(url, _.pick(vm.cloudSubnetModel, 'name', 'dhcp_enabled', 'gateway', 'allocation_pools', 'host_routes', 'dns_nameservers'), { complete: false });
- };
-
- vm.resetClicked = function(angularForm) {
- vm.cloudSubnetModel = angular.copy( vm.modelCopy );
- angularForm.$setPristine(true);
- miqService.miqFlash('warn', __('All changes have been reset'));
- };
-
- vm.filterNetworkManagerChanged = function(id) {
- if (id) {
- API.get('/api/cloud_networks?expand=resources&attributes=name,ems_ref&filter[]=ems_id=' + id).then(function(data) {
- vm.available_networks = data.resources;
- }).catch(miqService.handleFailure);
- miqService.getProviderTenants(function(data) {
- vm.available_tenants = data.resources;
- })(id);
- }
- };
-}
diff --git a/app/javascript/oldjs/components/index.js b/app/javascript/oldjs/components/index.js
index a8863be04d57..392ff5131d9a 100644
--- a/app/javascript/oldjs/components/index.js
+++ b/app/javascript/oldjs/components/index.js
@@ -1,7 +1,6 @@
require('./ae_resolve_options/ae-resolve-options.js');
require('./ansible-raw-stdout.js');
require('./auth-credentials.js');
-require('./cloud_subnet/cloud-subnet-form.js');
require('./cloud_volume_backup/cloud-volume-backup-form.js');
require('./datetime-delay-picker.js');
require('./generic_object/assign-buttons.js');
diff --git a/app/javascript/packs/component-definitions-common.js b/app/javascript/packs/component-definitions-common.js
index 475243331c91..bba022623d49 100644
--- a/app/javascript/packs/component-definitions-common.js
+++ b/app/javascript/packs/component-definitions-common.js
@@ -46,6 +46,7 @@ import ReportChartWidget from '../components/create-report-chart-form';
import ReportDataTable from '../components/report-data-table';
import RetirementForm from '../components/retirement-form';
import ServiceDialogFromForm from '../components/service-dialog-from-form/service-dialog-from';
+import SubnetForm from '../components/subnet-form';
import EditServiceForm from '../components/edit-service-form';
import SetOwnershipForm from '../components/set-ownership-form';
import ServersDataChart from '../components/provider-dashboard-charts/servers-pie-chart';
@@ -118,6 +119,7 @@ ManageIQ.component.addReact('ServiceDialogFromForm', ServiceDialogFromForm);
ManageIQ.component.addReact('EditServiceForm', EditServiceForm);
ManageIQ.component.addReact('SetOwnershipForm', SetOwnershipForm);
ManageIQ.component.addReact('ServersDataChart', ServersDataChart);
+ManageIQ.component.addReact('SubnetForm', SubnetForm);
ManageIQ.component.addReact('TableListView', TableListView);
ManageIQ.component.addReact('TableListViewWrapper', TableListViewWrapper);
ManageIQ.component.addReact('TagGroup', props => );
diff --git a/app/javascript/spec/subnet-form/__snapshots__/subnet-form.spec.js.snap b/app/javascript/spec/subnet-form/__snapshots__/subnet-form.spec.js.snap
new file mode 100644
index 000000000000..6f3fa0b48125
--- /dev/null
+++ b/app/javascript/spec/subnet-form/__snapshots__/subnet-form.spec.js.snap
@@ -0,0 +1,118 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Subnet form component renders the adding form variant 1`] = `
+
+`;
+
+exports[`Subnet form component renders the editing form variant 1`] = `
+
+
+
+`;
diff --git a/app/javascript/spec/subnet-form/subnet-form.spec.js b/app/javascript/spec/subnet-form/subnet-form.spec.js
new file mode 100644
index 000000000000..b384378107f4
--- /dev/null
+++ b/app/javascript/spec/subnet-form/subnet-form.spec.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import toJson from 'enzyme-to-json';
+import fetchMock from 'fetch-mock';
+import { shallow } from 'enzyme';
+import { act } from 'react-dom/test-utils';
+import SubnetForm from '../../components/subnet-form';
+import { mount } from '../helpers/mountForm';
+
+require('../helpers/miqSparkle.js');
+require('../helpers/miqAjaxButton.js');
+
+describe('Subnet form component', () => {
+ afterEach(() => {
+ fetchMock.reset();
+ fetchMock.restore();
+ });
+
+ it('renders the adding form variant', () => {
+ const wrapper = shallow();
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ it('renders the editing form variant', async(done) => {
+ fetchMock.get('/api/providers?expand=resources&attributes=id,name,supports_cloud_subnet_create&filter[]=supports_cloud_subnet_create=true', {
+ resources: [{ label: 'foo', value: 1 }],
+ });
+ fetchMock.getOnce('/api/cloud_subnets/1', { name: 'foo', ems_id: 1 });
+ fetchMock.mock('/api/cloud_subnets?ems_id=1', { data: { form_schema: { fields: [] } } }, { method: 'OPTIONS' });
+ let wrapper;
+ await act(async() => {
+ wrapper = mount();
+ });
+ expect(fetchMock.called('/api/cloud_subnets/1')).toBe(true);
+ expect(toJson(wrapper)).toMatchSnapshot();
+ done();
+ });
+});
diff --git a/app/views/cloud_subnet/edit.html.haml b/app/views/cloud_subnet/edit.html.haml
index 885cb265960a..ab4da8504753 100644
--- a/app/views/cloud_subnet/edit.html.haml
+++ b/app/views/cloud_subnet/edit.html.haml
@@ -1,6 +1,2 @@
= render :partial => "layouts/flash_msg"
-
-%cloud-subnet-form{'cloud-subnet-form-id' => @subnet.id}
-
-:javascript
- miq_bootstrap('cloud-subnet-form');
+= react 'SubnetForm', :recordId => @subnet.try(:id).try(:to_s)
diff --git a/app/views/cloud_subnet/new.html.haml b/app/views/cloud_subnet/new.html.haml
index baa43ef1062b..f7d9ea2caa03 100644
--- a/app/views/cloud_subnet/new.html.haml
+++ b/app/views/cloud_subnet/new.html.haml
@@ -1,6 +1,2 @@
= render :partial => "layouts/flash_msg"
-
-%cloud-subnet-form{'cloud-subnet-form-id' => 'new'}
-
-:javascript
- miq_bootstrap('cloud-subnet-form');
+= react 'SubnetForm'
diff --git a/app/views/static/cloud_subnet/cloud-subnet-form.html.haml b/app/views/static/cloud_subnet/cloud-subnet-form.html.haml
deleted file mode 100644
index 6d8fc35d2315..000000000000
--- a/app/views/static/cloud_subnet/cloud-subnet-form.html.haml
+++ /dev/null
@@ -1,154 +0,0 @@
-%form#form_div{'name' => 'angularForm',
- 'form-changed' => true,
- 'miq-form' => true,
- 'model' => 'vm.cloudSubnetModel',
- 'model-copy' => 'vm.modelCopy',
- 'ng-cloak' => '',
- 'ng-show' => 'vm.afterGet'}
-
- %h3
- = _('Network Management Provider')
- .form-horizontal
- .form-group{'ng-class' => '{"has-error": angularForm.ems_id.$invalid}'}
- %label.col-md-2.control-label
- = _('Network Manager')
- .col-md-8{'ng-if' => 'vm.newRecord'}
- %select{'name' => 'ems_id',
- 'ng-change' => 'vm.filterNetworkManagerChanged(vm.cloudSubnetModel.ems_id)',
- 'ng-model' => 'vm.cloudSubnetModel.ems_id',
- 'ng-options' => 'ems.id as ems.name for ems in vm.available_ems',
- 'miq-select' => true,
- 'required' => '',
- 'selectpicker-for-select-tag' => ''}
- %option{'value' => ''}
- = "<#{_('Choose')}>"
- %span.help-block{'ng-show' => 'angularForm.ems_id.$error.required'}
- = _('Required')
- .col-md-8{'ng-if' => '!vm.newRecord'}
- %input.form-control{'type' => 'text',
- 'name' => 'ems_name',
- 'disabled' => true,
- 'maxlength' => 128,
- 'ng-model' => 'vm.cloudSubnetModel.ext_management_system.name'}
- .form-group{'ng-class' => '{"has-error": angularForm.cloud_tenant_id.$invalid}',
- 'ng-if' => 'vm.cloudSubnetModel.ems_id && vm.available_tenants.length'}
- %label.col-md-2.control-label
- = _('Cloud Tenant Placement')
- .col-md-8{'ng-if' => 'vm.newRecord'}
- %select{'name' => 'cloud_tenant_id',
- 'ng-model' => 'vm.cloudSubnetModel.cloud_tenant.id',
- 'ng-options' => 'tenant.id as tenant.name for tenant in vm.available_tenants',
- 'miq-select' => true,
- 'required' => '',
- 'selectpicker-for-select-tag' => ''}
- %option{'value' => ''}
- = "<#{_('Choose')}>"
- %span.help-block{'ng-show' => "angularForm.cloud_tenant_id.$error.required"}
- = _('Required')
- .col-md-8{'ng-if' => '!vm.newRecord'}
- %input.form-control{'type' => 'text',
- 'name' => ' clout_tenant_name',
- 'maxlength' => 128,
- 'ng-model' => 'vm.cloudSubnetModel.cloud_tenant.name',
- 'disabled' => true}
- %h3
- = _('Cloud Subnet details')
- .form-horizontal
- .form-group{'ng-class' => '{"has-error": angularForm.network_id.$invalid}'}
- %label.col-md-2.control-label
- = _('Network')
- .col-md-8{'ng-if' => 'vm.newRecord'}
- %select{'name' => 'network_id',
- 'ng-model' => 'vm.cloudSubnetModel.network_id',
- 'ng-options' => 'network.ems_ref as network.name for network in vm.available_networks',
- 'miq-select' => true,
- 'required' => '',
- 'selectpicker-for-select-tag' => ''}
- %option{'value' => ''}
- = "<#{_('Choose')}>"
- %span.help-block{'ng-show' => 'angularForm.network_id.$error.required'}
- = _('Required')
- .col-md-8{'ng-if' => '!vm.newRecord'}
- %input.form-control{'type' => 'text',
- 'name' => 'network_name',
- 'maxlength' => 128,
- 'ng-model' => 'vm.cloudSubnetModel.cloud_network.name',
- 'disabled' => true}
- .form-horizontal
- .form-group
- %label.col-md-2.control-label
- = _('Subnet Name')
- .col-md-8
- %input.form-control{'type' => 'text',
- 'name' => 'name',
- 'ng-model' => 'vm.cloudSubnetModel.name',
- 'maxlength' => 128}
- .form-group
- %label.col-md-2.control-label
- = _('Gateway')
- .col-md-8
- %input.form-control{'type' => 'text',
- 'name' => 'gateway_ip',
- 'ng-model' => 'vm.cloudSubnetModel.gateway',
- 'maxlength' => 20}
- .form-group
- %label.control-label.col-md-2{'for' => 'dhcp'}
- = _('DHCP')
- .col-md-8
- %input{'type' => 'checkbox',
- 'name' => 'dhcp',
- 'bs-switch' => '',
- 'data' => {:on_text => _('Enabled'), :off_text => _('Disabled'), :size => 'mini'},
- 'ng-model' => 'vm.cloudSubnetModel.dhcp_enabled'}
- .form-group{'ng-class' => '{"has-error": angularForm.network_protocol.$invalid}'}
- %label.col-md-2.control-label
- = _('IP Version')
- .col-md-8
- %select{'name' => 'network_protocol',
- 'ng-model' => 'vm.cloudSubnetModel.network_protocol',
- 'ng-disabled' => '!vm.newRecord',
- 'ng-options' => 'protocol as protocol for protocol in vm.networkProtocols',
- 'miq-select' => true,
- 'required' => '',
- 'selectpicker-for-select-tag' => ''}
- %span.help-block{'ng-show' => 'angularForm.network_protocol.$error.required'}
- = _('Required')
-
- .form-group{'ng-class' => "{'has-error': angularForm.cidr.$invalid}"}
- %label.col-md-2.control-label
- = _('Subnet CIDR')
- .col-md-8
- %input.form-control{'type' => 'text',
- 'name' => 'cidr',
- 'ng-disabled' => '!vm.newRecord',
- 'ng-model' => 'vm.cloudSubnetModel.cidr',
- 'maxlength' => 20,
- 'required' => ''}
- %span.help-block{'ng-show' => 'angularForm.cidr.$error.required'}
- = _('Required')
- .form-group
- %label.col-md-2.control-label
- = _('Allocation Pools')
- .col-md-8
- %textarea.form-control{'type' => 'textarea',
- 'name' => 'allocation_pools',
- 'ng-model' => 'vm.cloudSubnetModel.allocation_pools',
- 'rows' => 4}
- .form-group
- %label.col-md-2.control-label
- = _('DNS Name Servers')
- .col-md-8
- %textarea.form-control{'type' => 'textarea',
- 'name' => 'dns_nameservers',
- 'ng-model' => 'vm.cloudSubnetModel.dns_nameservers',
- 'rows' => 4}
- .form-group
- %label.col-md-2.control-label
- = _('Host Routes')
- .col-md-8
- %textarea.form-control{'type' => 'textarea',
- 'name' => 'host_routes',
- 'ng-model' => 'vm.cloudSubnetModel.host_routes',
- 'rows' => 4}
-
- = render :partial => 'layouts/angular/generic_form_buttons'
diff --git a/config/routes.rb b/config/routes.rb
index a6b32b4ff69f..b445ecad6bc7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1556,7 +1556,6 @@
compare_get,
:post => %w(
button
- create
dynamic_checkbox_refresh
listnav_search_selected
protect
@@ -1565,7 +1564,6 @@
show
show_list
tagging_edit
- update
wait_for_task
) +
adv_search_post +
diff --git a/package.json b/package.json
index 5be6b186b905..2f5829a24c9b 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"@carbon/charts-react": "~0.41.38",
"@carbon/icons-react": "~10.26.0",
"@carbon/themes": "~10.28.0",
- "@data-driven-forms/carbon-component-mapper": "~2.24.3",
+ "@data-driven-forms/carbon-component-mapper": "~2.24.4",
"@data-driven-forms/pf3-component-mapper": "~2.20.3",
"@data-driven-forms/react-form-renderer": "~2.24.1",
"@manageiq/ui-components": "1.4.4",
diff --git a/spec/controllers/cloud_subnet_controller_spec.rb b/spec/controllers/cloud_subnet_controller_spec.rb
index 97a231ba566b..efc8d5b7d768 100644
--- a/spec/controllers/cloud_subnet_controller_spec.rb
+++ b/spec/controllers/cloud_subnet_controller_spec.rb
@@ -74,40 +74,10 @@
login_as FactoryBot.create(:user, :miq_groups => [group])
end
- it "raises exception wheh used have not privilege" do
- expect { post :new, :params => { :button => "new", :format => :js } }.to raise_error(MiqException::RbacPrivilegeException)
- end
-
- context "user don't have privilege for cloud tenants" do
- let(:feature) { MiqProductFeature.find_all_by_identifier(%w(cloud_subnet_new ems_network_show_list)) }
-
- it "raises exception" do
- expect { post :new, :params => { :button => "new", :format => :js } }.to raise_error(MiqException::RbacPrivilegeException)
- end
- end
-
- context "user don't have privilege for cloud networks" do
- let(:feature) { MiqProductFeature.find_all_by_identifier(%w(cloud_subnet_new ems_network_show_list cloud_tenant_show_list)) }
-
- it "raises exception" do
- expect { post :new, :params => { :button => "new", :format => :js } }.to raise_error(MiqException::RbacPrivilegeException)
- end
- end
-
- it 'asserts privileges and calls drop_breadcrumb' do
- expect(controller).to receive(:assert_privileges).with('cloud_subnet_new')
- expect(controller).to receive(:assert_privileges).with('ems_network_show_list')
- expect(controller).to receive(:assert_privileges).with('cloud_tenant_show_list')
- expect(controller).to receive(:assert_privileges).with('cloud_network_show_list')
+ it 'calls drop_breadcrumb' do
expect(controller).to receive(:drop_breadcrumb).with(:name => 'Add New Subnet', :url => '/cloud_subnet/new')
controller.send(:new)
end
-
- it 'renders cloud_subnet/new partial for user with privileges' do
- stub_user(:features => :all)
- post :new, :params => { :button => "new", :format => :js }
- expect(response.body).to include("")
- end
end
describe "#create" do
@@ -139,26 +109,6 @@
end
before { stub_user(:features => :all) }
-
- it "queues the create action" do
- expect(MiqTask).to receive(:generic_action_with_callback).with(task_options, hash_including(queue_options))
-
- post :create, :params => {
- :button => 'add',
- :controller => 'cloud_subnet',
- :format => :js,
- :cloud_tenant => {:id => cloud_tenant.id},
- :dhcp_enabled => true,
- :ems_id => ems.id,
- :id => 'new',
- :name => 'test',
- :network_protocol => 'ipv4',
- :network_id => cloud_network.ems_ref,
- :allocation_pools => "172.10.1.10,172.10.1.20",
- :host_routes => "172.12.1.0/24,172.12.1.1",
- :dns_nameservers => "172.11.1.1"
- }
- end
end
describe "#edit" do
@@ -190,58 +140,6 @@
expect(assigns(:flash_array)).to be_nil
end
-
- it "queues the update action" do
- expect(MiqTask).to receive(:generic_action_with_callback).with(task_options, hash_including(queue_options))
-
- post :update, :params => {
- :button => 'save',
- :format => :js,
- :id => cloud_subnet.id,
- :name => 'test2',
- :allocation_pools => "172.20.1.10,172.20.1.20",
- :host_routes => "172.22.1.0/24,172.22.1.1",
- :dns_nameservers => "172.21.1.1"
- }
- end
- end
-
- describe '#update' do
- before do
- allow(controller).to receive(:assert_privileges)
- controller.params = {:button => 'cancel', :id => cloud_subnet.id}
- end
-
- it 'calls flash_and_redirect for canceling editing Cloud Subnet' do
- expect(controller).to receive(:flash_and_redirect).with(_("Edit of Subnet \"%{name}\" was cancelled by the user") % {:name => cloud_subnet.name})
- controller.send(:update)
- end
- end
-
- describe '#update_finished' do
- let(:miq_task) { double("MiqTask", :state => 'Finished', :status => 'ok', :message => 'some message') }
-
- before do
- allow(MiqTask).to receive(:find).with(123).and_return(miq_task)
- allow(controller).to receive(:session).and_return(:async => {:params => {:task_id => 123, :name => cloud_subnet.name}})
- end
-
- it 'calls flash_and_redirect with appropriate arguments for succesful updating of a Cloud Subnet' do
- expect(controller).to receive(:flash_and_redirect).with(_("Cloud Subnet \"%{name}\" updated") % {:name => cloud_subnet.name})
- controller.send(:update_finished)
- end
-
- context 'unsuccesful updating of a Cloud Subnet' do
- let(:miq_task) { double("MiqTask", :state => 'Finished', :status => 'Error', :message => 'some message') }
-
- it 'calls flash_and_redirect with appropriate arguments' do
- expect(controller).to receive(:flash_and_redirect).with(_("Unable to update Cloud Subnet \"%{name}\": %{details}") % {
- :name => cloud_subnet.name,
- :details => miq_task.message
- }, :error)
- controller.send(:update_finished)
- end
- end
end
describe "#delete" do
diff --git a/spec/javascripts/components/cloud_subnet/cloud_subnet_form_spec.js b/spec/javascripts/components/cloud_subnet/cloud_subnet_form_spec.js
deleted file mode 100644
index 0c7962cd189c..000000000000
--- a/spec/javascripts/components/cloud_subnet/cloud_subnet_form_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-describe('cloud-subnet-form', function() {
- var $componentController, vm, miqService, API;
-
- describe('when vm.recordId is not defined', function () {
- beforeEach(module('ManageIQ'));
- beforeEach(inject(function (_$componentController_, _API_, _miqService_, $q) {
- $componentController = _$componentController_;
- API = _API_;
- miqService = _miqService_;
-
- spyOn(miqService, 'miqAjaxButton');
-
- var deferred = $q.defer();
- spyOn(API, 'get').and.callFake(function() {return deferred.promise;});
-
- var bindings = {cloudSubnetFormId: 'new'};
- vm = $componentController('cloudSubnetForm', null, bindings);
- vm.$onInit();
- }));
-
- it('sets newRecord to true', function () {
- expect(vm.newRecord).toBe(true);
- });
-
- it('sets afterGet', function () {
- expect(vm.afterGet).toBe(false);
- });
-
- it('adds a Cloud Subnet record', function () {
- vm.cloudSubnetModel.name = 'newSubnet';
- vm.cloudSubnetModel.ip_version = 'ipv4';
- vm.cloudSubnetModel.ems_id = 1;
- vm.addClicked();
- expect(miqService.miqAjaxButton).toHaveBeenCalledWith('create/new?button=add', vm.cloudSubnetModel, { complete: false });
- });
- });
-
- describe('when vm.recordId is defined', function () {
- beforeEach(module('ManageIQ'));
- beforeEach(inject(function (_$componentController_, _API_, _miqService_, $q) {
- $componentController = _$componentController_;
- API = _API_;
- miqService = _miqService_;
-
- spyOn(miqService, 'miqAjaxButton');
-
- var cloudSubnetFormResponse = {
- name: 'abc'
- };
-
- spyOn(API, 'get').and.callFake(function() {
- return {
- then: function (callback) {
- callback(cloudSubnetFormResponse);
- return {catch: function() {}};
- }
- };
- });
-
- var bindings = {cloudSubnetFormId: 1111};
- vm = $componentController('cloudSubnetForm', null, bindings);
- vm.$onInit();
- }));
-
- it('sets newRecord to false', function () {
- expect(vm.newRecord).toBe(false);
- });
-
- it('calls API.get with the appropriate URL', function () {
- expect(API.get).toHaveBeenCalledWith('/api/cloud_subnets/1111?expand=resources&attributes=ext_management_system.name,cloud_tenant.name,cloud_network.name');
- });
-
- it('sets vm.cloudSubnetModel.name', function () {
- expect(vm.cloudSubnetModel.name).toBe('abc');
- });
-
- it('updates a Cloud Subnet record', function () {
-
- vm.cloudSubnetModel.name = 'xyz';
- vm.cloudSubnetModel.gateway = '172.10.1.2';
- vm.cloudSubnetModel.dhcp_enabled = '172.10.1.2';
- vm.cloudSubnetModel.random_field_from_API = "random data from API";
- vm.cloudSubnetModel.allocation_pools = '172.10.1.10,172.10.1.20';
- vm.cloudSubnetModel.host_routes = '172.12.1.0/24,172.12.1.1';
- vm.cloudSubnetModel.dns_nameservers = '172.11.1.1';
- var correct_data = _.pick(vm.cloudSubnetModel, 'name', 'gateway', 'dhcp_enabled', 'allocation_pools', 'host_routes', 'dns_nameservers');
- vm.saveClicked();
-
- expect(miqService.miqAjaxButton).toHaveBeenCalledWith('/cloud_subnet/update/1111?button=save', correct_data, { complete: false });
- });
- });
-});