Coverage for nova/virt/libvirt/storage/lvm.py: 61%

69 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-17 15:08 +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# 

21 

22import os 

23 

24from oslo_concurrency import processutils 

25from oslo_log import log as logging 

26from oslo_utils import units 

27 

28import nova.conf 

29from nova import exception 

30from nova.i18n import _ 

31import nova.privsep.fs 

32 

33CONF = nova.conf.CONF 

34LOG = logging.getLogger(__name__) 

35 

36 

37# TODO(sbauza): Remove the possibility to ask for a sparse LV. 

38def create_volume(vg, lv, size, sparse=False): 

39 """Create LVM image. 

40 

41 Creates a LVM image with given size. 

42 

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'] 

50 

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}) 

61 

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) 

80 

81 

82def get_volume_group_info(vg): 

83 """Return free/used/total space info for a volume group in bytes 

84 

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 """ 

91 

92 out, err = nova.privsep.fs.vginfo(vg) 

93 

94 info = out.split('|') 

95 if len(info) != 2: 

96 raise RuntimeError(_("vg %s must be LVM volume group") % vg) 

97 

98 return {'total': int(info[0]), 

99 'free': int(info[1]), 

100 'used': int(info[0]) - int(info[1])} 

101 

102 

103def list_volumes(vg): 

104 """List logical volumes paths for given volume group. 

105 

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()] 

113 

114 

115def volume_info(path): 

116 """Get logical volume info. 

117 

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()] 

133 

134 if len(info) != 2: 

135 raise RuntimeError(_("Path %s must be LVM logical volume") % path) 

136 

137 return dict(zip(*info)) 

138 

139 

140def get_volume_size(path): 

141 """Get logical volume size in bytes. 

142 

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) 

156 

157 

158def clear_volume(path): 

159 """Obfuscate the logical volume. 

160 

161 :param path: logical volume path 

162 """ 

163 if CONF.libvirt.volume_clear == 'none': 

164 return 

165 

166 volume_clear_size = int(CONF.libvirt.volume_clear_size) * units.Mi 

167 

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 

173 

174 if volume_clear_size != 0 and volume_clear_size < volume_size: 

175 volume_size = volume_clear_size 

176 

177 nova.privsep.fs.clear(path, volume_size, 

178 shred=(CONF.libvirt.volume_clear == 'shred')) 

179 

180 

181def remove_volumes(paths): 

182 """Remove one or more logical volume.""" 

183 

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))