Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #35378 - Add systemd first boot service for host provisioning #9677

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
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
11 changes: 10 additions & 1 deletion app/controllers/unattended_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class UnattendedController < ApplicationController

before_action :permissions_check, if: -> { preview? }, only: [:host_template, :hostgroup_template]
before_action :set_admin_user, unless: -> { preview? }
before_action :load_host_details, only: [:host_template, :built, :failed]
before_action :load_host_details, only: [:host_template, :built, :failed, :change_host_status]

# all of our requests should be returned in text/plain
after_action :set_content_type
Expand Down Expand Up @@ -43,6 +43,15 @@ def failed
head(@host.built ? :created : :conflict)
end

def change_host_status
return unless verify_found_host
return unless params[:status].to_i >= 0 and params[:status].to_i <= 4

status = @host.host_statuses.detect{|i| i.type == HostStatus::BuildStatus.to_s}
status.update(status: params[:status].to_i)
head(:ok)
end

def hostgroup_template
return head(:not_found) unless (params.has_key?("id") && params.has_key?(:hostgroup))

Expand Down
5 changes: 4 additions & 1 deletion app/models/host_status/build_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ class BuildStatus < Status
TOKEN_EXPIRED = 2
BUILD_FAILED = 3
BUILT = 0
RUNNING = 4

OK_STATUSES = [PENDING, BUILT]
OK_STATUSES = [PENDING, BUILT, RUNNING]
WARN_STATUSES = []
ERROR_STATUSES = [TOKEN_EXPIRED, BUILD_FAILED]

Expand All @@ -14,13 +15,15 @@ class BuildStatus < Status
TOKEN_EXPIRED => N_("Token expired"),
BUILD_FAILED => N_("Installation error"),
BUILT => N_("Installed"),
RUNNING => N_("Host running"),
}.freeze

SEARCH = {
PENDING => 'build_status = pending',
TOKEN_EXPIRED => 'build_status = token_expired',
BUILD_FAILED => 'build_status = build_failed',
BUILT => 'build_status = built',
RUNNING => 'build_status = running',
}.freeze

def self.status_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ sed -e 's/DEFAULTKERNEL=kernel-uek/DEFAULTKERNEL=kernel/g' -i /etc/sysconfig/ker

<%= snippet 'insights' if host_param_true?('host_registration_insights') && os_major < 9 -%>

<%= snippet 'first_boot_setup' %>
Dyrkon marked this conversation as resolved.
Show resolved Hide resolved

touch /tmp/foreman_built

<% if host_param_true?('use_graphical_installer') -%>
Expand Down Expand Up @@ -388,12 +390,11 @@ The last post section halts Anaconda to prevent endless loop in case HTTP reques
<%= snippet 'eject_cdrom' -%>

if test -f /tmp/foreman_built; then
echo "calling home: build is done!"
<%= indent(2, skip1: true) { snippet('built', :variables => { :endpoint => 'built', :method => 'POST', :body_file => '/root/install.post.log' }) } -%>
Dyrkon marked this conversation as resolved.
Show resolved Hide resolved
echo "calling home: build is done!"
<%= indent(2, skip1: true) { snippet('built', :variables => { :endpoint => 'built', :method => 'POST', :body_file => '/root/install.post.log' }) } -%>
else
echo "calling home: build failed!"
<%= indent(2, skip1: true) { snippet('built', :variables => { :endpoint => 'failed', :method => 'POST', :body_file => '/root/install.post.log' }) } -%>
echo "calling home: build failed!"
<%= indent(2, skip1: true) { snippet('built', :variables => { :endpoint => 'failed', :method => 'POST', :body_file => '/root/install.post.log' }) } -%>
fi

sync
<%= section_end %>
Dyrkon marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<%#
kind: snippet
name: first_boot_service
model: ProvisioningTemplate
snippet: true
description: |
Post replacement service
-%>
[Unit]
Description=Initial setup callback
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/bash /root/first_boot_script.sh
Type=oneshot
ExecStartPost=/usr/bin/systemctl disable first_boot_service
Dyrkon marked this conversation as resolved.
Show resolved Hide resolved

[Install]
WantedBy=multi-user.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<%#
kind: snippet
name: first_boot_setup
model: ProvisioningTemplate
snippet: true
description: |
Post replacement for both systemd and non-systemd platforms
-%>
<%
os_major = @host.operatingsystem.major.to_i
rhel_compatible = @host.operatingsystem.family == 'Redhat' && @host.operatingsystem.name != 'Fedora'
has_systemd = (@host.operatingsystem.name == 'Fedora' && os_major >= 20) || (rhel_compatible && os_major >= 7) || (@host.operatingsystem.name == 'Ubuntu' && os_major >= 15) || (@host.operatingsystem.name == 'Debian' && os_major >= 8)
-%>
<% if has_systemd -%>
<%= save_to_file('/etc/systemd/system/first_boot_service.service', snippet('first_boot_service')) %>
Dyrkon marked this conversation as resolved.
Show resolved Hide resolved
<%= save_to_file('/root/first_boot_script.sh', snippet('running', :variables => { })) %>

systemctl enable first_boot_service
<% else -%>
<%= save_to_file('/root/first_boot_script.sh', snippet('running', :variables => { }) +
"\nmv /root/first_boot_script.sh /root/first_boot_script.disabled") %>
<%= save_to_file('/etc/init.d/first_boot_setup', snippet('first_boot_initd')) %>

chmod +x /etc/init.d/first_boot_setup
chkconfig --add /etc/init.d/first_boot_setup
chkconfig --level 2345 first_boot_setup on
<% end -%>

