/
vdsmd.py
executable file
·294 lines (231 loc) · 8.98 KB
/
vdsmd.py
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# SPDX-FileCopyrightText: Red Hat, Inc. and/or its affiliates.
# SPDX-License-Identifier: GPL-2.0-or-later
from __future__ import absolute_import
from __future__ import print_function
import atexit
import os
import os.path
import signal
import getpass
import pwd
import grp
import threading
import logging
import syslog
import resource
import tempfile
from logging import config as lconfig
from vdsm import constants
from vdsm import health
from vdsm import jobs
from vdsm import schedule
from vdsm import taskset
from vdsm import metrics
from vdsm.common import cmdutils
from vdsm.common import commands
from vdsm.common import dsaversion
from vdsm.common import hooks
from vdsm.common import lockfile
from vdsm.common import libvirtconnection
from vdsm.common import sigutils
from vdsm.common import supervdsm
from vdsm.common import time
from vdsm.common.panic import panic
from vdsm.config import config
from vdsm.network.initializer import init_unprivileged_network_components
from vdsm.network.initializer import stop_unprivileged_network_components
from vdsm.profiling import profile
from vdsm.storage.hsm import HSM
from vdsm.storage.dispatcher import Dispatcher
from vdsm.virt import periodic
loggerConfFile = constants.P_VDSM_CONF + 'logger.conf'
class FatalError(Exception):
""" Raised when vdsm fail to start """
def serve_clients(log):
cif = None
irs = None
scheduler = None
running = [True]
def sigtermHandler(signum, frame):
log.info("Received signal %s, shutting down" % signum)
running[0] = False
def sigusr1Handler(signum, frame):
"""
Called during fencing from spmprotect.sh when using export domain.
"""
if irs:
log.info("Received signal %s, stopping SPM" % signum)
# pylint: disable=no-member
# TODO remove when side effect removed from HSM.__init__ and
# initialize it in line #63
irs.spmStop(
irs.getConnectedStoragePoolsList()['poollist'][0])
sigutils.register()
signal.signal(signal.SIGTERM, sigtermHandler)
signal.signal(signal.SIGUSR1, sigusr1Handler)
profile.start()
metrics.start()
libvirtconnection.start_event_loop()
try:
if config.getboolean('irs', 'irs_enable'):
try:
irs = Dispatcher(HSM())
except:
panic("Error initializing IRS")
scheduler = schedule.Scheduler(name="vdsm.Scheduler",
clock=time.monotonic_time)
scheduler.start()
from vdsm.clientIF import clientIF # must import after config is read
cif = clientIF.getInstance(irs, log, scheduler)
jobs.start(scheduler, cif)
install_manhole({'irs': irs, 'cif': cif})
cif.start()
init_unprivileged_network_components(cif, supervdsm.getProxy())
periodic.start(cif, scheduler)
health.start()
try:
while running[0]:
sigutils.wait_for_signal()
profile.stop()
if config.getboolean('devel', 'coverage_enable'):
atexit._run_exitfuncs()
finally:
stop_unprivileged_network_components()
metrics.stop()
health.stop()
periodic.stop()
cif.prepareForShutdown()
jobs.stop()
scheduler.stop()
run_stop_hook()
finally:
libvirtconnection.stop_event_loop(wait=False)
def run():
try:
lconfig.fileConfig(loggerConfFile, disable_existing_loggers=False)
except Exception as e:
raise FatalError("Cannot configure logging: %s" % e)
# Shorten WARNING and CRITICAL to make the log align nicer.
logging.addLevelName(logging.WARNING, 'WARN')
logging.addLevelName(logging.CRITICAL, 'CRIT')
log = logging.getLogger('vds')
try:
logging.root.handlers.append(logging.StreamHandler())
log.handlers.append(logging.StreamHandler())
sysname, nodename, release, version, machine = os.uname()
log.info('(PID: %s) I am the actual vdsm %s %s (%s)',
os.getpid(), dsaversion.raw_version_revision, nodename,
release)
try:
__set_cpu_affinity()
except Exception:
log.exception('Failed to set affinity, running without')
serve_clients(log)
except:
log.error("Exception raised", exc_info=True)
log.info("Stopping threads")
for t in threading.enumerate():
if hasattr(t, 'stop'):
log.info("Stopping %s", t)
t.stop()
me = threading.current_thread()
for t in threading.enumerate():
if t is not me:
log.debug("%s is still running", t)
log.info("Exiting")
def install_manhole(locals):
if not config.getboolean('devel', 'manhole_enable'):
return
import manhole # pylint: disable=import-error
# locals: Set the locals in the manhole shell
# socket_path: Set to create secure and easy to use manhole socket,
# instead of /tmp/manhole-<vdsm-pid>.
# daemon_connection: Enable to ensure that manhole connection thread will
# not block shutdown.
# patch_fork: Disable to avoid creation of a manhole thread in the
# child process after fork.
# sigmask: Disable to avoid pointless modification of the
# process signal mask if signlfd module is available.
# redirect_stderr: Disable since Python prints ignored exepctions to
# stderr.
path = os.path.join(constants.P_VDSM_RUN, 'vdsmd.manhole')
manhole.install(locals=locals, socket_path=path, daemon_connection=True,
patch_fork=False, sigmask=None, redirect_stderr=False)
def __assertLogPermission():
if not os.access(constants.P_VDSM_LOG, os.W_OK):
raise FatalError("Cannot access vdsm log dirctory")
logfile = constants.P_VDSM_LOG + "/vdsm.log"
if not os.path.exists(logfile):
# if file not exist, and vdsm has an access to log directory- continue
return
if not os.access(logfile, os.W_OK):
raise FatalError("Cannot access vdsm log file")
def __assertSingleInstance():
try:
lockfile.lock(os.path.join(constants.P_VDSM_RUN, 'vdsmd.lock'))
except Exception as e:
raise FatalError(str(e))
def __assertVdsmUser():
username = getpass.getuser()
if username != constants.VDSM_USER:
raise FatalError("Not running as %r, trying to run as %r"
% (constants.VDSM_USER, username))
group = grp.getgrnam(constants.VDSM_GROUP)
if (constants.VDSM_USER not in group.gr_mem) and \
(pwd.getpwnam(constants.VDSM_USER).pw_gid != group.gr_gid):
raise FatalError("Vdsm user is not in KVM group")
def __assertVdsmHome():
home = os.path.expanduser("~")
if not os.access(home, os.F_OK | os.R_OK | os.W_OK | os.X_OK):
raise FatalError("Home directory: '%s' doesn't exist or doesn't "
"have correct permissions" % home)
def __assertSudoerPermissions():
with tempfile.NamedTemporaryFile() as dst:
# This cmd choice is arbitrary to validate that sudoers.d/50_vdsm file
# is read properly
cmd = [constants.EXT_CHOWN, "%s:%s" %
(constants.VDSM_USER, constants.QEMU_PROCESS_GROUP), dst.name]
try:
commands.run(cmd, sudo=True)
except cmdutils.Error as e:
msg = ("Vdsm user could not manage to run sudo operation: "
"(stderr: %s). Verify sudoer rules configuration" % e.err)
raise FatalError(msg)
def __set_cpu_affinity():
cpu_affinity = config.get('vars', 'cpu_affinity')
if cpu_affinity == "":
return
online_cpus = taskset.online_cpus()
log = logging.getLogger('vds')
if len(online_cpus) == 1:
log.debug('Only one cpu detected: affinity disabled')
return
if cpu_affinity.lower() == taskset.AUTOMATIC:
cpu_set = frozenset((taskset.pick_cpu(online_cpus),))
else:
cpu_set = frozenset(int(cpu.strip())
for cpu in cpu_affinity.split(","))
log.info('VDSM will run with cpu affinity: %s', cpu_set)
taskset.set(os.getpid(), cpu_set, all_tasks=True)
def run_stop_hook():
# TODO: Move to vdsmd.service ExecStopPost when systemd is fixed.
# https://bugzilla.redhat.com/1761260
log = logging.getLogger('vds')
try:
hooks.after_vdsm_stop()
except Exception:
log.exception("Error running stop hook")
def main():
try:
__assertSingleInstance()
__assertVdsmUser()
__assertVdsmHome()
__assertLogPermission()
__assertSudoerPermissions()
if not config.getboolean('vars', 'core_dump_enable'):
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
run()
except FatalError as e:
syslog.syslog("VDSM failed to start: %s" % e)
# Make it easy to debug via the shell
raise