Statistics
| Branch: | Tag: | Revision:

root / lib / block / drbd.py @ d01e51a5

History | View | Annotate | Download (46.5 kB)

1 89ff748d Thomas Thrainer
#
2 89ff748d Thomas Thrainer
#
3 89ff748d Thomas Thrainer
4 89ff748d Thomas Thrainer
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 89ff748d Thomas Thrainer
#
6 89ff748d Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 89ff748d Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 89ff748d Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 89ff748d Thomas Thrainer
# (at your option) any later version.
10 89ff748d Thomas Thrainer
#
11 89ff748d Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 89ff748d Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 89ff748d Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 89ff748d Thomas Thrainer
# General Public License for more details.
15 89ff748d Thomas Thrainer
#
16 89ff748d Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 89ff748d Thomas Thrainer
# along with this program; if not, write to the Free Software
18 89ff748d Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 89ff748d Thomas Thrainer
# 02110-1301, USA.
20 89ff748d Thomas Thrainer
21 89ff748d Thomas Thrainer
22 89ff748d Thomas Thrainer
"""DRBD block device related functionality"""
23 89ff748d Thomas Thrainer
24 89ff748d Thomas Thrainer
import errno
25 89ff748d Thomas Thrainer
import logging
26 89ff748d Thomas Thrainer
import pyparsing as pyp
27 89ff748d Thomas Thrainer
import re
28 89ff748d Thomas Thrainer
import shlex
29 89ff748d Thomas Thrainer
import time
30 89ff748d Thomas Thrainer
31 89ff748d Thomas Thrainer
from ganeti import constants
32 89ff748d Thomas Thrainer
from ganeti import utils
33 89ff748d Thomas Thrainer
from ganeti import errors
34 89ff748d Thomas Thrainer
from ganeti import compat
35 89ff748d Thomas Thrainer
from ganeti import netutils
36 89ff748d Thomas Thrainer
from ganeti import objects
37 89ff748d Thomas Thrainer
from ganeti.block import base
38 89ff748d Thomas Thrainer
39 89ff748d Thomas Thrainer
40 89ff748d Thomas Thrainer
# Size of reads in _CanReadDevice
41 89ff748d Thomas Thrainer
42 89ff748d Thomas Thrainer
_DEVICE_READ_SIZE = 128 * 1024
43 89ff748d Thomas Thrainer
44 89ff748d Thomas Thrainer
45 2fe690f1 Thomas Thrainer
class DRBD8Status(object): # pylint: disable=R0902
46 fd300bc7 Thomas Thrainer
  """A DRBD status representation class.
47 89ff748d Thomas Thrainer

48 fd300bc7 Thomas Thrainer
  Note that this class is meant to be used to parse one of the entries returned
49 dcc4579c Thomas Thrainer
  from L{DRBD8Info._JoinLinesPerMinor}.
50 fd300bc7 Thomas Thrainer

51 fd300bc7 Thomas Thrainer
  """
52 fd300bc7 Thomas Thrainer
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
53 fd300bc7 Thomas Thrainer
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
54 fd300bc7 Thomas Thrainer
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
55 fd300bc7 Thomas Thrainer
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
56 fd300bc7 Thomas Thrainer
                       # Due to a bug in drbd in the kernel, introduced in
57 fd300bc7 Thomas Thrainer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
58 fd300bc7 Thomas Thrainer
                       "(?:\s|M)"
59 fd300bc7 Thomas Thrainer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
60 fd300bc7 Thomas Thrainer
61 fd300bc7 Thomas Thrainer
  CS_UNCONFIGURED = "Unconfigured"
62 fd300bc7 Thomas Thrainer
  CS_STANDALONE = "StandAlone"
63 fd300bc7 Thomas Thrainer
  CS_WFCONNECTION = "WFConnection"
64 fd300bc7 Thomas Thrainer
  CS_WFREPORTPARAMS = "WFReportParams"
65 fd300bc7 Thomas Thrainer
  CS_CONNECTED = "Connected"
66 fd300bc7 Thomas Thrainer
  CS_STARTINGSYNCS = "StartingSyncS"
67 fd300bc7 Thomas Thrainer
  CS_STARTINGSYNCT = "StartingSyncT"
68 fd300bc7 Thomas Thrainer
  CS_WFBITMAPS = "WFBitMapS"
69 fd300bc7 Thomas Thrainer
  CS_WFBITMAPT = "WFBitMapT"
70 fd300bc7 Thomas Thrainer
  CS_WFSYNCUUID = "WFSyncUUID"
71 fd300bc7 Thomas Thrainer
  CS_SYNCSOURCE = "SyncSource"
72 fd300bc7 Thomas Thrainer
  CS_SYNCTARGET = "SyncTarget"
73 fd300bc7 Thomas Thrainer
  CS_PAUSEDSYNCS = "PausedSyncS"
74 fd300bc7 Thomas Thrainer
  CS_PAUSEDSYNCT = "PausedSyncT"
75 fd300bc7 Thomas Thrainer
  CSET_SYNC = compat.UniqueFrozenset([
76 fd300bc7 Thomas Thrainer
    CS_WFREPORTPARAMS,
77 fd300bc7 Thomas Thrainer
    CS_STARTINGSYNCS,
78 fd300bc7 Thomas Thrainer
    CS_STARTINGSYNCT,
79 fd300bc7 Thomas Thrainer
    CS_WFBITMAPS,
80 fd300bc7 Thomas Thrainer
    CS_WFBITMAPT,
81 fd300bc7 Thomas Thrainer
    CS_WFSYNCUUID,
82 fd300bc7 Thomas Thrainer
    CS_SYNCSOURCE,
83 fd300bc7 Thomas Thrainer
    CS_SYNCTARGET,
84 fd300bc7 Thomas Thrainer
    CS_PAUSEDSYNCS,
85 fd300bc7 Thomas Thrainer
    CS_PAUSEDSYNCT,
86 fd300bc7 Thomas Thrainer
    ])
87 fd300bc7 Thomas Thrainer
88 fd300bc7 Thomas Thrainer
  DS_DISKLESS = "Diskless"
89 fd300bc7 Thomas Thrainer
  DS_ATTACHING = "Attaching" # transient state
90 fd300bc7 Thomas Thrainer
  DS_FAILED = "Failed" # transient state, next: diskless
91 fd300bc7 Thomas Thrainer
  DS_NEGOTIATING = "Negotiating" # transient state
92 fd300bc7 Thomas Thrainer
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
93 fd300bc7 Thomas Thrainer
  DS_OUTDATED = "Outdated"
94 fd300bc7 Thomas Thrainer
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
95 fd300bc7 Thomas Thrainer
  DS_CONSISTENT = "Consistent"
96 fd300bc7 Thomas Thrainer
  DS_UPTODATE = "UpToDate" # normal state
97 fd300bc7 Thomas Thrainer
98 fd300bc7 Thomas Thrainer
  RO_PRIMARY = "Primary"
99 fd300bc7 Thomas Thrainer
  RO_SECONDARY = "Secondary"
100 fd300bc7 Thomas Thrainer
  RO_UNKNOWN = "Unknown"
101 fd300bc7 Thomas Thrainer
102 fd300bc7 Thomas Thrainer
  def __init__(self, procline):
103 fd300bc7 Thomas Thrainer
    u = self.UNCONF_RE.match(procline)
104 fd300bc7 Thomas Thrainer
    if u:
105 fd300bc7 Thomas Thrainer
      self.cstatus = self.CS_UNCONFIGURED
106 fd300bc7 Thomas Thrainer
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
107 fd300bc7 Thomas Thrainer
    else:
108 fd300bc7 Thomas Thrainer
      m = self.LINE_RE.match(procline)
109 fd300bc7 Thomas Thrainer
      if not m:
110 fd300bc7 Thomas Thrainer
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
111 fd300bc7 Thomas Thrainer
      self.cstatus = m.group(1)
112 fd300bc7 Thomas Thrainer
      self.lrole = m.group(2)
113 fd300bc7 Thomas Thrainer
      self.rrole = m.group(3)
114 fd300bc7 Thomas Thrainer
      self.ldisk = m.group(4)
115 fd300bc7 Thomas Thrainer
      self.rdisk = m.group(5)
116 fd300bc7 Thomas Thrainer
117 fd300bc7 Thomas Thrainer
    # end reading of data from the LINE_RE or UNCONF_RE
118 fd300bc7 Thomas Thrainer
119 fd300bc7 Thomas Thrainer
    self.is_standalone = self.cstatus == self.CS_STANDALONE
120 fd300bc7 Thomas Thrainer
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
121 fd300bc7 Thomas Thrainer
    self.is_connected = self.cstatus == self.CS_CONNECTED
122 2fe690f1 Thomas Thrainer
    self.is_unconfigured = self.cstatus == self.CS_UNCONFIGURED
123 fd300bc7 Thomas Thrainer
    self.is_primary = self.lrole == self.RO_PRIMARY
124 fd300bc7 Thomas Thrainer
    self.is_secondary = self.lrole == self.RO_SECONDARY
125 fd300bc7 Thomas Thrainer
    self.peer_primary = self.rrole == self.RO_PRIMARY
126 fd300bc7 Thomas Thrainer
    self.peer_secondary = self.rrole == self.RO_SECONDARY
127 fd300bc7 Thomas Thrainer
    self.both_primary = self.is_primary and self.peer_primary
128 fd300bc7 Thomas Thrainer
    self.both_secondary = self.is_secondary and self.peer_secondary
129 fd300bc7 Thomas Thrainer
130 fd300bc7 Thomas Thrainer
    self.is_diskless = self.ldisk == self.DS_DISKLESS
131 fd300bc7 Thomas Thrainer
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
132 fd300bc7 Thomas Thrainer
133 fd300bc7 Thomas Thrainer
    self.is_in_resync = self.cstatus in self.CSET_SYNC
134 fd300bc7 Thomas Thrainer
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
135 fd300bc7 Thomas Thrainer
136 fd300bc7 Thomas Thrainer
    m = self.SYNC_RE.match(procline)
137 fd300bc7 Thomas Thrainer
    if m:
138 fd300bc7 Thomas Thrainer
      self.sync_percent = float(m.group(1))
139 fd300bc7 Thomas Thrainer
      hours = int(m.group(2))
140 fd300bc7 Thomas Thrainer
      minutes = int(m.group(3))
141 fd300bc7 Thomas Thrainer
      seconds = int(m.group(4))
142 fd300bc7 Thomas Thrainer
      self.est_time = hours * 3600 + minutes * 60 + seconds
143 fd300bc7 Thomas Thrainer
    else:
