# Storage Management Providers
#
# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the FreeBSD Project.
#
# Authors: Jan Safranek <jsafrane@redhat.com>
#
"""
Partition management functions.
"""
from lmi.scripts.common.errors import LmiFailed
from lmi.scripts.common import get_logger
LOG = get_logger(__name__)
from lmi.scripts.storage import common
import pywbem
PARTITION_TYPE_PRIMARY = 1
PARTITION_TYPE_EXTENDED = 2
PARTITION_TYPE_LOGICAL = 3
PARTITION_TABLE_TYPE_GPT = 3 # from CIM_DiskPartitionConfigurationCapabilities
PARTITION_TABLE_TYPE_MSDOS = 2
[docs]def get_disk_partitions(ns, disk):
"""
Return list of partitions on the device (not necessarily disk).
:type device: LMIInstance/CIM_StorageExtent or string
:param device: Device which should be partitioned.
:rtype: List of LMIInstance/CIM_GenericDiskPartition.
"""
disk = common.str2device(ns, disk)
parts = disk.associators(
AssocClass="CIM_BasedOn", Role="Antecedent")
for part in parts:
yield part
# List also logical partitions, which are 'BasedOn'
# on extended partition.
if "PartitionType" in part.properties():
cls = ns.LMI_DiskPartition
if part.PartitionType == cls.PartitionTypeValues.Extended:
for logical in part.associators(
AssocClass="CIM_BasedOn", Role="Antecedent"):
yield logical
[docs]def get_partition_disk(ns, partition):
"""
Return a device on which is located the given partition.
:type partition: LMIInstance/CIM_GenericDiskPartition or string
:param partition: Partition to examine.
:rtype: LMIInstance/CIM_StorageExtent.
"""
partition = common.str2device(ns, partition)
device = partition.first_associator(
AssocClass="CIM_BasedOn", Role="Dependent")
if "PartitionType" in device.properties():
# we got extended partition, find the disk
device = device.first_associator(
AssocClass="CIM_BasedOn", Role="Dependent")
return device
[docs]def get_partitions(ns, devices=None):
"""
Retrieve list of partitions on given devices.
If no devices are given, all partitions on all devices are returned.
:type devices: List of LMIInstance/CIM_StorageExtent or list of string
:param devices: Devices to list partitions on.
:rtype: List of LMIInstance/CIM_GenericPartition.
"""
if devices:
for device in devices:
device = common.str2device(ns, device)
LOG().debug("Getting list of partitions on %s", device.Name)
parts = get_disk_partitions(ns, device)
for part in parts:
yield part
else:
# No devices supplied, list all partitions.
for part in ns.CIM_GenericDiskPartition.instances():
yield part
[docs]def create_partition(ns, device, size=None, partition_type=None):
"""
Create new partition on given device.
:type device: LMIInstance/CIM_StorageExtent or string
:param device: Device which should be partitioned.
:type size: int
:param size: Size of the device, in blocks. See device's BlockSize
to get it. If no size is provided, the largest possible partition
is created.
:type partition_type: int
:param partition_type: Requested partition type.
See PARTITION_TYPE_xxx variables. If no type is given, extended partition
will be automatically created as 4th partition on MS-DOS style partition
table with a logical partition with requested size on it.
:rtype: LMIInstance/CIM_GenericDiskPartition.
"""
device = common.str2device(ns, device)
setting = None
try:
args = { 'extent': device}
if size:
args['Size'] = pywbem.Uint64(size)
if partition_type:
# create a setting and modify it
caps = ns.LMI_DiskPartitionConfigurationCapabilities\
.first_instance()
(ret, outparams, err) = caps.CreateSetting()
if ret != 0:
if err:
LmiFailed("Cannot create " \
"LMI_DiskPartitionConfigurationSetting for the " \
"partition: %s." % err)
raise LmiFailed("Cannot create " \
"LMI_DiskPartitionConfigurationSetting for the " \
"partition: %d." % ret)
setting = outparams['setting'].to_instance()
setting.PartitionType = pywbem.Uint16(partition_type)
(ret, _outparams, err) = setting.push()
if ret != 0:
if err:
raise LmiFailed("Cannot change " \
"LMI_DiskPartitionConfigurationSetting for the " \
"partition: %s." % err)
raise LmiFailed("Cannot change " \
"LMI_DiskPartitionConfigurationSetting for the " \
"partition: %d." % ret)
args['Goal'] = setting
service = ns.LMI_DiskPartitionConfigurationService.first_instance()
(ret, outparams, err) = service.SyncLMI_CreateOrModifyPartition(**args)
if ret != 0:
if err:
raise LmiFailed("Cannot create the partition: %s." % err)
values = service.LMI_CreateOrModifyPartition\
.LMI_CreateOrModifyPartitionValues
raise LmiFailed("Cannot create the partition: %s."
% (values.value_name(ret)))
finally:
if setting:
setting.delete()
return outparams['Partition']
[docs]def delete_partition(ns, partition):
"""
Remove given partition
:type partition: LMIInstance/CIM_GenericDiskPartition
:param partition: Partition to delete.
"""
partition = common.str2device(ns, partition)
service = ns.LMI_DiskPartitionConfigurationService.first_instance()
(ret, _outparams, err) = service.SyncLMI_DeletePartition(
Partition=partition)
if ret != 0:
if err:
raise LmiFailed("Cannot delete the partition: %s." % err)
values = service.LMI_DeletePartition.LMI_DeletePartitionValues
raise LmiFailed("Cannot delete the partition: %s."
% (values.value_name(ret)))
[docs]def create_partition_table(ns, device, table_type):
"""
Create new partition table on a device. The device must be empty, i.e.
must not have any partitions on it.
:type device: LMIInstance/CIM_StorageExtent
:param device: Device where the partition table should be created.
:type table_type: int
:param table_type: Requested partition table type. See
PARTITION_TABLE_TYPE_xxx variables.
"""
device = common.str2device(ns, device)
query = "SELECT * FROM LMI_DiskPartitionConfigurationCapabilities WHERE " \
"PartitionStyle=%d" % table_type
caps = ns.wql(query)
if not caps:
raise LmiFailed("Unsupported partition table type: %d" % table_type)
cap = caps[0]
service = ns.LMI_DiskPartitionConfigurationService.first_instance()
(ret, _outparams, err) = service.SetPartitionStyle(
Extent=device,
PartitionStyle=cap)
if ret != 0:
if err:
raise LmiFailed("Cannot create partition table: %s." % err)
values = service.SetPartitionStyle.SetPartitionStyleValues
raise LmiFailed("Cannot create partition table: %s."
% (values.value_name(ret)))
[docs]def get_partition_tables(ns, devices=None):
"""
Returns list of partition tables on given devices.
If no devices are given, all partitions on all devices are returned.
:type devices: list of LMIInstance/CIM_StorageExtent or list of strings
:param devices: Devices to list partition tables on.
:rtype: List of tuples (LMIInstance/CIM_StorageExtent,
LMIInstance/LMI_DiskPartitionConfigurationCapabilities).
"""
if not devices:
tables = ns.LMI_InstalledPartitionTable.instances()
for table in tables:
yield table.Antecedent.to_instance(), table.Dependent.to_instance()
else:
for device in devices:
device = common.str2device(ns, device)
table = get_disk_partition_table(ns, device)
if table:
yield device, table
[docs]def get_disk_partition_table(ns, device):
"""
Returns LMI_DiskPartitionTableCapabilities representing partition table
on given disk.
:type device: LMIInstance/CIM_StorageExtent or string
:param device: Device which should be examined.
:rtype: LMIInstance/LMI_DiskPartitionConfigurationCapabilities.
"""
device = common.str2device(ns, device)
table = device.first_associator(
AssocClass="LMI_InstalledPartitionTable")
return table
[docs]def get_largest_partition_size(ns, device):
"""
Returns size of the largest free region (in blocks), which can accommodate
a partition on given device.
There must be partition table present on this device.
:type device: LMIInstance/CIM_StorageExtent or string
:param device: Device which should be examined.
:rtype: int
"""
device = common.str2device(ns, device)
# find the partition table (=LMI_DiskPartitionConfigurationCapabilities)
cap = device.first_associator(
ResultClass="LMI_DiskPartitionConfigurationCapabilities")
if not cap:
raise LmiFailed("Cannot find partition table on %s" % device.name)
(ret, outparams, err) = cap.FindPartitionLocation(Extent=device)
if ret == cap.FindPartitionLocation.FindPartitionLocationValues\
.NotEnoughFreeSpace:
return 0
if ret != 0:
if err:
LOG().warning("Cannot find largest partition size: %s." % err)
else:
LOG().warning("Cannot find largest partition size: %d." % ret)
return 0
blocks = outparams['EndingAddress'] - outparams['StartingAddress']
size = blocks * device.BlockSize
return size