Skip to content

Commit f7b5a0e

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

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

app/models/host/managed.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class Jail < ::Safemode::Jail
333333
before_validation :set_compute_attributes, :on => :create, :if => proc { compute_attributes_empty? }
334334
validate :check_if_provision_method_changed, :on => :update, :if => proc { |host| host.managed }
335335
validates :uuid, uniqueness: { :allow_blank => true }
336+
validate :check_if_rebuild_requires_poweroff, :if => proc { |host| host.build_changed?(from: false, to: true) }
336337

337338
before_validation :set_hostgroup_defaults, :set_ip_address
338339
after_validation :ensure_associations
@@ -500,6 +501,23 @@ def self.registered_provision_methods
500501
Foreman::Plugin.all.map(&:provision_methods).inject(:merge) || {}
501502
end
502503

504+
def check_if_rebuild_requires_poweroff
505+
return true unless rebuild_requires_poweroff && power.ready?
506+
507+
errors.add(:build, N_('Host must be powered off to initiate "Build".'))
508+
false
509+
rescue Foreman::Exception => _e
510+
logger.info "Could not read power state of #{name}. Allowing to build the host."
511+
true
512+
end
513+
514+
def rebuild_requires_poweroff
515+
method_name = "#{provision_method}_rebuild_requires_poweroff"
516+
return false unless respond_to?(method_name)
517+
518+
public_send(method_name)
519+
end
520+
503521
def self.valid_rebuild_only_values
504522
if Host::Managed.respond_to?(:rebuild_methods)
505523
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/models/host_test.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,45 @@ class HostTest < ActiveSupport::TestCase
549549
refute_equal original_status, new_status
550550
end
551551

552+
test "rebuild_requires_poweroff is validated and prevents to re-build a running host" do
553+
power = mock("power")
554+
power.stubs(:ready?).returns(true)
555+
556+
host = FactoryBot.create(:host, :managed)
557+
host.stubs(:power).returns(power)
558+
host.save!
559+
560+
host.expects(:build_rebuild_requires_poweroff).returns(true)
561+
host.build = true
562+
refute host.valid?
563+
end
564+
565+
test "rebuild_requires_poweroff is validated and allows to re-build the turned-off host" do
566+
power = mock("power")
567+
power.stubs(:ready?).returns(false)
568+
569+
host = FactoryBot.create(:host, :managed)
570+
host.stubs(:power).returns(power)
571+
host.save!
572+
573+
host.expects(:build_rebuild_requires_poweroff).returns(true)
574+
host.build = true
575+
assert host.valid?
576+
end
577+
578+
test "rebuild_requires_poweroff is validated and allows to re-build the host because provision_method allows it" do
579+
power = mock("power")
580+
power.stubs(:ready?).returns(true)
581+
582+
host = FactoryBot.create(:host, :managed)
583+
host.stubs(:power).returns(power)
584+
host.save!
585+
586+
host.expects(:build_rebuild_requires_poweroff).returns(false)
587+
host.build = true
588+
assert host.valid?
589+
end
590+
552591
context 'host assigned to location and organization' do
553592
setup do
554593
@host = FactoryBot.create(:host, :managed => false)

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/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)