chmod +x /root/first_boot_script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<%#
kind: snippet
name: first_boot_initd
model: ProvisioningTemplate
snippet: true
description: |
Post replacement initd script
-%>
#! /bin/bash

### BEGIN INIT INFO
# Provides: RedHat
# Required-Start: $local_fs $network
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Execute first boot script
# Description: Execute first boot script which executes after machine is successfully built for the first time.
### END INIT INFO

# Source function library
. /etc/rc.d/init.d/functions

##Service start/stop functions##
start() {
/tmp/first_boot_script
chkconfig --del first_boot_setup
}

start

exit 5
esac
exit $?
27 changes: 27 additions & 0 deletions app/views/unattended/provisioning_templates/snippet/running.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<%#
kind: snippet
name: running
model: ProvisioningTemplate
snippet: true
description: |
Sends 'running' request to Foreman to indicate that the host is running.
-%>
<%
endpoint = @endpoint || 'change-status'
method = @method || 'GET'
base_url = foreman_url(endpoint)
url = base_url.split('?').first + '/' + @host.mac + '/' + 4.to_s
curl_opts = ["-H 'Content-Type: text/plain'"]
wget_opts = ["--header 'Content-Type: text/plain'"]
if url.start_with?('https')
curl_opts << "--insecure"
wget_opts << "--no-check-certificate"
end
-%>
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* <%= curl_opts.join(' ') %> --silent '<%= url %>'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method <%= method %> <%= wget_opts.join(' ') %> '<%= url %>'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' '<%= url %>'
fi
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@
post 'unattended/failed/(:id(:format))', controller: 'unattended', action: 'failed', format: 'text'
# get for all unattended scripts
get 'unattended/(:kind/(:id(:format)))', controller: 'unattended', action: 'host_template', format: 'text'
get 'unattended/change-status/:mac/:status', controller: 'unattended', action: 'change_host_status', format: 'text'

get 'userdata/(:mac)/user-data', controller: 'userdata', action: 'userdata', format: 'text'
get 'userdata/(:mac)/meta-data', controller: 'userdata', action: 'metadata', format: 'text'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,35 @@ systemctl enable ansible-callback



cat << EOF-782969c0 > /etc/systemd/system/first_boot_service.service
[Unit]
Description=Initial setup callback
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/bash /root/first_boot_script.sh
Type=oneshot
ExecStartPost=/usr/bin/systemctl disable first_boot_service

[Install]
WantedBy=multi-user.target
EOF-782969c0
cat << EOF-76fe21d9 > /root/first_boot_script.sh
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
EOF-76fe21d9

systemctl enable first_boot_service

chmod +x /root/first_boot_script.sh


touch /tmp/foreman_built

chvt 1
Expand All @@ -179,16 +208,7 @@ cp -vf /tmp/*.pre.*.log /mnt/sysimage/root/
%post --erroronfail --log=/root/install-callhome.post.log


if test -f /tmp/foreman_built; then
echo "calling home: build is done!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
else
if ! test -f /tmp/foreman_built; then
echo "calling home: build failed!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/failed'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,35 @@ systemctl enable ansible-callback



cat << EOF-782969c0 > /etc/systemd/system/first_boot_service.service
[Unit]
Description=Initial setup callback
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/bash /root/first_boot_script.sh
Type=oneshot
ExecStartPost=/usr/bin/systemctl disable first_boot_service

[Install]
WantedBy=multi-user.target
EOF-782969c0
cat << EOF-76fe21d9 > /root/first_boot_script.sh
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
EOF-76fe21d9

systemctl enable first_boot_service

chmod +x /root/first_boot_script.sh


touch /tmp/foreman_built

chvt 1
Expand All @@ -179,16 +208,7 @@ cp -vf /tmp/*.pre.*.log /mnt/sysimage/root/
%post --erroronfail --log=/root/install-callhome.post.log


if test -f /tmp/foreman_built; then
echo "calling home: build is done!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
else
if ! test -f /tmp/foreman_built; then
echo "calling home: build failed!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/failed'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,35 @@ systemctl enable ansible-callback



cat << EOF-782969c0 > /etc/systemd/system/first_boot_service.service
[Unit]
Description=Initial setup callback
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/bash /root/first_boot_script.sh
Type=oneshot
ExecStartPost=/usr/bin/systemctl disable first_boot_service

[Install]
WantedBy=multi-user.target
EOF-782969c0
cat << EOF-76fe21d9 > /root/first_boot_script.sh
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
EOF-76fe21d9

systemctl enable first_boot_service

chmod +x /root/first_boot_script.sh


touch /tmp/foreman_built

chvt 1
Expand All @@ -179,16 +208,7 @@ cp -vf /tmp/*.pre.*.log /mnt/sysimage/root/
%post --erroronfail --log=/root/install-callhome.post.log


if test -f /tmp/foreman_built; then
echo "calling home: build is done!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/built'
elif [ -x /usr/bin/wget ]; then
/usr/bin/wget -q -O /dev/null --no-proxy --method POST --header 'Content-Type: text/plain' --body-file=/root/install.post.log 'http://foreman.example.com/unattended/built'
else
wget -q -O /dev/null --header 'Content-Type: text/plain' 'http://foreman.example.com/unattended/built'
fi
else
if ! test -f /tmp/foreman_built; then
echo "calling home: build failed!"
if [ -x /usr/bin/curl ]; then
/usr/bin/curl -o /dev/null --noproxy \* -H 'Content-Type: text/plain' --data @/root/install.post.log --silent 'http://foreman.example.com/unattended/failed'
Expand Down
Loading