import pwd
import grp
import textwrap
+import glob
CEPH_OSD_ONDISK_MAGIC = 'ceph osd volume v026'
CEPH_LOCKBOX_ONDISK_MAGIC = 'ceph lockbox volume v001'
return string.decode('utf-8') if isinstance(string, bytes) else string
+def command_init(arguments, **kwargs):
+ """
+ Safely execute a non-blocking ``subprocess.Popen`` call
+ making sure that the executable exists and raising a helpful
+ error message if it does not.
+
+ .. note:: This should be the preferred way of calling ``subprocess.Popen``
+ since it provides the caller with the safety net of making sure that
+ executables *will* be found and will error nicely otherwise.
+
+ This returns the process.
+ """
+
+ arguments = list(map(_bytes2str, _get_command_executable(arguments)))
+
+ LOG.info('Running command: %s' % ' '.join(arguments))
+ process = subprocess.Popen(
+ arguments,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ **kwargs)
+ return process
+
+
+def command_wait(process):
+ """
+ Wait for the process finish and parse its output.
+ """
+
+ out, err = process.communicate()
+
+ return _bytes2str(out), _bytes2str(err), process.returncode
+
+
def command_check_call(arguments, exit=False):
"""
Safely execute a ``subprocess.check_call`` call making sure that the
LOG.debug(err)
+def main_fix(args):
+ # A hash table containing 'path': ('uid', 'gid', blocking, recursive)
+ fix_table = [
+ ('/var/lib/ceph', 'ceph', 'ceph', True, False),
+ ]
+
+ # Relabel/chown all files under /var/lib/ceph/ recursively (except for osd)
+ for directory in glob.glob('/var/lib/ceph/*'):
+ if directory == '/var/lib/ceph/osd':
+ fix_table.append((directory, 'ceph', 'ceph', True, False))
+ else:
+ fix_table.append((directory, 'ceph', 'ceph', True, True))
+
+ # Relabel/chown the osds recursively and in parallel
+ for directory in glob.glob('/var/lib/ceph/osd/*'):
+ fix_table.append((directory, 'ceph', 'ceph', False, True))
+
+ LOG.debug("fix_table: " + str(fix_table))
+
+ # The lists of background processes
+ all_processes = []
+ permissions_processes = []
+ selinux_processes = []
+
+ # Preliminary checks
+ if args.selinux or args.all:
+ out, err, ret = command(['selinuxenabled'])
+ if ret:
+ LOG.error('SELinux is not enabled, please enable it, first.')
+ raise Error('no SELinux')
+
+ for daemon in ['ceph-mon', 'ceph-osd', 'ceph-mds', 'radosgw', 'ceph-mgr']:
+ out, err, ret = command(['pgrep', daemon])
+ if ret == 0:
+ LOG.error(daemon + ' is running, please stop it, first')
+ raise Error(daemon + ' running')
+
+ # Use find to relabel + chown ~simultaenously
+ if args.all:
+ for directory, uid, gid, blocking, recursive in fix_table:
+ c = [
+ 'find',
+ directory,
+ '-exec',
+ 'chown',
+ ':'.join((uid, gid)),
+ '{}',
+ '+',
+ '-exec',
+ 'restorecon',
+ '{}',
+ '+',
+ ]
+
+ # Just pass -maxdepth 0 for non-recursive calls
+ if not recursive:
+ c += ['-maxdepth', '0']
+
+ if blocking:
+ out, err, ret = command(c)
+
+ if ret:
+ LOG.error("Failed to fix " + directory)
+ LOG.error(err)
+ raise Error(directory + " fix failed")
+ else:
+ all_processes.append(command_init(c))
+
+ LOG.debug("all_processes: " + str(all_processes))
+ for process in all_processes:
+ out, err, ret = command_wait(process)
+ if ret:
+ LOG.error("A background find process failed")
+ LOG.error(err)
+ raise Error("background failed")
+
+ # Fix permissions
+ if args.permissions:
+ for directory, uid, gid, blocking, recursive in fix_table:
+ if recursive:
+ c = [
+ 'chown',
+ '-R',
+ ':'.join((uid, gid)),
+ directory
+ ]
+ else:
+ c = [
+ 'chown',
+ ':'.join((uid, gid)),
+ directory
+ ]
+
+ if blocking:
+ out, err, ret = command(c)
+
+ if ret:
+ LOG.error("Failed to chown " + directory)
+ LOG.error(err)
+ raise Error(directory + " chown failed")
+ else:
+ permissions_processes.append(command_init(c))
+
+ LOG.debug("permissions_processes: " + str(permissions_processes))
+ for process in permissions_processes:
+ out, err, ret = command_wait(process)
+ if ret:
+ LOG.error("A background permissions process failed")
+ LOG.error(err)
+ raise Error("background failed")
+
+ # Fix SELinux labels
+ if args.selinux:
+ for directory, uid, gid, blocking, recursive in fix_table:
+ if recursive:
+ c = [
+ 'restorecon',
+ '-R',
+ directory
+ ]
+ else:
+ c = [
+ 'restorecon',
+ directory
+ ]
+
+ if blocking:
+ out, err, ret = command(c)
+
+ if ret:
+ LOG.error("Failed to restore labels for " + directory)
+ LOG.error(err)
+ raise Error(directory + " relabel failed")
+ else:
+ selinux_processes.append(command_init(c))
+
+ LOG.debug("selinux_processes: " + str(selinux_processes))
+ for process in selinux_processes:
+ out, err, ret = command_wait(process)
+ if ret:
+ LOG.error("A background selinux process failed")
+ LOG.error(err)
+ raise Error("background failed")
+
+
def setup_statedir(dir):
# XXX The following use of globals makes linting
# really hard. Global state in Python is iffy and
make_destroy_parser(subparsers)
make_zap_parser(subparsers)
make_trigger_parser(subparsers)
+ make_fix_parser(subparsers)
args = parser.parse_args(argv)
return args
+def make_fix_parser(subparsers):
+ fix_parser = subparsers.add_parser(
+ 'fix',
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=textwrap.fill(textwrap.dedent("""\
+ """)),
+ help='fix SELinux labels and/or file permissions')
+
+ fix_parser.add_argument(
+ '--selinux',
+ action='store_true',
+ default=False,
+ help='fix SELinux labels for ceph data'
+ )
+ fix_parser.add_argument(
+ '--permissions',
+ action='store_true',
+ default=False,
+ help='fix file permissions for ceph data'
+ )
+ fix_parser.add_argument(
+ '--all',
+ action='store_true',
+ default=False,
+ help='fix file permissions as well as SELinux labels for ceph data'
+ )
+ fix_parser.set_defaults(
+ func=main_fix,
+ )
+ return fix_parser
+
+
def make_trigger_parser(subparsers):
trigger_parser = subparsers.add_parser(
'trigger',