summaryrefslogtreecommitdiffstats
path: root/core/modules/run-virt/data/opt/openslx/vmchooser/vmchooser-run_virt
blob: dfebebe7a812d651c5aa375f20b345500e249109 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/bin/bash
# Full bash required
# -----------------------------------------------------------------------------
#
# Copyright (c) 2007..2018 bwLehrpool-Projektteam
#
# This program/file is free software distributed under the GPL version 2.
# See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
#
# If you have any feedback please consult https://bwlehrpool.de and
# send your feedback to support@bwlehrpool.de.
#
# General information about bwLehrpool can be found at https://bwlehrpool.de
#
# -----------------------------------------------------------------------------
# run-virt.sh
#    - This is the generic wrapper for the several virtualization solutions.
#      The idea is to setup a set of variables used by at least two different
#      tools and then include the specific plugin which configures the speci-
#      fied virtualization tool.
################################################################################
SELF="$( readlink -f "${BASH_SOURCE[0]}" )"
if [ -z "$SELF" ] || ! [ -f "$SELF" ]; then
	SELF="$( readlink -f "$0" )"
fi
if [ -z "$SELF" ] || ! [ -f "$SELF" ]; then
	slxlog "run-virt" "Cannot find SELF"
	exit 1
fi
readonly SELF

# Global variables needed for the core functionality
declare -rg VMCHOOSER_DIR="$( dirname "$SELF" )"
declare -rg VMCHOOSER_CONF_DIR="$VMCHOOSER_DIR/config"
declare -rg USER="$(whoami)"
declare -rg TMPDIR="/tmp/virt/${USER}/$$"
declare -rg CONFDIR="${TMPDIR}/metadata"

if ! [ -d "$VMCHOOSER_DIR" ]; then
	slxlog "run-virt" "$SELF is not in a directory!?"
	exit 1
fi

# Useless without this
. /opt/openslx/config
. /opt/openslx/bin/slx-tools

# Debug mode?
if [ "$1" = "--debug" ]; then
	shift
	DEBUG=true
elif is_debug; then
	DEBUG=true
else
	DEBUG=false
fi
readonly DEBUG

# Check for existence of plugin
if [ "x$1" = "x--query" ]; then
	PLUGIN_ID="${2//[^a-z_\-]/}"
	if [ -z "$PLUGIN_ID" ]; then
		echo "Error: No plugin name given" >&2
		exit 1
	fi
	if ! [ -f "$VMCHOOSER_DIR/plugins/$PLUGIN_ID/run-virt.include" ]; then
		echo "Plugin '$PLUGIN_ID' does not exist." >&2
		exit 1
	fi
	# Maybe in the future we want to output information about the plugin here
	# to stdout in a simple to parse format like key=value....
	echo "Plugin '$PLUGIN_ID' found" >&2
	exit 0
fi

# This script expects the path to the xml file describing the VM to be started
declare -rg XML_FILE="$1"

# A path to the logfile can be given as second argument
declare -g LOGFILE="$2"
if [ -z "$LOGFILE" ] || ! touch "$LOGFILE" || ! [ -f "$LOGFILE" ]; then
	LOGFILE="/var/log/openslx/run-virt.${USER}.$$.log"
	touch "$LOGFILE" || LOGFILE="$(mktemp)"
fi
readonly LOGFILE