144 fd300bc7 Thomas Thrainer
      # we have (in this if branch) no percent information, but if
145 fd300bc7 Thomas Thrainer
      # we're resyncing we need to 'fake' a sync percent information,
146 fd300bc7 Thomas Thrainer
      # as this is how cmdlib determines if it makes sense to wait for
147 fd300bc7 Thomas Thrainer
      # resyncing or not
148 fd300bc7 Thomas Thrainer
      if self.is_in_resync:
149 fd300bc7 Thomas Thrainer
        self.sync_percent = 0
150 fd300bc7 Thomas Thrainer
      else:
151 fd300bc7 Thomas Thrainer
        self.sync_percent = None
152 fd300bc7 Thomas Thrainer
      self.est_time = None
153 fd300bc7 Thomas Thrainer
154 fd300bc7 Thomas Thrainer
155 2fe690f1 Thomas Thrainer
class DRBD8Info(object):
156 2fe690f1 Thomas Thrainer
  """Represents information DRBD exports (usually via /proc/drbd).
157 2fe690f1 Thomas Thrainer

158 2fe690f1 Thomas Thrainer
  An instance of this class is created by one of the CreateFrom... methods.
159 2fe690f1 Thomas Thrainer

160 2fe690f1 Thomas Thrainer
  """
161 2fe690f1 Thomas Thrainer
162 2fe690f1 Thomas Thrainer
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
163 2fe690f1 Thomas Thrainer
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
164 2fe690f1 Thomas Thrainer
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
165 2fe690f1 Thomas Thrainer
166 2fe690f1 Thomas Thrainer
  def __init__(self, lines):
167 2fe690f1 Thomas Thrainer
    self._version = self._ParseVersion(lines)
168 dcc4579c Thomas Thrainer
    self._minors, self._line_per_minor = self._JoinLinesPerMinor(lines)
169 2fe690f1 Thomas Thrainer
170 2fe690f1 Thomas Thrainer
  def GetVersion(self):
171 2fe690f1 Thomas Thrainer
    """Return the DRBD version.
172 2fe690f1 Thomas Thrainer

173 2fe690f1 Thomas Thrainer
    This will return a dict with keys:
174 2fe690f1 Thomas Thrainer
      - k_major
175 2fe690f1 Thomas Thrainer
      - k_minor
176 2fe690f1 Thomas Thrainer
      - k_point
177 2fe690f1 Thomas Thrainer
      - api
178 2fe690f1 Thomas Thrainer
      - proto
179 2fe690f1 Thomas Thrainer
      - proto2 (only on drbd > 8.2.X)
180 2fe690f1 Thomas Thrainer

181 2fe690f1 Thomas Thrainer
    """
182 2fe690f1 Thomas Thrainer
    return self._version
183 2fe690f1 Thomas Thrainer
184 2fe690f1 Thomas Thrainer
  def GetMinors(self):
185 2fe690f1 Thomas Thrainer
    """Return a list of minor for which information is available.
186 2fe690f1 Thomas Thrainer

187 2fe690f1 Thomas Thrainer
    This list is ordered in exactly the order which was found in the underlying
188 2fe690f1 Thomas Thrainer
    data.
189 2fe690f1 Thomas Thrainer

190 2fe690f1 Thomas Thrainer
    """
191 2fe690f1 Thomas Thrainer
    return self._minors
192 2fe690f1 Thomas Thrainer
193 2fe690f1 Thomas Thrainer
  def HasMinorStatus(self, minor):
194 2fe690f1 Thomas Thrainer
    return minor in self._line_per_minor
195 2fe690f1 Thomas Thrainer
196 2fe690f1 Thomas Thrainer
  def GetMinorStatus(self, minor):
197 2fe690f1 Thomas Thrainer
    return DRBD8Status(self._line_per_minor[minor])
198 2fe690f1 Thomas Thrainer
199 2fe690f1 Thomas Thrainer
  def _ParseVersion(self, lines):
200 2fe690f1 Thomas Thrainer
    first_line = lines[0].strip()
201 2fe690f1 Thomas Thrainer
    version = self._VERSION_RE.match(first_line)
202 2fe690f1 Thomas Thrainer
    if not version:
203 2fe690f1 Thomas Thrainer
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
204 2fe690f1 Thomas Thrainer
                                    first_line)
205 2fe690f1 Thomas Thrainer
206 2fe690f1 Thomas Thrainer
    values = version.groups()
207 2fe690f1 Thomas Thrainer
    retval = {
208 2fe690f1 Thomas Thrainer
      "k_major": int(values[0]),
209 2fe690f1 Thomas Thrainer
      "k_minor": int(values[1]),
210 2fe690f1 Thomas Thrainer
      "k_point": int(values[2]),
211 2fe690f1 Thomas Thrainer
      "api": int(values[3]),
212 2fe690f1 Thomas Thrainer
      "proto": int(values[4]),
213 2fe690f1 Thomas Thrainer
      }
214 2fe690f1 Thomas Thrainer
    if values[5] is not None:
215 2fe690f1 Thomas Thrainer
      retval["proto2"] = values[5]
216 2fe690f1 Thomas Thrainer
217 2fe690f1 Thomas Thrainer
    return retval
218 2fe690f1 Thomas Thrainer
219 dcc4579c Thomas Thrainer
  def _JoinLinesPerMinor(self, lines):
220 2fe690f1 Thomas Thrainer
    """Transform the raw lines into a dictionary based on the minor.
221 2fe690f1 Thomas Thrainer

222 2fe690f1 Thomas Thrainer
    @return: a dictionary of minor: joined lines from /proc/drbd
223 2fe690f1 Thomas Thrainer
        for that minor
224 2fe690f1 Thomas Thrainer

225 2fe690f1 Thomas Thrainer
    """
226 2fe690f1 Thomas Thrainer
    minors = []
227 2fe690f1 Thomas Thrainer
    results = {}
228 2fe690f1 Thomas Thrainer
    old_minor = old_line = None
229 2fe690f1 Thomas Thrainer
    for line in lines:
230 2fe690f1 Thomas Thrainer
      if not line: # completely empty lines, as can be returned by drbd8.0+
231 2fe690f1 Thomas Thrainer
        continue
232 2fe690f1 Thomas Thrainer
      lresult = self._VALID_LINE_RE.match(line)
233 2fe690f1 Thomas Thrainer
      if lresult is not None:
234 2fe690f1 Thomas Thrainer
        if old_minor is not None:
235 2fe690f1 Thomas Thrainer
          minors.append(old_minor)
236 2fe690f1 Thomas Thrainer
          results[old_minor] = old_line
237 2fe690f1 Thomas Thrainer
        old_minor = int(lresult.group(1))
238 2fe690f1 Thomas Thrainer
        old_line = line
239 2fe690f1 Thomas Thrainer
      else:
240 2fe690f1 Thomas Thrainer
        if old_minor is not None:
241 2fe690f1 Thomas Thrainer
          old_line += " " + line.strip()
242 2fe690f1 Thomas Thrainer
    # add last line
243 2fe690f1 Thomas Thrainer
    if old_minor is not None:
244 2fe690f1 Thomas Thrainer
      minors.append(old_minor)
245 2fe690f1 Thomas Thrainer
      results[old_minor] = old_line
246 2fe690f1 Thomas Thrainer
    return minors, results
247 2fe690f1 Thomas Thrainer
248 2fe690f1 Thomas Thrainer
  @staticmethod
249 2fe690f1 Thomas Thrainer
  def CreateFromLines(lines):
250 2fe690f1 Thomas Thrainer
    return DRBD8Info(lines)
251 2fe690f1 Thomas Thrainer
252 2fe690f1 Thomas Thrainer
  @staticmethod
253 2fe690f1 Thomas Thrainer
  def CreateFromFile(filename=constants.DRBD_STATUS_FILE):
254 2fe690f1 Thomas Thrainer
    try:
255 2fe690f1 Thomas Thrainer
      lines = utils.ReadFile(filename).splitlines()
256 2fe690f1 Thomas Thrainer
    except EnvironmentError, err:
257 2fe690f1 Thomas Thrainer
      if err.errno == errno.ENOENT:
258 2fe690f1 Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
259 2fe690f1 Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
260 2fe690f1 Thomas Thrainer
      else:
261 2fe690f1 Thomas Thrainer
        base.ThrowError("Can't read the DRBD proc file %s: %s",
262 2fe690f1 Thomas Thrainer
                        filename, str(err))
263 2fe690f1 Thomas Thrainer
    if not lines:
264 2fe690f1 Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
265 2fe690f1 Thomas Thrainer
    return DRBD8Info.CreateFromLines(lines)
266 2fe690f1 Thomas Thrainer
267 2fe690f1 Thomas Thrainer
268 d01e51a5 Thomas Thrainer
class DRBD8ShowInfo(object):
269 d01e51a5 Thomas Thrainer
  """Helper class which parses the output of drbdsetup show
270 d01e51a5 Thomas Thrainer

271 d01e51a5 Thomas Thrainer
  """
272 d01e51a5 Thomas Thrainer
  _PARSE_SHOW = None
273 d01e51a5 Thomas Thrainer
274 d01e51a5 Thomas Thrainer
  @classmethod
275 d01e51a5 Thomas Thrainer
  def _GetShowParser(cls):
276 d01e51a5 Thomas Thrainer
    """Return a parser for `drbd show` output.
277 d01e51a5 Thomas Thrainer

278 d01e51a5 Thomas Thrainer
    This will either create or return an already-created parser for the
279 d01e51a5 Thomas Thrainer
    output of the command `drbd show`.
280 d01e51a5 Thomas Thrainer

281 d01e51a5 Thomas Thrainer
    """
282 d01e51a5 Thomas Thrainer
    if cls._PARSE_SHOW is not None:
283 d01e51a5 Thomas Thrainer
      return cls._PARSE_SHOW
284 d01e51a5 Thomas Thrainer
285 d01e51a5 Thomas Thrainer
    # pyparsing setup
286 d01e51a5 Thomas Thrainer
    lbrace = pyp.Literal("{").suppress()
287 d01e51a5 Thomas Thrainer
    rbrace = pyp.Literal("}").suppress()
288 d01e51a5 Thomas Thrainer
    lbracket = pyp.Literal("[").suppress()
289 d01e51a5 Thomas Thrainer
    rbracket = pyp.Literal("]").suppress()
290 d01e51a5 Thomas Thrainer
    semi = pyp.Literal(";").suppress()
291 d01e51a5 Thomas Thrainer
    colon = pyp.Literal(":").suppress()
292 d01e51a5 Thomas Thrainer
    # this also converts the value to an int
