V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
firejoke
V2EX  ›  Python

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

  •  
  •   firejoke · 2019-03-22 17:10:21 +08:00 · 1810 次点击
    这是一个创建于 2102 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近一个需求需要为块存储单独划出一块分区
    有可能是从空白分区,有可能是从空白磁盘
    需要实施人员能从页面选择,所以后端要能给出数据,方便前面图形化
    网上能找到的方法都是用 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

    第 1 条附言  ·  2019-03-28 10:03:28 +08:00
    创建卷组可以用 lvm2py 模块,
    常用的方法有:
    查看有哪些卷组 LVM().vgscan()
    >> 返回由 VolumeGroup 实例组成的列表
    查看指定卷组用了哪些物理卷 vg_instance.pvscan()
    >> 返回由 PhysicalVolume 实例组成的列表
    创建卷组 LVM().create_vg(name, devices)
    >> 返回 vg_instance 参数内的 devices 必须是一个包含磁盘或分区路径的列表
    创建 vg LVM().get_vg(name, mode='r')
    >> 类似与 open 函数, 指定卷组名,和打开模式,默认是"读"模式
    3 条回复    2019-07-15 12:33:13 +08:00
    E1n
        1
    E1n  
       2019-03-22 17:46:20 +08:00
    思路排版都不错啊
    firejoke
        2
    firejoke  
    OP
       2019-03-24 01:00:14 +08:00 via Android
    @E1n 毕竟还是踩了好多坑的~
    firejoke
        3
    firejoke  
    OP
       2019-07-15 12:33:13 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2838 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:53 · PVG 19:53 · LAX 03:53 · JFK 06:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.