This hasn't been validated upstream and there doesn't appear to be anyone using it. It's time to drop support for this. This is mostly test and documentation damage, though there is some other cleanup going on, like the removal of the essentially noop 'pick_disk_driver_name' helper. Change-Id: I73305e82da5d8da548961b801a8e75fb0e8c4cf1 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
250 lines
8.6 KiB
Python
250 lines
8.6 KiB
Python
# Copyright 2016 Red Hat, Inc
|
|
# Copyright 2017 Rackspace Australia
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
libvirt specific routines.
|
|
"""
|
|
|
|
import binascii
|
|
import os
|
|
import stat
|
|
|
|
from oslo_concurrency import processutils
|
|
from oslo_log import log as logging
|
|
from oslo_utils import units
|
|
from oslo_utils import uuidutils
|
|
|
|
import nova.privsep
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def dmcrypt_create_volume(target, device, cipher, key_size, key):
|
|
"""Sets up a dmcrypt mapping
|
|
|
|
:param target: device mapper logical device name
|
|
:param device: underlying block device
|
|
:param cipher: encryption cipher string digestible by cryptsetup
|
|
:param key_size: encryption key size
|
|
:param key: encoded encryption key bytestring
|
|
"""
|
|
cmd = ('cryptsetup',
|
|
'create',
|
|
target,
|
|
device,
|
|
'--cipher=' + cipher,
|
|
'--key-size=' + str(key_size),
|
|
'--key-file=-')
|
|
key = binascii.hexlify(key).decode('utf-8')
|
|
processutils.execute(*cmd, process_input=key)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def dmcrypt_delete_volume(target):
|
|
"""Deletes a dmcrypt mapping
|
|
|
|
:param target: name of the mapped logical device
|
|
"""
|
|
processutils.execute('cryptsetup', 'remove', target)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def ploop_init(size, disk_format, fs_type, disk_path):
|
|
"""Initialize ploop disk, make it readable for non-root user
|
|
|
|
:param disk_format: data allocation format (raw or expanded)
|
|
:param fs_type: filesystem (ext4, ext3, none)
|
|
:param disk_path: ploop image file
|
|
"""
|
|
processutils.execute('ploop', 'init', '-s', size, '-f', disk_format, '-t',
|
|
fs_type, disk_path, check_exit_code=True)
|
|
|
|
# Add read access for all users, because "ploop init" creates
|
|
# disk with rw rights only for root. OpenStack user should have access
|
|
# to the disk to request info via "qemu-img info"
|
|
# TODO(mikal): this is a faithful rendition of the pre-privsep code from
|
|
# the libvirt driver, but it seems undesirable to me. It would be good to
|
|
# create the loop file with the right owner or group such that we don't
|
|
# need to have it world readable. I don't have access to a system to test
|
|
# this on however.
|
|
st = os.stat(disk_path)
|
|
os.chmod(disk_path, st.st_mode | stat.S_IROTH)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def ploop_resize(disk_path, size):
|
|
"""Resize ploop disk
|
|
|
|
:param disk_path: ploop image file
|
|
:param size: new size (in bytes)
|
|
"""
|
|
processutils.execute('prl_disk_tool', 'resize',
|
|
'--size', '%dM' % (size // units.Mi),
|
|
'--resize_partition',
|
|
'--hdd', disk_path,
|
|
check_exit_code=True)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def ploop_restore_descriptor(image_dir, base_delta, fmt):
|
|
"""Restore ploop disk descriptor XML
|
|
|
|
:param image_dir: path to where descriptor XML is created
|
|
:param base_delta: ploop image file containing the data
|
|
:param fmt: ploop data allocation format (raw or expanded)
|
|
"""
|
|
processutils.execute('ploop', 'restore-descriptor', '-f', fmt,
|
|
image_dir, base_delta,
|
|
check_exit_code=True)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def plug_infiniband_vif(vnic_mac, device_id, fabric, net_model, pci_slot):
|
|
processutils.execute('ebrctl', 'add-port', vnic_mac, device_id,
|
|
fabric, net_model, pci_slot)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def unplug_infiniband_vif(fabric, vnic_mac):
|
|
processutils.execute('ebrctl', 'del-port', fabric, vnic_mac)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def plug_midonet_vif(port_id, dev):
|
|
processutils.execute('mm-ctl', '--bind-port', port_id, dev)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def unplug_midonet_vif(port_id):
|
|
processutils.execute('mm-ctl', '--unbind-port', port_id)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def plug_plumgrid_vif(dev, iface_id, vif_address, net_id, tenant_id):
|
|
processutils.execute('ifc_ctl', 'gateway', 'add_port', dev)
|
|
processutils.execute('ifc_ctl', 'gateway', 'ifup', dev,
|
|
'access_vm', iface_id, vif_address,
|
|
'pgtag2=%s' % net_id, 'pgtag1=%s' % tenant_id)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def unplug_plumgrid_vif(dev):
|
|
processutils.execute('ifc_ctl', 'gateway', 'ifdown', dev)
|
|
processutils.execute('ifc_ctl', 'gateway', 'del_port', dev)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def readpty(path):
|
|
# TODO(mikal): I'm not a huge fan that we don't enforce a valid pty path
|
|
# here, but I haven't come up with a great way of doing that.
|
|
|
|
# NOTE(mikal): I am deliberately not catching the ImportError
|
|
# exception here... Some platforms (I'm looking at you Windows)
|
|
# don't have a fcntl and we may as well let them know that
|
|
# with an ImportError, not that they should be calling this at all.
|
|
import fcntl
|
|
|
|
try:
|
|
with open(path, 'r') as f:
|
|
current_flags = fcntl.fcntl(f.fileno(), fcntl.F_GETFL)
|
|
fcntl.fcntl(f.fileno(), fcntl.F_SETFL,
|
|
current_flags | os.O_NONBLOCK)
|
|
|
|
return f.read()
|
|
|
|
except Exception as exc:
|
|
# NOTE(mikal): dear internet, I see you looking at me with your
|
|
# judging eyes. There's a story behind why we do this. You see, the
|
|
# previous implementation did this:
|
|
#
|
|
# out, err = utils.execute('dd',
|
|
# 'if=%s' % pty,
|
|
# 'iflag=nonblock',
|
|
# run_as_root=True,
|
|
# check_exit_code=False)
|
|
# return out
|
|
#
|
|
# So, it never checked stderr or the return code of the process it
|
|
# ran to read the pty. Doing something better than that has turned
|
|
# out to be unexpectedly hard because there are a surprisingly large
|
|
# variety of errors which appear to be thrown when doing this read.
|
|
#
|
|
# Therefore for now we log the errors, but keep on rolling. Volunteers
|
|
# to help clean this up are welcome and will receive free beverages.
|
|
LOG.info(
|
|
'Ignored error while reading from instance console pty: %s', exc
|
|
)
|
|
return ''
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def create_mdev(physical_device, mdev_type, uuid=None):
|
|
"""Instantiate a mediated device."""
|
|
if uuid is None:
|
|
uuid = uuidutils.generate_uuid()
|
|
fpath = '/sys/class/mdev_bus/{0}/mdev_supported_types/{1}/create'
|
|
fpath = fpath.format(physical_device, mdev_type)
|
|
with open(fpath, 'w') as f:
|
|
f.write(uuid)
|
|
return uuid
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def systemd_run_qb_mount(qb_vol, mnt_base, cfg_file=None):
|
|
"""Mount QB volume in separate CGROUP"""
|
|
# Note(kaisers): Details on why we run without --user at bug #1756823
|
|
sysdr_cmd = ['systemd-run', '--scope', 'mount.quobyte', '--disable-xattrs',
|
|
qb_vol, mnt_base]
|
|
if cfg_file:
|
|
sysdr_cmd.extend(['-c', cfg_file])
|
|
return processutils.execute(*sysdr_cmd)
|
|
|
|
|
|
# NOTE(kaisers): this method is deliberately not wrapped in a privsep entry.
|
|
def unprivileged_qb_mount(qb_vol, mnt_base, cfg_file=None):
|
|
"""Mount QB volume"""
|
|
mnt_cmd = ['mount.quobyte', '--disable-xattrs', qb_vol, mnt_base]
|
|
if cfg_file:
|
|
mnt_cmd.extend(['-c', cfg_file])
|
|
return processutils.execute(*mnt_cmd)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def umount(mnt_base):
|
|
"""Unmount volume"""
|
|
unprivileged_umount(mnt_base)
|
|
|
|
|
|
# NOTE(kaisers): this method is deliberately not wrapped in a privsep entry.
|
|
def unprivileged_umount(mnt_base):
|
|
"""Unmount volume"""
|
|
umnt_cmd = ['umount', mnt_base]
|
|
return processutils.execute(*umnt_cmd)
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def get_pmem_namespaces():
|
|
ndctl_cmd = ['ndctl', 'list', '-X']
|
|
nss_info = processutils.execute(*ndctl_cmd)[0]
|
|
return nss_info
|
|
|
|
|
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
|
def cleanup_vpmem(devpath):
|
|
daxio_cmd = ['daxio', '-z', '-o', '%s' % devpath]
|
|
processutils.execute(*daxio_cmd)
|