用 pyparted 在未分配分区创建新分区

2019-03-22 17:10:21 +08:00
 firejoke

最近一个需求需要为块存储单独划出一块分区
有可能是从空白分区,有可能是从空白磁盘
需要实施人员能从页面选择,所以后端要能给出数据,方便前面图形化
网上能找到的方法都是用 Popen 调用 shell 命令, 私以为这样不够稳妥
遂找到了 pyparted 这个库,但几乎没有文档
只找到了一个别人写的范例
https://gist.github.com/herry13/5931cac426da99820de843477e41e89e
是整个磁盘重写整个分区表,参考着尝试了一下对已有分区表添加新分区
Let's think!

>>>import parted
>>>for d in parted.getAllDevices():
...     print d
... 
parted.Device instance --
  model: VMware, VMware Virtual S  path: /dev/sda  type: 1
  sectorSize: 512  physicalSectorSize:  512
  length: 1048576000  openCount: 0  readOnly: False
  externalMode: False  dirty: False  bootDirty: False
  host: 0  did: 0  busy: True
  hardwareGeometry: (65270, 255, 63)  biosGeometry: (65270, 255, 63)
  PedDevice: <_ped.Device object at 0x7f80d71459e0>
parted.Device instance --
  model: VMware, VMware Virtual S  path: /dev/sdb  type: 1
  sectorSize: 512  physicalSectorSize:  512
  length: 2147483648  openCount: 0  readOnly: False
  externalMode: False  dirty: False  bootDirty: False
  host: 0  did: 1  busy: False
  hardwareGeometry: (133674, 255, 63)  biosGeometry: (133674, 255, 63)
  PedDevice: <_ped.Device object at 0x7f80d7145a70>
parted.Device instance --
  model: VMware, VMware Virtual S  path: /dev/sdc  type: 1
  sectorSize: 512  physicalSectorSize:  512
  length: 2147483648  openCount: 0  readOnly: False
  externalMode: False  dirty: False  bootDirty: False
  host: 0  did: 2  busy: False
  hardwareGeometry: (133674, 255, 63)  biosGeometry: (133674, 255, 63)
  PedDevice: <_ped.Device object at 0x7f80d7145b00>

>>> device = parted.getAllDevices()[0]

虚拟机里三个磁盘,sda 给的是 500GB,系统分区是 100G,交换分区是 2G

>>> sda = parted.newDisk(device)
>>> sda.partitions
[<parted.partition.Partition object at 0x7f80d64d7550>, <parted.partition.Partition object at 0x7f80d64d7810>]
>>> partition = sda.getFirstPartition()
>>> while partition:
...     print partition.path
...     print partition.getSize(unit="GB")
...     print partition.type
...     print partition.geometry
...     if partition.type == parted.PARTITION_FREESPACE and (397 < partition.getSize(unit="GB") < 398):
...             par = partition
...     partition = partition.nextPartition()
... 
/dev/sda-1
3.00407409668e-05
8
parted.Geometry instance --
  start: 0  end: 62  length: 63
  device: <parted.device.Device object at 0x7f80d3461a10>  PedGeometry: <_ped.Geometry object at 0x7f80d3461b10>
/dev/sda-1
0.000946521759033
4
parted.Geometry instance --
  start: 63  end: 2047  length: 1985
  device: <parted.device.Device object at 0x7f80d3461d10>  PedGeometry: <_ped.Geometry object at 0x7f80d3461bd0>
/dev/sda1
100.0
0
parted.Geometry instance --
  start: 2048  end: 209717247  length: 209715200
  device: <parted.device.Device object at 0x7f80d3461a10>  PedGeometry: <_ped.Geometry object at 0x7f80d34619d0>
/dev/sda2
2.0
0
parted.Geometry instance --
  start: 209717248  end: 213911551  length: 4194304
  device: <parted.device.Device object at 0x7f80d3461d10>  PedGeometry: <_ped.Geometry object at 0x7f80d3461c90>
/dev/sda-1
397.992609978
4
parted.Geometry instance --
  start: 213911552  end: 1048562549  length: 834650998
  device: <parted.device.Device object at 0x7f80d3461b10>  PedGeometry: <_ped.Geometry object at 0x7f80d3461950>
