-
Notifications
You must be signed in to change notification settings - Fork 1
/
cbt_bitmap.py
106 lines (90 loc) · 3.04 KB
/
cbt_bitmap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""
Provides a class wrapping the return value of VDI.list_changed_blocks for
extracting useful information from the CBT bitmap.
"""
import base64
from bitstring import BitArray
# 64K blocks
BLOCK_SIZE = 64 * 1024
def _bitmap_to_extents(cbt_bitmap):
"""
Given a CBT bitmap with 64K block size, this function will return the
list of changed (offset in bytes, length in bytes) extents.
Args:
cbt_bitmap (bytes-like object): the bitmap to turn into extents
Returns:
An iterator containing the increasingly ordered sequence of the
non-overlapping extents corresponding to this bitmap.
"""
start = None
bitmap = BitArray(cbt_bitmap)
for i in range(0, len(bitmap)):
if bitmap[i]:
if start is None:
start = i
length = 1
else:
length += 1
else:
if start is not None:
yield (start * BLOCK_SIZE, length * BLOCK_SIZE)
start = None
if start is not None:
yield (start * BLOCK_SIZE, length * BLOCK_SIZE)
def _get_changed_blocks_size(cbt_bitmap):
"""
Returns the overall size of the changed 64K blocks in the
given bitmap in bytes.
"""
bitmap = BitArray(cbt_bitmap)
modified = 0
for bit in bitmap:
if bit:
modified += 1
return modified * BLOCK_SIZE
def _get_disk_size(cbt_bitmap):
bitmap = BitArray(cbt_bitmap)
return len(bitmap) * BLOCK_SIZE
def _get_extent_stats(extents):
average_length = None
max_length = None
min_length = None
changed_blocks_size = 0
n = 0
for (offset, length) in extents:
n += 1
changed_blocks_size += length
max_length = length if max_length is None else max(max_length, length)
min_length = length if min_length is None else min(min_length, length)
average_length = None if n == 0 else (changed_blocks_size / n)
return { 'average_extent_length': average_length,
'max_extent_length': max_length,
'min_extent_length': min_length,
'changed_blocks_size': changed_blocks_size,
'extents': n }
class CbtBitmap(object):
"""
Wraps a base64-encoded CBT bitmap, as returned by
VDI.list_changed_blocks, and provides methods for extracting various
data from the bitmap.
"""
def __init__(self, cbt_bitmap_b64):
"""
Decodes the given base64-encoded CBT bitmap.
"""
self.bitmap = base64.b64decode(cbt_bitmap_b64)
def get_extents(self):
"""
Returns an iterator containing the increasingly ordered sequence
of the non-overlapping extents corresponding to this bitmap.
"""
extents = _bitmap_to_extents(self.bitmap)
return extents
def get_statistics(self):
"""
Return the size of the disk, and the total size of the changed
blocks in a dictionary.
"""
stats = _get_extent_stats(self.get_extents())
stats["size"] = _get_disk_size(self.bitmap)
return stats