main() {
	declare -rg MAINPID="$BASHPID"
	# Don't have writelog yet
	echo "MAINPID: $MAINPID"
	# Functions needed by vmchooser-run_virt (writelog(), cleanexit(), safesource())
	declare -rg RUN_VIRT_INCLUDE_DIR="${VMCHOOSER_DIR}/run-virt-includes"
	if ! source "${RUN_VIRT_INCLUDE_DIR}/vmchooser_runvirt_functions.inc"; then
		slxlog "run-virt" "Could not source ${RUN_VIRT_INCLUDE_DIR}/vmchooser_runvirt_functions.inc"
		exit 1
	fi

	trap 'cleanexit $?' EXIT
	trap 'cleanexit 129' SIGHUP
	trap 'cleanexit 130' SIGINT
	trap 'cleanexit 143' SIGTERM

	# Starting sourcing the includes files. Note that the critical ones should use
	# the '--exit' option of safesource to trigger cleanexit in case of a corrupted/bad
	# include file.

	# Set core runvirt variables and directories
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/init_core.inc")
	writelog "#################### Initialization ####################"

	# Read vmchooser.conf, (generated) virtualization.conf and slx config files
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/load_configs.inc")

	# Declaration of hardware related variables
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/set_runvirt_hardware_variables.inc")

	# Window manager required early for user feedback through popups (e.g. errors) etc.
	$(safesource "${RUN_VIRT_INCLUDE_DIR}/start_windowmanager.inc")

	# Read needed variables from XML file
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/get_xml_file_variables.inc")

	# Download metadata from server (e.g. vmx for vmware)
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/download_vm_metadata.inc")

	# Try to use dnbd3 to access the image, nfs/cifs fallback
	$(safesource --exit "${RUN_VIRT_INCLUDE_DIR}/setup_image_access.inc")


	# Mark the end of generic run-virt part
	writelog "Done with generic run-virt. Now loading virtualizer-specific includes."
	writelog "#################### Plugin init: $PLUGIN_ID ####################"

	# NG: first include the hypervisor includes
	$(safesource "${RUN_VIRT_INCLUDE_DIR}/setup_vm_hypervisor.inc")

	# It must declare PLUGIN_FEATURES to set which features are needed.
	# Features are those defined by run-virt.d include files.
	# After sourcing the plugin, check that it defined both PLUGIN_FEATURES and
	# the main function 'run_plugin' which will be called later by the main scripts.
	if ! isset PLUGIN_FEATURES || ! is_function run_plugin; then
		writelog "Bad plugin '$PLUGIN_ID': either it did not set PLUGIN_FEATURES or did not define 'run_plugin'."
		EXIT_TYPE="internal" EXIT_REASON="Fehlerhaftes vmchooser plugin: '$PLUGIN_ID'." cleanexit 1
	fi
	writelog "Requested features:\t${PLUGIN_FEATURES}"

	# Source the *.inc files in run-virt.d
	for FILE in ${VMCHOOSER_DIR}/run-virt.d/*.inc; do
		$(safesource "$FILE")
	done

	# Now look which features were requested and call the handler if one is defined.
	for FEAT in $PLUGIN_FEATURES; do
		if notempty FEATURE_HANDLERS["${FEAT}"]; then
			writelog "Initialising '${FEAT}'..."
			if ! ${FEATURE_HANDLERS["$FEAT"]}; then
				writelog "\tFailed to run '${FEATURE_HANDLERS["$FEAT"]}'."
				error_user "Konnte Feature namens '$FEAT' nicht initialisieren!
	Diese Funktion wird nicht verfügbar sein!" # not critical, do not exit!
			fi
		else
			writelog "\tFeature '$FEAT' has no handler! This function will be unavailable."
			notify_user "Feature '$FEAT' nicht unterstützt"
		fi
	done
	# Prepare array for the command line options
	unset VIRTCMDOPTS
	declare -a VIRTCMDOPTS
	# The features should now be initialized, call the main 'run_plugin' function of the hypervisor
	writelog "#################### Plugin run: $PLUGIN_ID ####################"
	writelog "Calling 'run_plugin' of '$PLUGIN_ID'..."
	run_plugin || writelog "Failed to run 'run_plugin' of '$PLUGIN_ID'."

	# It should have set this variable if all went well
	if isempty VIRTCMD; then
		error_user "Fehler beim Starten der VM-Sitzung" "
	Das Start-Script für den Virtualisierer $PLUGIN_ID hat kein Kommando
	zum Starten der Sitzung definiert. Kann Sitzung nicht initialisieren."
		slxlog "virt-plugin-error" "run-virt.include for $PLUGIN_ID did not set VIRTCMD"
		cleanexit 1
	fi

	run_hooks "pre-exec" "$PLUGIN_ID" "$IMGUUID"

	# Launch COWGUI in CoW-Edit-Mode
	local cowpid cowurl vmpidfile
	cowpid=
	vmpidfile=
	if [ -n "$DMSD_COW_SESSION" ]; then
		vmpidfile="$( mktemp -p "/run/user/$( id -u )" )"
		cowurl="${SLX_VMCHOOSER_BASE_URL//"/vmchooser/"/"/cow/"}"
		(
		cntr=0
		while true; do
			cowgui --session "$DMSD_COW_SESSION" --url "$cowurl" --pid "$DNBD3_PID" && break
			# Unclean exit, let's see if it's worth relaunching
			state="$( curl -m 3 -sS -L "$cowurl/status/$DMSD_COW_SESSION" | jq -r .state )"
			[ -z "$state" ] && break
			[ "$state" = "PROCESSING" ] && break
			[ "$state" = "ERROR" ] && break
			[ "$state" = "COMPLETELY_DONE" ] && break
			(( cntr++ > 10 )) && break
		done
		[ -n "$vmpidfile" ] && kill "$( cat "$vmpidfile" )"
		) &
		cowpid=$!
	fi

	writelog "VM command: ${VIRTCMD} ${VIRTCMDOPTS[*]}"
	# This will start the VM
	writelog "---------- BEGIN VM command output ----------"
	local retval vmpid
	"${VIRTCMD}" "${VIRTCMDOPTS[@]}" &>> "${LOGFILE}" &
	vmpid=$!
	[ -n "$vmpidfile" ] && echo "$vmpid" > "$vmpidfile"
	wait "$vmpid"
	retval=${?}
	writelog "---------- END VM command output ----------"

	[ -n "$DNBD3_PID" ] && kill "$DNBD3_PID"
	# If cow, wait for GUI
	[ -n "$cowpid" ] && wait "$cowpid"

	run_hooks "post-exec" "$PLUGIN_ID" "$IMGUUID"

	writelog "Virtualizer exited with '$retval'. Bye."

	cleanexit 0
}
# Detach
main &
RUNVIRT_PID="$!"

# Give some time
usleep 100000
if ! kill -0 "${RUNVIRT_PID}"; then
	if [ -n "${LOGFILE}" ] && [ -f "${LOGFILE}" ]; then
		echo "Failed to start runvirt main function. Seems that it exited early (<100ms)." >> "${LOGFILE}"
	fi
fi

wait "$RUNVIRT_PID"
RUNVIRT_RET="$?"

if [ "${RUNVIRT_RET}" -ne 0 ]; then
	if [ "${RUNVIRT_RET}" -eq 141 ]; then
		# 141 happens on alt + print screen + k or upon automatic logout via systemd
		# just sleep here to avoid these annoying (and misleading) slxlogs....
		sleep 2 ; sleep 2
	fi
	[ -s "${LOGFILE}" ] && slxlog "run-virt-wrapper" "Failed to launch runvirt!" "${LOGFILE}"
	exit "$RUNVIRT_RET"
fi

exit 0