/dev/sda-1
0.00641345977783
8
parted.Geometry instance --
  start: 1048562550  end: 1048575999  length: 13450
  device: <parted.device.Device object at 0x7f80d3461bd0>  PedGeometry: <_ped.Geometry object at 0x7f80d3461c10>

type 值在 _ped 里的定义分别是
PARTITION_NORMAL = 0
PARTITION_METADATA = 8
PARTITION_HPSERVICE = 8
PARTITION_FREESPACE = 4
PARTITION_HIDDEN = 4

>>> par
<parted.partition.Partition object at 0x7f80d34619d0>
>>> par.path
'/dev/sda-1'
>>> par.getSize()
407544.4326171875
>>> par.getSize(unit="GB")
397.99260997772217

按照那个范例的思想,用紧跟在交换分区后面的扇区位置来创建一个新的分区,
再添加到磁盘里

>>> sda.partitions
[<parted.partition.Partition object at 0x7f80d64d7550>, <parted.partition.Partition object at 0x7f80d64d7810>]
>>> sda.partitions[1]
<parted.partition.Partition object at 0x7f80d64d7810>
>>> sda.partitions[1].getSize()
2048.0
>>> sda.partitions[1].getSize(unit="GB")
2.0
>>> sda.partitions[1].geometry.end
213911551L

创建并添加

>>> geometry2 = parted.Geometry(start = sda.partitions[1].geometry.end, length = parted.sizeToSectors(397, "GB", device.sectorSize), device = device)
>>> filesystem2 = parted.FileSystem(type="xfs", geometry = geometry2)
>>> par2 = parted.Partition(disk = sda, type = parted.PARTITION_NORMAL, fs = filesystem2, geometry = geometry2)
>>> sda.addPartition(par2, constraint = device.optimalAlignedConstraint)
True
>>> sda.commit()
True

看看效果

>>> sda.partitions
[<parted.partition.Partition object at 0x7f80d3461f10>, <parted.partition.Partition object at 0x7f80d3467110>, <parted.partition.Partition object at 0x7f80d3467510>]

>>> partition = sda.getFirstPartition()
>>> while partition:
...     print partition.path
...     print partition.getSize(unit="GB")
...     print partition.type
...     print partition.geometry
...     partition = partition.nextPartition()
... 
/dev/sda-1
3.00407409668e-05
8
parted.Geometry instance --
  start: 0  end: 62  length: 63
  device: <parted.device.Device object at 0x7f80d64d7710>  PedGeometry: <_ped.Geometry object at 0x7f80d3461f90>
/dev/sda-1
0.000946521759033
4
parted.Geometry instance --
  start: 63  end: 2047  length: 1985
  device: <parted.device.Device object at 0x7f80d64d74d0>  PedGeometry: <_ped.Geometry object at 0x7f80d64d7890>
/dev/sda1
100.0
0
parted.Geometry instance --
  start: 2048  end: 209717247  length: 209715200
  device: <parted.device.Device object at 0x7f80d64d7710>  PedGeometry: <_ped.Geometry object at 0x7f80d3461f90>
/dev/sda2
2.0
0
parted.Geometry instance --
  start: 209717248  end: 213911551  length: 4194304
  device: <parted.device.Device object at 0x7f80d64d74d0>  PedGeometry: <_ped.Geometry object at 0x7f80d64d7850>
/dev/sda-1
7.818359375
4
parted.Geometry instance --
  start: 213911552  end: 230307839  length: 16396288
  device: <parted.device.Device object at 0x7f80d64d7990>  PedGeometry: <_ped.Geometry object at 0x7f80d3461f90>
/dev/sda3
360.834960938
0
parted.Geometry instance --
  start: 230307840  end: 987033599  length: 756725760
  device: <parted.device.Device object at 0x7f80d64d7890>  PedGeometry: <_ped.Geometry object at 0x7f80d64d7910>
/dev/sda-1
29.3392896652
4
parted.Geometry instance --
  start: 987033600  end: 1048562549  length: 61528950
  device: <parted.device.Device object at 0x7f80d64d7990>  PedGeometry: <_ped.Geometry object at 0x7f80d3461f90>
