Revision 6b90c22e
b/lib/bdev.py | ||
---|---|---|
545 | 545 |
(self.dev_path, result.output)) |
546 | 546 |
|
547 | 547 |
|
548 |
class DRBD8Status(object): |
|
549 |
"""A DRBD status representation class. |
|
550 |
|
|
551 |
Note that this doesn't support unconfigured devices (cs:Unconfigured). |
|
552 |
|
|
553 |
""" |
|
554 |
LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+st:([^/]+)/(\S+)" |
|
555 |
"\s+ds:([^/]+)/(\S+)\s+.*$") |
|
556 |
SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*" |
|
557 |
"\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$") |
|
558 |
|
|
559 |
def __init__(self, procline): |
|
560 |
m = self.LINE_RE.match(procline) |
|
561 |
if not m: |
|
562 |
raise errors.BlockDeviceError("Can't parse input data '%s'" % procline) |
|
563 |
self.cstatus = m.group(1) |
|
564 |
self.lrole = m.group(2) |
|
565 |
self.rrole = m.group(3) |
|
566 |
self.ldisk = m.group(4) |
|
567 |
self.rdisk = m.group(5) |
|
568 |
|
|
569 |
self.is_standalone = self.cstatus == "StandAlone" |
|
570 |
self.is_wfconn = self.cstatus == "WFConnection" |
|
571 |
self.is_connected = self.cstatus == "Connected" |
|
572 |
self.is_primary = self.lrole == "Primary" |
|
573 |
self.is_secondary = self.lrole == "Secondary" |
|
574 |
self.peer_primary = self.rrole == "Primary" |
|
575 |
self.peer_secondary = self.rrole == "Secondary" |
|
576 |
self.both_primary = self.is_primary and self.peer_primary |
|
577 |
self.both_secondary = self.is_secondary and self.peer_secondary |
|
578 |
|
|
579 |
self.is_diskless = self.ldisk == "Diskless" |
|
580 |
self.is_disk_uptodate = self.ldisk == "UpToDate" |
|
581 |
|
|
582 |
m = self.SYNC_RE.match(procline) |
|
583 |
if m: |
|
584 |
self.sync_percent = float(m.group(1)) |
|
585 |
hours = int(m.group(2)) |
|
586 |
minutes = int(m.group(3)) |
|
587 |
seconds = int(m.group(4)) |
|
588 |
self.est_time = hours * 3600 + minutes * 60 + seconds |
|
589 |
else: |
|
590 |
self.sync_percent = None |
|
591 |
self.est_time = None |
|
592 |
|
|
593 |
self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget" |
|
594 |
self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource" |
|
595 |
self.is_resync = self.is_sync_target or self.is_sync_source |
|
596 |
|
|
597 |
|
|
548 | 598 |
class BaseDRBD(BlockDev): |
549 | 599 |
"""Base DRBD class. |
550 | 600 |
|
... | ... | |
560 | 610 |
_ST_WFCONNECTION = "WFConnection" |
561 | 611 |
_ST_CONNECTED = "Connected" |
562 | 612 |
|
613 |
_STATUS_FILE = "/proc/drbd" |
|
614 |
|
|
563 | 615 |
@staticmethod |
564 |
def _GetProcData(): |
|
616 |
def _GetProcData(filename=_STATUS_FILE):
|
|
565 | 617 |
"""Return data from /proc/drbd. |
566 | 618 |
|
567 | 619 |
""" |
568 |
stat = open("/proc/drbd", "r")
|
|
620 |
stat = open(filename, "r")
|
|
569 | 621 |
try: |
570 | 622 |
data = stat.read().splitlines() |
571 | 623 |
finally: |
572 | 624 |
stat.close() |
573 | 625 |
if not data: |
574 |
raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
|
|
626 |
raise errors.BlockDeviceError("Can't read any data from %s" % filename)
|
|
575 | 627 |
return data |
576 | 628 |
|
577 | 629 |
@staticmethod |
... | ... | |
1089 | 1141 |
(result.fail_reason, result.output)) |
1090 | 1142 |
return not result.failed and children_result |
1091 | 1143 |
|
1144 |
def GetProcStatus(self): |
|
1145 |
"""Return device data from /proc. |
|
1146 |
|
|
1147 |
""" |
|
1148 |
if self.minor is None: |
|
1149 |
raise errors.BlockDeviceError("GetStats() called while not attached") |
|
1150 |
proc_info = self._MassageProcData(self._GetProcData()) |
|
1151 |
if self.minor not in proc_info: |
|
1152 |
raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" % |
|
1153 |
self.minor) |
|
1154 |
return DRBD8Status(proc_info[self.minor]) |
|
1155 |
|
|
1092 | 1156 |
def GetSyncStatus(self): |
1093 | 1157 |
"""Returns the sync status of the device. |
1094 | 1158 |
|
... | ... | |
1109 | 1173 |
""" |
1110 | 1174 |
if self.minor is None and not self.Attach(): |
1111 | 1175 |
raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus") |
1112 |
proc_info = self._MassageProcData(self._GetProcData()) |
|
1113 |
if self.minor not in proc_info: |
|
1114 |
raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" % |
|
1115 |
self.minor) |
|
1116 |
line = proc_info[self.minor] |
|
1117 |
match = re.match("^.*sync'ed: *([0-9.]+)%.*" |
|
1118 |
" finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line) |
|
1119 |
if match: |
|
1120 |
sync_percent = float(match.group(1)) |
|
1121 |
hours = int(match.group(2)) |
|
1122 |
minutes = int(match.group(3)) |
|
1123 |
seconds = int(match.group(4)) |
|
1124 |
est_time = hours * 3600 + minutes * 60 + seconds |
|
1125 |
else: |
|
1126 |
sync_percent = None |
|
1127 |
est_time = None |
|
1128 |
match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line) |
|
1129 |
if not match: |
|
1130 |
raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" % |
|
1131 |
self.minor) |
|
1132 |
client_state = match.group(1) |
|
1133 |
local_disk_state = match.group(2) |
|
1134 |
ldisk = local_disk_state != "UpToDate" |
|
1135 |
is_degraded = client_state != "Connected" |
|
1136 |
return sync_percent, est_time, is_degraded or ldisk, ldisk |
|
1176 |
stats = self.GetProcStatus() |
|
1177 |
ldisk = not stats.is_disk_uptodate |
|
1178 |
is_degraded = not stats.is_connected |
|
1179 |
return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk |
|
1137 | 1180 |
|
1138 | 1181 |
def Open(self, force=False): |
1139 | 1182 |
"""Make the local state primary. |
b/test/data/proc_drbd8.txt | ||
---|---|---|
1 |
version: 8.0.12 (api:86/proto:86) |
|
2 |
GIT-hash: 5c9f89594553e32adb87d9638dce591782f947e3 build by XXX |
|
3 |
0: cs:Connected st:Primary/Secondary ds:UpToDate/UpToDate C r--- |
|
4 |
ns:4375577 nr:0 dw:4446279 dr:674 al:1067 bm:69 lo:0 pe:0 ua:0 ap:0 |
|
5 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
6 |
act_log: used:0/257 hits:793749 misses:1067 starving:0 dirty:0 changed:1067 |
|
7 |
1: cs:Connected st:Secondary/Primary ds:UpToDate/UpToDate C r--- |
|
8 |
ns:738320 nr:0 dw:738320 dr:554400 al:67 bm:0 lo:0 pe:0 ua:0 ap:0 |
|
9 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
10 |
act_log: used:0/257 hits:92464 misses:67 starving:0 dirty:0 changed:67 |
|
11 |
2: cs:Unconfigured |
|
12 |
4: cs:WFConnection st:Primary/Unknown ds:UpToDate/DUnknown C r--- |
|
13 |
ns:738320 nr:0 dw:738320 dr:554400 al:67 bm:0 lo:0 pe:0 ua:0 ap:0 |
|
14 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
15 |
act_log: used:0/257 hits:92464 misses:67 starving:0 dirty:0 changed:67 |
|
16 |
5: cs:Connected st:Primary/Secondary ds:UpToDate/Diskless C r--- |
|
17 |
ns:4375581 nr:0 dw:4446283 dr:674 al:1069 bm:69 lo:0 pe:0 ua:0 ap:0 |
|
18 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
19 |
act_log: used:0/257 hits:793750 misses:1069 starving:0 dirty:0 changed:1069 |
|
20 |
6: cs:Connected st:Secondary/Primary ds:Diskless/UpToDate C r--- |
|
21 |
ns:0 nr:4375581 dw:5186925 dr:327 al:75 bm:214 lo:0 pe:0 ua:0 ap:0 |
|
22 |
7: cs:WFConnection st:Secondary/Unknown ds:UpToDate/DUnknown C r--- |
|
23 |
ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 |
|
24 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
25 |
act_log: used:0/257 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
26 |
8: cs:StandAlone st:Secondary/Unknown ds:UpToDate/DUnknown r--- |
|
27 |
ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 |
|
28 |
resync: used:0/61 hits:0 misses:0 starving:0 dirty:0 changed:0 |
|
29 |
act_log: used:0/257 hits:0 misses:0 starving:0 dirty:0 changed:0 |
b/test/ganeti.bdev_unittest.py | ||
---|---|---|
26 | 26 |
import unittest |
27 | 27 |
|
28 | 28 |
from ganeti import bdev |
29 |
from ganeti import errors |
|
29 | 30 |
|
30 | 31 |
|
31 | 32 |
class TestDRBD8Runner(unittest.TestCase): |
... | ... | |
108 | 109 |
"remote_addr" not in result), |
109 | 110 |
"Should not find network info") |
110 | 111 |
|
112 |
|
|
113 |
class TestDRBD8Status(unittest.TestCase): |
|
114 |
"""Testing case for DRBD8 /proc status""" |
|
115 |
|
|
116 |
def setUp(self): |
|
117 |
"""Read in txt data""" |
|
118 |
self.proc_data = bdev.DRBD8._GetProcData(filename="data/proc_drbd8.txt") |
|
119 |
self.mass_data = bdev.DRBD8._MassageProcData(self.proc_data) |
|
120 |
|
|
121 |
def testMinorNotFound(self): |
|
122 |
"""Test not-found-minor in /proc""" |
|
123 |
self.failUnless(9 not in self.mass_data) |
|
124 |
|
|
125 |
def testLineNotMatch(self): |
|
126 |
"""Test wrong line passed to DRBD8Status""" |
|
127 |
self.assertRaises(errors.BlockDeviceError, bdev.DRBD8Status, "foo") |
|
128 |
|
|
129 |
def testMinor0(self): |
|
130 |
"""Test connected, primary device""" |
|
131 |
stats = bdev.DRBD8Status(self.mass_data[0]) |
|
132 |
self.failUnless(stats.is_connected and stats.is_primary and |
|
133 |
stats.peer_secondary and stats.is_disk_uptodate) |
|
134 |
|
|
135 |
def testMinor1(self): |
|
136 |
"""Test connected, secondary device""" |
|
137 |
stats = bdev.DRBD8Status(self.mass_data[1]) |
|
138 |
self.failUnless(stats.is_connected and stats.is_secondary and |
|
139 |
stats.peer_primary and stats.is_disk_uptodate) |
|
140 |
|
|
141 |
def testMinor4(self): |
|
142 |
"""Test WFconn device""" |
|
143 |
stats = bdev.DRBD8Status(self.mass_data[4]) |
|
144 |
self.failUnless(stats.is_wfconn and stats.is_primary and |
|
145 |
stats.rrole == 'Unknown' and |
|
146 |
stats.is_disk_uptodate) |
|
147 |
|
|
148 |
def testMinor6(self): |
|
149 |
"""Test diskless device""" |
|
150 |
stats = bdev.DRBD8Status(self.mass_data[6]) |
|
151 |
self.failUnless(stats.is_connected and stats.is_secondary and |
|
152 |
stats.peer_primary and stats.is_diskless) |
|
153 |
|
|
154 |
def testMinor8(self): |
|
155 |
"""Test standalone device""" |
|
156 |
stats = bdev.DRBD8Status(self.mass_data[8]) |
|
157 |
self.failUnless(stats.is_standalone and |
|
158 |
stats.rrole == 'Unknown' and |
|
159 |
stats.is_disk_uptodate) |
|
160 |
|
|
111 | 161 |
if __name__ == '__main__': |
112 | 162 |
unittest.main() |
Also available in: Unified diff