Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/controllers/api/v2/hosts_bulk_actions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class HostsBulkActionsController < V2::BaseController
before_action :validate_power_action, :only => [:change_power_state]

def_param_group :bulk_host_ids do
param :organization_id, :number, :required => true, :desc => N_("ID of the organization")
param :included, Hash, :desc => N_("Hosts to include in the action"), :required => true, :action_aware => true do
param :search, String, :required => false, :desc => N_("Search string describing which hosts to perform the action on")
param :ids, Array, :required => false, :desc => N_("List of host ids to perform the action on")
Expand Down
10 changes: 8 additions & 2 deletions app/controllers/concerns/api/v2/bulk_hosts_extension.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Api::V2::BulkHostsExtension
extend ActiveSupport::Concern

def bulk_hosts_relation(permission, org)
def bulk_hosts_relation(permission, org, location)
relation = ::Host::Managed.authorized(permission)
relation = relation.where(organization: org) if org
relation = relation.where(location: location) if location
relation
end

Expand All @@ -18,7 +19,8 @@ def find_bulk_hosts(permission, bulk_params, restrict_to = nil)
end

find_organization
@hosts = bulk_hosts_relation(permission, @organization)
find_location
@hosts = bulk_hosts_relation(permission, @organization, @location)

if bulk_params[:included][:ids].present?
@hosts = @hosts.where(id: bulk_params[:included][:ids])
Expand All @@ -42,4 +44,8 @@ def find_bulk_hosts(permission, bulk_params, restrict_to = nil)
def find_organization
@organization ||= Organization.find_by_id(params[:organization_id])
end

