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