19
19
# License for the specific language governing permissions and limitations
20
20
# under the License.
25
28
from nova import exception
26
29
from nova import flags
30
from nova import log as logging
31
from nova.openstack.common import cfg
27
32
from nova import utils
28
33
from nova.virt import images
36
LOG = logging.getLogger(__name__)
40
cfg.StrOpt('image_info_filename_pattern',
41
default='$instances_path/$base_dir_name/%(image)s.info',
42
help='Allows image information files to be stored in '
43
'non-standard locations')
46
flags.DECLARE('instances_path', 'nova.compute.manager')
47
flags.DECLARE('base_dir_name', 'nova.compute.manager')
31
48
FLAGS = flags.FLAGS
49
FLAGS.register_opts(util_opts)
34
52
def execute(*args, **kwargs):
69
87
:param path: Desired location of the COW image
71
89
execute('qemu-img', 'create', '-f', 'qcow2', '-o',
72
'cluster_size=2M,backing_file=%s' % backing_file, path)
90
'backing_file=%s' % backing_file, path)
75
93
def get_disk_size(path):
92
110
:returns: a path to the image's backing store
94
112
out, err = execute('qemu-img', 'info', path)
95
backing_file = [i.split('actual path:')[1].strip()[:-1]
96
for i in out.split('\n') if 0 <= i.find('backing file')]
115
for line in out.split('\n'):
116
if line.startswith('backing file: '):
117
if 'actual path: ' in line:
118
backing_file = line.split('actual path: ')[1][:-1]
120
backing_file = line.split('backing file: ')[1]
98
backing_file = os.path.basename(backing_file[0])
123
backing_file = os.path.basename(backing_file)
99
125
return backing_file
286
312
def fetch_image(context, target, image_id, user_id, project_id):
288
314
images.fetch_to_raw(context, image_id, target, user_id, project_id)
317
def get_info_filename(base_path):
318
"""Construct a filename for storing addtional information about a base
324
base_file = os.path.basename(base_path)
325
return (FLAGS.image_info_filename_pattern
326
% {'image': base_file})
329
def is_valid_info_file(path):
330
"""Test if a given path matches the pattern for info files."""
332
digest_size = hashlib.sha1().digestsize * 2
333
regexp = (FLAGS.image_info_filename_pattern
334
% {'image': ('([0-9a-f]{%(digest_size)d}|'
335
'[0-9a-f]{%(digest_size)d}_sm|'
336
'[0-9a-f]{%(digest_size)d}_[0-9]+)'
337
% {'digest_size': digest_size})})
338
m = re.match(regexp, path)
344
def read_stored_info(base_path, field=None):
345
"""Read information about an image.
347
Returns an empty dictionary if there is no info, just the field value if
348
a field is requested, or the entire dictionary otherwise.
351
info_file = get_info_filename(base_path)
352
if not os.path.exists(info_file):
353
# Special case to handle essex checksums being converted
354
old_filename = base_path + '.sha1'
355
if field == 'sha1' and os.path.exists(old_filename):
356
hash_file = open(old_filename)
357
hash_value = hash_file.read()
360
write_stored_info(base_path, field=field, value=hash_value)
361
os.remove(old_filename)
362
d = {field: hash_value}
368
LOG.info(_('Reading image info file: %s'), info_file)
369
f = open(info_file, 'r')
370
serialized = f.read().rstrip()
372
LOG.info(_('Read: %s'), serialized)
375
d = json.loads(serialized)
377
except ValueError, e:
378
LOG.error(_('Error reading image info file %(filename)s: '
380
{'filename': info_file,
385
return d.get(field, None)
389
def write_stored_info(target, field=None, value=None):
390
"""Write information about an image."""
395
info_file = get_info_filename(target)
396
ensure_tree(os.path.dirname(info_file))
398
d = read_stored_info(info_file)
400
serialized = json.dumps(d)
402
LOG.info(_('Writing image info file: %s'), info_file)
403
LOG.info(_('Wrote: %s'), serialized)
404
f = open(info_file, 'w')