293 d01e51a5 Thomas Thrainer
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
294 d01e51a5 Thomas Thrainer
295 d01e51a5 Thomas Thrainer
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
296 d01e51a5 Thomas Thrainer
    defa = pyp.Literal("_is_default").suppress()
297 d01e51a5 Thomas Thrainer
    dbl_quote = pyp.Literal('"').suppress()
298 d01e51a5 Thomas Thrainer
299 d01e51a5 Thomas Thrainer
    keyword = pyp.Word(pyp.alphanums + "-")
300 d01e51a5 Thomas Thrainer
301 d01e51a5 Thomas Thrainer
    # value types
302 d01e51a5 Thomas Thrainer
    value = pyp.Word(pyp.alphanums + "_-/.:")
303 d01e51a5 Thomas Thrainer
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
304 d01e51a5 Thomas Thrainer
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
305 d01e51a5 Thomas Thrainer
                 pyp.Word(pyp.nums + ".") + colon + number)
306 d01e51a5 Thomas Thrainer
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
307 d01e51a5 Thomas Thrainer
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
308 d01e51a5 Thomas Thrainer
                 pyp.Optional(rbracket) + colon + number)
309 d01e51a5 Thomas Thrainer
    # meta device, extended syntax
310 d01e51a5 Thomas Thrainer
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
311 d01e51a5 Thomas Thrainer
    # device name, extended syntax
312 d01e51a5 Thomas Thrainer
    device_value = pyp.Literal("minor").suppress() + number
313 d01e51a5 Thomas Thrainer
314 d01e51a5 Thomas Thrainer
    # a statement
315 d01e51a5 Thomas Thrainer
    stmt = (~rbrace + keyword + ~lbrace +
316 d01e51a5 Thomas Thrainer
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
317 d01e51a5 Thomas Thrainer
                         device_value) +
318 d01e51a5 Thomas Thrainer
            pyp.Optional(defa) + semi +
319 d01e51a5 Thomas Thrainer
            pyp.Optional(pyp.restOfLine).suppress())
320 d01e51a5 Thomas Thrainer
321 d01e51a5 Thomas Thrainer
    # an entire section
322 d01e51a5 Thomas Thrainer
    section_name = pyp.Word(pyp.alphas + "_")
323 d01e51a5 Thomas Thrainer
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
324 d01e51a5 Thomas Thrainer
325 d01e51a5 Thomas Thrainer
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
326 d01e51a5 Thomas Thrainer
    bnf.ignore(comment)
327 d01e51a5 Thomas Thrainer
328 d01e51a5 Thomas Thrainer
    cls._PARSE_SHOW = bnf
329 d01e51a5 Thomas Thrainer
330 d01e51a5 Thomas Thrainer
    return bnf
331 d01e51a5 Thomas Thrainer
332 d01e51a5 Thomas Thrainer
  @classmethod
333 d01e51a5 Thomas Thrainer
  def GetDevInfo(cls, show_data):
334 d01e51a5 Thomas Thrainer
    """Parse details about a given DRBD minor.
335 d01e51a5 Thomas Thrainer

336 d01e51a5 Thomas Thrainer
    This returns, if available, the local backing device (as a path)
337 d01e51a5 Thomas Thrainer
    and the local and remote (ip, port) information from a string
338 d01e51a5 Thomas Thrainer
    containing the output of the `drbdsetup show` command as returned
339 d01e51a5 Thomas Thrainer
    by DRBD8._GetShowData.
340 d01e51a5 Thomas Thrainer

341 d01e51a5 Thomas Thrainer
    This will return a dict with keys:
342 d01e51a5 Thomas Thrainer
      - local_dev
343 d01e51a5 Thomas Thrainer
      - meta_dev
344 d01e51a5 Thomas Thrainer
      - meta_index
345 d01e51a5 Thomas Thrainer
      - local_addr
346 d01e51a5 Thomas Thrainer
      - remote_addr
347 d01e51a5 Thomas Thrainer

348 d01e51a5 Thomas Thrainer
    """
349 d01e51a5 Thomas Thrainer
    retval = {}
350 d01e51a5 Thomas Thrainer
    if not show_data:
351 d01e51a5 Thomas Thrainer
      return retval
352 d01e51a5 Thomas Thrainer
353 d01e51a5 Thomas Thrainer
    try:
354 d01e51a5 Thomas Thrainer
      # run pyparse
355 d01e51a5 Thomas Thrainer
      results = (cls._GetShowParser()).parseString(show_data)
356 d01e51a5 Thomas Thrainer
    except pyp.ParseException, err:
357 d01e51a5 Thomas Thrainer
      base.ThrowError("Can't parse drbdsetup show output: %s", str(err))
358 d01e51a5 Thomas Thrainer
359 d01e51a5 Thomas Thrainer
    # and massage the results into our desired format
360 d01e51a5 Thomas Thrainer
    for section in results:
361 d01e51a5 Thomas Thrainer
      sname = section[0]
362 d01e51a5 Thomas Thrainer
      if sname == "_this_host":
363 d01e51a5 Thomas Thrainer
        for lst in section[1:]:
364 d01e51a5 Thomas Thrainer
          if lst[0] == "disk":
365 d01e51a5 Thomas Thrainer
            retval["local_dev"] = lst[1]
366 d01e51a5 Thomas Thrainer
          elif lst[0] == "meta-disk":
367 d01e51a5 Thomas Thrainer
            retval["meta_dev"] = lst[1]
368 d01e51a5 Thomas Thrainer
            retval["meta_index"] = lst[2]
369 d01e51a5 Thomas Thrainer
          elif lst[0] == "address":
370 d01e51a5 Thomas Thrainer
            retval["local_addr"] = tuple(lst[1:])
371 d01e51a5 Thomas Thrainer
      elif sname == "_remote_host":
372 d01e51a5 Thomas Thrainer
        for lst in section[1:]:
373 d01e51a5 Thomas Thrainer
          if lst[0] == "address":
374 d01e51a5 Thomas Thrainer
            retval["remote_addr"] = tuple(lst[1:])
375 d01e51a5 Thomas Thrainer
    return retval
376 d01e51a5 Thomas Thrainer
377 d01e51a5 Thomas Thrainer
378 fd300bc7 Thomas Thrainer
class DRBD8(base.BlockDev):
379 fd300bc7 Thomas Thrainer
  """DRBD v8.x block device.
380 fd300bc7 Thomas Thrainer

381 fd300bc7 Thomas Thrainer
  This implements the local host part of the DRBD device, i.e. it
382 fd300bc7 Thomas Thrainer
  doesn't do anything to the supposed peer. If you need a fully
383 fd300bc7 Thomas Thrainer
  connected DRBD pair, you need to use this class on both hosts.
384 fd300bc7 Thomas Thrainer

385 fd300bc7 Thomas Thrainer
  The unique_id for the drbd device is a (local_ip, local_port,
386 fd300bc7 Thomas Thrainer
  remote_ip, remote_port, local_minor, secret) tuple, and it must have
387 fd300bc7 Thomas Thrainer
  two children: the data device and the meta_device. The meta device
388 fd300bc7 Thomas Thrainer
  is checked for valid size and is zeroed on create.
389 89ff748d Thomas Thrainer

390 89ff748d Thomas Thrainer
  """
391 89ff748d Thomas Thrainer
  _DRBD_MAJOR = 147
392 89ff748d Thomas Thrainer
393 89ff748d Thomas Thrainer
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
394 89ff748d Thomas Thrainer
395 fd300bc7 Thomas Thrainer
  _MAX_MINORS = 255
396 fd300bc7 Thomas Thrainer
397 fd300bc7 Thomas Thrainer
  # timeout constants
398 fd300bc7 Thomas Thrainer
  _NET_RECONFIG_TIMEOUT = 60
399 fd300bc7 Thomas Thrainer
400 fd300bc7 Thomas Thrainer
  # command line options for barriers
401 fd300bc7 Thomas Thrainer
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
402 fd300bc7 Thomas Thrainer
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
403 fd300bc7 Thomas Thrainer
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
404 fd300bc7 Thomas Thrainer
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
405 fd300bc7 Thomas Thrainer
406 fd300bc7 Thomas Thrainer
  def __init__(self, unique_id, children, size, params):
407 fd300bc7 Thomas Thrainer
    if children and children.count(None) > 0:
408 fd300bc7 Thomas Thrainer
      children = []
409 fd300bc7 Thomas Thrainer
    if len(children) not in (0, 2):
410 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(children))
411 fd300bc7 Thomas Thrainer
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
412 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(unique_id))
413 fd300bc7 Thomas Thrainer
    (self._lhost, self._lport,
414 fd300bc7 Thomas Thrainer
     self._rhost, self._rport,
415 fd300bc7 Thomas Thrainer
     self._aminor, self._secret) = unique_id
416 fd300bc7 Thomas Thrainer
    if children:
417 fd300bc7 Thomas Thrainer
      if not _CanReadDevice(children[1].dev_path):
418 fd300bc7 Thomas Thrainer
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
419 fd300bc7 Thomas Thrainer
        children = []
420 fd300bc7 Thomas Thrainer
    super(DRBD8, self).__init__(unique_id, children, size, params)
421 fd300bc7 Thomas Thrainer
    self.major = self._DRBD_MAJOR
422 2fe690f1 Thomas Thrainer
423 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
424 2fe690f1 Thomas Thrainer
    version = drbd_info.GetVersion()
425 fd300bc7 Thomas Thrainer
    if version["k_major"] != 8:
426 fd300bc7 Thomas Thrainer
      base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
427 fd300bc7 Thomas Thrainer
                      " usage: kernel is %s.%s, ganeti wants 8.x",
428 fd300bc7 Thomas Thrainer
                      version["k_major"], version["k_minor"])
429 fd300bc7 Thomas Thrainer
430 fd300bc7 Thomas Thrainer
    if (self._lhost is not None and self._lhost == self._rhost and
431 fd300bc7 Thomas Thrainer
            self._lport == self._rport):
432 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data, same local/remote %s" %
433 fd300bc7 Thomas Thrainer
                       (unique_id,))
434 fd300bc7 Thomas Thrainer
    self.Attach()
435 fd300bc7 Thomas Thrainer
436 89ff748d Thomas Thrainer
  @staticmethod
437 89ff748d Thomas Thrainer
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
438 89ff748d Thomas Thrainer
    """Returns DRBD usermode_helper currently set.
439 89ff748d Thomas Thrainer

440 89ff748d Thomas Thrainer
    """
441 89ff748d Thomas Thrainer
    try:
442 89ff748d Thomas Thrainer
      helper = utils.ReadFile(filename).splitlines()[0]
