Skip to content

Commit

Permalink
Use BlockDev's BTRFS plugin instead of devicelibs/btrfs.py
Browse files Browse the repository at this point in the history
Except for the few things that are not provided by BlockDev.
  • Loading branch information
vpodzime committed Feb 19, 2015
1 parent 75accb3 commit 8af8c55
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 189 deletions.
167 changes: 0 additions & 167 deletions blivet/devicelibs/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
# Author(s): David Lehman <dlehman@redhat.com>
#

import os
import re

from . import raid
from .. import util
from ..errors import BTRFSError, BTRFSValueError
from ..size import Size

import logging
Expand All @@ -40,165 +35,3 @@
RAID_levels = raid.RAIDLevels(["raid0", "raid1", "raid10", "single"])

metadata_levels = raid.RAIDLevels(["raid0", "raid1", "raid10", "single", "dup"])

def btrfs(args, capture=False):
if capture:
exec_func = util.capture_output
else:
exec_func = util.run_program

argv = ["btrfs"] + args

ret = exec_func(argv)
if ret and not capture:
raise BTRFSError(ret)
return ret

def create_volume(devices, label=None, data=None, metadata=None):
"""Create a btrfs filesystem on the list of devices specified by devices.
:param data: a raid level for data
:type data: :class:`~.devicelibs.raid.RAIDLevel` or str
:param metadata: a raid level for metadata
:type metadata: :class:`~.devicelibs.raid.RAIDLevel` or str
Note that if a level is specified as a string, rather than by means
of a RAIDLevel object, it is not checked for validity. It is the
responsibility of the invoking method to verify that mkfs.btrfs
recognizes the string.
"""
if not devices:
raise BTRFSValueError("no devices specified")
elif any([not os.path.exists(d) for d in devices]):
raise BTRFSValueError("one or more specified devices not present")

args = []
if data:
args.append("--data=%s" % data)

if metadata:
args.append("--metadata=%s" % metadata)

if label:
args.append("--label=%s" % label)

args.extend(devices)

ret = util.run_program(["mkfs.btrfs"] + args)
if ret:
raise BTRFSError(ret)

return ret

# destroy is handled using wipefs

def add(mountpoint, device):
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

return btrfs(["device", "add", device, mountpoint])

def remove(mountpoint, device):
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

return btrfs(["device", "delete", device, mountpoint])

def create_subvolume(mountpoint, name):
"""Create a subvolume named name below mountpoint mountpoint."""
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "create", path]
return btrfs(args)

def delete_subvolume(mountpoint, name):
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "delete", path]
return btrfs(args)

_SUBVOL_REGEX_STR = r'ID (?P<id>\d+) gen \d+ (cgen \d+ )?parent (?P<parent>\d+) top level \d+ (otime \d{4}-\d{2}-\d{2} \d\d:\d\d:\d\d )?path (?P<path>.+)\n'
_SUBVOL_REGEX = re.compile(_SUBVOL_REGEX_STR)

# get a list of subvolumes from a mounted btrfs filesystem
def list_subvolumes(mountpoint, snapshots_only=False):
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

args = ["subvol", "list", "-p", mountpoint]
if snapshots_only:
args.insert(2, "-s")

buf = btrfs(args, capture=True)
vols = []
for m in _SUBVOL_REGEX.finditer(buf):
vols.append({"id": int(m.group('id')), "parent": int(m.group('parent')),
"path": m.group('path')})

return vols

def get_default_subvolume(mountpoint):
if not os.path.ismount(mountpoint):
raise BTRFSValueError("volume not mounted")

args = ["subvol", "get-default", mountpoint]
buf = btrfs(args, capture=True)
m = re.match(r'ID (\d+) .*', buf)
try:
default = int(m.groups()[0])
except IndexError:
raise BTRFSError("failed to get default subvolume from %s" % mountpoint)

return default

def set_default_subvolume(mountpoint, subvol_id):
""" Sets the subvolume of mountpoint which is mounted as default.
:param str mountpoint: path of mountpoint
:param int subvol_id: the subvolume id to set as the default
"""
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")

args = ["subvol", "set-default", str(subvol_id), mountpoint]
return btrfs(args)

def create_snapshot(source, dest, ro=False):
"""
:param str source: path to source subvolume
:param str dest: path to new snapshot subvolume
:keyword bool ro: whether to create a read-only snapshot
"""
if not os.path.ismount(source):
raise BTRFSValueError("source is not a mounted subvolume")

args = ["subvol", "snapshot"]
if ro:
args.append("-r")

args.extend([source, dest])
return btrfs(args)

_DEVICE_REGEX_STR = r'devid[ \t]+(\d+)[ \t]+size[ \t]+(\S+)[ \t]+used[ \t]+(\S+)[ \t]+path[ \t]+(\S+)\n'
_DEVICE_REGEX = re.compile(_DEVICE_REGEX_STR)

def list_devices(device):
"""List the devices in the filesystem in which this device participates. """
args = ["filesystem", "show", device]
buf = btrfs(args, capture=True)
return [{"id" : g[0], "size" : g[1], "used" : g[2], "path" : g[3] } for g in _DEVICE_REGEX.findall(buf)]

_HEADER_REGEX_STR = r'Label: (?P<label>\S+)[ \t]+uuid: (?P<uuid>\S+)\s+Total devices (?P<num_devices>\d+)[ \t]+FS bytes used (?P<fs_bytes_used>\S+)\n'
_HEADER_REGEX = re.compile(_HEADER_REGEX_STR)

def summarize_filesystem(device):
"""Summarize some general information about the filesystem in which this
device participates.
"""
args = ["filesystem", "show", device]
buf = btrfs(args, capture=True)
return _HEADER_REGEX.search(buf).groupdict()
42 changes: 26 additions & 16 deletions blivet/devices/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import os
import copy
import tempfile
from gi.repository import BlockDev as blockdev
from gi.repository import GLib

