diff --git a/app/assets/javascripts/controllers/network_router/network_router_form_controller.js b/app/assets/javascripts/controllers/network_router/network_router_form_controller.js index 69ea77a69462..9ee707899083 100644 --- a/app/assets/javascripts/controllers/network_router/network_router_form_controller.js +++ b/app/assets/javascripts/controllers/network_router/network_router_form_controller.js @@ -1,5 +1,8 @@ ManageIQ.angular.app.controller('networkRouterFormController', ['$http', '$scope', 'networkRouterFormId', 'miqService', function($http, $scope, networkRouterFormId, miqService) { - $scope.networkRouterModel = { name: '' }; + $scope.networkRouterModel = { + name: '', + cloud_subnet_id: '', + }; $scope.formId = networkRouterFormId; $scope.afterGet = false; $scope.modelCopy = angular.copy( $scope.networkRouterModel ); @@ -9,6 +12,7 @@ ManageIQ.angular.app.controller('networkRouterFormController', ['$http', '$scope if (networkRouterFormId == 'new') { $scope.networkRouterModel.name = ""; + $scope.networkRouterModel.cloud_subnet_id = ""; $scope.newRecord = true; } else { miqService.sparkleOn(); @@ -16,6 +20,7 @@ ManageIQ.angular.app.controller('networkRouterFormController', ['$http', '$scope $http.get('/network_router/network_router_form_fields/' + networkRouterFormId).success(function(data) { $scope.afterGet = true; $scope.networkRouterModel.name = data.name; + $scope.networkRouterModel.cloud_subnet_id = ""; $scope.modelCopy = angular.copy( $scope.networkRouterModel ); miqService.sparkleOff(); @@ -41,6 +46,30 @@ ManageIQ.angular.app.controller('networkRouterFormController', ['$http', '$scope miqService.miqAjaxButton(url, $scope.networkRouterModel, { complete: false }); }; + $scope.addInterfaceClicked = function() { + miqService.sparkleOn(); + var url = '/network_router/add_interface/' + networkRouterFormId + '?button=addInterface'; + miqService.miqAjaxButton(url, $scope.networkRouterModel, { complete: false }); + }; + + $scope.removeInterfaceClicked = function() { + miqService.sparkleOn(); + var url = '/network_router/remove_interface/' + networkRouterFormId + '?button=removeInterface'; + miqService.miqAjaxButton(url, $scope.networkRouterModel, { complete: false }); + }; + + $scope.cancelAddInterfaceClicked = function() { + miqService.sparkleOn(); + var url = '/network_router/add_interface/' + networkRouterFormId + '?button=cancel'; + miqService.miqAjaxButton(url); + }; + + $scope.cancelRemoveInterfaceClicked = function() { + miqService.sparkleOn(); + var url = '/network_router/remove_interface/' + networkRouterFormId + '?button=cancel'; + miqService.miqAjaxButton(url); + }; + $scope.resetClicked = function() { $scope.networkRouterModel = angular.copy( $scope.modelCopy ); $scope.angularForm.$setPristine(true); diff --git a/app/controllers/network_router_controller.rb b/app/controllers/network_router_controller.rb index 0469dcc7b108..d747eb16f29b 100644 --- a/app/controllers/network_router_controller.rb +++ b/app/controllers/network_router_controller.rb @@ -28,6 +28,10 @@ def button javascript_redirect :action => "edit", :id => checked_item_id elsif params[:pressed] == "network_router_new" javascript_redirect :action => "new" + elsif params[:pressed] == "network_router_add_interface" + javascript_redirect :action => "add_interface_select", :id => checked_item_id + elsif params[:pressed] == "network_router_remove_interface" + javascript_redirect :action => "remove_interface_select", :id => checked_item_id elsif !flash_errors? && @refresh_div == "main_div" && @lastaction == "show_list" replace_gtl_main_div else @@ -81,7 +85,7 @@ def create when "add" @router = NetworkRouter.new - options = form_params + options = form_params(params) ems = ExtManagementSystem.find(options[:ems_id]) options.delete(:ems_id) task_id = ems.create_network_router_queue(session[:userid], options) @@ -176,7 +180,7 @@ def edit def update assert_privileges("network_router_edit") @router = find_by_id_filtered(NetworkRouter, params[:id]) - options = form_params + options = form_params(params) case params[:button] when "cancel" cancel_action(_("Edit of Router \"%{name}\" was cancelled by the user") % { @@ -217,18 +221,208 @@ def update_finished javascript_redirect previous_breadcrumb_url end + def add_interface_select + assert_privileges("network_router_add_interface") + @router = find_by_id_filtered(NetworkRouter, params[:id]) + @in_a_form = true + @subnet_choices = {} + + (@router.ext_management_system.cloud_subnets - @router.cloud_subnets).each do |subnet| + @subnet_choices[subnet.name] = subnet.id + end + if @subnet_choices.empty? + add_flash(_("No subnets available to add interfaces to Router \"%{name}\"") % { + :name => @router.name + }, :error) + session[:flash_msgs] = @flash_array + @in_a_form = false + if @lastaction == "show_list" + redirect_to(:action => "show_list") + else + redirect_to(:action => "show", :id => params[:id]) + end + else + drop_breadcrumb( + :name => _("Add Interface to Router \"%{name}\"") % {:name => @router.name}, + :url => "/network_router/add_interface/#{@router.id}" + ) + end + end + + def add_interface + assert_privileges("network_router_add_interface") + @router = find_by_id_filtered(NetworkRouter, params[:id]) + + case params[:button] + when "cancel" + cancel_action(_("Add Interface on Subnet to Router \"%{name}\" was cancelled by the user") % { + :name => @router.name + }) + + when "addInterface" + options = form_params(params) + cloud_subnet = find_by_id_filtered(CloudSubnet, options[:cloud_subnet_id]) + + if @router.supports?(:add_interface) + task_id = @router.add_interface_queue(session[:userid], cloud_subnet) + + unless task_id.kind_of?(Fixnum) + add_flash(_("Add Interface on Subnet to Router \"%{name}\" failed: Task start failed: ID [%{id}]") % { + :name => @router.name, + :id => task_id.inspect + }, :error) + end + + if @flash_array + javascript_flash(:spinner_off => true) + else + initiate_wait_for_task(:task_id => task_id, :action => "add_interface_finished") + end + else + @in_a_form = true + add_flash(_("Add Interface not supported by Router \"%{name}\"") % { + :name => @router.name + }, :error) + @breadcrumbs.pop if @breadcrumbs + javascript_flash + end + end + end + + def add_interface_finished + task_id = session[:async][:params][:task_id] + router_id = session[:async][:params][:id] + router_name = session[:async][:params][:name] + cloud_subnet_id = session[:async][:params][:cloud_subnet_id] + + task = MiqTask.find(task_id) + cloud_subnet = CloudSubnet.find(cloud_subnet_id) + if MiqTask.status_ok?(task.status) + add_flash(_("Subnet \"%{subnetname}\" added to Router \"%{name}\"") % { + :subnetname => cloud_subnet.name, + :name => router_name + }) + else + add_flash(_("Unable to add Subnet \"%{name}\": %{details}") % { + :name => router_name, + :details => task.message + }, :error) + end + + @breadcrumbs.pop if @breadcrumbs + session[:edit] = nil + session[:flash_msgs] = @flash_array.dup if @flash_array + + javascript_redirect :action => "show", :id => router_id + end + + def remove_interface_select + assert_privileges("network_router_remove_interface") + @router = find_by_id_filtered(NetworkRouter, params[:id]) + @in_a_form = true + @subnet_choices = {} + + @router.cloud_subnets.each do |subnet| + @subnet_choices[subnet.name] = subnet.id + end + if @subnet_choices.empty? + add_flash(_("No subnets to remove interfaces to Router \"%{name}\"") % { + :name => @router.name + }, :error) + session[:flash_msgs] = @flash_array + @in_a_form = false + if @lastaction == "show_list" + redirect_to(:action => "show_list") + else + redirect_to(:action => "show", :id => params[:id]) + end + else + drop_breadcrumb( + :name => _("Remove Interface from Router \"%{name}\"") % {:name => @router.name}, + :url => "/network_router/remove_interface/#{@router.id}" + ) + end + end + + def remove_interface + assert_privileges("network_router_remove_interface") + @router = find_by_id_filtered(NetworkRouter, params[:id]) + + case params[:button] + when "cancel" + cancel_action(_("Remove Interface on Subnet from Router \"%{name}\" was cancelled by the user") % { + :name => @router.name + }) + + when "removeInterface" + options = form_params(params) + cloud_subnet = find_by_id_filtered(CloudSubnet, options[:cloud_subnet_id]) + + if @router.supports?(:remove_interface) + task_id = @router.remove_interface_queue(session[:userid], cloud_subnet) + + unless task_id.kind_of?(Fixnum) + add_flash(_("Remove Interface on Subnet from Router \"%{name}\" failed: Task start failed: ID [%{id}]") % { + :name => @router.name, + :id => task_id.inspect + }, :error) + end + + if @flash_array + javascript_flash(:spinner_off => true) + else + initiate_wait_for_task(:task_id => task_id, :action => "remove_interface_finished") + end + else + @in_a_form = true + add_flash(_("Remove Interface not supported by Router \"%{name}\"") % { + :name => @router.name + }, :error) + @breadcrumbs.pop if @breadcrumbs + javascript_flash + end + end + end + + def remove_interface_finished + task_id = session[:async][:params][:task_id] + router_id = session[:async][:params][:id] + router_name = session[:async][:params][:name] + cloud_subnet_id = session[:async][:params][:cloud_subnet_id] + + task = MiqTask.find(task_id) + cloud_subnet = CloudSubnet.find(cloud_subnet_id) + if MiqTask.status_ok?(task.status) + add_flash(_("Subnet \"%{subnetname}\" removed from Router \"%{name}\"") % { + :subnetname => cloud_subnet.name, + :name => router_name + }) + else + add_flash(_("Unable to remove Subnet \"%{name}\": %{details}") % { + :name => router_name, + :details => task.message + }, :error) + end + + @breadcrumbs.pop if @breadcrumbs + session[:edit] = nil + session[:flash_msgs] = @flash_array.dup if @flash_array + + javascript_redirect :action => "show", :id => router_id + end + private - def form_params + def form_params(in_params) options = {} - options[:name] = params[:name] if params[:name] - options[:ems_id] = params[:ems_id] if params[:ems_id] - options[:admin_state_up] = params[:admin_state_up] if params[:admin_state_up] - - # Relationships - options[:cloud_tenant] = find_by_id_filtered(CloudTenant, params[:cloud_tenant_id]) if params[:cloud_tenant_id] - options[:cloud_network_id] = params[:cloud_network_id].gsub(/number:/, '') if params[:cloud_network_id] - options[:cloud_group_id] = params[:cloud_group_id] if params[:cloud_group_id] + [:name, :ems_id, :admin_state_up, :cloud_group_id, + :cloud_subnet_id, :cloud_network_id].each do |param| + options[param] = in_params[param] if in_params[param] + end + options[:cloud_network_id].gsub!(/number:/, '') if options[:cloud_network_id] + if in_params[:cloud_tenant_id] + options[:cloud_tenant] = find_by_id_filtered(CloudTenant, in_params[:cloud_tenant_id]) + end options end diff --git a/app/helpers/application_helper/toolbar/network_router_center.rb b/app/helpers/application_helper/toolbar/network_router_center.rb index 79a2712e9696..04464e53852d 100644 --- a/app/helpers/application_helper/toolbar/network_router_center.rb +++ b/app/helpers/application_helper/toolbar/network_router_center.rb @@ -15,6 +15,24 @@ class ApplicationHelper::Toolbar::NetworkRouterCenter < ApplicationHelper::Toolb t, :url_parms => 'main_div' ), + button( + :network_router_add_interface, + 'pficon pficon-edit fa-lg', + t = N_('Add Interface to this Router'), + t, + :url_parms => 'main_div', + :klass => ApplicationHelper::Button::GenericFeatureButtonWithDisable, + :options => {:feature => :add_interface} + ), + button( + :network_router_remove_interface, + 'pficon pficon-edit fa-lg', + t = N_('Remove Interface from this Router'), + t, + :url_parms => 'main_div', + :klass => ApplicationHelper::Button::GenericFeatureButtonWithDisable, + :options => {:feature => :remove_interface} + ), button( :network_router_delete, 'pficon pficon-delete fa-lg', diff --git a/app/helpers/application_helper/toolbar/network_routers_center.rb b/app/helpers/application_helper/toolbar/network_routers_center.rb index 7d92080e57e0..73f9908b7869 100644 --- a/app/helpers/application_helper/toolbar/network_routers_center.rb +++ b/app/helpers/application_helper/toolbar/network_routers_center.rb @@ -24,6 +24,22 @@ class ApplicationHelper::Toolbar::NetworkRoutersCenter < ApplicationHelper::Tool :enabled => false, :onwhen => '1' ), + button( + :network_router_add_interface, + 'pficon pficon-edit fa-lg', + t = N_('Add Interface to selected Router'), + t, + :url_parms => "main_div", + :enabled => false, + :onwhen => "1"), + button( + :network_router_remove_interface, + 'pficon pficon-edit fa-lg', + t = N_('Remove Interface from selected Router'), + t, + :url_parms => "main_div", + :enabled => false, + :onwhen => "1"), button( :network_router_delete, 'pficon pficon-delete fa-lg', diff --git a/app/views/network_router/add_interface_select.html.haml b/app/views/network_router/add_interface_select.html.haml new file mode 100644 index 000000000000..ce527cf281ce --- /dev/null +++ b/app/views/network_router/add_interface_select.html.haml @@ -0,0 +1,27 @@ +%form#form_div{:name => "angularForm", 'ng-controller' => "networkRouterFormController"} + = render :partial => "layouts/flash_msg" + %h3 + = _('Add Interface to Router') + .form-horizontal + .form-group + %label.col-md-2.control-label + = _('Subnet') + .col-md-8 + = select_tag("cloud_subnet_id", + options_for_select([["<#{_('Choose')}>", nil]] + @subnet_choices.sort), + "ng-model" => "networkRouterModel.cloud_subnet_id", + "required" => "", + :miqrequired => true, + :checkchange => true, + "selectpicker-for-select-tag" => "") + %table{:width => '100%'} + %tr + %td{:align => 'right'} + #buttons_on + = render :partial => "layouts/angular/x_custom_form_buttons_angular", + :locals => {:button_label => N_("Add"), + :button_click => "addInterfaceClicked()"} + +:javascript + ManageIQ.angular.app.value('networkRouterFormId', '#{@router.id}'); + miq_bootstrap('#form_div'); diff --git a/app/views/network_router/remove_interface_select.html.haml b/app/views/network_router/remove_interface_select.html.haml new file mode 100644 index 000000000000..bcc6dc16e657 --- /dev/null +++ b/app/views/network_router/remove_interface_select.html.haml @@ -0,0 +1,27 @@ +%form#form_div{:name => "angularForm", 'ng-controller' => "networkRouterFormController"} + = render :partial => "layouts/flash_msg" + %h3 + = _('Remove Interface from Router') + .form-horizontal + .form-group + %label.col-md-2.control-label + = _('Subnet') + .col-md-8 + = select_tag("cloud_subnet_id", + options_for_select([["<#{_('Choose')}>", nil]] + @subnet_choices.sort), + "ng-model" => "networkRouterModel.cloud_subnet_id", + "required" => "", + :miqrequired => true, + :checkchange => true, + "selectpicker-for-select-tag" => "") + %table{:width => '100%'} + %tr + %td{:align => 'right'} + #buttons_on + = render :partial => "layouts/angular/x_custom_form_buttons_angular", + :locals => {:button_label => N_("Remove"), + :button_click => "removeInterfaceClicked()"} + +:javascript + ManageIQ.angular.app.value('networkRouterFormId', '#{@router.id}'); + miq_bootstrap('#form_div'); diff --git a/config/routes.rb b/config/routes.rb index 3f52ddd43116..e0c4a3c137d9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1638,6 +1638,7 @@ :network_router => { :get => %w( + add_interface_select delete_network_routers download_data edit @@ -1645,17 +1646,22 @@ network_router_form_fields network_router_networks_by_ems new + remove_interface_select show show_list tagging_edit ) + compare_get, :post => %w( + add_interface + add_interface_select button create form_field_changed listnav_search_selected quick_search + remove_interface + remove_interface_select sections_field_changed show show_list diff --git a/spec/controllers/network_router_controller_spec.rb b/spec/controllers/network_router_controller_spec.rb index c6b4bb2c3b36..ff0004f4de9c 100644 --- a/spec/controllers/network_router_controller_spec.rb +++ b/spec/controllers/network_router_controller_spec.rb @@ -204,4 +204,96 @@ end end end + + describe "#add_interface" do + before do + stub_user(:features => :all) + EvmSpecHelper.create_guid_miq_server_zone + @ems = FactoryGirl.create(:ems_openstack).network_manager + @router = FactoryGirl.create(:network_router_openstack, + :ext_management_system => @ems) + @subnet = FactoryGirl.create(:cloud_subnet, :ext_management_system => @ems) + end + + context "#add_interface" do + let(:task_options) do + { + :action => "Adding Interface to Network Router for user %{user}" % {:user => controller.current_user.userid}, + :userid => controller.current_user.userid + } + end + let(:queue_options) do + { + :class_name => @router.class.name, + :method_name => "add_interface", + :instance_id => @router.id, + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => @ems.my_zone, + :args => [@subnet.id] + } + end + + it "builds add interface screen" do + post :button, :params => { :pressed => "network_router_add_interface", :format => :js, :id => @router.id } + expect(assigns(:flash_array)).to be_nil + end + + it "queues the add interface action" do + expect(MiqTask).to receive(:generic_action_with_callback).with(task_options, queue_options) + post :add_interface, :params => { + :button => "addInterface", + :format => :js, + :id => @router.id, + :cloud_subnet_id => @subnet.id + } + end + end + end + + describe "#remove_interface" do + before do + stub_user(:features => :all) + EvmSpecHelper.create_guid_miq_server_zone + @ems = FactoryGirl.create(:ems_openstack).network_manager + @router = FactoryGirl.create(:network_router_openstack, + :ext_management_system => @ems) + @subnet = FactoryGirl.create(:cloud_subnet, :ext_management_system => @ems) + end + + context "#remove_interface" do + let(:task_options) do + { + :action => "Removing Interface from Network Router for user %{user}" % {:user => controller.current_user.userid}, + :userid => controller.current_user.userid + } + end + let(:queue_options) do + { + :class_name => @router.class.name, + :method_name => "remove_interface", + :instance_id => @router.id, + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => @ems.my_zone, + :args => [@subnet.id] + } + end + + it "builds remove interface screen" do + post :button, :params => { :pressed => "network_router_remove_interface", :format => :js, :id => @router.id } + expect(assigns(:flash_array)).to be_nil + end + + it "queues the remove interface action" do + expect(MiqTask).to receive(:generic_action_with_callback).with(task_options, queue_options) + post :remove_interface, :params => { + :button => "removeInterface", + :format => :js, + :id => @router.id, + :cloud_subnet_id => @subnet.id + } + end + end + end end