443 89ff748d Thomas Thrainer
    except EnvironmentError, err:
444 89ff748d Thomas Thrainer
      if err.errno == errno.ENOENT:
445 89ff748d Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
446 89ff748d Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
447 89ff748d Thomas Thrainer
      else:
448 89ff748d Thomas Thrainer
        base.ThrowError("Can't read DRBD helper file %s: %s",
449 89ff748d Thomas Thrainer
                        filename, str(err))
450 89ff748d Thomas Thrainer
    if not helper:
451 89ff748d Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
452 89ff748d Thomas Thrainer
    return helper
453 89ff748d Thomas Thrainer
454 89ff748d Thomas Thrainer
  @staticmethod
455 89ff748d Thomas Thrainer
  def _DevPath(minor):
456 89ff748d Thomas Thrainer
    """Return the path to a drbd device for a given minor.
457 89ff748d Thomas Thrainer

458 89ff748d Thomas Thrainer
    """
459 89ff748d Thomas Thrainer
    return "/dev/drbd%d" % minor
460 89ff748d Thomas Thrainer
461 89ff748d Thomas Thrainer
  @classmethod
462 89ff748d Thomas Thrainer
  def GetUsedDevs(cls):
463 89ff748d Thomas Thrainer
    """Compute the list of used DRBD devices.
464 89ff748d Thomas Thrainer

465 89ff748d Thomas Thrainer
    """
466 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
467 2fe690f1 Thomas Thrainer
    return filter(lambda m: not drbd_info.GetMinorStatus(m).is_unconfigured,
468 2fe690f1 Thomas Thrainer
                  drbd_info.GetMinors())
469 89ff748d Thomas Thrainer
470 89ff748d Thomas Thrainer
  def _SetFromMinor(self, minor):
471 89ff748d Thomas Thrainer
    """Set our parameters based on the given minor.
472 89ff748d Thomas Thrainer

473 89ff748d Thomas Thrainer
    This sets our minor variable and our dev_path.
474 89ff748d Thomas Thrainer

475 89ff748d Thomas Thrainer
    """
476 89ff748d Thomas Thrainer
    if minor is None:
477 89ff748d Thomas Thrainer
      self.minor = self.dev_path = None
478 89ff748d Thomas Thrainer
      self.attached = False
479 89ff748d Thomas Thrainer
    else:
480 89ff748d Thomas Thrainer
      self.minor = minor
481 89ff748d Thomas Thrainer
      self.dev_path = self._DevPath(minor)
482 89ff748d Thomas Thrainer
      self.attached = True
483 89ff748d Thomas Thrainer
484 89ff748d Thomas Thrainer
  @staticmethod
485 89ff748d Thomas Thrainer
  def _CheckMetaSize(meta_device):
486 89ff748d Thomas Thrainer
    """Check if the given meta device looks like a valid one.
487 89ff748d Thomas Thrainer

488 89ff748d Thomas Thrainer
    This currently only checks the size, which must be around
489 89ff748d Thomas Thrainer
    128MiB.
490 89ff748d Thomas Thrainer

491 89ff748d Thomas Thrainer
    """
492 89ff748d Thomas Thrainer
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
493 89ff748d Thomas Thrainer
    if result.failed:
494 89ff748d Thomas Thrainer
      base.ThrowError("Failed to get device size: %s - %s",
495 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
496 89ff748d Thomas Thrainer
    try:
497 89ff748d Thomas Thrainer
      sectors = int(result.stdout)
498 89ff748d Thomas Thrainer
    except (TypeError, ValueError):
499 89ff748d Thomas Thrainer
      base.ThrowError("Invalid output from blockdev: '%s'", result.stdout)
500 89ff748d Thomas Thrainer
    num_bytes = sectors * 512
501 89ff748d Thomas Thrainer
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
502 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too small (%.2fMib)",
503 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
504 89ff748d Thomas Thrainer
    # the maximum *valid* size of the meta device when living on top
505 89ff748d Thomas Thrainer
    # of LVM is hard to compute: it depends on the number of stripes
506 89ff748d Thomas Thrainer
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
507 89ff748d Thomas Thrainer
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
508 89ff748d Thomas Thrainer
    # size meta device; as such, we restrict it to 1GB (a little bit
509 89ff748d Thomas Thrainer
    # too generous, but making assumptions about PE size is hard)
510 89ff748d Thomas Thrainer
    if num_bytes > 1024 * 1024 * 1024:
511 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too big (%.2fMiB)",
512 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
513 89ff748d Thomas Thrainer
514 89ff748d Thomas Thrainer
  @classmethod
515 89ff748d Thomas Thrainer
  def _InitMeta(cls, minor, dev_path):
516 89ff748d Thomas Thrainer
    """Initialize a meta device.
517 89ff748d Thomas Thrainer

518 89ff748d Thomas Thrainer
    This will not work if the given minor is in use.
519 89ff748d Thomas Thrainer

520 89ff748d Thomas Thrainer
    """
521 89ff748d Thomas Thrainer
    # Zero the metadata first, in order to make sure drbdmeta doesn't
522 89ff748d Thomas Thrainer
    # try to auto-detect existing filesystems or similar (see
523 89ff748d Thomas Thrainer
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
524 89ff748d Thomas Thrainer
    # care about the first 128MB of data in the device, even though it
525 89ff748d Thomas Thrainer
    # can be bigger
526 89ff748d Thomas Thrainer
    result = utils.RunCmd([constants.DD_CMD,
527 89ff748d Thomas Thrainer
                           "if=/dev/zero", "of=%s" % dev_path,
528 89ff748d Thomas Thrainer
                           "bs=1048576", "count=128", "oflag=direct"])
529 89ff748d Thomas Thrainer
    if result.failed:
530 89ff748d Thomas Thrainer
      base.ThrowError("Can't wipe the meta device: %s", result.output)
531 89ff748d Thomas Thrainer
532 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
533 89ff748d Thomas Thrainer
                           "v08", dev_path, "0", "create-md"])
534 89ff748d Thomas Thrainer
    if result.failed:
535 89ff748d Thomas Thrainer
      base.ThrowError("Can't initialize meta device: %s", result.output)
536 89ff748d Thomas Thrainer
537 2fe690f1 Thomas Thrainer
  def _FindUnusedMinor(self):
538 89ff748d Thomas Thrainer
    """Find an unused DRBD device.
539 89ff748d Thomas Thrainer

540 89ff748d Thomas Thrainer
    This is specific to 8.x as the minors are allocated dynamically,
541 89ff748d Thomas Thrainer
    so non-existing numbers up to a max minor count are actually free.
542 89ff748d Thomas Thrainer

543 89ff748d Thomas Thrainer
    """
544 89ff748d Thomas Thrainer
545 89ff748d Thomas Thrainer
    highest = None
546 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
547 2fe690f1 Thomas Thrainer
    for minor in drbd_info.GetMinors():
548 2fe690f1 Thomas Thrainer
      status = drbd_info.GetMinorStatus(minor)
549 2fe690f1 Thomas Thrainer
      if not status.is_in_use:
550 2fe690f1 Thomas Thrainer
        return minor
551 2fe690f1 Thomas Thrainer
      highest = max(highest, minor)
552 2fe690f1 Thomas Thrainer
553 89ff748d Thomas Thrainer
    if highest is None: # there are no minors in use at all
554 89ff748d Thomas Thrainer
      return 0
555 2fe690f1 Thomas Thrainer
    if highest >= self._MAX_MINORS:
556 89ff748d Thomas Thrainer
      logging.error("Error: no free drbd minors!")
557 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
558 2fe690f1 Thomas Thrainer
559 89ff748d Thomas Thrainer
    return highest + 1
560 89ff748d Thomas Thrainer
561 89ff748d Thomas Thrainer
  @classmethod
562 89ff748d Thomas Thrainer
  def _GetShowData(cls, minor):
563 89ff748d Thomas Thrainer
    """Return the `drbdsetup show` data for a minor.
564 89ff748d Thomas Thrainer

565 89ff748d Thomas Thrainer
    """
566 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
567 89ff748d Thomas Thrainer
    if result.failed:
568 89ff748d Thomas Thrainer
      logging.error("Can't display the drbd config: %s - %s",
569 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
570 89ff748d Thomas Thrainer
      return None
571 89ff748d Thomas Thrainer
    return result.stdout
572 89ff748d Thomas Thrainer
573 89ff748d Thomas Thrainer
  def _MatchesLocal(self, info):
574 89ff748d Thomas Thrainer
    """Test if our local config matches with an existing device.
575 89ff748d Thomas Thrainer

576 89ff748d Thomas Thrainer
    The parameter should be as returned from `_GetDevInfo()`. This
577 89ff748d Thomas Thrainer
    method tests if our local backing device is the same as the one in
578 89ff748d Thomas Thrainer
    the info parameter, in effect testing if we look like the given
579 89ff748d Thomas Thrainer
    device.
580 89ff748d Thomas Thrainer

581 89ff748d Thomas Thrainer
    """
582 89ff748d Thomas Thrainer
    if self._children:
583 89ff748d Thomas Thrainer
      backend, meta = self._children
584 89ff748d Thomas Thrainer
    else:
585 89ff748d Thomas Thrainer
      backend = meta = None
586 89ff748d Thomas Thrainer
587 89ff748d Thomas Thrainer
    if backend is not None:
588 89ff748d Thomas Thrainer
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
589 89ff748d Thomas Thrainer
    else:
590 89ff748d Thomas Thrainer
      retval = ("local_dev" not in info)
591 89ff748d Thomas Thrainer
592 89ff748d Thomas Thrainer
    if meta is not None:
593 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" in info and
594 89ff748d Thomas Thrainer
                           info["meta_dev"] == meta.dev_path)
595 89ff748d Thomas Thrainer
      retval = retval and ("meta_index" in info and
596 89ff748d Thomas Thrainer
                           info["meta_index"] == 0)
597 89ff748d Thomas Thrainer
    else:
598 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" not in info and
599 89ff748d Thomas Thrainer
                           "meta_index" not in info)
600 89ff748d Thomas Thrainer
    return retval
601 89ff748d Thomas Thrainer
602 89ff748d Thomas Thrainer
  def _MatchesNet(self, info):
603 89ff748d Thomas Thrainer
    """Test if our network config matches with an existing device.
604 89ff748d Thomas Thrainer

605 89ff748d Thomas Thrainer
    The parameter should be as returned from `_GetDevInfo()`. This
606 89ff748d Thomas Thrainer
    method tests if our network configuration is the same as the one
607 89ff748d Thomas Thrainer
    in the info parameter, in effect testing if we look like the given
608 89ff748d Thomas Thrainer
    device.
609 89ff748d Thomas Thrainer

610 89ff748d Thomas Thrainer
    """