/dev/sda-1
0.00641345977783
8
parted.Geometry instance --
  start: 1048562550  end: 1048575999  length: 13450
  device: <parted.device.Device object at 0x7f80d64d7410>  PedGeometry: <_ped.Geometry object at 0x7f80d64d7850>

添加成功,但多了两个未分配的分区
占了 29.3 和 7.8,换个 filesystem type 试试 让我们销毁新建的分区重新来过

>>> sda.removePartition(par2)
True
>>> sda.commit()
True
>>> sda.partitions
[<parted.partition.Partition object at 0x7f80d64d7910>, <parted.partition.Partition object at 0x7f80d64d7410>]

换过 filesystem type 依然一样
从范例的参考来源发现一点点不同 https://github.com/dcantrell/pyparted/issues/38
讨论的调整分区的大小,用到了不一样的 constraint 这里是完全按生成的扇区对齐

constraint = parted.Constraint(exactGeom=geom)

在生成扇区那里改一下, 不指定长度, 将 start 和 end 指定为最后一个空闲分区的扇区 start 和 end
添加分区那里, 照葫芦画瓢

>>> geometry2 = parted.Geometry(start = sda.getFreeSpacePartitions()[1].geometry.start, end = sda.getFreeSpacePartitions()[1].geometry.end, device = device)
>>> filesystem2 = parted.FileSystem(type="ext4", geometry = geometry2)
>>> par2 = parted.Partition(disk = sda, type = parted.PARTITION_NORMAL, fs = filesystem2, geometry = geometry2)
>>> sda.addPartition(par2, constraint = parted.Constraint(exactGeom=geometry2))
True
>>> sda.commit()
True

检查一下

>>> sda.getFreeSpacePartitions()
[<parted.partition.Partition object at 0x7f80d3467e50>]
>>> partition = sda.getFirstPartition()
>>> while partition:
...      print partition.path
...      print partition.getSize(unit="GB")
...      print partition.type
...      print partition.geometry
...      partition = partition.nextPartition()
... 
/dev/sda-1
3.00407409668e-05
8
parted.Geometry instance --
  start: 0  end: 62  length: 63
  device: <parted.device.Device object at 0x7f80d3470a90>  PedGeometry: <_ped.Geometry object at 0x7f80d3467e50>
/dev/sda-1
0.000946521759033
4
parted.Geometry instance --
  start: 63  end: 2047  length: 1985
  device: <parted.device.Device object at 0x7f80d3470c10>  PedGeometry: <_ped.Geometry object at 0x7f80d3470710>
/dev/sda1
100.0
0
parted.Geometry instance --
  start: 2048  end: 209717247  length: 209715200
  device: <parted.device.Device object at 0x7f80d3470a90>  PedGeometry: <_ped.Geometry object at 0x7f80d3467e50>
/dev/sda2
2.0
0
parted.Geometry instance --
  start: 209717248  end: 213911551  length: 4194304
  device: <parted.device.Device object at 0x7f80d3470c10>  PedGeometry: <_ped.Geometry object at 0x7f80d3470a50>
/dev/sda3
397.992609978
0
parted.Geometry instance --
  start: 213911552  end: 1048562549  length: 834650998
  device: <parted.device.Device object at 0x7f80d3470910>  PedGeometry: <_ped.Geometry object at 0x7f80d3467e50>
/dev/sda-1
0.00641345977783
8
parted.Geometry instance --
  start: 1048562550  end: 1048575999  length: 13450
  device: <parted.device.Device object at 0x7f80d3470710>  PedGeometry: <_ped.Geometry object at 0x7f80d34708d0>

很好,但在系统的磁盘管理界面里,最后 type 为 8 的这个分区居然显示为 free space?
不过才 6M 多,影响不大
这里再一次感受到 py 的方便
也重温了一遍 Linux 磁盘相关知识
end

1690 次点击
所在节点    Python
3 条回复
E1n
2019-03-22 17:46:20 +08:00
思路排版都不错啊
firejoke
2019-03-24 01:00:14 +08:00
@E1n 毕竟还是踩了好多坑的~
firejoke
2019-07-15 12:33:13 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/547486

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX