2015-07-10 20:58:25 +00:00
|
|
|
# Copyright 2015 Canonical, Ltd.
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
from collections import OrderedDict
|
2015-08-24 22:34:55 +00:00
|
|
|
import logging
|
2015-07-22 01:15:22 +00:00
|
|
|
import os
|
2015-08-24 22:34:55 +00:00
|
|
|
import re
|
2015-07-10 20:58:25 +00:00
|
|
|
import yaml
|
|
|
|
|
2015-08-10 13:42:19 +00:00
|
|
|
from .actions import (
|
2015-11-13 16:59:38 +00:00
|
|
|
BcacheAction,
|
2015-07-22 01:15:22 +00:00
|
|
|
DiskAction,
|
2015-07-10 20:58:25 +00:00
|
|
|
PartitionAction,
|
|
|
|
FormatAction,
|
2015-12-01 19:30:53 +00:00
|
|
|
LVMVolGroupAction,
|
2015-10-02 00:03:54 +00:00
|
|
|
MountAction,
|
|
|
|
RaidAction,
|
2015-07-10 20:58:25 +00:00
|
|
|
)
|
|
|
|
|
2015-07-23 12:32:55 +00:00
|
|
|
log = logging.getLogger("subiquity.filesystem.blockdev")
|
2015-09-01 16:28:23 +00:00
|
|
|
FIRST_PARTITION_OFFSET = 1 << 20 # 1K offset/aligned
|
2015-09-01 22:01:07 +00:00
|
|
|
GPT_END_RESERVE = 1 << 20 # save room at the end for GPT
|
2015-07-23 12:32:55 +00:00
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-10-05 20:09:47 +00:00
|
|
|
# round up length by 1M
|
|
|
|
def blockdev_align_up(size, block_size=1 << 30):
|
|
|
|
return size + (block_size - (size % block_size))
|
|
|
|
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
class Disk():
|
|
|
|
def __init__(self, devpath, serial, model, parttype, size=0):
|
|
|
|
self._devpath = devpath
|
|
|
|
self._serial = serial
|
2015-07-22 01:15:22 +00:00
|
|
|
self._parttype = parttype
|
2015-08-27 19:26:37 +00:00
|
|
|
self._model = model
|
|
|
|
self._size = self._get_size(devpath, size)
|
|
|
|
self._partitions = OrderedDict()
|
|
|
|
|
2015-10-06 20:23:29 +00:00
|
|
|
def __eq__(self, other):
|
2015-11-02 22:48:37 +00:00
|
|
|
if isinstance(other, self.__class__):
|
2015-10-06 20:23:29 +00:00
|
|
|
print('disk same class, checking members')
|
|
|
|
return (self._devpath == other._devpath and
|
|
|
|
self._serial == other._serial and
|
|
|
|
self._parttype == other._parttype and
|
|
|
|
self._model == other._model and
|
|
|
|
self._size == other._size and
|
|
|
|
self._partitions == other._partitions)
|
|
|
|
else:
|
|
|
|
return False
|
2015-11-02 22:48:37 +00:00
|
|
|
|
|
|
|
__hash__ = None # declare we don't supply a hash
|
|
|
|
|
2015-10-06 20:23:29 +00:00
|
|
|
def __ne__(self, other):
|
|
|
|
return not self.__eq__(other)
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
def _get_size(self, devpath, size):
|
|
|
|
if size:
|
|
|
|
return size
|
|
|
|
sysblock = os.path.join('/sys/block', os.path.basename(devpath))
|
|
|
|
nr_blocks_f = os.path.join(sysblock, 'size')
|
|
|
|
block_sz_f = os.path.join(sysblock, 'queue', 'logical_block_size')
|
2015-08-28 18:19:13 +00:00
|
|
|
|
|
|
|
if not os.path.exists(sysblock):
|
|
|
|
log.warn('disk at devpath:{} not present'.format(devpath))
|
|
|
|
return 0
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
with open(nr_blocks_f, 'r') as r:
|
|
|
|
nr_blocks = int(r.read())
|
|
|
|
with open(block_sz_f, 'r') as r:
|
|
|
|
block_sz = int(r.read())
|
|
|
|
|
|
|
|
return nr_blocks * block_sz
|
|
|
|
|
2015-10-06 20:23:29 +00:00
|
|
|
def __repr__(self):
|
|
|
|
o = {
|
2015-11-02 22:48:37 +00:00
|
|
|
'devpath': self.devpath,
|
|
|
|
'serial': self.serial,
|
|
|
|
'model': self.model,
|
|
|
|
'parttype': self.parttype,
|
|
|
|
'size': self.size,
|
|
|
|
'partitions': self.partitions
|
2015-10-06 20:23:29 +00:00
|
|
|
}
|
2015-10-07 20:24:19 +00:00
|
|
|
return yaml.dump(o, default_flow_style=False)
|
2015-10-06 20:23:29 +00:00
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
@property
|
|
|
|
def devpath(self):
|
|
|
|
return self._devpath
|
|
|
|
|
|
|
|
@property
|
|
|
|
def serial(self):
|
|
|
|
return self._serial
|
|
|
|
|
|
|
|
@property
|
|
|
|
def model(self):
|
|
|
|
return self._model
|
|
|
|
|
|
|
|
@property
|
|
|
|
def parttype(self):
|
|
|
|
return self._parttype
|
|
|
|
|
|
|
|
@property
|
|
|
|
def size(self):
|
|
|
|
return self._size
|
|
|
|
|
|
|
|
@property
|
|
|
|
def partitions(self):
|
|
|
|
return self._partitions
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
self._partitions = OrderedDict()
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Blockdev():
|
|
|
|
def __init__(self, devpath, serial, model, parttype='gpt', size=0):
|
|
|
|
self.disk = Disk(devpath, serial, model, parttype, size)
|
|
|
|
self._filesystems = {}
|
2015-08-21 18:49:45 +00:00
|
|
|
self._mounts = {}
|
2015-10-26 14:57:19 +00:00
|
|
|
self._mountactions = {}
|
2015-11-17 22:12:45 +00:00
|
|
|
self._tag = ''
|
2015-07-10 20:58:25 +00:00
|
|
|
self.bcache = []
|
|
|
|
self.lvm = []
|
2015-08-27 19:26:37 +00:00
|
|
|
self.baseaction = DiskAction(os.path.basename(self.disk.devpath),
|
|
|
|
self.disk.model, self.disk.serial,
|
|
|
|
self.disk.parttype)
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-10-06 20:23:29 +00:00
|
|
|
def __eq__(self, other):
|
2015-11-02 22:48:37 +00:00
|
|
|
if isinstance(other, self.__class__):
|
2015-10-06 20:23:29 +00:00
|
|
|
return (self.disk == other.disk and
|
|
|
|
self._filesystems == other._filesystems and
|
|
|
|
self._mounts == other._mounts and
|
2015-10-26 14:57:19 +00:00
|
|
|
self._mountactions == other._mountactions and
|
2015-10-06 20:23:29 +00:00
|
|
|
self.bcache == other.bcache and
|
2015-11-02 22:48:37 +00:00
|
|
|
self.lvm == other.lvm and
|
2015-10-06 20:23:29 +00:00
|
|
|
self.baseaction == other.baseaction)
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2015-11-02 22:48:37 +00:00
|
|
|
__hash__ = None # declare we don't supply a hash
|
|
|
|
|
2015-10-06 20:23:29 +00:00
|
|
|
def __ne__(self, other):
|
|
|
|
return not self.__eq__(other)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return str(self.get_actions())
|
|
|
|
|
2015-07-23 15:04:19 +00:00
|
|
|
def reset(self):
|
|
|
|
''' Wipe out any actions queued for this disk '''
|
2015-08-27 19:26:37 +00:00
|
|
|
self.disk.reset()
|
|
|
|
self._filesystems = {}
|
2015-08-21 18:49:45 +00:00
|
|
|
self._mounts = {}
|
2015-10-26 14:57:19 +00:00
|
|
|
self._mountactions = {}
|
2015-07-23 15:04:19 +00:00
|
|
|
self.bcache = []
|
|
|
|
self.lvm = []
|
2015-11-17 22:12:45 +00:00
|
|
|
self.tag = ''
|
2015-07-23 15:04:19 +00:00
|
|
|
|
2015-10-26 14:57:19 +00:00
|
|
|
@property
|
|
|
|
def id(self):
|
|
|
|
return self.baseaction.action_id
|
|
|
|
|
2015-10-05 15:56:59 +00:00
|
|
|
@property
|
|
|
|
def blocktype(self):
|
|
|
|
return self.baseaction.type
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
@property
|
|
|
|
def devpath(self):
|
|
|
|
return self.disk.devpath
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-10-05 15:56:59 +00:00
|
|
|
@property
|
|
|
|
def path(self):
|
|
|
|
return self.disk.devpath
|
|
|
|
|
|
|
|
@property
|
|
|
|
def model(self):
|
|
|
|
return self.disk.model
|
|
|
|
|
2015-10-21 21:25:44 +00:00
|
|
|
@property
|
|
|
|
def serial(self):
|
|
|
|
return self.disk.serial
|
|
|
|
|
2015-08-21 18:49:45 +00:00
|
|
|
@property
|
|
|
|
def mounts(self):
|
|
|
|
return self._mounts.values()
|
|
|
|
|
2015-07-22 01:15:22 +00:00
|
|
|
@property
|
|
|
|
def parttype(self):
|
2015-08-27 19:26:37 +00:00
|
|
|
return self.disk.parttype
|
2015-07-22 01:15:22 +00:00
|
|
|
|
|
|
|
@parttype.setter # NOQA
|
|
|
|
def parttype(self, value):
|
|
|
|
self._parttype = value
|
|
|
|
|
2015-08-21 18:07:42 +00:00
|
|
|
@property
|
|
|
|
def size(self):
|
2015-08-27 19:26:37 +00:00
|
|
|
return self.disk.size
|
2015-08-21 18:07:42 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def partitions(self):
|
|
|
|
return self.disk.partitions
|
|
|
|
|
2015-10-05 15:56:59 +00:00
|
|
|
@property
|
|
|
|
def partnames(self):
|
|
|
|
return ['{}{}'.format(self.devpath, num) for (num, _) in
|
|
|
|
self.partitions.items()]
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
@property
|
|
|
|
def filesystems(self):
|
|
|
|
return self._filesystems
|
|
|
|
|
2015-10-02 00:03:54 +00:00
|
|
|
@property
|
|
|
|
def percent_free(self):
|
|
|
|
''' return the device free percentage of the whole device'''
|
2015-10-26 15:37:01 +00:00
|
|
|
if self.size == 0:
|
|
|
|
return 0
|
|
|
|
|
2015-10-02 18:49:10 +00:00
|
|
|
percent = (int((1.0 - (self.usedspace / self.size)) * 100))
|
2015-10-02 00:03:54 +00:00
|
|
|
return percent
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
@property
|
|
|
|
def available(self):
|
|
|
|
''' return True if has free space or partitions not
|
2015-11-13 19:42:36 +00:00
|
|
|
assigned, and no holders '''
|
2015-12-02 15:40:17 +00:00
|
|
|
if not self.is_mounted() and self.percent_free > 0:
|
2015-07-10 20:58:25 +00:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2015-10-05 15:56:59 +00:00
|
|
|
@property
|
|
|
|
def available_partitions(self):
|
2015-11-02 22:48:37 +00:00
|
|
|
''' return list of non-zero sized partitions that are
|
2015-10-07 20:24:19 +00:00
|
|
|
defined but not mounted, not formatted, and not used in
|
2015-10-05 15:56:59 +00:00
|
|
|
raid, lvm, bcache'''
|
|
|
|
return [part.devpath for (num, part) in self.partitions.items()
|
2015-11-02 22:48:37 +00:00
|
|
|
if (part.size > 0 and
|
|
|
|
part.flags not in ['raid', 'lvm', 'bcache'] and
|
|
|
|
(part.devpath not in self._mounts.keys() and
|
|
|
|
part.devpath not in self._filesystems.keys()))]
|
2015-10-05 15:56:59 +00:00
|
|
|
|
2015-09-28 16:28:26 +00:00
|
|
|
@property
|
|
|
|
def mounted(self):
|
|
|
|
return self.is_mounted()
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
@property
|
2015-08-21 18:07:42 +00:00
|
|
|
def usedspace(self, unit='b'):
|
|
|
|
''' return amount of used space'''
|
2015-08-27 19:26:37 +00:00
|
|
|
space = 0
|
2015-10-26 14:57:19 +00:00
|
|
|
if self.devpath in self.filesystems:
|
|
|
|
space = self.size
|
|
|
|
else:
|
|
|
|
for (num, action) in self.disk.partitions.items():
|
|
|
|
space += int(action.offset)
|
|
|
|
space += int(action.size)
|
2015-08-27 19:26:37 +00:00
|
|
|
|
|
|
|
return space
|
2015-08-21 18:07:42 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def freespace(self, unit='B'):
|
2015-07-10 20:58:25 +00:00
|
|
|
''' return amount of free space '''
|
2015-10-02 18:49:10 +00:00
|
|
|
return self.size - self.usedspace
|
2015-07-10 20:58:25 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def lastpartnumber(self):
|
2015-08-27 19:26:37 +00:00
|
|
|
return len(self.disk.partitions)
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-11-17 22:12:45 +00:00
|
|
|
@property
|
|
|
|
def tag(self):
|
|
|
|
return self._tag
|
|
|
|
|
|
|
|
def set_tag(self, tag):
|
|
|
|
self._tag = tag
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
def delete_partition(self, partnum=None, sector=None, mountpoint=None):
|
|
|
|
# find part and then call deletePartition()
|
|
|
|
# find and remove from self.fstable
|
|
|
|
pass
|
|
|
|
|
2015-07-23 19:56:45 +00:00
|
|
|
def add_partition(self, partnum, size, fstype, mountpoint=None, flag=None):
|
2015-07-10 20:58:25 +00:00
|
|
|
''' add a new partition to this disk '''
|
2015-07-23 19:56:45 +00:00
|
|
|
log.debug('add_partition:'
|
|
|
|
' partnum:%s size:%s fstype:%s mountpoint:%s flag=%s' % (
|
|
|
|
partnum, size, fstype, mountpoint, flag))
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
if size > self.freespace:
|
2015-09-01 16:28:23 +00:00
|
|
|
raise Exception('Not enough space (requested:{} free:{}'.format(
|
|
|
|
size, self.freespace))
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-10-07 20:24:19 +00:00
|
|
|
# ensure we always use integers for partitions
|
|
|
|
partnum = int(partnum)
|
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
if len(self.disk.partitions) == 0:
|
2015-09-01 16:28:23 +00:00
|
|
|
offset = FIRST_PARTITION_OFFSET
|
2015-07-10 20:58:25 +00:00
|
|
|
else:
|
2015-08-27 19:26:37 +00:00
|
|
|
offset = 0
|
|
|
|
|
2015-09-01 22:01:07 +00:00
|
|
|
log.debug('Aligning start and length on 1M boundaries')
|
2015-10-05 20:09:47 +00:00
|
|
|
new_size = blockdev_align_up(size + offset)
|
2015-09-01 22:01:07 +00:00
|
|
|
if new_size > self.freespace - GPT_END_RESERVE:
|
|
|
|
new_size = self.freespace - GPT_END_RESERVE
|
|
|
|
log.debug('Old size: {} New size: {}'.format(size, new_size))
|
|
|
|
|
|
|
|
log.debug('requested start: {} length: {}'.format(offset,
|
|
|
|
new_size - offset))
|
2015-08-27 19:26:37 +00:00
|
|
|
# create partition and add
|
|
|
|
part_action = PartitionAction(self.baseaction, partnum,
|
2015-09-01 22:01:07 +00:00
|
|
|
offset, new_size - offset, flag)
|
|
|
|
|
2015-09-01 16:28:23 +00:00
|
|
|
log.debug('PartitionAction:\n{}'.format(part_action.get()))
|
2015-08-27 19:26:37 +00:00
|
|
|
|
|
|
|
self.disk.partitions.update({partnum: part_action})
|
2015-09-21 21:40:26 +00:00
|
|
|
partpath = "{}{}".format(self.disk.devpath, partnum)
|
2015-08-27 19:26:37 +00:00
|
|
|
|
|
|
|
# record filesystem formating
|
2015-10-07 20:24:19 +00:00
|
|
|
if fstype and fstype not in ['leave unformatted']:
|
2015-08-27 19:26:37 +00:00
|
|
|
fs_action = FormatAction(part_action, fstype)
|
2015-09-01 16:28:23 +00:00
|
|
|
log.debug('Adding filesystem on {}'.format(partpath))
|
|
|
|
log.debug('FormatAction:\n{}'.format(fs_action.get()))
|
2015-08-27 19:26:37 +00:00
|
|
|
self.filesystems.update({partpath: fs_action})
|
2015-07-10 20:58:25 +00:00
|
|
|
|
|
|
|
# associate partition devpath with mountpoint
|
|
|
|
if mountpoint:
|
2015-08-21 18:49:45 +00:00
|
|
|
self._mounts[partpath] = mountpoint
|
2015-10-26 14:57:19 +00:00
|
|
|
self._mountactions[partpath] = MountAction(fs_action, mountpoint)
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
log.debug('Partition Added')
|
2015-09-01 22:01:07 +00:00
|
|
|
return new_size
|
2015-08-27 19:26:37 +00:00
|
|
|
|
2016-01-04 13:26:22 +00:00
|
|
|
def clear_ptable(self):
|
|
|
|
''' clear any partition table setting on underlying device '''
|
|
|
|
if self.baseaction.type == 'disk':
|
|
|
|
self.baseaction.clear_ptable()
|
|
|
|
|
2015-10-26 14:57:19 +00:00
|
|
|
def format_device(self, fstype, mountpoint):
|
|
|
|
log.debug('format:' ' fstype:%s mountpoint:%s' % (
|
|
|
|
fstype, mountpoint))
|
|
|
|
|
|
|
|
mntdev = self.devpath
|
|
|
|
|
|
|
|
# create partition and add
|
|
|
|
fs_action = FormatAction(self.baseaction, fstype)
|
|
|
|
log.debug('Adding filesystem on {}'.format(mntdev))
|
|
|
|
log.debug('FormatAction:\n{}'.format(fs_action.get()))
|
|
|
|
self.filesystems.update({mntdev: fs_action})
|
|
|
|
|
|
|
|
# associate partition devpath with mountpoint
|
|
|
|
if mountpoint:
|
|
|
|
self._mounts[mntdev] = mountpoint
|
|
|
|
self._mountactions[mntdev] = MountAction(fs_action, mountpoint)
|
|
|
|
log.debug('Mounting {} at {}'.format(mntdev, mountpoint))
|
|
|
|
|
2016-01-04 13:26:22 +00:00
|
|
|
# remove any partition table
|
|
|
|
self.clear_ptable()
|
|
|
|
|
2015-10-05 15:56:59 +00:00
|
|
|
def get_partition(self, devpath):
|
|
|
|
[partnum] = re.findall('\d+$', devpath)
|
2015-10-06 16:23:10 +00:00
|
|
|
return self.disk.partitions[int(partnum)]
|
2015-10-05 15:56:59 +00:00
|
|
|
|
2015-08-24 22:34:55 +00:00
|
|
|
def is_mounted(self):
|
|
|
|
with open('/proc/mounts') as pm:
|
|
|
|
mounts = pm.read()
|
|
|
|
|
2015-09-28 16:28:26 +00:00
|
|
|
# collect any /dev/* device and use
|
|
|
|
# dict to uniq the list of devices mounted
|
|
|
|
mounted_devs = {}
|
|
|
|
for mnt in re.findall('/dev/.*', mounts):
|
|
|
|
(devpath, mount, *_) = mnt.split()
|
|
|
|
# resolve any symlinks
|
|
|
|
mounted_devs.update(
|
|
|
|
{os.path.realpath(devpath): mount})
|
|
|
|
|
|
|
|
matches = [dev for dev in mounted_devs.keys()
|
|
|
|
if dev.startswith(self.disk.devpath)]
|
2015-08-24 22:34:55 +00:00
|
|
|
if len(matches) > 0:
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
def get_actions(self):
|
2015-08-24 22:34:55 +00:00
|
|
|
if self.is_mounted():
|
2015-11-02 22:48:37 +00:00
|
|
|
log.debug('Emitting no actions, device '
|
|
|
|
'({}) is mounted'.format(self.devpath))
|
2015-08-24 22:34:55 +00:00
|
|
|
return []
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
actions = []
|
2015-08-27 19:26:37 +00:00
|
|
|
action = self.baseaction.get()
|
2015-11-02 22:48:37 +00:00
|
|
|
part_actions = [part.get() for (num, part) in
|
|
|
|
self.disk.partitions.items()]
|
2015-10-26 14:57:19 +00:00
|
|
|
fs_actions = [fs.get() for fs in self.filesystems.values()]
|
|
|
|
mount_actions = [m.get() for m in self._mountactions.values()]
|
2015-11-02 22:48:37 +00:00
|
|
|
actions = [action] + part_actions + fs_actions + mount_actions
|
2015-10-26 14:57:19 +00:00
|
|
|
log.debug('actions ({}):\n{}'.format(len(actions), actions))
|
2015-09-23 03:19:54 +00:00
|
|
|
|
|
|
|
return actions
|
2015-07-10 20:58:25 +00:00
|
|
|
|
|
|
|
def get_fs_table(self):
|
2015-10-06 16:23:10 +00:00
|
|
|
''' list(mountpoint, size, fstype, partition_path) '''
|
2015-07-10 20:58:25 +00:00
|
|
|
fs_table = []
|
2015-08-27 19:26:37 +00:00
|
|
|
for (num, part) in self.disk.partitions.items():
|
|
|
|
partpath = "{}{}".format(self.disk.devpath, part.partnum)
|
|
|
|
if partpath in self.filesystems:
|
|
|
|
fs = self.filesystems[partpath]
|
|
|
|
mntpoint = self._mounts.get(partpath, fs.fstype)
|
2015-07-10 20:58:25 +00:00
|
|
|
fs_table.append(
|
2015-08-27 19:26:37 +00:00
|
|
|
(mntpoint, part.size, fs.fstype, partpath))
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-10-26 14:57:19 +00:00
|
|
|
# /dev/md0 as ext4 mounted at /storage
|
|
|
|
# /dev/md1 as xfs not mounted ?
|
|
|
|
for (dev, fs) in self.filesystems.items():
|
|
|
|
if dev not in self.partnames:
|
|
|
|
mntpoint = self._mounts.get(dev, fs.fstype)
|
|
|
|
fs_table.append(
|
|
|
|
(mntpoint, self.size, fs.fstype, dev))
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
return fs_table
|
|
|
|
|
|
|
|
|
2015-10-02 00:03:54 +00:00
|
|
|
class Raiddev(Blockdev):
|
|
|
|
def __init__(self, devpath, serial, model, parttype, size,
|
|
|
|
raid_level, raid_devices, spare_devices):
|
|
|
|
super().__init__(devpath, serial, model, parttype, size)
|
|
|
|
self._raid_devices = raid_devices
|
|
|
|
self._raid_level = raid_level
|
|
|
|
self._spare_devices = spare_devices
|
|
|
|
self.baseaction = RaidAction(os.path.basename(self.disk.devpath),
|
|
|
|
self._raid_devices,
|
|
|
|
self._raid_level,
|
|
|
|
self._spare_devices)
|
|
|
|
|
2015-11-02 22:48:37 +00:00
|
|
|
|
2015-12-01 19:30:53 +00:00
|
|
|
class LVMDev(Blockdev):
|
|
|
|
def __init__(self, devpath, serial, model, parttype, size,
|
|
|
|
volgroup, devices):
|
|
|
|
super().__init__(devpath, serial, model, parttype, size)
|
|
|
|
self._volgroup = volgroup
|
|
|
|
self._devices = devices
|
|
|
|
self.baseaction = (
|
|
|
|
LVMVolGroupAction(os.path.basename(self.disk.devpath),
|
|
|
|
self._volgroup, self._devices))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def volgroup(self):
|
|
|
|
return self.baseaction.volgroup
|
|
|
|
|
|
|
|
@property
|
|
|
|
def devices(self):
|
|
|
|
return self.baseaction.devices
|
|
|
|
|
|
|
|
|
2015-11-13 16:59:38 +00:00
|
|
|
class Bcachedev(Blockdev):
|
|
|
|
def __init__(self, devpath, serial, model, parttype, size,
|
|
|
|
backing_device, cache_device):
|
|
|
|
super().__init__(devpath, serial, model, parttype, size)
|
|
|
|
self._backing_device = backing_device
|
|
|
|
self._cache_device = cache_device
|
|
|
|
self.baseaction = BcacheAction(os.path.basename(self.disk.devpath),
|
2016-01-04 13:26:22 +00:00
|
|
|
self._backing_device.id,
|
|
|
|
self._cache_device.id)
|
2015-11-13 16:59:38 +00:00
|
|
|
|
2015-11-17 21:02:23 +00:00
|
|
|
@property
|
|
|
|
def cache_device(self):
|
2016-01-04 13:26:22 +00:00
|
|
|
return self._cache_device.devpath
|
2015-11-17 21:02:23 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def backing_device(self):
|
2016-01-04 13:26:22 +00:00
|
|
|
return self._backing_device.devpath
|
2015-11-17 21:02:23 +00:00
|
|
|
|
2015-11-13 16:59:38 +00:00
|
|
|
|
2015-10-26 14:57:19 +00:00
|
|
|
def sort_actions(actions):
|
|
|
|
def type_index(t):
|
2015-12-01 19:30:53 +00:00
|
|
|
order = ['disk', 'partition', 'raid', 'bcache', 'lvm_volgroup',
|
|
|
|
'lvm_partition', 'format', 'mount']
|
2015-10-26 14:57:19 +00:00
|
|
|
return order.index(t.get('type'))
|
|
|
|
|
|
|
|
def path_count(p):
|
2015-10-26 19:09:03 +00:00
|
|
|
if p.get('path') == "/":
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return p.get('path').count('/')
|
2015-10-26 14:57:19 +00:00
|
|
|
|
|
|
|
def order_sort(a):
|
|
|
|
# sort by type first
|
|
|
|
score = type_index(a)
|
|
|
|
# for type==mount, count the number of dirs
|
|
|
|
if a.get('type') == 'mount':
|
|
|
|
score += path_count(a)
|
2015-11-02 22:48:37 +00:00
|
|
|
elif (a.get('type') == 'partition' and
|
|
|
|
a.get('id').startswith('md')):
|
2015-10-26 14:57:19 +00:00
|
|
|
score += 2
|
|
|
|
log.debug('a={} score={}'.format(a, score))
|
|
|
|
return score
|
|
|
|
|
|
|
|
log.debug(actions)
|
|
|
|
actions = sorted(actions, key=order_sort)
|
|
|
|
log.debug('sorted')
|
|
|
|
log.debug(actions)
|
|
|
|
|
|
|
|
return actions
|
|
|
|
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
def get_filesystems(devices):
|
|
|
|
print("FILE SYSTEM")
|
|
|
|
for dev in devices:
|
|
|
|
for mnt, size, fstype, path in dev.get_fs_table():
|
|
|
|
print("{}\t\t{} Gb\t{}\t{}".format(mnt, size, fstype, path))
|
|
|
|
|
|
|
|
def get_used_disks(devices):
|
|
|
|
print("USED DISKS")
|
|
|
|
|
|
|
|
devices = []
|
2015-12-04 15:15:49 +00:00
|
|
|
# Blockdev(devpath, serial, model, parttype='gpt'):
|
2015-08-27 19:26:37 +00:00
|
|
|
GB = 1 << 30
|
|
|
|
sda = Blockdev('/dev/sda', 'QM_TARGET_01', 'QEMU SSD DISK',
|
|
|
|
parttype='gpt', size=128 * GB)
|
|
|
|
sdb = Blockdev('/dev/sdb', 'dafunk', 'QEMU SPINNER', size=500 * GB)
|
2015-07-10 20:58:25 +00:00
|
|
|
|
2015-08-27 19:26:37 +00:00
|
|
|
print(sda.freespace)
|
2015-07-22 01:15:22 +00:00
|
|
|
sda.add_partition(1, 8 * 1024 * 1024 * 1024, 'ext4', '/', 'bios_grub')
|
2015-08-27 19:26:37 +00:00
|
|
|
print(sda.freespace)
|
2015-07-22 01:15:22 +00:00
|
|
|
sda.add_partition(2, 2 * 1024 * 1024 * 1024, 'ext4', '/home')
|
2015-08-27 19:26:37 +00:00
|
|
|
print(sda.freespace)
|
2015-07-10 20:58:25 +00:00
|
|
|
sdb.add_partition(1, 50 * 1024 * 1024 * 1024, 'btrfs', '/opt')
|
|
|
|
|
|
|
|
get_filesystems([sda, sdb])
|
|
|
|
print()
|
2015-07-22 01:15:22 +00:00
|
|
|
HEADER = '''
|
|
|
|
reporter:
|
|
|
|
subiquity:
|
|
|
|
path: /tmp/curtin_progress_subiquity
|
|
|
|
progress: True
|
|
|
|
|
|
|
|
partitioning_commands:
|
|
|
|
builtin: curtin block-meta custom
|
|
|
|
'''
|
|
|
|
print(HEADER)
|
|
|
|
|
2015-07-10 20:58:25 +00:00
|
|
|
actions = {'storage': sda.get_actions() + sdb.get_actions()}
|
|
|
|
print(yaml.dump(actions, default_flow_style=False))
|