611 89ff748d Thomas Thrainer
    if (((self._lhost is None and not ("local_addr" in info)) and
612 89ff748d Thomas Thrainer
         (self._rhost is None and not ("remote_addr" in info)))):
613 89ff748d Thomas Thrainer
      return True
614 89ff748d Thomas Thrainer
615 89ff748d Thomas Thrainer
    if self._lhost is None:
616 89ff748d Thomas Thrainer
      return False
617 89ff748d Thomas Thrainer
618 89ff748d Thomas Thrainer
    if not ("local_addr" in info and
619 89ff748d Thomas Thrainer
            "remote_addr" in info):
620 89ff748d Thomas Thrainer
      return False
621 89ff748d Thomas Thrainer
622 89ff748d Thomas Thrainer
    retval = (info["local_addr"] == (self._lhost, self._lport))
623 89ff748d Thomas Thrainer
    retval = (retval and
624 89ff748d Thomas Thrainer
              info["remote_addr"] == (self._rhost, self._rport))
625 89ff748d Thomas Thrainer
    return retval
626 89ff748d Thomas Thrainer
627 89ff748d Thomas Thrainer
  def _AssembleLocal(self, minor, backend, meta, size):
628 89ff748d Thomas Thrainer
    """Configure the local part of a DRBD device.
629 89ff748d Thomas Thrainer

630 89ff748d Thomas Thrainer
    """
631 89ff748d Thomas Thrainer
    args = ["drbdsetup", self._DevPath(minor), "disk",
632 89ff748d Thomas Thrainer
            backend, meta, "0",
633 89ff748d Thomas Thrainer
            "-e", "detach",
634 89ff748d Thomas Thrainer
            "--create-device"]
635 89ff748d Thomas Thrainer
    if size:
636 89ff748d Thomas Thrainer
      args.extend(["-d", "%sm" % size])
637 89ff748d Thomas Thrainer
638 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
639 2fe690f1 Thomas Thrainer
    version = drbd_info.GetVersion()
640 89ff748d Thomas Thrainer
    vmaj = version["k_major"]
641 89ff748d Thomas Thrainer
    vmin = version["k_minor"]
642 89ff748d Thomas Thrainer
    vrel = version["k_point"]
643 89ff748d Thomas Thrainer
644 89ff748d Thomas Thrainer
    barrier_args = \
645 89ff748d Thomas Thrainer
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
646 89ff748d Thomas Thrainer
                                   self.params[constants.LDP_BARRIERS],
647 89ff748d Thomas Thrainer
                                   self.params[constants.LDP_NO_META_FLUSH])
648 89ff748d Thomas Thrainer
    args.extend(barrier_args)
649 89ff748d Thomas Thrainer
650 89ff748d Thomas Thrainer
    if self.params[constants.LDP_DISK_CUSTOM]:
651 89ff748d Thomas Thrainer
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
652 89ff748d Thomas Thrainer
653 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
654 89ff748d Thomas Thrainer
    if result.failed:
655 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach local disk: %s",
656 89ff748d Thomas Thrainer
                      minor, result.output)
657 89ff748d Thomas Thrainer
658 89ff748d Thomas Thrainer
  @classmethod
659 89ff748d Thomas Thrainer
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
660 89ff748d Thomas Thrainer
                              disable_meta_flush):
661 89ff748d Thomas Thrainer
    """Compute the DRBD command line parameters for disk barriers
662 89ff748d Thomas Thrainer

663 89ff748d Thomas Thrainer
    Returns a list of the disk barrier parameters as requested via the
664 89ff748d Thomas Thrainer
    disabled_barriers and disable_meta_flush arguments, and according to the
665 89ff748d Thomas Thrainer
    supported ones in the DRBD version vmaj.vmin.vrel
666 89ff748d Thomas Thrainer

667 89ff748d Thomas Thrainer
    If the desired option is unsupported, raises errors.BlockDeviceError.
668 89ff748d Thomas Thrainer

669 89ff748d Thomas Thrainer
    """
670 89ff748d Thomas Thrainer
    disabled_barriers_set = frozenset(disabled_barriers)
671 89ff748d Thomas Thrainer
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
672 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
673 89ff748d Thomas Thrainer
                                    " barriers" % disabled_barriers)
674 89ff748d Thomas Thrainer
675 89ff748d Thomas Thrainer
    args = []
676 89ff748d Thomas Thrainer
677 89ff748d Thomas Thrainer
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
678 89ff748d Thomas Thrainer
    # does not exist)
679 89ff748d Thomas Thrainer
    if not vmaj == 8 and vmin in (0, 2, 3):
680 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
681 89ff748d Thomas Thrainer
                                    (vmaj, vmin, vrel))
682 89ff748d Thomas Thrainer
683 89ff748d Thomas Thrainer
    def _AppendOrRaise(option, min_version):
684 89ff748d Thomas Thrainer
      """Helper for DRBD options"""
685 89ff748d Thomas Thrainer
      if min_version is not None and vrel >= min_version:
686 89ff748d Thomas Thrainer
        args.append(option)
687 89ff748d Thomas Thrainer
      else:
688 89ff748d Thomas Thrainer
        raise errors.BlockDeviceError("Could not use the option %s as the"
689 89ff748d Thomas Thrainer
                                      " DRBD version %d.%d.%d does not support"
690 89ff748d Thomas Thrainer
                                      " it." % (option, vmaj, vmin, vrel))
691 89ff748d Thomas Thrainer
692 89ff748d Thomas Thrainer
    # the minimum version for each feature is encoded via pairs of (minor
693 89ff748d Thomas Thrainer
    # version -> x) where x is version in which support for the option was
694 89ff748d Thomas Thrainer
    # introduced.
695 89ff748d Thomas Thrainer
    meta_flush_supported = disk_flush_supported = {
696 89ff748d Thomas Thrainer
      0: 12,
697 89ff748d Thomas Thrainer
      2: 7,
698 89ff748d Thomas Thrainer
      3: 0,
699 89ff748d Thomas Thrainer
      }
700 89ff748d Thomas Thrainer
701 89ff748d Thomas Thrainer
    disk_drain_supported = {
702 89ff748d Thomas Thrainer
      2: 7,
703 89ff748d Thomas Thrainer
      3: 0,
704 89ff748d Thomas Thrainer
      }
705 89ff748d Thomas Thrainer
706 89ff748d Thomas Thrainer
    disk_barriers_supported = {
707 89ff748d Thomas Thrainer
      3: 0,
708 89ff748d Thomas Thrainer
      }
709 89ff748d Thomas Thrainer
710 89ff748d Thomas Thrainer
    # meta flushes
711 89ff748d Thomas Thrainer
    if disable_meta_flush:
712 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
713 89ff748d Thomas Thrainer
                     meta_flush_supported.get(vmin, None))
714 89ff748d Thomas Thrainer
715 89ff748d Thomas Thrainer
    # disk flushes
716 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
717 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
718 89ff748d Thomas Thrainer
                     disk_flush_supported.get(vmin, None))
719 89ff748d Thomas Thrainer
720 89ff748d Thomas Thrainer
    # disk drain
721 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
722 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
723 89ff748d Thomas Thrainer
                     disk_drain_supported.get(vmin, None))
724 89ff748d Thomas Thrainer
725 89ff748d Thomas Thrainer
    # disk barriers
726 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
727 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
728 89ff748d Thomas Thrainer
                     disk_barriers_supported.get(vmin, None))
729 89ff748d Thomas Thrainer
730 89ff748d Thomas Thrainer
    return args
731 89ff748d Thomas Thrainer
732 89ff748d Thomas Thrainer
  def _AssembleNet(self, minor, net_info, protocol,
733 89ff748d Thomas Thrainer
                   dual_pri=False, hmac=None, secret=None):
734 89ff748d Thomas Thrainer
    """Configure the network part of the device.
735 89ff748d Thomas Thrainer

736 89ff748d Thomas Thrainer
    """
737 89ff748d Thomas Thrainer
    lhost, lport, rhost, rport = net_info
738 89ff748d Thomas Thrainer
    if None in net_info:
739 89ff748d Thomas Thrainer
      # we don't want network connection and actually want to make
740 89ff748d Thomas Thrainer
      # sure its shutdown
741 89ff748d Thomas Thrainer
      self._ShutdownNet(minor)
742 89ff748d Thomas Thrainer
      return
743 89ff748d Thomas Thrainer
744 89ff748d Thomas Thrainer
    # Workaround for a race condition. When DRBD is doing its dance to
745 89ff748d Thomas Thrainer
    # establish a connection with its peer, it also sends the
746 89ff748d Thomas Thrainer
    # synchronization speed over the wire. In some cases setting the
747 89ff748d Thomas Thrainer
    # sync speed only after setting up both sides can race with DRBD
748 89ff748d Thomas Thrainer
    # connecting, hence we set it here before telling DRBD anything
749 89ff748d Thomas Thrainer
    # about its peer.
750 89ff748d Thomas Thrainer
    sync_errors = self._SetMinorSyncParams(minor, self.params)
751 89ff748d Thomas Thrainer
    if sync_errors:
752 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
753 89ff748d Thomas Thrainer
                      (minor, utils.CommaJoin(sync_errors)))
754 89ff748d Thomas Thrainer
755 89ff748d Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
756 89ff748d Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
757 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
758 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
759 89ff748d Thomas Thrainer
      family = "ipv6"
760 89ff748d Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
761 89ff748d Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
762 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
763 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
764 89ff748d Thomas Thrainer
      family = "ipv4"
765 89ff748d Thomas Thrainer
    else:
766 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
767 89ff748d Thomas Thrainer
768 89ff748d Thomas Thrainer
    args = ["drbdsetup", self._DevPath(minor), "net",
769 89ff748d Thomas Thrainer
            "%s:%s:%s" % (family, lhost, lport),
770 89ff748d Thomas Thrainer
            "%s:%s:%s" % (family, rhost, rport), protocol,
771 89ff748d Thomas Thrainer
            "-A", "discard-zero-changes",
772 89ff748d Thomas Thrainer
            "-B", "consensus",
773 89ff748d Thomas Thrainer
            "--create-device",
774 89ff748d Thomas Thrainer
            ]
775 89ff748d Thomas Thrainer
    if dual_pri:
776 89ff748d Thomas Thrainer
      args.append("-m")
777 89ff748d Thomas Thrainer
    if hmac and secret:
778 89ff748d Thomas Thrainer
      args.extend(["-a", hmac, "-x", secret])
779 89ff748d Thomas Thrainer
780 89ff748d Thomas Thrainer
    if self.params[constants.LDP_NET_CUSTOM]:
781 89ff748d Thomas Thrainer
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
782 89ff748d Thomas Thrainer
783 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
784 89ff748d Thomas Thrainer
    if result.failed:
785 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't setup network: %s - %s",
786 89ff748d Thomas Thrainer
                      minor, result.fail_reason, result.output)
787 89ff748d Thomas Thrainer
788 89ff748d Thomas Thrainer
    def _CheckNetworkConfig():
789 d01e51a5 Thomas Thrainer
      info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))
790 89ff748d Thomas Thrainer
      if not "local_addr" in info or not "remote_addr" in info:
791 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
792 89ff748d Thomas Thrainer
793 89ff748d Thomas Thrainer
      if (info["local_addr"] != (lhost, lport) or
794 89ff748d Thomas Thrainer
          info["remote_addr"] != (rhost, rport)):
795 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
796 89ff748d Thomas Thrainer
797 89ff748d Thomas Thrainer
    try:
798 89ff748d Thomas Thrainer
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
799 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
800 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: timeout while configuring network", minor)
801 89ff748d Thomas Thrainer
802 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
803 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
804 89ff748d Thomas Thrainer

805 89ff748d Thomas Thrainer
    """
806 89ff748d Thomas Thrainer
    if self.minor is None:
807 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
808 89ff748d Thomas Thrainer
                      self._aminor)
809 89ff748d Thomas Thrainer
    if len(devices) != 2:
810 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
811 d01e51a5 Thomas Thrainer
    info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(self.minor))
812 89ff748d Thomas Thrainer
    if "local_dev" in info:
813 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
814 89ff748d Thomas Thrainer
    backend, meta = devices
815 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
816 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
817 89ff748d Thomas Thrainer
                      self.minor)
818 89ff748d Thomas Thrainer
    backend.Open()
819 89ff748d Thomas Thrainer
    meta.Open()
820 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
821 89ff748d Thomas Thrainer
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
822 89ff748d Thomas Thrainer
823 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
824 89ff748d Thomas Thrainer
    self._children = devices
825 89ff748d Thomas Thrainer
826 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
827 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
828 89ff748d Thomas Thrainer

829 89ff748d Thomas Thrainer
    """
830 89ff748d Thomas Thrainer
    if self.minor is None:
831 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
832 89ff748d Thomas Thrainer
                      self._aminor)
833 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
834 d01e51a5 Thomas Thrainer
    info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(self.minor))
835 89ff748d Thomas Thrainer
    if "local_dev" not in info:
836 89ff748d Thomas Thrainer
      return
837 89ff748d Thomas Thrainer
    if len(self._children) != 2:
838 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
839 89ff748d Thomas Thrainer
                      self._children)
840 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
841 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
842 89ff748d Thomas Thrainer
      return
843 89ff748d Thomas Thrainer
    if len(devices) != 2:
844 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
845 89ff748d Thomas Thrainer
                      self.minor)
846 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
847 89ff748d Thomas Thrainer
      if dev != child.dev_path:
848 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
849 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
850 89ff748d Thomas Thrainer
851 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
852 89ff748d Thomas Thrainer
    self._children = []
853 89ff748d Thomas Thrainer
854 2fe690f1 Thomas Thrainer
  def _SetMinorSyncParams(self, minor, params):
855 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
856 89ff748d Thomas Thrainer

857 89ff748d Thomas Thrainer
    This is the low-level implementation.
858 89ff748d Thomas Thrainer

859 89ff748d Thomas Thrainer
    @type minor: int
860 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
861 89ff748d Thomas Thrainer
    @type params: dict
862 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
863 89ff748d Thomas Thrainer
    @rtype: list
864 89ff748d Thomas Thrainer
    @return: a list of error messages
865 89ff748d Thomas Thrainer

866 89ff748d Thomas Thrainer
    """
867 89ff748d Thomas Thrainer
868 2fe690f1 Thomas Thrainer
    args = ["drbdsetup", self._DevPath(minor), "syncer"]
869 89ff748d Thomas Thrainer
    if params[constants.LDP_DYNAMIC_RESYNC]:
870 2fe690f1 Thomas Thrainer
      drbd_info = DRBD8Info.CreateFromFile()
871 2fe690f1 Thomas Thrainer
      version = drbd_info.GetVersion()
872 89ff748d Thomas Thrainer
      vmin = version["k_minor"]
873 89ff748d Thomas Thrainer
      vrel = version["k_point"]
874 89ff748d Thomas Thrainer
875 89ff748d Thomas Thrainer
      # By definition we are using 8.x, so just check the rest of the version
876 89ff748d Thomas Thrainer
      # number
877 89ff748d Thomas Thrainer
      if vmin != 3 or vrel < 9:
878 89ff748d Thomas Thrainer
        msg = ("The current DRBD version (8.%d.%d) does not support the "
879 89ff748d Thomas Thrainer
               "dynamic resync speed controller" % (vmin, vrel))
880 89ff748d Thomas Thrainer
        logging.error(msg)
881 89ff748d Thomas Thrainer
        return [msg]
882 89ff748d Thomas Thrainer
883 89ff748d Thomas Thrainer
      if params[constants.LDP_PLAN_AHEAD] == 0:
884 89ff748d Thomas Thrainer
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
885 89ff748d Thomas Thrainer
               " controller at DRBD level. If you want to disable it, please"
886 89ff748d Thomas Thrainer
               " set the dynamic-resync disk parameter to False.")
887 89ff748d Thomas Thrainer
        logging.error(msg)
888 89ff748d Thomas Thrainer
        return [msg]
889 89ff748d Thomas Thrainer
890 89ff748d Thomas Thrainer
      # add the c-* parameters to args
891 89ff748d Thomas Thrainer
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
892 89ff748d Thomas Thrainer
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
893 89ff748d Thomas Thrainer
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
894 89ff748d Thomas Thrainer
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
895 89ff748d Thomas Thrainer
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
896 89ff748d Thomas Thrainer
                   ])
897 89ff748d Thomas Thrainer
898 89ff748d Thomas Thrainer
    else:
899 89ff748d Thomas Thrainer
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
900 89ff748d Thomas Thrainer
901 89ff748d Thomas Thrainer
    args.append("--create-device")
902 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
903 89ff748d Thomas Thrainer
    if result.failed:
904 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
905 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
906 89ff748d Thomas Thrainer
      logging.error(msg)
907 89ff748d Thomas Thrainer
      return [msg]
908 89ff748d Thomas Thrainer
909 89ff748d Thomas Thrainer
    return []
910 89ff748d Thomas Thrainer
911 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
912 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
913 89ff748d Thomas Thrainer

914 89ff748d Thomas Thrainer
    @type params: dict
915 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
916 89ff748d Thomas Thrainer
    @rtype: list
917 89ff748d Thomas Thrainer
    @return: a list of error messages, emitted both by the current node and by
918 89ff748d Thomas Thrainer
    children. An empty list means no errors
919 89ff748d Thomas Thrainer

920 89ff748d Thomas Thrainer
    """
921 89ff748d Thomas Thrainer
    if self.minor is None:
922 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
923 89ff748d Thomas Thrainer
      logging.info(err)
924 89ff748d Thomas Thrainer
      return [err]
925 89ff748d Thomas Thrainer
926 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).SetSyncParams(params)
927 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
928 89ff748d Thomas Thrainer
    return children_result
929 89ff748d Thomas Thrainer
930 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
931 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
932 89ff748d Thomas Thrainer

933 89ff748d Thomas Thrainer
    @param pause: Wether to pause or resume
934 89ff748d Thomas Thrainer
    @return: the success of the operation
935 89ff748d Thomas Thrainer

936 89ff748d Thomas Thrainer
    """
937 89ff748d Thomas Thrainer
    if self.minor is None:
938 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
939 89ff748d Thomas Thrainer
      return False
940 89ff748d Thomas Thrainer
941 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
942 89ff748d Thomas Thrainer
943 89ff748d Thomas Thrainer
    if pause:
944 89ff748d Thomas Thrainer
      cmd = "pause-sync"
945 89ff748d Thomas Thrainer
    else:
946 89ff748d Thomas Thrainer
      cmd = "resume-sync"
947 89ff748d Thomas Thrainer
948 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
949 89ff748d Thomas Thrainer
    if result.failed:
950 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
951 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
952 89ff748d Thomas Thrainer
    return not result.failed and children_result
953 89ff748d Thomas Thrainer
954 89ff748d Thomas Thrainer
  def GetProcStatus(self):
955 89ff748d Thomas Thrainer
    """Return device data from /proc.
956 89ff748d Thomas Thrainer

957 89ff748d Thomas Thrainer
    """
958 89ff748d Thomas Thrainer
    if self.minor is None:
959 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
960 89ff748d Thomas Thrainer
                      self._aminor)
961 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
962 2fe690f1 Thomas Thrainer
    if not drbd_info.HasMinorStatus(self.minor):
963 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
964 2fe690f1 Thomas Thrainer
    return drbd_info.GetMinorStatus(self.minor)
965 89ff748d Thomas Thrainer
966 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
967 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
968 89ff748d Thomas Thrainer

969 89ff748d Thomas Thrainer

970 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
971 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
972 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
973 89ff748d Thomas Thrainer

974 89ff748d Thomas Thrainer

975 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
976 89ff748d Thomas Thrainer
    network not connected or local disk missing.
977 89ff748d Thomas Thrainer

978 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
979 89ff748d Thomas Thrainer
    disk or not.
980 89ff748d Thomas Thrainer

981 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
982 89ff748d Thomas Thrainer

983 89ff748d Thomas Thrainer
    """
984 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
985 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
986 89ff748d Thomas Thrainer
987 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
988 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
989 89ff748d Thomas Thrainer
990 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
991 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
992 89ff748d Thomas Thrainer
    elif stats.is_diskless:
993 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
994 89ff748d Thomas Thrainer
    else:
995 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
996 89ff748d Thomas Thrainer
997 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
998 89ff748d Thomas Thrainer
                                  major=self.major,
999 89ff748d Thomas Thrainer
                                  minor=self.minor,
1000 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
1001 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
1002 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
1003 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
1004 89ff748d Thomas Thrainer
1005 89ff748d Thomas Thrainer
  def Open(self, force=False):
1006 89ff748d Thomas Thrainer
    """Make the local state primary.
1007 89ff748d Thomas Thrainer

1008 89ff748d Thomas Thrainer
    If the 'force' parameter is given, the '-o' option is passed to
1009 89ff748d Thomas Thrainer
    drbdsetup. Since this is a potentially dangerous operation, the
1010 89ff748d Thomas Thrainer
    force flag should be only given after creation, when it actually
1011 89ff748d Thomas Thrainer
    is mandatory.
1012 89ff748d Thomas Thrainer

1013 89ff748d Thomas Thrainer
    """
1014 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
1015 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
1016 89ff748d Thomas Thrainer
      return False
1017 89ff748d Thomas Thrainer
    cmd = ["drbdsetup", self.dev_path, "primary"]
1018 89ff748d Thomas Thrainer
    if force:
1019 89ff748d Thomas Thrainer
      cmd.append("-o")
1020 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
1021 89ff748d Thomas Thrainer
    if result.failed:
1022 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1023 89ff748d Thomas Thrainer
                      result.output)
1024 89ff748d Thomas Thrainer
1025 89ff748d Thomas Thrainer
  def Close(self):
1026 89ff748d Thomas Thrainer
    """Make the local state secondary.
1027 89ff748d Thomas Thrainer

1028 89ff748d Thomas Thrainer
    This will, of course, fail if the device is in use.
1029 89ff748d Thomas Thrainer

1030 89ff748d Thomas Thrainer
    """
1031 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
1032 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1033 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1034 89ff748d Thomas Thrainer
    if result.failed:
1035 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1036 89ff748d Thomas Thrainer
                      self.minor, result.output)
1037 89ff748d Thomas Thrainer
1038 89ff748d Thomas Thrainer
  def DisconnectNet(self):
1039 89ff748d Thomas Thrainer
    """Removes network configuration.
1040 89ff748d Thomas Thrainer

1041 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
1042 89ff748d Thomas Thrainer

1043 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
1044 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
1045 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
1046 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
1047 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
1048 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
1049 89ff748d Thomas Thrainer
    exception.
1050 89ff748d Thomas Thrainer

1051 89ff748d Thomas Thrainer
    """
1052 89ff748d Thomas Thrainer
    if self.minor is None:
1053 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: disk not attached in re-attach net",
1054 89ff748d Thomas Thrainer
                      self._aminor)
1055 89ff748d Thomas Thrainer
1056 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1057 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: DRBD disk missing network info in"
1058 89ff748d Thomas Thrainer
                      " DisconnectNet()", self.minor)
1059 89ff748d Thomas Thrainer
1060 89ff748d Thomas Thrainer
    class _DisconnectStatus:
1061 89ff748d Thomas Thrainer
      def __init__(self, ever_disconnected):
1062 89ff748d Thomas Thrainer
        self.ever_disconnected = ever_disconnected
1063 89ff748d Thomas Thrainer
1064 89ff748d Thomas Thrainer
    dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
1065 89ff748d Thomas Thrainer
1066 89ff748d Thomas Thrainer
    def _WaitForDisconnect():
1067 89ff748d Thomas Thrainer
      if self.GetProcStatus().is_standalone:
1068 89ff748d Thomas Thrainer
        return
1069 89ff748d Thomas Thrainer
1070 89ff748d Thomas Thrainer
      # retry the disconnect, it seems possible that due to a well-time
1071 89ff748d Thomas Thrainer
      # disconnect on the peer, my disconnect command might be ignored and
1072 89ff748d Thomas Thrainer
      # forgotten
1073 89ff748d Thomas Thrainer
      dstatus.ever_disconnected = \
1074 89ff748d Thomas Thrainer
        base.IgnoreError(self._ShutdownNet, self.minor) or \
1075 89ff748d Thomas Thrainer
        dstatus.ever_disconnected
1076 89ff748d Thomas Thrainer
1077 89ff748d Thomas Thrainer
      raise utils.RetryAgain()
1078 89ff748d Thomas Thrainer
1079 89ff748d Thomas Thrainer
    # Keep start time
1080 89ff748d Thomas Thrainer
    start_time = time.time()
1081 89ff748d Thomas Thrainer
1082 89ff748d Thomas Thrainer
    try:
1083 89ff748d Thomas Thrainer
      # Start delay at 100 milliseconds and grow up to 2 seconds
1084 89ff748d Thomas Thrainer
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1085 89ff748d Thomas Thrainer
                  self._NET_RECONFIG_TIMEOUT)
1086 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
1087 89ff748d Thomas Thrainer
      if dstatus.ever_disconnected:
1088 89ff748d Thomas Thrainer
        msg = ("drbd%d: device did not react to the"
1089 89ff748d Thomas Thrainer
               " 'disconnect' command in a timely manner")
1090 89ff748d Thomas Thrainer
      else:
1091 89ff748d Thomas Thrainer
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1092 89ff748d Thomas Thrainer
1093 89ff748d Thomas Thrainer
      base.ThrowError(msg, self.minor)
1094 89ff748d Thomas Thrainer
1095 89ff748d Thomas Thrainer
    reconfig_time = time.time() - start_time
1096 89ff748d Thomas Thrainer
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1097 89ff748d Thomas Thrainer
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1098 89ff748d Thomas Thrainer
                   self.minor, reconfig_time)
1099 89ff748d Thomas Thrainer
1100 89ff748d Thomas Thrainer
  def AttachNet(self, multimaster):
1101 89ff748d Thomas Thrainer
    """Reconnects the network.
1102 89ff748d Thomas Thrainer

1103 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
1104 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
1105 89ff748d Thomas Thrainer
    but have valid network configuration data.
1106 89ff748d Thomas Thrainer

1107 89ff748d Thomas Thrainer
    Args:
1108 89ff748d Thomas Thrainer
      - multimaster: init the network in dual-primary mode
1109 89ff748d Thomas Thrainer

1110 89ff748d Thomas Thrainer
    """
1111 89ff748d Thomas Thrainer
    if self.minor is None:
1112 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1113 89ff748d Thomas Thrainer
1114 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1115 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1116 89ff748d Thomas Thrainer
1117 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
1118 89ff748d Thomas Thrainer
1119 89ff748d Thomas Thrainer
    if not status.is_standalone:
1120 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
1121 89ff748d Thomas Thrainer
                      self.minor)
1122 89ff748d Thomas Thrainer
1123 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
1124 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
1125 89ff748d Thomas Thrainer
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1126 89ff748d Thomas Thrainer
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1127 89ff748d Thomas Thrainer
1128 89ff748d Thomas Thrainer
  def Attach(self):
1129 89ff748d Thomas Thrainer
    """Check if our minor is configured.
1130 89ff748d Thomas Thrainer

1131 89ff748d Thomas Thrainer
    This doesn't do any device configurations - it only checks if the
1132 89ff748d Thomas Thrainer
    minor is in a state different from Unconfigured.
1133 89ff748d Thomas Thrainer

1134 89ff748d Thomas Thrainer
    Note that this function will not change the state of the system in
1135 89ff748d Thomas Thrainer
    any way (except in case of side-effects caused by reading from
1136 89ff748d Thomas Thrainer
    /proc).
1137 89ff748d Thomas Thrainer

1138 89ff748d Thomas Thrainer
    """
1139 89ff748d Thomas Thrainer
    used_devs = self.GetUsedDevs()
1140 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
1141 89ff748d Thomas Thrainer
      minor = self._aminor
1142 89ff748d Thomas Thrainer
    else:
1143 89ff748d Thomas Thrainer
      minor = None
1144 89ff748d Thomas Thrainer
1145 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1146 89ff748d Thomas Thrainer
    return minor is not None
1147 89ff748d Thomas Thrainer
1148 89ff748d Thomas Thrainer
  def Assemble(self):
1149 89ff748d Thomas Thrainer
    """Assemble the drbd.
1150 89ff748d Thomas Thrainer

1151 89ff748d Thomas Thrainer
    Method:
1152 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
1153 89ff748d Thomas Thrainer
        our config
1154 89ff748d Thomas Thrainer
      - if not, we create it from zero
1155 89ff748d Thomas Thrainer
      - anyway, set the device parameters
1156 89ff748d Thomas Thrainer

1157 89ff748d Thomas Thrainer
    """
1158 89ff748d Thomas Thrainer
    super(DRBD8, self).Assemble()
1159 89ff748d Thomas Thrainer
1160 89ff748d Thomas Thrainer
    self.Attach()
1161 89ff748d Thomas Thrainer
    if self.minor is None:
1162 89ff748d Thomas Thrainer
      # local device completely unconfigured
1163 89ff748d Thomas Thrainer
      self._FastAssemble()
1164 89ff748d Thomas Thrainer
    else:
1165 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
1166 89ff748d Thomas Thrainer
      # the device
1167 89ff748d Thomas Thrainer
      self._SlowAssemble()
1168 89ff748d Thomas Thrainer
1169 89ff748d Thomas Thrainer
    sync_errors = self.SetSyncParams(self.params)
1170 89ff748d Thomas Thrainer
    if sync_errors:
1171 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1172 89ff748d Thomas Thrainer
                      (self.minor, utils.CommaJoin(sync_errors)))
1173 89ff748d Thomas Thrainer
1174 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
1175 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
1176 89ff748d Thomas Thrainer

1177 89ff748d Thomas Thrainer
    In case of partially attached (local device matches but no network
1178 89ff748d Thomas Thrainer
    setup), we perform the network attach. If successful, we re-test
1179 89ff748d Thomas Thrainer
    the attach if can return success.
1180 89ff748d Thomas Thrainer