def find_location
@location ||= Location.find_by_id(params[:location_id])
end
end
28 changes: 28 additions & 0 deletions test/controllers/api/v2/hosts_bulk_actions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def setup
def valid_bulk_params(host_ids = @host_ids)
{
:organization_id => @organization.id,
:location_id => @location.id,
:included => {
:ids => host_ids,
},
Expand Down Expand Up @@ -107,6 +108,33 @@ def valid_power_params(host_ids = @host_ids, action = 'start')
end
end

test "should scope searched hosts by organization and location" do
other_location = FactoryBot.create(:location)
other_host = FactoryBot.create(:host, :managed, :organization => @organization, :location => other_location)

put :change_owner, params: {
:organization_id => @organization.id,
:location_id => @location.id,
:included => {
:search => 'name ~ *',
},
:excluded => {
:ids => [],
},
:owner_id => @user.id_and_type,
}

assert_response :success

[@host1, @host2, @host3].each do |host|
host.reload
assert_equal @user.id_and_type, host.is_owned_by
end

other_host.reload
refute_equal @user.id_and_type, other_host.is_owned_by
end

context "change_power_state" do
test "successfully changes power state for all hosts" do
Host.any_instance.stubs(:supports_power?).returns(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
API_REQUEST_KEY,
} from '../../../../routes/Hosts/constants';
import TaxonomySelect from './TaxonomySelect';
import { buildBulkRequestBody } from '../helpers';

export const BulkAssignOrganizationModal = props => (
<BulkAssignTaxonomyModal modalType={MODAL_TYPES.ORGANIZATION} {...props} />
Expand All @@ -52,6 +53,8 @@ const BulkAssignTaxonomyModal = ({
selectAllHostsMode,
selectedCount,
fetchBulkParams,
organizationId,
locationId,
modalType,
}) => {
const org = modalType === MODAL_TYPES.ORGANIZATION;
Expand Down Expand Up @@ -136,13 +139,13 @@ const BulkAssignTaxonomyModal = ({
};

const handleSave = () => {
const requestBody = {
included: {
search: fetchBulkParams(),
},
const requestBody = buildBulkRequestBody({
fetchBulkParams,
organizationId,
locationId,
id: taxId,
mismatch_setting: fixRadioChecked,
};
});

org
? dispatch(
Expand Down Expand Up @@ -243,10 +246,14 @@ BulkAssignTaxonomyModal.propTypes = {
selectedCount: PropTypes.number.isRequired,
selectAllHostsMode: PropTypes.bool.isRequired,
fetchBulkParams: PropTypes.func.isRequired,
organizationId: PropTypes.number,
locationId: PropTypes.number,
modalType: PropTypes.string.isRequired,
};

BulkAssignTaxonomyModal.defaultProps = {
isOpen: false,
closeModal: () => {},
organizationId: undefined,
locationId: undefined,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,43 @@ import {
} from './BulkAssignTaxonomyModal';

export const BulkAssignOrganizationModalScene = ({ isOpen, closeModal }) => {
const { selectAllHostsMode, selectedCount, fetchBulkParams } = useContext(
ForemanActionsBarContext
);
const {
selectAllHostsMode,
selectedCount,
fetchBulkParams,
organizationId,
locationId,
} = useContext(ForemanActionsBarContext);
return (
<BulkAssignOrganizationModal
key="bulk-assign-organization-modal"
selectAllHostsMode={selectAllHostsMode}
selectedCount={selectedCount}
fetchBulkParams={fetchBulkParams}
organizationId={organizationId}
locationId={locationId}
isOpen={isOpen}
closeModal={closeModal}
/>
);
};

export const BulkAssignLocationModalScene = ({ isOpen, closeModal }) => {
const { selectAllHostsMode, selectedCount, fetchBulkParams } = useContext(
ForemanActionsBarContext
);
const {
selectAllHostsMode,
selectedCount,
fetchBulkParams,
organizationId,
locationId,
} = useContext(ForemanActionsBarContext);
return (
<BulkAssignLocationModal
key="bulk-assign-location-modal"
selectAllHostsMode={selectAllHostsMode}
selectedCount={selectedCount}
fetchBulkParams={fetchBulkParams}
organizationId={organizationId}
locationId={locationId}
isOpen={isOpen}
closeModal={closeModal}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@patternfly/react-core';
import { addToast } from '../../../ToastsList/slice';
import { translate as __ } from '../../../../common/I18n';
import { failedHostsToastParams } from '../helpers';
import { buildBulkRequestBody, failedHostsToastParams } from '../helpers';
import { STATUS } from '../../../../constants';
import { selectAPIStatus } from '../../../../redux/API/APISelectors';
import { bulkBuildHosts, HOST_BUILD_KEY } from './actions';
Expand All @@ -22,6 +22,8 @@ const BulkBuildHostModal = ({
closeModal,
selectedCount,
fetchBulkParams,
organizationId,
locationId,
}) => {
const dispatch = useDispatch();
const [buildRadioChecked, setBuildRadioChecked] = useState(true);
Expand All @@ -44,13 +46,13 @@ const BulkBuildHostModal = ({
);
};
const handleSave = () => {
const requestBody = {
included: {
search: fetchBulkParams(),
},
const requestBody = buildBulkRequestBody({
fetchBulkParams,
organizationId,
locationId,
reboot: rebootChecked,
rebuild_configuration: !buildRadioChecked,
};
});

dispatch(bulkBuildHosts(requestBody, handleModalClose, handleError));
};
Expand Down Expand Up @@ -156,11 +158,15 @@ BulkBuildHostModal.propTypes = {
closeModal: PropTypes.func,
selectedCount: PropTypes.number.isRequired,
fetchBulkParams: PropTypes.func.isRequired,
organizationId: PropTypes.number,
locationId: PropTypes.number,
};

BulkBuildHostModal.defaultProps = {
isOpen: false,
closeModal: () => {},
organizationId: undefined,
locationId: undefined,
};

export default BulkBuildHostModal;
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import { ForemanActionsBarContext } from '../../../../components/HostDetails/Act
import BulkBuildHostModal from './BulkBuildHostModal';

const BulkBuildHostModalScene = ({ isOpen, closeModal }) => {
const { selectedCount, fetchBulkParams } = useContext(
ForemanActionsBarContext
);
const {
selectedCount,
fetchBulkParams,
organizationId,
locationId,
} = useContext(ForemanActionsBarContext);
return (
<BulkBuildHostModal
key="bulk-build-hosts-modal"
selectedCount={selectedCount}
fetchBulkParams={fetchBulkParams}
organizationId={organizationId}
locationId={locationId}
isOpen={isOpen}
closeModal={closeModal}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ import { visit, foremanUrl } from '../../../common/helpers';
import { sprintf, translate as __ } from '../../../common/I18n';
import { openConfirmModal } from '../../ConfirmModal';
import { APIActions } from '../../../redux/API';
import { bulkActionTaxonomyParams } from './helpers';
import './bulkDeleteModal.scss';

export const bulkDeleteHosts = ({
bulkParams,
organizationId,
locationId,
selectedCount,
destroyVmOnHostDelete,
}) => dispatch => {
const successToast = () => sprintf(__('%s hosts deleted'), selectedCount);
const errorToast = ({ message }) => message;
const url = foremanUrl(`/api/v2/hosts/bulk?search=${bulkParams}`);
const queryParams = new URLSearchParams({
search: bulkParams,
...bulkActionTaxonomyParams({ organizationId, locationId }),
});
const url = foremanUrl(`/api/v2/hosts/bulk?${queryParams.toString()}`);

// TODO: Replace with a checkbox instead of a global setting for cascade host destroy
const cascadeMessage = () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ import {
HOSTS_API_PATH,
API_REQUEST_KEY,
} from '../../../../routes/Hosts/constants';
import { failedHostsToastParams } from '../helpers';
import { buildBulkRequestBody, failedHostsToastParams } from '../helpers';

const BulkChangeOwnerModal = ({
isOpen,
closeModal,
selectAllHostsMode,
selectedCount,
fetchBulkParams,
organizationId,
locationId,
}) => {
const dispatch = useDispatch();
const [ownerId, setOwnerId] = useState('');
Expand Down Expand Up @@ -131,12 +133,12 @@ const BulkChangeOwnerModal = ({
};

const handleConfirm = () => {
const requestBody = {
included: {
search: fetchBulkParams(),
},
const requestBody = buildBulkRequestBody({
fetchBulkParams,
organizationId,
locationId,
owner_id: ownerId,
};
});

dispatch(bulkChangeOwner(requestBody, handleSuccess, handleError));
};
Expand Down Expand Up @@ -250,11 +252,15 @@ BulkChangeOwnerModal.propTypes = {
fetchBulkParams: PropTypes.func.isRequired,
selectedCount: PropTypes.number.isRequired,
selectAllHostsMode: PropTypes.bool.isRequired,
organizationId: PropTypes.number,
locationId: PropTypes.number,
};

BulkChangeOwnerModal.defaultProps = {
isOpen: false,
closeModal: () => {},
organizationId: undefined,
locationId: undefined,
};

export default BulkChangeOwnerModal;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const BulkChangeOwnerModalScene = ({ isOpen, closeModal }) => {
selectedCount,
selectedResults,
fetchBulkParams,
organizationId,
locationId,
} = useContext(ForemanActionsBarContext);
return (
<BulkChangeOwnerModal
Expand All @@ -17,6 +19,8 @@ const BulkChangeOwnerModalScene = ({ isOpen, closeModal }) => {
selectedCount={selectedCount}
selectedResults={selectedResults}
fetchBulkParams={fetchBulkParams}
organizationId={organizationId}
locationId={locationId}
isOpen={isOpen}
closeModal={closeModal}
/>
Expand Down
Loading
Loading