some updates
This commit is contained in:
2
computelib/volume/__init__.py
Normal file
2
computelib/volume/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .storage_pool import StoragePool
|
||||
from .volume import DiskInfo, Volume, VolumeInfo
|
70
computelib/volume/storage_pool.py
Normal file
70
computelib/volume/storage_pool.py
Normal file
@ -0,0 +1,70 @@
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
|
||||
import libvirt
|
||||
from lxml import etree
|
||||
|
||||
from ..exceptions import StoragePoolError
|
||||
from .volume import Volume, VolumeInfo
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StoragePool:
|
||||
def __init__(self, pool: libvirt.virStoragePool):
|
||||
self.pool = pool
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.pool.name()
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
xml = etree.fromstring(self.pool.XMLDesc())
|
||||
return xml.xpath('/pool/target/path/text()')[0]
|
||||
|
||||
@property
|
||||
def usage(self) -> 'StoragePoolUsage':
|
||||
xml = etree.fromstring(self.pool.XMLDesc())
|
||||
StoragePoolUsage = namedtuple('StoagePoolUsage',
|
||||
['capacity', 'allocation', 'available'])
|
||||
return StoragePoolUsage(
|
||||
capacity=int(xml.xpath('/pool/capacity/text()')[0])
|
||||
allocation=int(xml.xpath('/pool/allocation/text()')[0])
|
||||
available=int(xml.xpath('/pool/available/text()')[0]))
|
||||
|
||||
def dump_xml(self) -> str:
|
||||
return self.pool.XMLDesc()
|
||||
|
||||
def refresh(self) -> None:
|
||||
self.pool.refresh()
|
||||
|
||||
def create_volume(self, vol_info: VolumeInfo) -> Volume:
|
||||
"""
|
||||
Create storage volume and return Volume instance.
|
||||
"""
|
||||
logger.info('Create storage volume vol=%s in pool=%s',
|
||||
vol_info.name, self.pool)
|
||||
vol = self.pool.createXML(
|
||||
vol_info.to_xml(),
|
||||
flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA)
|
||||
return Volume(self.pool, vol)
|
||||
|
||||
def get_volume(self, name: str) -> Volume | None:
|
||||
"""Lookup and return Volume instance or None."""
|
||||
logger.info('Lookup for storage volume vol=%s in pool=%s',
|
||||
name, self.pool.name)
|
||||
try:
|
||||
vol = self.pool.storageVolLookupByName(name)
|
||||
return Volume(self.pool, vol)
|
||||
except libvirt.libvirtError as err:
|
||||
if (err.get_error_domain() == libvirt.VIR_FROM_STORAGE or
|
||||
err.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL):
|
||||
logger.error(err.get_error_message())
|
||||
return None
|
||||
logger.error('libvirt error: %s' err)
|
||||
raise StoragePoolError(f'libvirt error: {err}') from err
|
||||
|
||||
def list_volumes(self) -> list[Volume]:
|
||||
return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()]
|
80
computelib/volume/volume.py
Normal file
80
computelib/volume/volume.py
Normal file
@ -0,0 +1,80 @@
|
||||
from dataclasses import dataclass
|
||||
from time import time
|
||||
|
||||
import libvirt
|
||||
from lxml import etree
|
||||
from lxml.builder import E
|
||||
|
||||
|
||||
@dataclass
|
||||
class VolumeInfo:
|
||||
name: str
|
||||
path: str
|
||||
capacity: int
|
||||
|
||||
def to_xml(self) -> str:
|
||||
unixtime = str(int(time()))
|
||||
xml = E.volume(type='file')
|
||||
xml.append(E.name(self.name))
|
||||
xml.append(E.key(self.path))
|
||||
xml.append(E.source())
|
||||
xml.append(E.capacity(str(self.capacity * 1024 * 1024), unit='bytes'))
|
||||
xml.append(E.allocation('0'))
|
||||
xml.append(E.target(
|
||||
E.path(self.path),
|
||||
E.format(type='qcow2'),
|
||||
E.timestamps(
|
||||
E.atime(unixtime),
|
||||
E.mtime(unixtime),
|
||||
E.ctime(unixtime)),
|
||||
E.compat('1.1'),
|
||||
E.features(E.lazy_refcounts())
|
||||
))
|
||||
return etree.tostring(xml, encoding='unicode', pretty_print=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiskInfo:
|
||||
target: str
|
||||
path: str
|
||||
readonly: bool = False
|
||||
|
||||
def to_xml(self) -> str:
|
||||
xml = E.disk(type='file', device='disk')
|
||||
xml.append(E.driver(name='qemu', type='qcow2', cache='writethrough'))
|
||||
xml.append(E.source(file=self.path))
|
||||
xml.append(E.target(dev=self.target, bus='virtio'))
|
||||
if self.readonly:
|
||||
xml.append(E.readonly())
|
||||
return etree.tostring(xml, encoding='unicode', pretty_print=True)
|
||||
|
||||
|
||||
class Volume:
|
||||
def __init__(self, pool: libvirt.virStoragePool,
|
||||
vol: libvirt.virStorageVol):
|
||||
self.pool = pool
|
||||
self.vol = vol
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.vol.name()
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
return self.vol.path()
|
||||
|
||||
def dump_xml(self) -> str:
|
||||
return self.vol.XMLDesc()
|
||||
|
||||
def clone(self, vol_info: VolumeInfo) -> None:
|
||||
self.pool.createXMLFrom(
|
||||
vol_info.to_xml(),
|
||||
self.vol,
|
||||
flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA)
|
||||
|
||||
def resize(self, capacity: int):
|
||||
"""Resize volume to `capacity`. Unit is mebibyte."""
|
||||
self.vol.resize(capacity * 1024 * 1024)
|
||||
|
||||
def delete(self) -> None:
|
||||
self.vol.delete()
|
Reference in New Issue
Block a user