1181 89ff748d Thomas Thrainer
    """
1182 89ff748d Thomas Thrainer
    # TODO: Rewrite to not use a for loop just because there is 'break'
1183 89ff748d Thomas Thrainer
    # pylint: disable=W0631
1184 89ff748d Thomas Thrainer
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1185 89ff748d Thomas Thrainer
    for minor in (self._aminor,):
1186 d01e51a5 Thomas Thrainer
      info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))
1187 89ff748d Thomas Thrainer
      match_l = self._MatchesLocal(info)
1188 89ff748d Thomas Thrainer
      match_r = self._MatchesNet(info)
1189 89ff748d Thomas Thrainer
1190 89ff748d Thomas Thrainer
      if match_l and match_r:
1191 89ff748d Thomas Thrainer
        # everything matches
1192 89ff748d Thomas Thrainer
        break
1193 89ff748d Thomas Thrainer
1194 89ff748d Thomas Thrainer
      if match_l and not match_r and "local_addr" not in info:
1195 89ff748d Thomas Thrainer
        # disk matches, but not attached to network, attach and recheck
1196 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1197 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1198 d01e51a5 Thomas Thrainer
        if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
1199 89ff748d Thomas Thrainer
          break
1200 89ff748d Thomas Thrainer
        else:
1201 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1202 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1203 89ff748d Thomas Thrainer
1204 89ff748d Thomas Thrainer
      if match_r and "local_dev" not in info:
1205 89ff748d Thomas Thrainer
        # no local disk, but network attached and it matches
1206 89ff748d Thomas Thrainer
        self._AssembleLocal(minor, self._children[0].dev_path,
1207 89ff748d Thomas Thrainer
                            self._children[1].dev_path, self.size)
1208 d01e51a5 Thomas Thrainer
        if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
1209 89ff748d Thomas Thrainer
          break
1210 89ff748d Thomas Thrainer
        else:
1211 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1212 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1213 89ff748d Thomas Thrainer
1214 89ff748d Thomas Thrainer
      # this case must be considered only if we actually have local
1215 89ff748d Thomas Thrainer
      # storage, i.e. not in diskless mode, because all diskless
1216 89ff748d Thomas Thrainer
      # devices are equal from the point of view of local
1217 89ff748d Thomas Thrainer
      # configuration
1218 89ff748d Thomas Thrainer
      if (match_l and "local_dev" in info and
1219 89ff748d Thomas Thrainer
          not match_r and "local_addr" in info):
1220 89ff748d Thomas Thrainer
        # strange case - the device network part points to somewhere
1221 89ff748d Thomas Thrainer
        # else, even though its local storage is ours; as we own the
1222 89ff748d Thomas Thrainer
        # drbd space, we try to disconnect from the remote peer and
1223 89ff748d Thomas Thrainer
        # reconnect to our correct one
1224 89ff748d Thomas Thrainer
        try:
1225 89ff748d Thomas Thrainer
          self._ShutdownNet(minor)
1226 89ff748d Thomas Thrainer
        except errors.BlockDeviceError, err:
1227 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: device has correct local storage, wrong"
1228 89ff748d Thomas Thrainer
                          " remote peer and is unable to disconnect in order"
1229 89ff748d Thomas Thrainer
                          " to attach to the correct peer: %s", minor, str(err))
1230 89ff748d Thomas Thrainer
        # note: _AssembleNet also handles the case when we don't want
1231 89ff748d Thomas Thrainer
        # local storage (i.e. one or more of the _[lr](host|port) is
1232 89ff748d Thomas Thrainer
        # None)
1233 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1234 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1235 d01e51a5 Thomas Thrainer
        if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
1236 89ff748d Thomas Thrainer
          break
1237 89ff748d Thomas Thrainer
        else:
1238 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1239 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1240 89ff748d Thomas Thrainer
1241 89ff748d Thomas Thrainer
    else:
1242 89ff748d Thomas Thrainer
      minor = None
1243 89ff748d Thomas Thrainer
1244 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1245 89ff748d Thomas Thrainer
    if minor is None:
1246 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1247 89ff748d Thomas Thrainer
                      self._aminor)
1248 89ff748d Thomas Thrainer
1249 89ff748d Thomas Thrainer
  def _FastAssemble(self):
1250 89ff748d Thomas Thrainer
    """Assemble the drbd device from zero.
1251 89ff748d Thomas Thrainer

1252 89ff748d Thomas Thrainer
    This is run when in Assemble we detect our minor is unused.
1253 89ff748d Thomas Thrainer

1254 89ff748d Thomas Thrainer
    """
1255 89ff748d Thomas Thrainer
    minor = self._aminor
1256 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
1257 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
1258 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
1259 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
1260 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
1261 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
1262 89ff748d Thomas Thrainer
                        constants.DRBD_NET_PROTOCOL,
1263 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1264 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1265 89ff748d Thomas Thrainer
1266 89ff748d Thomas Thrainer
  @classmethod
1267 89ff748d Thomas Thrainer
  def _ShutdownLocal(cls, minor):
1268 89ff748d Thomas Thrainer
    """Detach from the local device.
1269 89ff748d Thomas Thrainer

1270 89ff748d Thomas Thrainer
    I/Os will continue to be served from the remote device. If we
1271 89ff748d Thomas Thrainer
    don't have a remote device, this operation will fail.
1272 89ff748d Thomas Thrainer

1273 89ff748d Thomas Thrainer
    """
1274 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1275 89ff748d Thomas Thrainer
    if result.failed:
1276 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
1277 89ff748d Thomas Thrainer
                      minor, result.output)
1278 89ff748d Thomas Thrainer
1279 89ff748d Thomas Thrainer
  @classmethod
1280 89ff748d Thomas Thrainer
  def _ShutdownNet(cls, minor):
1281 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
1282 89ff748d Thomas Thrainer

1283 89ff748d Thomas Thrainer
    This fails if we don't have a local device.
1284 89ff748d Thomas Thrainer

1285 89ff748d Thomas Thrainer
    """
1286 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1287 89ff748d Thomas Thrainer
    if result.failed:
1288 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
1289 89ff748d Thomas Thrainer
                      minor, result.output)
1290 89ff748d Thomas Thrainer
1291 89ff748d Thomas Thrainer
  @classmethod
1292 89ff748d Thomas Thrainer
  def _ShutdownAll(cls, minor):
1293 89ff748d Thomas Thrainer
    """Deactivate the device.
1294 89ff748d Thomas Thrainer

1295 89ff748d Thomas Thrainer
    This will, of course, fail if the device is in use.
1296 89ff748d Thomas Thrainer

1297 89ff748d Thomas Thrainer
    """
1298 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1299 89ff748d Thomas Thrainer
    if result.failed:
1300 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown drbd device: %s",
1301 89ff748d Thomas Thrainer
                      minor, result.output)
1302 89ff748d Thomas Thrainer
1303 89ff748d Thomas Thrainer
  def Shutdown(self):
1304 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
1305 89ff748d Thomas Thrainer

1306 89ff748d Thomas Thrainer
    """
1307 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
1308 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1309 89ff748d Thomas Thrainer
      return
1310 89ff748d Thomas Thrainer
    minor = self.minor
1311 89ff748d Thomas Thrainer
    self.minor = None
1312 89ff748d Thomas Thrainer
    self.dev_path = None
1313 89ff748d Thomas Thrainer
    self._ShutdownAll(minor)
1314 89ff748d Thomas Thrainer
1315 89ff748d Thomas Thrainer
  def Remove(self):
1316 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
1317 89ff748d Thomas Thrainer

1318 89ff748d Thomas Thrainer
    """
1319 89ff748d Thomas Thrainer
    self.Shutdown()
1320 89ff748d Thomas Thrainer
1321 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
1322 fd300bc7 Thomas Thrainer
    """Rename a device.
1323 fd300bc7 Thomas Thrainer

1324 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
1325 fd300bc7 Thomas Thrainer

1326 fd300bc7 Thomas Thrainer
    """
1327 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
1328 fd300bc7 Thomas Thrainer
1329 89ff748d Thomas Thrainer
  @classmethod
1330 89ff748d Thomas Thrainer
  def Create(cls, unique_id, children, size, params, excl_stor):
1331 89ff748d Thomas Thrainer
    """Create a new DRBD8 device.
1332 89ff748d Thomas Thrainer

1333 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
1334 89ff748d Thomas Thrainer
    function only initializes the metadata.
1335 89ff748d Thomas Thrainer

1336 89ff748d Thomas Thrainer
    """
1337 89ff748d Thomas Thrainer
    if len(children) != 2:
1338 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1339 89ff748d Thomas Thrainer
    if excl_stor:
1340 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("DRBD device requested with"
1341 89ff748d Thomas Thrainer
                                   " exclusive_storage")
1342 89ff748d Thomas Thrainer
    # check that the minor is unused
1343 89ff748d Thomas Thrainer
    aminor = unique_id[4]
1344 2fe690f1 Thomas Thrainer
1345 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
1346 2fe690f1 Thomas Thrainer
    if drbd_info.HasMinorStatus(aminor):
1347 2fe690f1 Thomas Thrainer
      status = drbd_info.GetMinorStatus(aminor)
1348 89ff748d Thomas Thrainer
      in_use = status.is_in_use
1349 89ff748d Thomas Thrainer
    else:
1350 89ff748d Thomas Thrainer
      in_use = False
1351 89ff748d Thomas Thrainer
    if in_use:
1352 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: minor is already in use at Create() time",
1353 89ff748d Thomas Thrainer
                      aminor)
1354 89ff748d Thomas Thrainer
    meta = children[1]
1355 89ff748d Thomas Thrainer
    meta.Assemble()
1356 89ff748d Thomas Thrainer
    if not meta.Attach():
1357 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to meta device '%s'",
1358 89ff748d Thomas Thrainer
                      aminor, meta)
1359 89ff748d Thomas Thrainer
    cls._CheckMetaSize(meta.dev_path)
1360 89ff748d Thomas Thrainer
    cls._InitMeta(aminor, meta.dev_path)
1361 89ff748d Thomas Thrainer
    return cls(unique_id, children, size, params)
1362 89ff748d Thomas Thrainer
1363 89ff748d Thomas Thrainer
  def Grow(self, amount, dryrun, backingstore):
1364 89ff748d Thomas Thrainer
    """Resize the DRBD device and its backing storage.
1365 89ff748d Thomas Thrainer

1366 89ff748d Thomas Thrainer
    """
1367 89ff748d Thomas Thrainer
    if self.minor is None:
1368 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
1369 89ff748d Thomas Thrainer
    if len(self._children) != 2 or None in self._children:
1370 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
1371 89ff748d Thomas Thrainer
    self._children[0].Grow(amount, dryrun, backingstore)
1372 89ff748d Thomas Thrainer
    if dryrun or backingstore:
1373 89ff748d Thomas Thrainer
      # DRBD does not support dry-run mode and is not backing storage,
1374 89ff748d Thomas Thrainer
      # so we'll return here
1375 89ff748d Thomas Thrainer
      return
1376 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1377 89ff748d Thomas Thrainer
                           "%dm" % (self.size + amount)])
1378 89ff748d Thomas Thrainer
    if result.failed:
1379 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1380 89ff748d Thomas Thrainer
1381 89ff748d Thomas Thrainer
1382 89ff748d Thomas Thrainer
def _CanReadDevice(path):
1383 89ff748d Thomas Thrainer
  """Check if we can read from the given device.
1384 89ff748d Thomas Thrainer

1385 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
1386 89ff748d Thomas Thrainer

1387 89ff748d Thomas Thrainer
  """
1388 89ff748d Thomas Thrainer
  try:
1389 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1390 89ff748d Thomas Thrainer
    return True
1391 89ff748d Thomas Thrainer
  except EnvironmentError:
1392 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
1393 89ff748d Thomas Thrainer
    return False