Coverage for nova/privsep/libvirt.py: 90%
94 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
1# Copyright 2016 Red Hat, Inc
2# Copyright 2017 Rackspace Australia
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16"""
17libvirt specific routines.
18"""
20import binascii
21import os
22import stat
24from oslo_concurrency import processutils
25from oslo_log import log as logging
26from oslo_utils import units
27from oslo_utils import uuidutils
29import nova.privsep
32LOG = logging.getLogger(__name__)
35@nova.privsep.sys_admin_pctxt.entrypoint
36def dmcrypt_create_volume(target, device, cipher, key_size, key):
37 """Sets up a dmcrypt mapping
39 :param target: device mapper logical device name
40 :param device: underlying block device
41 :param cipher: encryption cipher string digestible by cryptsetup
42 :param key_size: encryption key size
43 :param key: encoded encryption key bytestring
44 """
45 cmd = ('cryptsetup',
46 'create',
47 target,
48 device,
49 '--cipher=' + cipher,
50 '--key-size=' + str(key_size),
51 '--key-file=-')
52 key = binascii.hexlify(key).decode('utf-8')
53 processutils.execute(*cmd, process_input=key)
56@nova.privsep.sys_admin_pctxt.entrypoint
57def dmcrypt_delete_volume(target):
58 """Deletes a dmcrypt mapping
60 :param target: name of the mapped logical device
61 """
62 processutils.execute('cryptsetup', 'remove', target)
65@nova.privsep.sys_admin_pctxt.entrypoint
66def ploop_init(size, disk_format, fs_type, disk_path):
67 """Initialize ploop disk, make it readable for non-root user
69 :param disk_format: data allocation format (raw or expanded)
70 :param fs_type: filesystem (ext4, ext3, none)
71 :param disk_path: ploop image file
72 """
73 processutils.execute('ploop', 'init', '-s', size, '-f', disk_format, '-t',
74 fs_type, disk_path, check_exit_code=True)
76 # Add read access for all users, because "ploop init" creates
77 # disk with rw rights only for root. OpenStack user should have access
78 # to the disk to request info via "qemu-img info"
79 # TODO(mikal): this is a faithful rendition of the pre-privsep code from
80 # the libvirt driver, but it seems undesirable to me. It would be good to
81 # create the loop file with the right owner or group such that we don't
82 # need to have it world readable. I don't have access to a system to test
83 # this on however.
84 st = os.stat(disk_path)
85 os.chmod(disk_path, st.st_mode | stat.S_IROTH)
88@nova.privsep.sys_admin_pctxt.entrypoint
89def ploop_resize(disk_path, size):
90 """Resize ploop disk
92 :param disk_path: ploop image file
93 :param size: new size (in bytes)
94 """
95 processutils.execute('prl_disk_tool', 'resize',
96 '--size', '%dM' % (size // units.Mi),
97 '--resize_partition',
98 '--hdd', disk_path,
99 check_exit_code=True)
102@nova.privsep.sys_admin_pctxt.entrypoint
103def ploop_restore_descriptor(image_dir, base_delta, fmt):
104 """Restore ploop disk descriptor XML
106 :param image_dir: path to where descriptor XML is created
107 :param base_delta: ploop image file containing the data
108 :param fmt: ploop data allocation format (raw or expanded)
109 """
110 processutils.execute('ploop', 'restore-descriptor', '-f', fmt,
111 image_dir, base_delta,
112 check_exit_code=True)
115@nova.privsep.sys_admin_pctxt.entrypoint
116def plug_infiniband_vif(vnic_mac, device_id, fabric, net_model, pci_slot):
117 processutils.execute('ebrctl', 'add-port', vnic_mac, device_id,
118 fabric, net_model, pci_slot)
121@nova.privsep.sys_admin_pctxt.entrypoint
122def unplug_infiniband_vif(fabric, vnic_mac):
123 processutils.execute('ebrctl', 'del-port', fabric, vnic_mac)
126@nova.privsep.sys_admin_pctxt.entrypoint
127def plug_midonet_vif(port_id, dev):
128 processutils.execute('mm-ctl', '--bind-port', port_id, dev)
131@nova.privsep.sys_admin_pctxt.entrypoint
132def unplug_midonet_vif(port_id):
133 processutils.execute('mm-ctl', '--unbind-port', port_id)
136@nova.privsep.sys_admin_pctxt.entrypoint
137def plug_plumgrid_vif(dev, iface_id, vif_address, net_id, tenant_id):
138 processutils.execute('ifc_ctl', 'gateway', 'add_port', dev)
139 processutils.execute('ifc_ctl', 'gateway', 'ifup', dev,
140 'access_vm', iface_id, vif_address,
141 'pgtag2=%s' % net_id, 'pgtag1=%s' % tenant_id)
144@nova.privsep.sys_admin_pctxt.entrypoint
145def unplug_plumgrid_vif(dev):
146 processutils.execute('ifc_ctl', 'gateway', 'ifdown', dev)
147 processutils.execute('ifc_ctl', 'gateway', 'del_port', dev)
150@nova.privsep.sys_admin_pctxt.entrypoint
151def readpty(path):
152 # TODO(mikal): I'm not a huge fan that we don't enforce a valid pty path
153 # here, but I haven't come up with a great way of doing that.
155 # NOTE(mikal): I am deliberately not catching the ImportError
156 # exception here... Some platforms (I'm looking at you Windows)
157 # don't have a fcntl and we may as well let them know that
158 # with an ImportError, not that they should be calling this at all.
159 import fcntl
161 try:
162 with open(path, 'r') as f:
163 current_flags = fcntl.fcntl(f.fileno(), fcntl.F_GETFL)
164 fcntl.fcntl(f.fileno(), fcntl.F_SETFL,
165 current_flags | os.O_NONBLOCK)
167 return f.read()
169 except Exception as exc:
170 # NOTE(mikal): dear internet, I see you looking at me with your
171 # judging eyes. There's a story behind why we do this. You see, the
172 # previous implementation did this:
173 #
174 # out, err = utils.execute('dd',
175 # 'if=%s' % pty,
176 # 'iflag=nonblock',
177 # run_as_root=True,
178 # check_exit_code=False)
179 # return out
180 #
181 # So, it never checked stderr or the return code of the process it
182 # ran to read the pty. Doing something better than that has turned
183 # out to be unexpectedly hard because there are a surprisingly large
184 # variety of errors which appear to be thrown when doing this read.
185 #
186 # Therefore for now we log the errors, but keep on rolling. Volunteers
187 # to help clean this up are welcome and will receive free beverages.
188 LOG.info(
189 'Ignored error while reading from instance console pty: %s', exc
190 )
191 return ''
194@nova.privsep.sys_admin_pctxt.entrypoint
195def create_mdev(physical_device, mdev_type, uuid=None):
196 """Instantiate a mediated device."""
197 if uuid is None: 197 ↛ 198line 197 didn't jump to line 198 because the condition on line 197 was never true
198 uuid = uuidutils.generate_uuid()
199 fpath = '/sys/class/mdev_bus/{0}/mdev_supported_types/{1}/create'
200 fpath = fpath.format(physical_device, mdev_type)
201 with open(fpath, 'w') as f:
202 f.write(uuid)
203 return uuid
206@nova.privsep.sys_admin_pctxt.entrypoint
207def systemd_run_qb_mount(qb_vol, mnt_base, cfg_file=None):
208 """Mount QB volume in separate CGROUP"""
209 # Note(kaisers): Details on why we run without --user at bug #1756823
210 sysdr_cmd = ['systemd-run', '--scope', 'mount.quobyte', '--disable-xattrs',
211 qb_vol, mnt_base]
212 if cfg_file:
213 sysdr_cmd.extend(['-c', cfg_file])
214 return processutils.execute(*sysdr_cmd)
217# NOTE(kaisers): this method is deliberately not wrapped in a privsep entry.
218def unprivileged_qb_mount(qb_vol, mnt_base, cfg_file=None):
219 """Mount QB volume"""
220 mnt_cmd = ['mount.quobyte', '--disable-xattrs', qb_vol, mnt_base]
221 if cfg_file:
222 mnt_cmd.extend(['-c', cfg_file])
223 return processutils.execute(*mnt_cmd)
226@nova.privsep.sys_admin_pctxt.entrypoint
227def umount(mnt_base):
228 """Unmount volume"""
229 unprivileged_umount(mnt_base)
232# NOTE(kaisers): this method is deliberately not wrapped in a privsep entry.
233def unprivileged_umount(mnt_base):
234 """Unmount volume"""
235 umnt_cmd = ['umount', mnt_base]
236 return processutils.execute(*umnt_cmd)
239@nova.privsep.sys_admin_pctxt.entrypoint
240def get_pmem_namespaces():
241 ndctl_cmd = ['ndctl', 'list', '-X']
242 nss_info = processutils.execute(*ndctl_cmd)[0]
243 return nss_info
246@nova.privsep.sys_admin_pctxt.entrypoint
247def cleanup_vpmem(devpath):
248 daxio_cmd = ['daxio', '-z', '-o', '%s' % devpath]
249 processutils.execute(*daxio_cmd)