from ..devicelibs import btrfs
from ..devicelibs import raid
Expand Down Expand Up @@ -326,9 +328,9 @@ def listSubVolumes(self, snapshotsOnly=False):
return subvols

try:
subvols = btrfs.list_subvolumes(self.originalFormat._mountpoint,
snapshots_only=snapshotsOnly)
except errors.BTRFSError as e:
subvols = blockdev.btrfs_list_subvolumes(self.originalFormat._mountpoint,
snapshots_only=snapshotsOnly)
except GLib.GError as e:
log.debug("failed to list subvolumes: %s", e)
else:
self._getDefaultSubVolumeID()
Expand All @@ -353,8 +355,8 @@ def removeSubVolume(self, name):
def _getDefaultSubVolumeID(self):
subvolid = None
try:
subvolid = btrfs.get_default_subvolume(self.originalFormat._mountpoint)
except errors.BTRFSError as e:
subvolid = blockdev.btrfs_get_default_subvolume_id(self.originalFormat._mountpoint)
except GLib.Error as e:
log.debug("failed to get default subvolume id: %s", e)

self._defaultSubVolumeID = subvolid
Expand All @@ -365,8 +367,8 @@ def _setDefaultSubVolumeID(self, vol_id):
This writes the change to the filesystem, which must be mounted.
"""
try:
btrfs.set_default_subvolume(self.originalFormat._mountpoint, vol_id)
except errors.BTRFSError as e:
blockdev.btrfs_set_default_subvolume(self.originalFormat._mountpoint, vol_id)
except GLib.Error as e:
log.error("failed to set new default subvolume id (%s): %s",
vol_id, e)
# The only time we set a new default subvolume is so we can remove
Expand Down Expand Up @@ -398,10 +400,18 @@ def _preCreate(self):

def _create(self):
log_method_call(self, self.name, status=self.status)
btrfs.create_volume(devices=[d.path for d in self.parents],
label=self.format.label,
data=self.dataLevel,
metadata=self.metaDataLevel)
if self.dataLevel:
data_level = str(self.dataLevel)
else:
data_level = None
if self.metaDataLevel:
md_level = str(self.metaDataLevel)
else:
md_level = None
blockdev.btrfs_create_volume([d.path for d in self.parents],
label=self.format.label,
data_level=data_level,
md_level=md_level)

def _postCreate(self):
super(BTRFSVolumeDevice, self)._postCreate()
Expand Down Expand Up @@ -429,7 +439,7 @@ def _remove(self, member):
raise

try:
btrfs.remove(self.originalFormat._mountpoint, member.path)
blockdev.btrfs_remove_device(self.originalFormat._mountpoint, member.path)
finally:
self._undo_temp_mount()

Expand All @@ -441,7 +451,7 @@ def _add(self, member):
raise

try:
btrfs.add(self.originalFormat._mountpoint, member.path)
blockdev.btrfs_add_device(self.originalFormat._mountpoint, member.path)
finally:
self._undo_temp_mount()

Expand Down Expand Up @@ -531,7 +541,7 @@ def _create(self):
raise RuntimeError("btrfs subvol create requires mounted volume")

try:
btrfs.create_subvolume(mountpoint, self.name)
blockdev.btrfs_create_subvolume(mountpoint, self.name)
finally:
self.volume._undo_temp_mount()

Expand All @@ -549,7 +559,7 @@ def _destroy(self):
mountpoint = self.volume.originalFormat._mountpoint
if not mountpoint:
raise RuntimeError("btrfs subvol destroy requires mounted volume")
btrfs.delete_subvolume(mountpoint, self.name)
blockdev.btrfs_delete_subvolume(mountpoint, self.name)
self.volume._undo_temp_mount()

def removeHook(self, modparent=True):
Expand Down Expand Up @@ -633,7 +643,7 @@ def _create(self):

dest_path = "%s/%s" % (mountpoint, self.name)
try:
btrfs.create_snapshot(source_path, dest_path, ro=self.readOnly)
blockdev.btrfs_create_snapshot(source_path, dest_path, ro=self.readOnly)
finally:
self.volume._undo_temp_mount()

Expand Down
8 changes: 4 additions & 4 deletions blivet/devicetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1716,11 +1716,11 @@ def handleBTRFSFormat(self, info, device):

if not btrfs_dev.subvolumes:
snapshots = btrfs_dev.listSubVolumes(snapshotsOnly=True)
snapshot_ids = [s["id"] for s in snapshots]
snapshot_ids = [s.id for s in snapshots]
for subvol_dict in btrfs_dev.listSubVolumes():
vol_id = subvol_dict["id"]
vol_path = subvol_dict["path"]
parent_id = subvol_dict["parent"]
vol_id = subvol_dict.id
vol_path = subvol_dict.path
parent_id = subvol_dict.parent_id
if vol_path in [sv.name for sv in btrfs_dev.subvolumes]:
continue

Expand Down
4 changes: 2 additions & 2 deletions blivet/formats/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1125,11 +1125,11 @@ def __init__(self, **kwargs):
self.volUUID = kwargs.pop("volUUID", None)

def create(self, **kwargs):
# filesystem creation is done in storage.devicelibs.btrfs.create_volume
# filesystem creation is done in blockdev.btrfs_create_volume
self.exists = True

def destroy(self, **kwargs):
# filesystem creation is done in storage.devicelibs.btrfs.delete_volume
# filesystem deletion is done in blockdev.btrfs_delete_volume
self.exists = False

def setup(self, **kwargs):
Expand Down

0 comments on commit 8af8c55

Please sign in to comment.