Skip to content

Commit 9adb425

Browse files
committed
Fixes #39067 - rebuild only if host powered off
1 parent e8ff59d commit 9adb425

File tree

9 files changed

+47
-8
lines changed

9 files changed

+47
-8
lines changed

app/controllers/api/v2/hosts_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ def facts
298298
param :id, :identifier_dottable, :required => true
299299
param :only, Array, :desc => N_("Limit rebuild steps, valid steps are %{host_rebuild_steps}")
300300
def rebuild_config
301+
if @host.rebuild_requires_poweroff
302+
return render_error :custom_error, :status => :unprocessable_entity, :locals => { :message => _('Host must be powered off to initiate "Build".') }
303+
end
304+
301305
result = @host.recreate_config(params[:only])
302306
failures = result.reject { |key, value| value }.keys.map { |k| _(k) }
303307
if failures.empty?

app/models/host/managed.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,13 @@ def self.registered_provision_methods
500500
Foreman::Plugin.all.map(&:provision_methods).inject(:merge) || {}
501501
end
502502

503+
def rebuild_requires_poweroff
504+
method_name = "#{provision_method}_rebuild_requires_poweroff"
505+
return false unless respond_to?(method_name)
506+
507+
public_send(method_name)
508+
end
509+
503510
def self.valid_rebuild_only_values
504511
if Host::Managed.respond_to?(:rebuild_methods)
505512
Nic::Managed.rebuild_methods.values + Host::Managed.rebuild_methods.values

app/views/api/v2/hosts/main.json.rabl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ attributes :ip, :ip6, :last_report, :mac, :realm_id, :realm_name,
1313
:sp_mac, :sp_ip, :sp_name, :domain_id, :domain_name, :architecture_id, :architecture_name, :operatingsystem_id, :operatingsystem_name,
1414
:subnet_id, :subnet_name, :subnet6_id, :subnet6_name, :sp_subnet_id, :ptable_id, :ptable_name, :medium_id, :medium_name, :pxe_loader,
1515
:build, :comment, :disk, :initiated_at, :installed_at, :model_id, :hostgroup_id, :owner_id, :owner_name, :owner_type, :creator_id, :creator,
16-
:enabled, :managed, :use_image, :image_file, :uuid,
16+
:enabled, :managed, :use_image, :image_file, :uuid, :rebuild_requires_poweroff,
1717
:compute_resource_id, :compute_resource_name,
1818
:compute_profile_id, :compute_profile_name, :capabilities, :provision_method,
1919
:certname, :image_id, :image_name, :created_at, :updated_at,

test/controllers/api/v2/hosts_controller_test.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,23 @@ def test_rebuild_config_pessimistic
603603
assert_response 422
604604
end
605605

606+
def test_rebuild_config_but_dont_requires_poweroff
607+
Host.any_instance.expects(:recreate_config).returns({ "TFTP" => true })
608+
Host.any_instance.expects(:rebuild_requires_poweroff).returns(false)
609+
host = FactoryBot.create(:host)
610+
post :rebuild_config, params: { :id => host.to_param }, session: set_session_user
611+
assert_response :success
612+
end
613+
614+
def test_rebuild_config_but_requires_poweroff
615+
Host.any_instance.expects(:recreate_config).returns({ "TFTP" => true })
616+
Host.any_instance.expects(:provision_method).returns("bootdisk")
617+
Host.any_instance.expects(:bootdisk_rebuild_requires_poweroff).returns(true)
618+
host = FactoryBot.create(:host)
619+
post :rebuild_config, params: { :id => host.to_param }, session: set_session_user
620+
assert_response 422
621+
end
622+
606623
def test_rebuild_tftp_config
607624
Host.any_instance.expects(:recreate_config).returns({ "TFTP" => true })
608625
host = FactoryBot.create(:host)

webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/actions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { openConfirmModal } from '../../ConfirmModal';
66
import { APIActions } from '../../../redux/API';
77
import { HOST_DETAILS_KEY } from '../consts';
88
import { selectAPIResponse } from '../../../redux/API/APISelectors';
9-
import { POWER_REQURST_KEY } from '../DetailsCard/PowerStatus/constants';
9+
import { POWER_REQUEST_KEY } from '../DetailsCard/PowerStatus/constants';
1010

