Merge "Split objects/partition.py into smaller files"
This commit is contained in:
commit
7629d78abd
@ -11,7 +11,6 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from fuel_agent.objects.bootloader import Grub
|
||||
from fuel_agent.objects.configdrive import ConfigDriveCommon
|
||||
from fuel_agent.objects.configdrive import ConfigDriveMcollective
|
||||
@ -22,22 +21,49 @@ from fuel_agent.objects.image import Image
|
||||
from fuel_agent.objects.image import ImageScheme
|
||||
from fuel_agent.objects.operating_system import OperatingSystem
|
||||
from fuel_agent.objects.operating_system import Ubuntu
|
||||
from fuel_agent.objects.partition import FS
|
||||
from fuel_agent.objects.partition import LV
|
||||
from fuel_agent.objects.partition import MD
|
||||
from fuel_agent.objects.partition import Parted
|
||||
from fuel_agent.objects.partition import Partition
|
||||
from fuel_agent.objects.partition import PartitionScheme
|
||||
from fuel_agent.objects.partition import PV
|
||||
from fuel_agent.objects.partition import VG
|
||||
from fuel_agent.objects.partition.fs import FileSystem
|
||||
from fuel_agent.objects.partition.lv import LogicalVolume
|
||||
from fuel_agent.objects.partition.md import MultipleDevice
|
||||
from fuel_agent.objects.partition.parted import Parted
|
||||
from fuel_agent.objects.partition.parted import Partition
|
||||
from fuel_agent.objects.partition.pv import PhysicalVolume
|
||||
from fuel_agent.objects.partition.scheme import PartitionScheme
|
||||
from fuel_agent.objects.partition.vg import VolumeGroup
|
||||
from fuel_agent.objects.repo import DEBRepo
|
||||
from fuel_agent.objects.repo import Repo
|
||||
|
||||
|
||||
PV = PhysicalVolume
|
||||
VG = VolumeGroup
|
||||
LV = LogicalVolume
|
||||
MD = MultipleDevice
|
||||
FS = FileSystem
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Partition', 'Parted', 'PV', 'VG', 'LV', 'MD', 'FS', 'PartitionScheme',
|
||||
'ConfigDriveCommon', 'ConfigDrivePuppet', 'ConfigDriveMcollective',
|
||||
'ConfigDriveScheme', 'Image', 'ImageScheme', 'Grub',
|
||||
'OperatingSystem', 'Ubuntu',
|
||||
'Repo', 'DEBRepo',
|
||||
'Partition',
|
||||
'Parted',
|
||||
'PhysicalVolume',
|
||||
'PV',
|
||||
'VolumeGroup',
|
||||
'VG',
|
||||
'LogicalVolume',
|
||||
'LV',
|
||||
'MultipleDevice',
|
||||
'MD',
|
||||
'FileSystem',
|
||||
'FS',
|
||||
'PartitionScheme',
|
||||
'ConfigDriveCommon',
|
||||
'ConfigDrivePuppet',
|
||||
'ConfigDriveMcollective',
|
||||
'ConfigDriveScheme',
|
||||
'Image',
|
||||
'ImageScheme',
|
||||
'Grub',
|
||||
'OperatingSystem',
|
||||
'Ubuntu',
|
||||
'Repo',
|
||||
'DEBRepo',
|
||||
'Loop',
|
||||
]
|
||||
|
@ -1,532 +0,0 @@
|
||||
# Copyright 2014 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects import base
|
||||
from fuel_agent.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Parted(base.Serializable):
|
||||
|
||||
def __init__(self, name, label, partitions=None, install_bootloader=False):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.partitions = partitions or []
|
||||
self.install_bootloader = install_bootloader
|
||||
|
||||
def add_partition(self, **kwargs):
|
||||
# TODO(kozhukalov): validate before appending
|
||||
# calculating partition name based on device name and partition count
|
||||
kwargs['name'] = self.next_name()
|
||||
kwargs['count'] = self.next_count()
|
||||
kwargs['device'] = self.name
|
||||
# if begin is given use its value else use end of last partition
|
||||
kwargs['begin'] = kwargs.get('begin', self.next_begin())
|
||||
# if end is given use its value else
|
||||
# try to calculate it based on size kwarg or
|
||||
# raise KeyError
|
||||
# (kwargs.pop['size'] will raise error if size is not set)
|
||||
kwargs['end'] = kwargs.get('end') or \
|
||||
kwargs['begin'] + kwargs.pop('size')
|
||||
# if partition_type is given use its value else
|
||||
# try to calculate it automatically
|
||||
kwargs['partition_type'] = \
|
||||
kwargs.get('partition_type', self.next_type())
|
||||
partition = Partition(**kwargs)
|
||||
self.partitions.append(partition)
|
||||
return partition
|
||||
|
||||
@property
|
||||
def logical(self):
|
||||
return filter(lambda x: x.type == 'logical', self.partitions)
|
||||
|
||||
@property
|
||||
def primary(self):
|
||||
return filter(lambda x: x.type == 'primary', self.partitions)
|
||||
|
||||
@property
|
||||
def extended(self):
|
||||
found = filter(lambda x: x.type == 'extended', self.partitions)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def next_type(self):
|
||||
if self.label == 'gpt':
|
||||
return 'primary'
|
||||
elif self.label == 'msdos':
|
||||
if self.extended:
|
||||
return 'logical'
|
||||
elif len(self.partitions) < 3 and not self.extended:
|
||||
return 'primary'
|
||||
elif len(self.partitions) == 3 and not self.extended:
|
||||
return 'extended'
|
||||
# NOTE(agordeev): how to reach that condition?
|
||||
else:
|
||||
return 'logical'
|
||||
|
||||
def next_count(self, next_type=None):
|
||||
next_type = next_type or self.next_type()
|
||||
if next_type == 'logical':
|
||||
return len(self.logical) + 5
|
||||
return len(self.partitions) + 1
|
||||
|
||||
def next_begin(self):
|
||||
if not self.partitions:
|
||||
return 1
|
||||
if self.partitions[-1] == self.extended:
|
||||
return self.partitions[-1].begin
|
||||
return self.partitions[-1].end
|
||||
|
||||
def next_name(self):
|
||||
if self.next_type() == 'extended':
|
||||
return None
|
||||
separator = ''
|
||||
special_devices = ('cciss', 'nvme', 'loop')
|
||||
if any(n in self.name for n in special_devices):
|
||||
separator = 'p'
|
||||
return '%s%s%s' % (self.name, separator, self.next_count())
|
||||
|
||||
def partition_by_name(self, name):
|
||||
found = filter(lambda x: (x.name == name), self.partitions)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def to_dict(self):
|
||||
partitions = [partition.to_dict() for partition in self.partitions]
|
||||
return {
|
||||
'name': self.name,
|
||||
'label': self.label,
|
||||
'partitions': partitions,
|
||||
'install_bootloader': self.install_bootloader,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
data = copy.deepcopy(data)
|
||||
raw_partitions = data.pop('partitions')
|
||||
partitions = [Partition.from_dict(partition)
|
||||
for partition in raw_partitions]
|
||||
return cls(partitions=partitions, **data)
|
||||
|
||||
|
||||
class Partition(base.Serializable):
|
||||
|
||||
def __init__(self, name, count, device, begin, end, partition_type,
|
||||
flags=None, guid=None, configdrive=False, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.count = count
|
||||
self.device = device
|
||||
self.begin = begin
|
||||
self.end = end
|
||||
self.type = partition_type
|
||||
self.flags = flags or []
|
||||
self.guid = guid
|
||||
self.configdrive = configdrive
|
||||
|
||||
def set_flag(self, flag):
|
||||
if flag not in self.flags:
|
||||
self.flags.append(flag)
|
||||
|
||||
def set_guid(self, guid):
|
||||
self.guid = guid
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'count': self.count,
|
||||
'device': self.device,
|
||||
'begin': self.begin,
|
||||
'end': self.end,
|
||||
'partition_type': self.type,
|
||||
'flags': self.flags,
|
||||
'guid': self.guid,
|
||||
'configdrive': self.configdrive,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
class PhysicalVolume(base.Serializable):
|
||||
|
||||
def __init__(self, name, metadatasize=16,
|
||||
metadatacopies=2, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.metadatasize = metadatasize
|
||||
self.metadatacopies = metadatacopies
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'metadatasize': self.metadatasize,
|
||||
'metadatacopies': self.metadatacopies,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
PV = PhysicalVolume
|
||||
|
||||
|
||||
class VolumeGroup(base.Serializable):
|
||||
|
||||
def __init__(self, name, pvnames=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.pvnames = pvnames or []
|
||||
|
||||
def add_pv(self, pvname):
|
||||
if pvname not in self.pvnames:
|
||||
self.pvnames.append(pvname)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'pvnames': self.pvnames,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
VG = VolumeGroup
|
||||
|
||||
|
||||
class LogicalVolume(base.Serializable):
|
||||
|
||||
def __init__(self, name, vgname, size, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.vgname = vgname
|
||||
self.size = size
|
||||
|
||||
@property
|
||||
def device_name(self):
|
||||
return '/dev/mapper/%s-%s' % (self.vgname.replace('-', '--'),
|
||||
self.name.replace('-', '--'))
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'vgname': self.vgname,
|
||||
'size': self.size,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
LV = LogicalVolume
|
||||
|
||||
|
||||
class MultipleDevice(base.Serializable):
|
||||
|
||||
def __init__(self, name, level,
|
||||
devices=None, spares=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.level = level
|
||||
self.devices = devices or []
|
||||
self.spares = spares or []
|
||||
|
||||
def add_device(self, device):
|
||||
if device in self.devices or device in self.spares:
|
||||
raise errors.MDDeviceDuplicationError(
|
||||
'Error while attaching device to md: '
|
||||
'device %s is already attached' % device)
|
||||
self.devices.append(device)
|
||||
|
||||
def add_spare(self, device):
|
||||
if device in self.devices or device in self.spares:
|
||||
raise errors.MDDeviceDuplicationError(
|
||||
'Error while attaching device to md: '
|
||||
'device %s is already attached' % device)
|
||||
self.spares.append(device)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'level': self.level,
|
||||
'devices': self.devices,
|
||||
'spares': self.spares,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
MD = MultipleDevice
|
||||
|
||||
|
||||
class FileSystem(base.Serializable):
|
||||
|
||||
def __init__(self, device, mount=None, fs_type=None,
|
||||
fs_options=None, fs_label=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.device = device
|
||||
self.mount = mount
|
||||
self.type = fs_type or 'xfs'
|
||||
self.options = fs_options or ''
|
||||
self.label = fs_label or ''
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'device': self.device,
|
||||
'mount': self.mount,
|
||||
'fs_type': self.type,
|
||||
'fs_options': self.options,
|
||||
'fs_label': self.label,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
||||
|
||||
|
||||
FS = FileSystem
|
||||
|
||||
|
||||
class PartitionScheme(object):
|
||||
def __init__(self):
|
||||
self.parteds = []
|
||||
self.mds = []
|
||||
self.pvs = []
|
||||
self.vgs = []
|
||||
self.lvs = []
|
||||
self.fss = []
|
||||
|
||||
def add_parted(self, **kwargs):
|
||||
parted = Parted(**kwargs)
|
||||
self.parteds.append(parted)
|
||||
return parted
|
||||
|
||||
def add_pv(self, **kwargs):
|
||||
pv = PV(**kwargs)
|
||||
self.pvs.append(pv)
|
||||
return pv
|
||||
|
||||
def add_vg(self, **kwargs):
|
||||
vg = VG(**kwargs)
|
||||
self.vgs.append(vg)
|
||||
return vg
|
||||
|
||||
def add_lv(self, **kwargs):
|
||||
lv = LV(**kwargs)
|
||||
self.lvs.append(lv)
|
||||
return lv
|
||||
|
||||
def add_fs(self, **kwargs):
|
||||
fs = FS(**kwargs)
|
||||
self.fss.append(fs)
|
||||
return fs
|
||||
|
||||
def add_md(self, **kwargs):
|
||||
mdkwargs = {}
|
||||
mdkwargs['name'] = kwargs.get('name') or self.md_next_name()
|
||||
mdkwargs['level'] = kwargs.get('level') or 'mirror'
|
||||
md = MD(**mdkwargs)
|
||||
self.mds.append(md)
|
||||
return md
|
||||
|
||||
def md_by_name(self, name):
|
||||
found = filter(lambda x: x.name == name, self.mds)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def md_by_mount(self, mount):
|
||||
fs = self.fs_by_mount(mount)
|
||||
if fs:
|
||||
return self.md_by_name(fs.device)
|
||||
|
||||
def md_attach_by_mount(self, device, mount, spare=False, **kwargs):
|
||||
md = self.md_by_mount(mount)
|
||||
if not md:
|
||||
md = self.add_md(**kwargs)
|
||||
fskwargs = {}
|
||||
fskwargs['device'] = md.name
|
||||
fskwargs['mount'] = mount
|
||||
fskwargs['fs_type'] = kwargs.pop('fs_type', None)
|
||||
fskwargs['fs_options'] = kwargs.pop('fs_options', None)
|
||||
fskwargs['fs_label'] = kwargs.pop('fs_label', None)
|
||||
self.add_fs(**fskwargs)
|
||||
md.add_spare(device) if spare else md.add_device(device)
|
||||
return md
|
||||
|
||||
def md_next_name(self):
|
||||
count = 0
|
||||
while True:
|
||||
name = '/dev/md%s' % count
|
||||
if name not in [md.name for md in self.mds]:
|
||||
return name
|
||||
if count >= 127:
|
||||
raise errors.MDAlreadyExistsError(
|
||||
'Error while generating md name: '
|
||||
'names from /dev/md0 to /dev/md127 seem to be busy, '
|
||||
'try to generate md name manually')
|
||||
count += 1
|
||||
|
||||
def partition_by_name(self, name):
|
||||
return next((parted.partition_by_name(name)
|
||||
for parted in self.parteds
|
||||
if parted.partition_by_name(name)), None)
|
||||
|
||||
def vg_by_name(self, vgname):
|
||||
found = filter(lambda x: (x.name == vgname), self.vgs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def pv_by_name(self, pvname):
|
||||
found = filter(lambda x: (x.name == pvname), self.pvs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def vg_attach_by_name(self, pvname, vgname,
|
||||
metadatasize=16, metadatacopies=2):
|
||||
vg = self.vg_by_name(vgname) or self.add_vg(name=vgname)
|
||||
pv = self.pv_by_name(pvname) or self.add_pv(
|
||||
name=pvname, metadatasize=metadatasize,
|
||||
metadatacopies=metadatacopies)
|
||||
vg.add_pv(pv.name)
|
||||
|
||||
def fs_by_mount(self, mount):
|
||||
found = filter(lambda x: (x.mount and x.mount == mount), self.fss)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def fs_by_device(self, device):
|
||||
found = filter(lambda x: x.device == device, self.fss)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def fs_sorted_by_depth(self, reverse=False):
|
||||
"""Getting file systems sorted by path length.
|
||||
|
||||
Shorter paths earlier.
|
||||
['/', '/boot', '/var', '/var/lib/mysql']
|
||||
:param reverse: Sort backward (Default: False)
|
||||
"""
|
||||
def key(x):
|
||||
return x.mount.rstrip(os.path.sep).count(os.path.sep)
|
||||
return sorted(self.fss, key=key, reverse=reverse)
|
||||
|
||||
def lv_by_device_name(self, device_name):
|
||||
found = filter(lambda x: x.device_name == device_name, self.lvs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def root_device(self):
|
||||
fs = self.fs_by_mount('/')
|
||||
if not fs:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find root device: '
|
||||
'root file system not found')
|
||||
return fs.device
|
||||
|
||||
def boot_device(self, grub_version=2):
|
||||
# We assume /boot is a separate partition. If it is not
|
||||
# then we try to use root file system
|
||||
boot_fs = self.fs_by_mount('/boot') or self.fs_by_mount('/')
|
||||
if not boot_fs:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'boot file system not fount, '
|
||||
'it must be a separate mount point')
|
||||
|
||||
if grub_version == 1:
|
||||
# Legacy GRUB has a limitation. It is not able to mount MD devices.
|
||||
# If it is MD compatible it is only able to ignore MD metadata
|
||||
# and to mount one of those devices which are parts of MD device,
|
||||
# but it is possible only if MD device is a MIRROR.
|
||||
md = self.md_by_name(boot_fs.device)
|
||||
if md:
|
||||
try:
|
||||
return md.devices[0]
|
||||
except IndexError:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'md device %s does not have devices attached' %
|
||||
md.name)
|
||||
# Legacy GRUB is not able to mount LVM devices.
|
||||
if self.lv_by_device_name(boot_fs.device):
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'found device is %s but legacy grub is not able to '
|
||||
'mount logical volumes' %
|
||||
boot_fs.device)
|
||||
|
||||
return boot_fs.device
|
||||
|
||||
def configdrive_device(self):
|
||||
# Configdrive device must be a small (about 10M) partition
|
||||
# on one of node hard drives. This partition is necessary
|
||||
# only if one uses cloud-init with configdrive.
|
||||
for parted in self.parteds:
|
||||
for prt in parted.partitions:
|
||||
if prt.configdrive:
|
||||
return prt.name
|
||||
|
||||
def elevate_keep_data(self):
|
||||
LOG.debug('Elevate keep_data flag from partitions')
|
||||
|
||||
for vg in self.vgs:
|
||||
for pvname in vg.pvnames:
|
||||
partition = self.partition_by_name(pvname)
|
||||
if partition and partition.keep_data:
|
||||
partition.keep_data = False
|
||||
vg.keep_data = True
|
||||
LOG.debug('Set keep_data to vg=%s' % vg.name)
|
||||
|
||||
for lv in self.lvs:
|
||||
vg = self.vg_by_name(lv.vgname)
|
||||
if vg.keep_data:
|
||||
lv.keep_data = True
|
||||
|
||||
# Need to loop over lv again to remove keep flag from vg
|
||||
for lv in self.lvs:
|
||||
vg = self.vg_by_name(lv.vgname)
|
||||
if vg.keep_data and lv.keep_data:
|
||||
vg.keep_data = False
|
||||
|
||||
for fs in self.fss:
|
||||
lv = self.lv_by_device_name(fs.device)
|
||||
if lv:
|
||||
if lv.keep_data:
|
||||
lv.keep_data = False
|
||||
fs.keep_data = True
|
||||
LOG.debug('Set keep_data to fs=%s from lv=%s' %
|
||||
(fs.mount, lv.name))
|
||||
continue
|
||||
partition = self.partition_by_name(fs.device)
|
||||
if partition and partition.keep_data:
|
||||
partition.keep_data = False
|
||||
fs.keep_data = True
|
||||
LOG.debug('Set keep flag to fs=%s from partition=%s' %
|
||||
(fs.mount, partition.name))
|
||||
|
||||
@property
|
||||
def skip_partitioning(self):
|
||||
if any(fs.keep_data for fs in self.fss):
|
||||
return True
|
||||
if any(lv.keep_data for lv in self.lvs):
|
||||
return True
|
||||
if any(vg.keep_data for vg in self.vgs):
|
||||
return True
|
||||
for parted in self.parteds:
|
||||
if any(prt.keep_data for prt in parted.partitions):
|
||||
return True
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'parteds': [parted.to_dict() for parted in self.parteds],
|
||||
'mds': [md.to_dict() for md in self.mds],
|
||||
'pvs': [pv.to_dict() for pv in self.pvs],
|
||||
'vgs': [vg.to_dict() for vg in self.vgs],
|
||||
'lvs': [lv.to_dict() for lv in self.lvs],
|
||||
'fss': [fs.to_dict() for fs in self.fss],
|
||||
}
|
0
fuel_agent/objects/partition/__init__.py
Normal file
0
fuel_agent/objects/partition/__init__.py
Normal file
38
fuel_agent/objects/partition/fs.py
Normal file
38
fuel_agent/objects/partition/fs.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from fuel_agent.objects import base
|
||||
|
||||
|
||||
class FileSystem(base.Serializable):
|
||||
|
||||
def __init__(self, device, mount=None, fs_type=None,
|
||||
fs_options=None, fs_label=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.device = device
|
||||
self.mount = mount
|
||||
self.type = fs_type or 'xfs'
|
||||
self.options = fs_options or ''
|
||||
self.label = fs_label or ''
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'device': self.device,
|
||||
'mount': self.mount,
|
||||
'fs_type': self.type,
|
||||
'fs_options': self.options,
|
||||
'fs_label': self.label,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
38
fuel_agent/objects/partition/lv.py
Normal file
38
fuel_agent/objects/partition/lv.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from fuel_agent.objects import base
|
||||
|
||||
|
||||
class LogicalVolume(base.Serializable):
|
||||
|
||||
def __init__(self, name, vgname, size, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.vgname = vgname
|
||||
self.size = size
|
||||
|
||||
@property
|
||||
def device_name(self):
|
||||
return '/dev/mapper/%s-%s' % (self.vgname.replace('-', '--'),
|
||||
self.name.replace('-', '--'))
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'vgname': self.vgname,
|
||||
'size': self.size,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
51
fuel_agent/objects/partition/md.py
Normal file
51
fuel_agent/objects/partition/md.py
Normal file
@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects import base
|
||||
|
||||
|
||||
class MultipleDevice(base.Serializable):
|
||||
|
||||
def __init__(self, name, level,
|
||||
devices=None, spares=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.level = level
|
||||
self.devices = devices or []
|
||||
self.spares = spares or []
|
||||
|
||||
def add_device(self, device):
|
||||
if device in self.devices or device in self.spares:
|
||||
raise errors.MDDeviceDuplicationError(
|
||||
'Error while attaching device to md: '
|
||||
'device %s is already attached' % device)
|
||||
self.devices.append(device)
|
||||
|
||||
def add_spare(self, device):
|
||||
if device in self.devices or device in self.spares:
|
||||
raise errors.MDDeviceDuplicationError(
|
||||
'Error while attaching device to md: '
|
||||
'device %s is already attached' % device)
|
||||
self.spares.append(device)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'level': self.level,
|
||||
'devices': self.devices,
|
||||
'spares': self.spares,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
163
fuel_agent/objects/partition/parted.py
Normal file
163
fuel_agent/objects/partition/parted.py
Normal file
@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from fuel_agent.objects import base
|
||||
from fuel_agent.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Parted(base.Serializable):
|
||||
|
||||
def __init__(self, name, label, partitions=None, install_bootloader=False):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.partitions = partitions or []
|
||||
self.install_bootloader = install_bootloader
|
||||
|
||||
def add_partition(self, **kwargs):
|
||||
# TODO(kozhukalov): validate before appending
|
||||
# calculating partition name based on device name and partition count
|
||||
kwargs['name'] = self.next_name()
|
||||
kwargs['count'] = self.next_count()
|
||||
kwargs['device'] = self.name
|
||||
# if begin is given use its value else use end of last partition
|
||||
kwargs['begin'] = kwargs.get('begin', self.next_begin())
|
||||
# if end is given use its value else
|
||||
# try to calculate it based on size kwarg or
|
||||
# raise KeyError
|
||||
# (kwargs.pop['size'] will raise error if size is not set)
|
||||
kwargs['end'] = kwargs.get('end') or \
|
||||
kwargs['begin'] + kwargs.pop('size')
|
||||
# if partition_type is given use its value else
|
||||
# try to calculate it automatically
|
||||
kwargs['partition_type'] = \
|
||||
kwargs.get('partition_type', self.next_type())
|
||||
partition = Partition(**kwargs)
|
||||
self.partitions.append(partition)
|
||||
return partition
|
||||
|
||||
@property
|
||||
def logical(self):
|
||||
return filter(lambda x: x.type == 'logical', self.partitions)
|
||||
|
||||
@property
|
||||
def primary(self):
|
||||
return filter(lambda x: x.type == 'primary', self.partitions)
|
||||
|
||||
@property
|
||||
def extended(self):
|
||||
found = filter(lambda x: x.type == 'extended', self.partitions)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def next_type(self):
|
||||
if self.label == 'gpt':
|
||||
return 'primary'
|
||||
elif self.label == 'msdos':
|
||||
if self.extended:
|
||||
return 'logical'
|
||||
elif len(self.partitions) < 3 and not self.extended:
|
||||
return 'primary'
|
||||
elif len(self.partitions) == 3 and not self.extended:
|
||||
return 'extended'
|
||||
# NOTE(agordeev): how to reach that condition?
|
||||
else:
|
||||
return 'logical'
|
||||
|
||||
def next_count(self, next_type=None):
|
||||
next_type = next_type or self.next_type()
|
||||
if next_type == 'logical':
|
||||
return len(self.logical) + 5
|
||||
return len(self.partitions) + 1
|
||||
|
||||
def next_begin(self):
|
||||
if not self.partitions:
|
||||
return 1
|
||||
if self.partitions[-1] == self.extended:
|
||||
return self.partitions[-1].begin
|
||||
return self.partitions[-1].end
|
||||
|
||||
def next_name(self):
|
||||
if self.next_type() == 'extended':
|
||||
return None
|
||||
separator = ''
|
||||
special_devices = ('cciss', 'nvme', 'loop')
|
||||
if any(n in self.name for n in special_devices):
|
||||
separator = 'p'
|
||||
return '%s%s%s' % (self.name, separator, self.next_count())
|
||||
|
||||
def partition_by_name(self, name):
|
||||
found = filter(lambda x: (x.name == name), self.partitions)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def to_dict(self):
|
||||
partitions = [partition.to_dict() for partition in self.partitions]
|
||||
return {
|
||||
'name': self.name,
|
||||
'label': self.label,
|
||||
'partitions': partitions,
|
||||
'install_bootloader': self.install_bootloader,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
data = copy.deepcopy(data)
|
||||
raw_partitions = data.pop('partitions')
|
||||
partitions = [Partition.from_dict(partition)
|
||||
for partition in raw_partitions]
|
||||
return cls(partitions=partitions, **data)
|
||||
|
||||
|
||||
class Partition(base.Serializable):
|
||||
|
||||
def __init__(self, name, count, device, begin, end, partition_type,
|
||||
flags=None, guid=None, configdrive=False, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.count = count
|
||||
self.device = device
|
||||
self.begin = begin
|
||||
self.end = end
|
||||
self.type = partition_type
|
||||
self.flags = flags or []
|
||||
self.guid = guid
|
||||
self.configdrive = configdrive
|
||||
|
||||
def set_flag(self, flag):
|
||||
if flag not in self.flags:
|
||||
self.flags.append(flag)
|
||||
|
||||
def set_guid(self, guid):
|
||||
self.guid = guid
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'count': self.count,
|
||||
'device': self.device,
|
||||
'begin': self.begin,
|
||||
'end': self.end,
|
||||
'partition_type': self.type,
|
||||
'flags': self.flags,
|
||||
'guid': self.guid,
|
||||
'configdrive': self.configdrive,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
34
fuel_agent/objects/partition/pv.py
Normal file
34
fuel_agent/objects/partition/pv.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from fuel_agent.objects import base
|
||||
|
||||
|
||||
class PhysicalVolume(base.Serializable):
|
||||
|
||||
def __init__(self, name, metadatasize=16,
|
||||
metadatacopies=2, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.metadatasize = metadatasize
|
||||
self.metadatacopies = metadatacopies
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'metadatasize': self.metadatasize,
|
||||
'metadatacopies': self.metadatacopies,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
269
fuel_agent/objects/partition/scheme.py
Normal file
269
fuel_agent/objects/partition/scheme.py
Normal file
@ -0,0 +1,269 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import os
|
||||
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects.partition.fs import FileSystem
|
||||
from fuel_agent.objects.partition.lv import LogicalVolume
|
||||
from fuel_agent.objects.partition.md import MultipleDevice
|
||||
from fuel_agent.objects.partition.parted import Parted
|
||||
from fuel_agent.objects.partition.pv import PhysicalVolume
|
||||
from fuel_agent.objects.partition.vg import VolumeGroup
|
||||
|
||||
from fuel_agent.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PartitionScheme(object):
|
||||
def __init__(self):
|
||||
self.parteds = []
|
||||
self.mds = []
|
||||
self.pvs = []
|
||||
self.vgs = []
|
||||
self.lvs = []
|
||||
self.fss = []
|
||||
|
||||
def add_parted(self, **kwargs):
|
||||
parted = Parted(**kwargs)
|
||||
self.parteds.append(parted)
|
||||
return parted
|
||||
|
||||
def add_pv(self, **kwargs):
|
||||
pv = PhysicalVolume(**kwargs)
|
||||
self.pvs.append(pv)
|
||||
return pv
|
||||
|
||||
def add_vg(self, **kwargs):
|
||||
vg = VolumeGroup(**kwargs)
|
||||
self.vgs.append(vg)
|
||||
return vg
|
||||
|
||||
def add_lv(self, **kwargs):
|
||||
lv = LogicalVolume(**kwargs)
|
||||
self.lvs.append(lv)
|
||||
return lv
|
||||
|
||||
def add_fs(self, **kwargs):
|
||||
fs = FileSystem(**kwargs)
|
||||
self.fss.append(fs)
|
||||
return fs
|
||||
|
||||
def add_md(self, **kwargs):
|
||||
mdkwargs = {}
|
||||
mdkwargs['name'] = kwargs.get('name') or self.md_next_name()
|
||||
mdkwargs['level'] = kwargs.get('level') or 'mirror'
|
||||
md = MultipleDevice(**mdkwargs)
|
||||
self.mds.append(md)
|
||||
return md
|
||||
|
||||
def md_by_name(self, name):
|
||||
found = filter(lambda x: x.name == name, self.mds)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def md_by_mount(self, mount):
|
||||
fs = self.fs_by_mount(mount)
|
||||
if fs:
|
||||
return self.md_by_name(fs.device)
|
||||
|
||||
def md_attach_by_mount(self, device, mount, spare=False, **kwargs):
|
||||
md = self.md_by_mount(mount)
|
||||
if not md:
|
||||
md = self.add_md(**kwargs)
|
||||
fskwargs = {}
|
||||
fskwargs['device'] = md.name
|
||||
fskwargs['mount'] = mount
|
||||
fskwargs['fs_type'] = kwargs.pop('fs_type', None)
|
||||
fskwargs['fs_options'] = kwargs.pop('fs_options', None)
|
||||
fskwargs['fs_label'] = kwargs.pop('fs_label', None)
|
||||
self.add_fs(**fskwargs)
|
||||
md.add_spare(device) if spare else md.add_device(device)
|
||||
return md
|
||||
|
||||
def md_next_name(self):
|
||||
count = 0
|
||||
while True:
|
||||
name = '/dev/md%s' % count
|
||||
if name not in [md.name for md in self.mds]:
|
||||
return name
|
||||
if count >= 127:
|
||||
raise errors.MDAlreadyExistsError(
|
||||
'Error while generating md name: '
|
||||
'names from /dev/md0 to /dev/md127 seem to be busy, '
|
||||
'try to generate md name manually')
|
||||
count += 1
|
||||
|
||||
def partition_by_name(self, name):
|
||||
return next((parted.partition_by_name(name)
|
||||
for parted in self.parteds
|
||||
if parted.partition_by_name(name)), None)
|
||||
|
||||
def vg_by_name(self, vgname):
|
||||
found = filter(lambda x: (x.name == vgname), self.vgs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def pv_by_name(self, pvname):
|
||||
found = filter(lambda x: (x.name == pvname), self.pvs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def vg_attach_by_name(self, pvname, vgname,
|
||||
metadatasize=16, metadatacopies=2):
|
||||
vg = self.vg_by_name(vgname) or self.add_vg(name=vgname)
|
||||
pv = self.pv_by_name(pvname) or self.add_pv(
|
||||
name=pvname, metadatasize=metadatasize,
|
||||
metadatacopies=metadatacopies)
|
||||
vg.add_pv(pv.name)
|
||||
|
||||
def fs_by_mount(self, mount):
|
||||
found = filter(lambda x: (x.mount and x.mount == mount), self.fss)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def fs_by_device(self, device):
|
||||
found = filter(lambda x: x.device == device, self.fss)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def fs_sorted_by_depth(self, reverse=False):
|
||||
"""Getting file systems sorted by path length.
|
||||
|
||||
Shorter paths earlier.
|
||||
['/', '/boot', '/var', '/var/lib/mysql']
|
||||
:param reverse: Sort backward (Default: False)
|
||||
"""
|
||||
def key(x):
|
||||
return x.mount.rstrip(os.path.sep).count(os.path.sep)
|
||||
return sorted(self.fss, key=key, reverse=reverse)
|
||||
|
||||
def lv_by_device_name(self, device_name):
|
||||
found = filter(lambda x: x.device_name == device_name, self.lvs)
|
||||
if found:
|
||||
return found[0]
|
||||
|
||||
def root_device(self):
|
||||
fs = self.fs_by_mount('/')
|
||||
if not fs:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find root device: '
|
||||
'root file system not found')
|
||||
return fs.device
|
||||
|
||||
def boot_device(self, grub_version=2):
|
||||
# We assume /boot is a separate partition. If it is not
|
||||
# then we try to use root file system
|
||||
boot_fs = self.fs_by_mount('/boot') or self.fs_by_mount('/')
|
||||
if not boot_fs:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'boot file system not fount, '
|
||||
'it must be a separate mount point')
|
||||
|
||||
if grub_version == 1:
|
||||
# Legacy GRUB has a limitation. It is not able to mount MD devices.
|
||||
# If it is MD compatible it is only able to ignore MD metadata
|
||||
# and to mount one of those devices which are parts of MD device,
|
||||
# but it is possible only if MD device is a MIRROR.
|
||||
md = self.md_by_name(boot_fs.device)
|
||||
if md:
|
||||
try:
|
||||
return md.devices[0]
|
||||
except IndexError:
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'md device %s does not have devices attached' %
|
||||
md.name)
|
||||
# Legacy GRUB is not able to mount LVM devices.
|
||||
if self.lv_by_device_name(boot_fs.device):
|
||||
raise errors.WrongPartitionSchemeError(
|
||||
'Error while trying to find boot device: '
|
||||
'found device is %s but legacy grub is not able to '
|
||||
'mount logical volumes' %
|
||||
boot_fs.device)
|
||||
|
||||
return boot_fs.device
|
||||
|
||||
def configdrive_device(self):
|
||||
# Configdrive device must be a small (about 10M) partition
|
||||
# on one of node hard drives. This partition is necessary
|
||||
# only if one uses cloud-init with configdrive.
|
||||
for parted in self.parteds:
|
||||
for prt in parted.partitions:
|
||||
if prt.configdrive:
|
||||
return prt.name
|
||||
|
||||
def elevate_keep_data(self):
|
||||
LOG.debug('Elevate keep_data flag from partitions')
|
||||
|
||||
for vg in self.vgs:
|
||||
for pvname in vg.pvnames:
|
||||
partition = self.partition_by_name(pvname)
|
||||
if partition and partition.keep_data:
|
||||
partition.keep_data = False
|
||||
vg.keep_data = True
|
||||
LOG.debug('Set keep_data to vg=%s' % vg.name)
|
||||
|
||||
for lv in self.lvs:
|
||||
vg = self.vg_by_name(lv.vgname)
|
||||
if vg.keep_data:
|
||||
lv.keep_data = True
|
||||
|
||||
# Need to loop over lv again to remove keep flag from vg
|
||||
for lv in self.lvs:
|
||||
vg = self.vg_by_name(lv.vgname)
|
||||
if vg.keep_data and lv.keep_data:
|
||||
vg.keep_data = False
|
||||
|
||||
for fs in self.fss:
|
||||
lv = self.lv_by_device_name(fs.device)
|
||||
if lv:
|
||||
if lv.keep_data:
|
||||
lv.keep_data = False
|
||||
fs.keep_data = True
|
||||
LOG.debug('Set keep_data to fs=%s from lv=%s' %
|
||||
(fs.mount, lv.name))
|
||||
continue
|
||||
partition = self.partition_by_name(fs.device)
|
||||
if partition and partition.keep_data:
|
||||
partition.keep_data = False
|
||||
fs.keep_data = True
|
||||
LOG.debug('Set keep flag to fs=%s from partition=%s' %
|
||||
(fs.mount, partition.name))
|
||||
|
||||
@property
|
||||
def skip_partitioning(self):
|
||||
if any(fs.keep_data for fs in self.fss):
|
||||
return True
|
||||
if any(lv.keep_data for lv in self.lvs):
|
||||
return True
|
||||
if any(vg.keep_data for vg in self.vgs):
|
||||
return True
|
||||
for parted in self.parteds:
|
||||
if any(prt.keep_data for prt in parted.partitions):
|
||||
return True
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'parteds': [parted.to_dict() for parted in self.parteds],
|
||||
'mds': [md.to_dict() for md in self.mds],
|
||||
'pvs': [pv.to_dict() for pv in self.pvs],
|
||||
'vgs': [vg.to_dict() for vg in self.vgs],
|
||||
'lvs': [lv.to_dict() for lv in self.lvs],
|
||||
'fss': [fs.to_dict() for fs in self.fss],
|
||||
}
|
35
fuel_agent/objects/partition/vg.py
Normal file
35
fuel_agent/objects/partition/vg.py
Normal file
@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from fuel_agent.objects import base
|
||||
|
||||
|
||||
class VolumeGroup(base.Serializable):
|
||||
|
||||
def __init__(self, name, pvnames=None, keep_data=False):
|
||||
self.keep_data = keep_data
|
||||
self.name = name
|
||||
self.pvnames = pvnames or []
|
||||
|
||||
def add_pv(self, pvname):
|
||||
if pvname not in self.pvnames:
|
||||
self.pvnames.append(pvname)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'pvnames': self.pvnames,
|
||||
'keep_data': self.keep_data,
|
||||
}
|
@ -24,7 +24,6 @@ from fuel_agent.drivers import nailgun
|
||||
from fuel_agent import errors
|
||||
from fuel_agent import manager
|
||||
from fuel_agent import objects
|
||||
from fuel_agent.objects import partition
|
||||
from fuel_agent.tests import test_nailgun
|
||||
from fuel_agent.utils import artifact as au
|
||||
from fuel_agent.utils import fs as fu
|
||||
@ -315,7 +314,7 @@ class TestManager(test_base.BaseTestCase):
|
||||
|
||||
@mock.patch('yaml.load')
|
||||
@mock.patch.object(utils, 'init_http_request')
|
||||
@mock.patch.object(partition.PartitionScheme, 'configdrive_device')
|
||||
@mock.patch.object(objects.PartitionScheme, 'configdrive_device')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(utils, 'render_and_save')
|
||||
@mock.patch.object(hu, 'list_block_devices')
|
||||
|
@ -17,14 +17,14 @@ import mock
|
||||
import unittest2
|
||||
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects import partition
|
||||
from fuel_agent import objects
|
||||
|
||||
|
||||
class TestMultipleDevice(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.md = partition.MD(name='name', level='level')
|
||||
self.md = objects.MultipleDevice(name='name', level='level')
|
||||
|
||||
def test_add_device_ok(self):
|
||||
self.assertEqual(0, len(self.md.devices))
|
||||
@ -71,7 +71,7 @@ class TestMultipleDevice(unittest2.TestCase):
|
||||
'spares': ['device_b', ],
|
||||
'keep_data': False,
|
||||
}
|
||||
new_md = partition.MD.from_dict(serialized)
|
||||
new_md = objects.MultipleDevice.from_dict(serialized)
|
||||
assert serialized == new_md.to_dict()
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ class TestPartition(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartition, self).setUp()
|
||||
self.pt = partition.Partition('name', 'count', 'device', 'begin',
|
||||
self.pt = objects.Partition('name', 'count', 'device', 'begin',
|
||||
'end', 'partition_type')
|
||||
|
||||
def test_set_flag(self):
|
||||
@ -104,7 +104,7 @@ class TestPartition(unittest2.TestCase):
|
||||
'partition_type': 'partition_type',
|
||||
'keep_data': False,
|
||||
}
|
||||
new_pt = partition.Partition.from_dict(serialized)
|
||||
new_pt = objects.Partition.from_dict(serialized)
|
||||
assert serialized == new_pt.to_dict()
|
||||
|
||||
|
||||
@ -112,37 +112,38 @@ class TestPartitionScheme(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartitionScheme, self).setUp()
|
||||
self.p_scheme = partition.PartitionScheme()
|
||||
self.p_scheme = objects.PartitionScheme()
|
||||
|
||||
def test_root_device_not_found(self):
|
||||
self.assertRaises(errors.WrongPartitionSchemeError,
|
||||
self.p_scheme.root_device)
|
||||
|
||||
def test_fs_by_device(self):
|
||||
expected_fs = partition.FS('device')
|
||||
expected_fs = objects.FileSystem('device')
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.FS('wrong_device'))
|
||||
self.p_scheme.fss.append(objects.FileSystem('wrong_device'))
|
||||
actual_fs = self.p_scheme.fs_by_device('device')
|
||||
self.assertEqual(expected_fs, actual_fs)
|
||||
|
||||
def test_fs_by_mount(self):
|
||||
expected_fs = partition.FS('d', mount='mount')
|
||||
expected_fs = objects.FileSystem('d', mount='mount')
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.FS('w_d', mount='wrong_mount'))
|
||||
self.p_scheme.fss.append(objects.FileSystem('w_d',
|
||||
mount='wrong_mount'))
|
||||
actual_fs = self.p_scheme.fs_by_mount('mount')
|
||||
self.assertEqual(expected_fs, actual_fs)
|
||||
|
||||
def test_pv_by_name(self):
|
||||
expected_pv = partition.PV('pv')
|
||||
expected_pv = objects.PhysicalVolume('pv')
|
||||
self.p_scheme.pvs.append(expected_pv)
|
||||
self.p_scheme.pvs.append(partition.PV('wrong_pv'))
|
||||
self.p_scheme.pvs.append(objects.PhysicalVolume('wrong_pv'))
|
||||
actual_pv = self.p_scheme.pv_by_name('pv')
|
||||
self.assertEqual(expected_pv, actual_pv)
|
||||
|
||||
def test_vg_by_name(self):
|
||||
expected_vg = partition.VG('vg')
|
||||
expected_vg = objects.VolumeGroup('vg')
|
||||
self.p_scheme.vgs.append(expected_vg)
|
||||
self.p_scheme.vgs.append(partition.VG('wrong_vg'))
|
||||
self.p_scheme.vgs.append(objects.VolumeGroup('wrong_vg'))
|
||||
actual_vg = self.p_scheme.vg_by_name('vg')
|
||||
self.assertEqual(expected_vg, actual_vg)
|
||||
|
||||
@ -159,7 +160,9 @@ class TestPartitionScheme(unittest2.TestCase):
|
||||
|
||||
def test_md_next_name_fail(self):
|
||||
self.p_scheme.mds = [
|
||||
partition.MD('/dev/md%s' % x, 'level') for x in range(0, 128)]
|
||||
objects.MultipleDevice('/dev/md%s' % x, 'level')
|
||||
for x in range(0, 128)
|
||||
]
|
||||
self.assertRaises(errors.MDAlreadyExistsError,
|
||||
self.p_scheme.md_next_name)
|
||||
|
||||
@ -175,27 +178,27 @@ class TestPartitionScheme(unittest2.TestCase):
|
||||
|
||||
def test_md_by_name(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
expected_md = partition.MD('name', 'level')
|
||||
expected_md = objects.MultipleDevice('name', 'level')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.mds.append(partition.MD('wrong_name', 'level'))
|
||||
self.p_scheme.mds.append(objects.MultipleDevice('wrong_name', 'level'))
|
||||
self.assertEqual(expected_md, self.p_scheme.md_by_name('name'))
|
||||
|
||||
def test_md_by_mount(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
self.assertEqual(0, len(self.p_scheme.fss))
|
||||
expected_md = partition.MD('name', 'level')
|
||||
expected_fs = partition.FS('name', mount='mount')
|
||||
expected_md = objects.MultipleDevice('name', 'level')
|
||||
expected_fs = objects.FileSystem('name', mount='mount')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.FS('wrong_name',
|
||||
self.p_scheme.fss.append(objects.FileSystem('wrong_name',
|
||||
mount='wrong_mount'))
|
||||
self.assertEqual(expected_md, self.p_scheme.md_by_mount('mount'))
|
||||
|
||||
def test_md_attach_by_mount_md_exists(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
self.assertEqual(0, len(self.p_scheme.fss))
|
||||
expected_md = partition.MD('name', 'level')
|
||||
expected_fs = partition.FS('name', mount='mount')
|
||||
expected_md = objects.MultipleDevice('name', 'level')
|
||||
expected_fs = objects.FileSystem('name', mount='mount')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
actual_md = self.p_scheme.md_attach_by_mount('device', 'mount')
|
||||
@ -244,25 +247,25 @@ class TestPartitionScheme(unittest2.TestCase):
|
||||
class TestParted(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
super(TestParted, self).setUp()
|
||||
self.prtd = partition.Parted('name', 'label')
|
||||
self.prtd = objects.Parted('name', 'label')
|
||||
|
||||
@mock.patch.object(partition.Parted, 'next_count')
|
||||
@mock.patch.object(partition.Parted, 'next_type')
|
||||
@mock.patch.object(objects.Parted, 'next_count')
|
||||
@mock.patch.object(objects.Parted, 'next_type')
|
||||
def test_next_name_none(self, nt_mock, nc_mock):
|
||||
nc_mock.return_value = 1
|
||||
nt_mock.return_value = 'extended'
|
||||
self.assertEqual(None, self.prtd.next_name())
|
||||
|
||||
@mock.patch.object(partition.Parted, 'next_count')
|
||||
@mock.patch.object(partition.Parted, 'next_type')
|
||||
@mock.patch.object(objects.Parted, 'next_count')
|
||||
@mock.patch.object(objects.Parted, 'next_type')
|
||||
def test_next_name_no_separator(self, nt_mock, nc_mock):
|
||||
nc_mock.return_value = 1
|
||||
nt_mock.return_value = 'not_extended'
|
||||
expected_name = '%s%s' % (self.prtd.name, 1)
|
||||
self.assertEqual(expected_name, self.prtd.next_name())
|
||||
|
||||
@mock.patch.object(partition.Parted, 'next_count')
|
||||
@mock.patch.object(partition.Parted, 'next_type')
|
||||
@mock.patch.object(objects.Parted, 'next_count')
|
||||
@mock.patch.object(objects.Parted, 'next_type')
|
||||
def test_next_name_with_separator(self, nt_mock, nc_mock):
|
||||
nc_mock.return_value = 1
|
||||
nt_mock.return_value = 'not_extended'
|
||||
@ -281,13 +284,13 @@ class TestParted(unittest2.TestCase):
|
||||
|
||||
def test_next_begin_last_extended_partition(self):
|
||||
self.prtd.partitions.append(
|
||||
partition.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
objects.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
'extended'))
|
||||
self.assertEqual('begin', self.prtd.next_begin())
|
||||
|
||||
def test_next_begin_no_last_extended_partition(self):
|
||||
self.prtd.partitions.append(
|
||||
partition.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
objects.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
'primary'))
|
||||
self.assertEqual('end', self.prtd.next_begin())
|
||||
|
||||
@ -296,7 +299,7 @@ class TestParted(unittest2.TestCase):
|
||||
|
||||
def test_next_count_has_logical(self):
|
||||
self.prtd.partitions.append(
|
||||
partition.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
objects.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
'logical'))
|
||||
self.assertEqual(6, self.prtd.next_count('logical'))
|
||||
|
||||
@ -308,19 +311,19 @@ class TestParted(unittest2.TestCase):
|
||||
self.prtd.label = 'msdos'
|
||||
self.assertEqual('primary', self.prtd.next_type())
|
||||
self.prtd.partitions.extend(
|
||||
3 * [partition.Partition('name', 'count', 'device', 'begin',
|
||||
3 * [objects.Partition('name', 'count', 'device', 'begin',
|
||||
'end', 'primary')])
|
||||
self.assertEqual('extended', self.prtd.next_type())
|
||||
|
||||
def test_next_type_has_extended(self):
|
||||
self.prtd.label = 'msdos'
|
||||
self.prtd.partitions.append(
|
||||
partition.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
objects.Partition('name', 'count', 'device', 'begin', 'end',
|
||||
'extended'))
|
||||
self.assertEqual('logical', self.prtd.next_type())
|
||||
|
||||
def test_primary(self):
|
||||
expected_partitions = [partition.Partition('name', 'count', 'device',
|
||||
expected_partitions = [objects.Partition('name', 'count', 'device',
|
||||
'begin', 'end', 'primary')]
|
||||
self.prtd.partitions.extend(expected_partitions)
|
||||
self.assertEqual(expected_partitions, self.prtd.primary)
|
||||
@ -334,7 +337,7 @@ class TestParted(unittest2.TestCase):
|
||||
self.assertEqual(expected_prt, actual_prt)
|
||||
|
||||
def test_conversion(self):
|
||||
prt = partition.Partition(
|
||||
prt = objects.Partition(
|
||||
name='name',
|
||||
count='count',
|
||||
device='device',
|
||||
@ -353,14 +356,14 @@ class TestParted(unittest2.TestCase):
|
||||
],
|
||||
'install_bootloader': False,
|
||||
}
|
||||
new_prtd = partition.Parted.from_dict(serialized)
|
||||
new_prtd = objects.Parted.from_dict(serialized)
|
||||
assert serialized == new_prtd.to_dict()
|
||||
|
||||
|
||||
class TestLogicalVolume(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
lv = partition.LV(
|
||||
lv = objects.LogicalVolume(
|
||||
name='lv-name',
|
||||
vgname='vg-name',
|
||||
size=1234
|
||||
@ -372,14 +375,14 @@ class TestLogicalVolume(unittest2.TestCase):
|
||||
'size': 1234,
|
||||
'keep_data': False,
|
||||
}
|
||||
new_lv = partition.LV.from_dict(serialized)
|
||||
new_lv = objects.LogicalVolume.from_dict(serialized)
|
||||
assert serialized == new_lv.to_dict()
|
||||
|
||||
|
||||
class TestPhisicalVolume(unittest2.TestCase):
|
||||
class TestPhysicalVolume(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
pv = partition.PV(
|
||||
pv = objects.PhysicalVolume(
|
||||
name='pv-name',
|
||||
metadatasize=987,
|
||||
metadatacopies=112,
|
||||
@ -391,14 +394,14 @@ class TestPhisicalVolume(unittest2.TestCase):
|
||||
'metadatacopies': 112,
|
||||
'keep_data': False,
|
||||
}
|
||||
new_pv = partition.PV.from_dict(serialized)
|
||||
new_pv = objects.PhysicalVolume.from_dict(serialized)
|
||||
assert serialized == new_pv.to_dict()
|
||||
|
||||
|
||||
class TestVolumesGroup(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
vg = partition.VG(
|
||||
vg = objects.VolumeGroup(
|
||||
name='vg-name',
|
||||
pvnames=['pv-name-a', ]
|
||||
)
|
||||
@ -408,14 +411,14 @@ class TestVolumesGroup(unittest2.TestCase):
|
||||
'pvnames': ['pv-name-a', ],
|
||||
'keep_data': False,
|
||||
}
|
||||
new_vg = partition.VG.from_dict(serialized)
|
||||
new_vg = objects.VolumeGroup.from_dict(serialized)
|
||||
assert serialized == new_vg.to_dict()
|
||||
|
||||
|
||||
class TestFileSystem(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
fs = partition.FS(
|
||||
fs = objects.FileSystem(
|
||||
device='some-device',
|
||||
mount='/mount',
|
||||
fs_type='type',
|
||||
@ -431,5 +434,5 @@ class TestFileSystem(unittest2.TestCase):
|
||||
'fs_label': 'some-label',
|
||||
'keep_data': False,
|
||||
}
|
||||
new_fs = partition.FS.from_dict(serialized)
|
||||
new_fs = objects.FileSystem.from_dict(serialized)
|
||||
assert serialized == new_fs.to_dict()
|
||||
|
Loading…
x
Reference in New Issue
Block a user