2019-12-20 12:50:41 -05:00
|
|
|
import logging
|
2019-11-01 02:54:51 -04:00
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
##
|
|
|
|
import psutil
|
2019-12-20 12:50:41 -05:00
|
|
|
from lxml import etree
|
2019-11-01 02:54:51 -04:00
|
|
|
##
|
|
|
|
import aif.disk.block_fallback as block
|
|
|
|
import aif.disk.luks_fallback as luks
|
|
|
|
import aif.disk.lvm_fallback as lvm
|
|
|
|
import aif.disk.mdadm_fallback as mdadm
|
2019-11-06 02:21:04 -05:00
|
|
|
import aif.utils
|
2019-11-01 02:54:51 -04:00
|
|
|
|
2019-11-06 02:21:04 -05:00
|
|
|
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2019-11-06 02:21:04 -05:00
|
|
|
FS_FSTYPES = aif.utils.kernelFilesystems()
|
2019-11-01 02:54:51 -04:00
|
|
|
|
|
|
|
|
|
|
|
class FS(object):
|
|
|
|
def __init__(self, fs_xml, sourceobj):
|
|
|
|
self.xml = fs_xml
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.debug('fs_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
2019-11-06 02:21:04 -05:00
|
|
|
if not isinstance(sourceobj, (block.Disk,
|
|
|
|
block.Partition,
|
|
|
|
luks.LUKS,
|
|
|
|
lvm.LV,
|
|
|
|
mdadm.Array)):
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.error(('sourceobj must be of type '
|
|
|
|
'aif.disk.block.Partition, '
|
|
|
|
'aif.disk.luks.LUKS, '
|
|
|
|
'aif.disk.lvm.LV, or'
|
|
|
|
'aif.disk.mdadm.Array.'))
|
|
|
|
raise ValueError('Invalid sourceobj type')
|
2019-11-06 02:21:04 -05:00
|
|
|
self.id = self.xml.attrib['id']
|
2019-11-01 02:54:51 -04:00
|
|
|
self.source = sourceobj
|
|
|
|
self.devpath = sourceobj.devpath
|
|
|
|
self.formatted = False
|
|
|
|
self.fstype = self.xml.attrib.get('type')
|
2019-12-20 12:50:41 -05:00
|
|
|
if self.fstype not in FS_FSTYPES:
|
|
|
|
_logger.error('{0} is not a supported filesystem type on this system.'.format(self.fstype))
|
|
|
|
raise ValueError('Invalid filesystem type')
|
2019-11-01 02:54:51 -04:00
|
|
|
|
|
|
|
def format(self):
|
|
|
|
if self.formatted:
|
2019-12-20 12:50:41 -05:00
|
|
|
return(None)
|
2019-11-01 02:54:51 -04:00
|
|
|
# This is a safeguard. We do *not* want to high-format a disk that is mounted.
|
2019-11-06 02:21:04 -05:00
|
|
|
aif.utils.checkMounted(self.devpath)
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.info('Formatting {0}.'.format(self.devpath))
|
|
|
|
cmd_str = ['mkfs',
|
|
|
|
'-t', self.fstype]
|
2019-11-01 02:54:51 -04:00
|
|
|
for o in self.xml.findall('opt'):
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str.append(o.attrib['name'])
|
2019-11-01 02:54:51 -04:00
|
|
|
if o.text:
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str.append(o.text)
|
|
|
|
cmd_str.append(self.devpath)
|
|
|
|
cmd = subprocess.run(cmd_str, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
|
|
|
_logger.info('Executed: {0}'.format(' '.join(cmd.args)))
|
|
|
|
if cmd.returncode != 0:
|
|
|
|
_logger.warning('Command returned non-zero status')
|
|
|
|
_logger.debug('Exit status: {0}'.format(str(cmd.returncode)))
|
|
|
|
for a in ('stdout', 'stderr'):
|
|
|
|
x = getattr(cmd, a)
|
|
|
|
if x:
|
|
|
|
_logger.debug('{0}: {1}'.format(a.upper(), x.decode('utf-8').strip()))
|
|
|
|
raise RuntimeError('Failed to format successfully')
|
|
|
|
else:
|
|
|
|
self.formatted = True
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|
2019-11-06 02:21:04 -05:00
|
|
|
|
|
|
|
|
|
|
|
class Mount(object):
|
|
|
|
def __init__(self, mount_xml, fsobj):
|
|
|
|
self.xml = mount_xml
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.debug('mount_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
2019-11-06 02:21:04 -05:00
|
|
|
self.id = self.xml.attrib['id']
|
|
|
|
if not isinstance(fsobj, FS):
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.error('partobj must be of type aif.disk.filesystem.FS.')
|
|
|
|
raise ValueError('Invalid type for fsobj')
|
2019-11-06 02:21:04 -05:00
|
|
|
self.id = self.xml.attrib['id']
|
|
|
|
self.fs = fsobj
|
|
|
|
self.source = self.fs.devpath
|
|
|
|
self.target = os.path.realpath(self.xml.attrib['target'])
|
|
|
|
self.opts = {}
|
|
|
|
for o in self.xml.findall('opt'):
|
|
|
|
self.opts[o.attrib['name']] = o.text
|
|
|
|
self.mounted = False
|
|
|
|
|
|
|
|
def _parseOpts(self):
|
|
|
|
opts = []
|
|
|
|
for k, v in self.opts.items():
|
|
|
|
if v and v is not True: # Python's boolean determination is weird sometimes.
|
|
|
|
opts.append('{0}={1}'.format(k, v))
|
|
|
|
else:
|
|
|
|
opts.append(k)
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.debug('Rendered mount opts: {0}'.format(opts))
|
2019-11-06 02:21:04 -05:00
|
|
|
return(opts)
|
|
|
|
|
|
|
|
def mount(self):
|
|
|
|
if self.mounted:
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.info('Mounting {0} at {1} as {2}.'.format(self.source, self.target, self.fs.fstype))
|
2019-11-06 02:21:04 -05:00
|
|
|
os.makedirs(self.target, exist_ok = True)
|
|
|
|
opts = self._parseOpts()
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str = ['/usr/bin/mount',
|
|
|
|
'--types', self.fs.fstype]
|
2019-11-06 02:21:04 -05:00
|
|
|
if opts:
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str.extend(['--options', ','.join(opts)])
|
|
|
|
cmd_str.extend([self.source, self.target])
|
|
|
|
cmd = subprocess.run(cmd_str, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
|
|
|
_logger.info('Executed: {0}'.format(' '.join(cmd.args)))
|
|
|
|
if cmd.returncode != 0:
|
|
|
|
_logger.warning('Command returned non-zero status')
|
|
|
|
_logger.debug('Exit status: {0}'.format(str(cmd.returncode)))
|
|
|
|
for a in ('stdout', 'stderr'):
|
|
|
|
x = getattr(cmd, a)
|
|
|
|
if x:
|
|
|
|
_logger.debug('{0}: {1}'.format(a.upper(), x.decode('utf-8').strip()))
|
|
|
|
raise RuntimeError('Failed to mount successfully')
|
|
|
|
else:
|
|
|
|
self.mounted = True
|
|
|
|
_logger.debug('{0} mounted.'.format(self.source))
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|
2019-11-06 02:21:04 -05:00
|
|
|
|
|
|
|
def unmount(self, lazy = False, force = False):
|
|
|
|
self.updateMount()
|
|
|
|
if not self.mounted and not force:
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.info('Unmounting {0}.'.format(self.target))
|
|
|
|
cmd_str = ['/usr/bin/umount']
|
2019-11-06 02:21:04 -05:00
|
|
|
if lazy:
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str.append('--lazy')
|
2019-11-06 02:21:04 -05:00
|
|
|
if force:
|
2019-12-20 12:50:41 -05:00
|
|
|
cmd_str.append('--force')
|
|
|
|
cmd_str.append(self.target)
|
|
|
|
cmd = subprocess.run(cmd_str, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
|
|
|
_logger.info('Executed: {0}'.format(' '.join(cmd.args)))
|
|
|
|
if cmd.returncode != 0:
|
|
|
|
_logger.warning('Command returned non-zero status')
|
|
|
|
_logger.debug('Exit status: {0}'.format(str(cmd.returncode)))
|
|
|
|
for a in ('stdout', 'stderr'):
|
|
|
|
x = getattr(cmd, a)
|
|
|
|
if x:
|
|
|
|
_logger.debug('{0}: {1}'.format(a.upper(), x.decode('utf-8').strip()))
|
|
|
|
raise RuntimeError('Failed to unmount successfully')
|
|
|
|
else:
|
|
|
|
self.mounted = False
|
|
|
|
_logger.debug('{0} unmounted.'.format(self.source))
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|
2019-11-06 02:21:04 -05:00
|
|
|
|
|
|
|
def updateMount(self):
|
2019-12-20 12:50:41 -05:00
|
|
|
_logger.debug('Fetching mount status for {0}'.format(self.source))
|
2019-11-06 02:21:04 -05:00
|
|
|
if self.source in [p.device for p in psutil.disk_partitions(all = True)]:
|
|
|
|
self.mounted = True
|
|
|
|
else:
|
|
|
|
self.mounted = False
|
2019-12-11 04:33:15 -05:00
|
|
|
return(None)
|