Coverage for nova/virt/libvirt/storage/lvm.py: 61%
69 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
1# Copyright 2010 United States Government as represented by the
2# Administrator of the National Aeronautics and Space Administration.
3# All Rights Reserved.
4# Copyright (c) 2010 Citrix Systems, Inc.
5# Copyright (c) 2011 Piston Cloud Computing, Inc
6# Copyright (c) 2011 OpenStack Foundation
7# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
8#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License. You may obtain
11# a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18# License for the specific language governing permissions and limitations
19# under the License.
20#
22import os
24from oslo_concurrency import processutils
25from oslo_log import log as logging
26from oslo_utils import units
28import nova.conf
29from nova import exception
30from nova.i18n import _
31import nova.privsep.fs
33CONF = nova.conf.CONF
34LOG = logging.getLogger(__name__)
37# TODO(sbauza): Remove the possibility to ask for a sparse LV.
38def create_volume(vg, lv, size, sparse=False):
39 """Create LVM image.
41 Creates a LVM image with given size.
43 :param vg: existing volume group which should hold this image
44 :param lv: name for this image (logical volume)
45 :size: size of image in bytes
46 :sparse: create sparse logical volume
47 """
48 vg_info = get_volume_group_info(vg)
49 free_space = vg_info['free']
51 def check_size(vg, lv, size):
52 if size > free_space:
53 raise RuntimeError(_('Insufficient Space on Volume Group %(vg)s.'
54 ' Only %(free_space)db available,'
55 ' but %(size)d bytes required'
56 ' by volume %(lv)s.') %
57 {'vg': vg,
58 'free_space': free_space,
59 'size': size,
60 'lv': lv})
62 if sparse:
63 preallocated_space = 64 * units.Mi
64 check_size(vg, lv, preallocated_space)
65 if free_space < size:
66 LOG.warning('Volume group %(vg)s will not be able'
67 ' to hold sparse volume %(lv)s.'
68 ' Virtual volume size is %(size)d bytes,'
69 ' but free space on volume group is'
70 ' only %(free_space)db.',
71 {'vg': vg,
72 'free_space': free_space,
73 'size': size,
74 'lv': lv})
75 nova.privsep.fs.lvcreate(size, lv, vg,
76 preallocated=preallocated_space)
77 else:
78 check_size(vg, lv, size)
79 nova.privsep.fs.lvcreate(size, lv, vg)
82def get_volume_group_info(vg):
83 """Return free/used/total space info for a volume group in bytes
85 :param vg: volume group name
86 :returns: A dict containing:
87 :total: How big the filesystem is (in bytes)
88 :free: How much space is free (in bytes)
89 :used: How much space is used (in bytes)
90 """
92 out, err = nova.privsep.fs.vginfo(vg)
94 info = out.split('|')
95 if len(info) != 2:
96 raise RuntimeError(_("vg %s must be LVM volume group") % vg)
98 return {'total': int(info[0]),
99 'free': int(info[1]),
100 'used': int(info[0]) - int(info[1])}
103def list_volumes(vg):
104 """List logical volumes paths for given volume group.
106 :param vg: volume group name
107 :returns: Return a logical volume list for given volume group
108 : Data format example
109 : ['volume-aaa', 'volume-bbb', 'volume-ccc']
110 """
111 out, err = nova.privsep.fs.lvlist(vg)
112 return [line.strip() for line in out.splitlines()]
115def volume_info(path):
116 """Get logical volume info.
118 :param path: logical volume path
119 :returns: Return a dict object including info of given logical volume
120 : Data format example
121 : {'#Seg': '1', 'Move': '', 'Log': '', 'Meta%': '', 'Min': '-1',
122 : ...
123 : 'Free': '9983', 'LV': 'volume-aaa', 'Host': 'xyz.com',
124 : 'Active': 'active', 'Path': '/dev/vg/volume-aaa', '#LV': '3',
125 : 'Maj': '-1', 'VSize': '50.00g', 'VFree': '39.00g', 'Pool': '',
126 : 'VG Tags': '', 'KMaj': '253', 'Convert': '', 'LProfile': '',
127 : '#Ext': '12799', 'Attr': '-wi-a-----', 'VG': 'vg',
128 : ...
129 : 'LSize': '1.00g', '#PV': '1', '#VMdaCps': 'unmanaged'}
130 """
131 out, err = nova.privsep.fs.lvinfo(path)
132 info = [line.split('|') for line in out.splitlines()]
134 if len(info) != 2:
135 raise RuntimeError(_("Path %s must be LVM logical volume") % path)
137 return dict(zip(*info))
140def get_volume_size(path):
141 """Get logical volume size in bytes.
143 :param path: logical volume path
144 :raises: processutils.ProcessExecutionError if getting the volume size
145 fails in some unexpected way.
146 :raises: exception.VolumeBDMPathNotFound if the volume path does not exist.
147 """
148 try:
149 out, _err = nova.privsep.fs.blockdev_size(path)
150 except processutils.ProcessExecutionError:
151 if not os.path.exists(path):
152 raise exception.VolumeBDMPathNotFound(path=path)
153 else:
154 raise
155 return int(out)
158def clear_volume(path):
159 """Obfuscate the logical volume.
161 :param path: logical volume path
162 """
163 if CONF.libvirt.volume_clear == 'none':
164 return
166 volume_clear_size = int(CONF.libvirt.volume_clear_size) * units.Mi
168 try:
169 volume_size = get_volume_size(path)
170 except exception.VolumeBDMPathNotFound:
171 LOG.warning('Ignoring missing logical volume %(path)s', {'path': path})
172 return
174 if volume_clear_size != 0 and volume_clear_size < volume_size:
175 volume_size = volume_clear_size
177 nova.privsep.fs.clear(path, volume_size,
178 shred=(CONF.libvirt.volume_clear == 'shred'))
181def remove_volumes(paths):
182 """Remove one or more logical volume."""
184 errors = []
185 for path in paths:
186 clear_volume(path)
187 try:
188 nova.privsep.fs.lvremove(path)
189 except processutils.ProcessExecutionError as exp:
190 errors.append(str(exp))
191 if errors:
192 raise exception.VolumesNotRemoved(reason=(', ').join(errors))