1111
export const deleteHost = (
1212
hostName,
@@ -106,6 +106,6 @@ export const cancelBuild = (hostId, hostName) => dispatch => {
106106
};
107107

108108
export const isHostTurnOn = store => {
109-
const { state } = selectAPIResponse(store, POWER_REQURST_KEY);
109+
const { state } = selectAPIResponse(store, POWER_REQUEST_KEY);
110110
return state === 'on';
111111
};

webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const ActionsBar = ({
4545
hostName,
4646
computeId,
4747
isBuild,
48+
buildRequiresPowerOff,
4849
permissions: {
4950
destroy_hosts: canDestroy,
5051
create_hosts: canCreate,
@@ -90,7 +91,14 @@ const ActionsBar = ({
9091
onClick={buildHandler}
9192
key="build"
9293
component="button"
93-
isDisabled={!canBuild}
94+
description={
95+
canBuild && !isBuild && buildRequiresPowerOff && isHostActive
96+
? __('Host must be powered off to initiate "Build".')
97+
: ''
98+
}
99+
isDisabled={
100+
!canBuild || (!isBuild && buildRequiresPowerOff && isHostActive)
101+
}
94102
icon={
95103
<Icon>
96104
<BuildIcon />
@@ -232,6 +240,7 @@ ActionsBar.propTypes = {
232240
computeId: PropTypes.number,
233241
permissions: PropTypes.object,
234242
isBuild: PropTypes.bool,
243+
buildRequiresPowerOff: PropTypes.bool,
235244
};
236245
ActionsBar.defaultProps = {
237246
hostId: undefined,
@@ -245,6 +254,7 @@ ActionsBar.defaultProps = {
245254
build_hosts: false,
246255
},
247256
isBuild: false,
257+
buildRequiresPowerOff: false,
248258
};
249259

250260
export default ActionsBar;

webpack/assets/javascripts/react_app/components/HostDetails/DetailsCard/PowerStatus/actions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { translate as __, sprintf } from '../../../../common/I18n';
22
import { put } from '../../../../redux/API';
3-
import { POWER_REQURST_KEY, SUPPORTED_POWER_STATES } from './constants';
3+
import { POWER_REQUEST_KEY, SUPPORTED_POWER_STATES } from './constants';
44

55
export const changeHostPower = (state, hostID) =>
66
put({
7-
key: POWER_REQURST_KEY,
7+
key: POWER_REQUEST_KEY,
88
params: { power_action: state },
99
url: `/api/hosts/${hostID}/power`,
1010
errorToast: err => sprintf(__('an error occurred: %s'), err),

webpack/assets/javascripts/react_app/components/HostDetails/DetailsCard/PowerStatus/constants.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { translate as __ } from '../../../../common/I18n';
22

3-
export const POWER_REQURST_KEY = 'HOST_TOGGLE_POWER';
4-
export const POWER_REQUEST_OPTIONS = { key: POWER_REQURST_KEY, params: { timeout: 30 } };
3+
export const POWER_REQUEST_KEY = 'HOST_TOGGLE_POWER';
4+
export const POWER_REQUEST_OPTIONS = { key: POWER_REQUEST_KEY, params: { timeout: 30 } };
55
export const BASE_POWER_STATES = { off: __('Off'), on: __('On') };
66
export const BMC_POWER_STATES = { reboot: __('Reboot'), reset: __('Reset'), soft: __('Soft'), cycle: __('Cycle') };
77
export const SUPPORTED_POWER_STATES = {

webpack/assets/javascripts/react_app/components/HostDetails/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ const HostDetails = ({
216216
hostName={response.name}
217217
permissions={response.permissions}
218218
isBuild={response.build}
219+
buildRequiresPowerOff={response.rebuild_requires_poweroff}
219220
/>
220221
</FlexItem>
221222
</Flex>

0 commit comments

Comments
 (0)