Statistics
| Branch: | Tag: | Revision:

root / lib / block / drbd.py @ fd300bc7

History | View | Annotate | Download (45.6 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 fd300bc7 Thomas Thrainer
class DRBD8Status(object):
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 fd300bc7 Thomas Thrainer
  from L{DRBD8._JoinProcDataPerMinor}.
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 fd300bc7 Thomas Thrainer
    self.is_primary = self.lrole == self.RO_PRIMARY
123 fd300bc7 Thomas Thrainer
    self.is_secondary = self.lrole == self.RO_SECONDARY
124 fd300bc7 Thomas Thrainer
    self.peer_primary = self.rrole == self.RO_PRIMARY
125 fd300bc7 Thomas Thrainer
    self.peer_secondary = self.rrole == self.RO_SECONDARY
126 fd300bc7 Thomas Thrainer
    self.both_primary = self.is_primary and self.peer_primary
127 fd300bc7 Thomas Thrainer
    self.both_secondary = self.is_secondary and self.peer_secondary
128 fd300bc7 Thomas Thrainer
129 fd300bc7 Thomas Thrainer
    self.is_diskless = self.ldisk == self.DS_DISKLESS
130 fd300bc7 Thomas Thrainer
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
131 fd300bc7 Thomas Thrainer
132 fd300bc7 Thomas Thrainer
    self.is_in_resync = self.cstatus in self.CSET_SYNC
133 fd300bc7 Thomas Thrainer
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
134 fd300bc7 Thomas Thrainer
135 fd300bc7 Thomas Thrainer
    m = self.SYNC_RE.match(procline)
136 fd300bc7 Thomas Thrainer
    if m:
137 fd300bc7 Thomas Thrainer
      self.sync_percent = float(m.group(1))
138 fd300bc7 Thomas Thrainer
      hours = int(m.group(2))
139 fd300bc7 Thomas Thrainer
      minutes = int(m.group(3))
140 fd300bc7 Thomas Thrainer
      seconds = int(m.group(4))
141 fd300bc7 Thomas Thrainer
      self.est_time = hours * 3600 + minutes * 60 + seconds
142 fd300bc7 Thomas Thrainer
    else:
143 fd300bc7 Thomas Thrainer
      # we have (in this if branch) no percent information, but if
144 fd300bc7 Thomas Thrainer
      # we're resyncing we need to 'fake' a sync percent information,
145 fd300bc7 Thomas Thrainer
      # as this is how cmdlib determines if it makes sense to wait for
146 fd300bc7 Thomas Thrainer
      # resyncing or not
147 fd300bc7 Thomas Thrainer
      if self.is_in_resync:
148 fd300bc7 Thomas Thrainer
        self.sync_percent = 0
149 fd300bc7 Thomas Thrainer
      else:
150 fd300bc7 Thomas Thrainer
        self.sync_percent = None
151 fd300bc7 Thomas Thrainer
      self.est_time = None
152 fd300bc7 Thomas Thrainer
153 fd300bc7 Thomas Thrainer
154 fd300bc7 Thomas Thrainer
class DRBD8(base.BlockDev):
155 fd300bc7 Thomas Thrainer
  """DRBD v8.x block device.
156 fd300bc7 Thomas Thrainer

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

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

166 89ff748d Thomas Thrainer
  """
167 89ff748d Thomas Thrainer
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
168 89ff748d Thomas Thrainer
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
169 89ff748d Thomas Thrainer
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
170 89ff748d Thomas Thrainer
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
171 89ff748d Thomas Thrainer
172 89ff748d Thomas Thrainer
  _DRBD_MAJOR = 147
173 fd300bc7 Thomas Thrainer
  _ST_UNCONFIGURED = DRBD8Status.CS_UNCONFIGURED
174 fd300bc7 Thomas Thrainer
  _ST_WFCONNECTION = DRBD8Status.CS_WFCONNECTION
175 fd300bc7 Thomas Thrainer
  _ST_CONNECTED = DRBD8Status.CS_CONNECTED
176 89ff748d Thomas Thrainer
177 89ff748d Thomas Thrainer
  _STATUS_FILE = constants.DRBD_STATUS_FILE
178 89ff748d Thomas Thrainer
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
179 89ff748d Thomas Thrainer
180 fd300bc7 Thomas Thrainer
  _MAX_MINORS = 255
181 fd300bc7 Thomas Thrainer
  _PARSE_SHOW = None
182 fd300bc7 Thomas Thrainer
183 fd300bc7 Thomas Thrainer
  # timeout constants
184 fd300bc7 Thomas Thrainer
  _NET_RECONFIG_TIMEOUT = 60
185 fd300bc7 Thomas Thrainer
186 fd300bc7 Thomas Thrainer
  # command line options for barriers
187 fd300bc7 Thomas Thrainer
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
188 fd300bc7 Thomas Thrainer
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
189 fd300bc7 Thomas Thrainer
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
190 fd300bc7 Thomas Thrainer
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
191 fd300bc7 Thomas Thrainer
192 fd300bc7 Thomas Thrainer
  def __init__(self, unique_id, children, size, params):
193 fd300bc7 Thomas Thrainer
    if children and children.count(None) > 0:
194 fd300bc7 Thomas Thrainer
      children = []
195 fd300bc7 Thomas Thrainer
    if len(children) not in (0, 2):
196 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(children))
197 fd300bc7 Thomas Thrainer
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
198 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(unique_id))
199 fd300bc7 Thomas Thrainer
    (self._lhost, self._lport,
200 fd300bc7 Thomas Thrainer
     self._rhost, self._rport,
201 fd300bc7 Thomas Thrainer
     self._aminor, self._secret) = unique_id
202 fd300bc7 Thomas Thrainer
    if children:
203 fd300bc7 Thomas Thrainer
      if not _CanReadDevice(children[1].dev_path):
204 fd300bc7 Thomas Thrainer
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
205 fd300bc7 Thomas Thrainer
        children = []
206 fd300bc7 Thomas Thrainer
    super(DRBD8, self).__init__(unique_id, children, size, params)
207 fd300bc7 Thomas Thrainer
    self.major = self._DRBD_MAJOR
208 fd300bc7 Thomas Thrainer
    version = self._GetVersion(self._GetProcData())
209 fd300bc7 Thomas Thrainer
    if version["k_major"] != 8:
210 fd300bc7 Thomas Thrainer
      base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
211 fd300bc7 Thomas Thrainer
                      " usage: kernel is %s.%s, ganeti wants 8.x",
212 fd300bc7 Thomas Thrainer
                      version["k_major"], version["k_minor"])
213 fd300bc7 Thomas Thrainer
214 fd300bc7 Thomas Thrainer
    if (self._lhost is not None and self._lhost == self._rhost and
215 fd300bc7 Thomas Thrainer
            self._lport == self._rport):
216 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data, same local/remote %s" %
217 fd300bc7 Thomas Thrainer
                       (unique_id,))
218 fd300bc7 Thomas Thrainer
    self.Attach()
219 fd300bc7 Thomas Thrainer
220 89ff748d Thomas Thrainer
  @staticmethod
221 89ff748d Thomas Thrainer
  def _GetProcData(filename=_STATUS_FILE):
222 89ff748d Thomas Thrainer
    """Return data from /proc/drbd.
223 89ff748d Thomas Thrainer

224 89ff748d Thomas Thrainer
    """
225 89ff748d Thomas Thrainer
    try:
226 89ff748d Thomas Thrainer
      data = utils.ReadFile(filename).splitlines()
227 89ff748d Thomas Thrainer
    except EnvironmentError, err:
228 89ff748d Thomas Thrainer
      if err.errno == errno.ENOENT:
229 89ff748d Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
230 89ff748d Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
231 89ff748d Thomas Thrainer
      else:
232 89ff748d Thomas Thrainer
        base.ThrowError("Can't read the DRBD proc file %s: %s",
233 89ff748d Thomas Thrainer
                        filename, str(err))
234 89ff748d Thomas Thrainer
    if not data:
235 89ff748d Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
236 89ff748d Thomas Thrainer
    return data
237 89ff748d Thomas Thrainer
238 89ff748d Thomas Thrainer
  @classmethod
239 38396ae2 Thomas Thrainer
  def _JoinProcDataPerMinor(cls, data):
240 89ff748d Thomas Thrainer
    """Transform the output of _GetProdData into a nicer form.
241 89ff748d Thomas Thrainer

242 89ff748d Thomas Thrainer
    @return: a dictionary of minor: joined lines from /proc/drbd
243 89ff748d Thomas Thrainer
        for that minor
244 89ff748d Thomas Thrainer

245 89ff748d Thomas Thrainer
    """
246 89ff748d Thomas Thrainer
    results = {}
247 89ff748d Thomas Thrainer
    old_minor = old_line = None
248 89ff748d Thomas Thrainer
    for line in data:
249 89ff748d Thomas Thrainer
      if not line: # completely empty lines, as can be returned by drbd8.0+
250 89ff748d Thomas Thrainer
        continue
251 89ff748d Thomas Thrainer
      lresult = cls._VALID_LINE_RE.match(line)
252 89ff748d Thomas Thrainer
      if lresult is not None:
253 89ff748d Thomas Thrainer
        if old_minor is not None:
254 89ff748d Thomas Thrainer
          results[old_minor] = old_line
255 89ff748d Thomas Thrainer
        old_minor = int(lresult.group(1))
256 89ff748d Thomas Thrainer
        old_line = line
257 89ff748d Thomas Thrainer
      else:
258 89ff748d Thomas Thrainer
        if old_minor is not None:
259 89ff748d Thomas Thrainer
          old_line += " " + line.strip()
260 89ff748d Thomas Thrainer
    # add last line
261 89ff748d Thomas Thrainer
    if old_minor is not None:
262 89ff748d Thomas Thrainer
      results[old_minor] = old_line
263 89ff748d Thomas Thrainer
    return results
264 89ff748d Thomas Thrainer
265 89ff748d Thomas Thrainer
  @classmethod
266 89ff748d Thomas Thrainer
  def _GetVersion(cls, proc_data):
267 89ff748d Thomas Thrainer
    """Return the DRBD version.
268 89ff748d Thomas Thrainer

269 89ff748d Thomas Thrainer
    This will return a dict with keys:
270 89ff748d Thomas Thrainer
      - k_major
271 89ff748d Thomas Thrainer
      - k_minor
272 89ff748d Thomas Thrainer
      - k_point
273 89ff748d Thomas Thrainer
      - api
274 89ff748d Thomas Thrainer
      - proto
275 89ff748d Thomas Thrainer
      - proto2 (only on drbd > 8.2.X)
276 89ff748d Thomas Thrainer

277 89ff748d Thomas Thrainer
    """
278 89ff748d Thomas Thrainer
    first_line = proc_data[0].strip()
279 89ff748d Thomas Thrainer
    version = cls._VERSION_RE.match(first_line)
280 89ff748d Thomas Thrainer
    if not version:
281 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
282 89ff748d Thomas Thrainer
                                    first_line)
283 89ff748d Thomas Thrainer
284 89ff748d Thomas Thrainer
    values = version.groups()
285 89ff748d Thomas Thrainer
    retval = {
286 89ff748d Thomas Thrainer
      "k_major": int(values[0]),
287 89ff748d Thomas Thrainer
      "k_minor": int(values[1]),
288 89ff748d Thomas Thrainer
      "k_point": int(values[2]),
289 89ff748d Thomas Thrainer
      "api": int(values[3]),
290 89ff748d Thomas Thrainer
      "proto": int(values[4]),
291 89ff748d Thomas Thrainer
      }
292 89ff748d Thomas Thrainer
    if values[5] is not None:
293 89ff748d Thomas Thrainer
      retval["proto2"] = values[5]
294 89ff748d Thomas Thrainer
295 89ff748d Thomas Thrainer
    return retval
296 89ff748d Thomas Thrainer
297 89ff748d Thomas Thrainer
  @staticmethod
298 89ff748d Thomas Thrainer
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
299 89ff748d Thomas Thrainer
    """Returns DRBD usermode_helper currently set.
300 89ff748d Thomas Thrainer

301 89ff748d Thomas Thrainer
    """
302 89ff748d Thomas Thrainer
    try:
303 89ff748d Thomas Thrainer
      helper = utils.ReadFile(filename).splitlines()[0]
304 89ff748d Thomas Thrainer
    except EnvironmentError, err:
305 89ff748d Thomas Thrainer
      if err.errno == errno.ENOENT:
306 89ff748d Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
307 89ff748d Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
308 89ff748d Thomas Thrainer
      else:
309 89ff748d Thomas Thrainer
        base.ThrowError("Can't read DRBD helper file %s: %s",
310 89ff748d Thomas Thrainer
                        filename, str(err))
311 89ff748d Thomas Thrainer
    if not helper:
312 89ff748d Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
313 89ff748d Thomas Thrainer
    return helper
314 89ff748d Thomas Thrainer
315 89ff748d Thomas Thrainer
  @staticmethod
316 89ff748d Thomas Thrainer
  def _DevPath(minor):
317 89ff748d Thomas Thrainer
    """Return the path to a drbd device for a given minor.
318 89ff748d Thomas Thrainer

319 89ff748d Thomas Thrainer
    """
320 89ff748d Thomas Thrainer
    return "/dev/drbd%d" % minor
321 89ff748d Thomas Thrainer
322 89ff748d Thomas Thrainer
  @classmethod
323 89ff748d Thomas Thrainer
  def GetUsedDevs(cls):
324 89ff748d Thomas Thrainer
    """Compute the list of used DRBD devices.
325 89ff748d Thomas Thrainer

326 89ff748d Thomas Thrainer
    """
327 89ff748d Thomas Thrainer
    data = cls._GetProcData()
328 89ff748d Thomas Thrainer
329 89ff748d Thomas Thrainer
    used_devs = {}
330 89ff748d Thomas Thrainer
    for line in data:
331 89ff748d Thomas Thrainer
      match = cls._VALID_LINE_RE.match(line)
332 89ff748d Thomas Thrainer
      if not match:
333 89ff748d Thomas Thrainer
        continue
334 89ff748d Thomas Thrainer
      minor = int(match.group(1))
335 89ff748d Thomas Thrainer
      state = match.group(2)
336 89ff748d Thomas Thrainer
      if state == cls._ST_UNCONFIGURED:
337 89ff748d Thomas Thrainer
        continue
338 89ff748d Thomas Thrainer
      used_devs[minor] = state, line
339 89ff748d Thomas Thrainer
340 89ff748d Thomas Thrainer
    return used_devs
341 89ff748d Thomas Thrainer
342 89ff748d Thomas Thrainer
  def _SetFromMinor(self, minor):
343 89ff748d Thomas Thrainer
    """Set our parameters based on the given minor.
344 89ff748d Thomas Thrainer

345 89ff748d Thomas Thrainer
    This sets our minor variable and our dev_path.
346 89ff748d Thomas Thrainer

347 89ff748d Thomas Thrainer
    """
348 89ff748d Thomas Thrainer
    if minor is None:
349 89ff748d Thomas Thrainer
      self.minor = self.dev_path = None
350 89ff748d Thomas Thrainer
      self.attached = False
351 89ff748d Thomas Thrainer
    else:
352 89ff748d Thomas Thrainer
      self.minor = minor
353 89ff748d Thomas Thrainer
      self.dev_path = self._DevPath(minor)
354 89ff748d Thomas Thrainer
      self.attached = True
355 89ff748d Thomas Thrainer
356 89ff748d Thomas Thrainer
  @staticmethod
357 89ff748d Thomas Thrainer
  def _CheckMetaSize(meta_device):
358 89ff748d Thomas Thrainer
    """Check if the given meta device looks like a valid one.
359 89ff748d Thomas Thrainer

360 89ff748d Thomas Thrainer
    This currently only checks the size, which must be around
361 89ff748d Thomas Thrainer
    128MiB.
362 89ff748d Thomas Thrainer

363 89ff748d Thomas Thrainer
    """
364 89ff748d Thomas Thrainer
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
365 89ff748d Thomas Thrainer
    if result.failed:
366 89ff748d Thomas Thrainer
      base.ThrowError("Failed to get device size: %s - %s",
367 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
368 89ff748d Thomas Thrainer
    try:
369 89ff748d Thomas Thrainer
      sectors = int(result.stdout)
370 89ff748d Thomas Thrainer
    except (TypeError, ValueError):
371 89ff748d Thomas Thrainer
      base.ThrowError("Invalid output from blockdev: '%s'", result.stdout)
372 89ff748d Thomas Thrainer
    num_bytes = sectors * 512
373 89ff748d Thomas Thrainer
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
374 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too small (%.2fMib)",
375 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
376 89ff748d Thomas Thrainer
    # the maximum *valid* size of the meta device when living on top
377 89ff748d Thomas Thrainer
    # of LVM is hard to compute: it depends on the number of stripes
378 89ff748d Thomas Thrainer
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
379 89ff748d Thomas Thrainer
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
380 89ff748d Thomas Thrainer
    # size meta device; as such, we restrict it to 1GB (a little bit
381 89ff748d Thomas Thrainer
    # too generous, but making assumptions about PE size is hard)
382 89ff748d Thomas Thrainer
    if num_bytes > 1024 * 1024 * 1024:
383 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too big (%.2fMiB)",
384 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
385 89ff748d Thomas Thrainer
386 89ff748d Thomas Thrainer
  @classmethod
387 89ff748d Thomas Thrainer
  def _InitMeta(cls, minor, dev_path):
388 89ff748d Thomas Thrainer
    """Initialize a meta device.
389 89ff748d Thomas Thrainer

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

392 89ff748d Thomas Thrainer
    """
393 89ff748d Thomas Thrainer
    # Zero the metadata first, in order to make sure drbdmeta doesn't
394 89ff748d Thomas Thrainer
    # try to auto-detect existing filesystems or similar (see
395 89ff748d Thomas Thrainer
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
396 89ff748d Thomas Thrainer
    # care about the first 128MB of data in the device, even though it
397 89ff748d Thomas Thrainer
    # can be bigger
398 89ff748d Thomas Thrainer
    result = utils.RunCmd([constants.DD_CMD,
399 89ff748d Thomas Thrainer
                           "if=/dev/zero", "of=%s" % dev_path,
400 89ff748d Thomas Thrainer
                           "bs=1048576", "count=128", "oflag=direct"])
401 89ff748d Thomas Thrainer
    if result.failed:
402 89ff748d Thomas Thrainer
      base.ThrowError("Can't wipe the meta device: %s", result.output)
403 89ff748d Thomas Thrainer
404 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
405 89ff748d Thomas Thrainer
                           "v08", dev_path, "0", "create-md"])
406 89ff748d Thomas Thrainer
    if result.failed:
407 89ff748d Thomas Thrainer
      base.ThrowError("Can't initialize meta device: %s", result.output)
408 89ff748d Thomas Thrainer
409 89ff748d Thomas Thrainer
  @classmethod
410 89ff748d Thomas Thrainer
  def _FindUnusedMinor(cls):
411 89ff748d Thomas Thrainer
    """Find an unused DRBD device.
412 89ff748d Thomas Thrainer

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

416 89ff748d Thomas Thrainer
    """
417 89ff748d Thomas Thrainer
    data = cls._GetProcData()
418 89ff748d Thomas Thrainer
419 89ff748d Thomas Thrainer
    highest = None
420 89ff748d Thomas Thrainer
    for line in data:
421 89ff748d Thomas Thrainer
      match = cls._UNUSED_LINE_RE.match(line)
422 89ff748d Thomas Thrainer
      if match:
423 89ff748d Thomas Thrainer
        return int(match.group(1))
424 89ff748d Thomas Thrainer
      match = cls._VALID_LINE_RE.match(line)
425 89ff748d Thomas Thrainer
      if match:
426 89ff748d Thomas Thrainer
        minor = int(match.group(1))
427 89ff748d Thomas Thrainer
        highest = max(highest, minor)
428 89ff748d Thomas Thrainer
    if highest is None: # there are no minors in use at all
429 89ff748d Thomas Thrainer
      return 0
430 89ff748d Thomas Thrainer
    if highest >= cls._MAX_MINORS:
431 89ff748d Thomas Thrainer
      logging.error("Error: no free drbd minors!")
432 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
433 89ff748d Thomas Thrainer
    return highest + 1
434 89ff748d Thomas Thrainer
435 89ff748d Thomas Thrainer
  @classmethod
436 89ff748d Thomas Thrainer
  def _GetShowParser(cls):
437 89ff748d Thomas Thrainer
    """Return a parser for `drbd show` output.
438 89ff748d Thomas Thrainer

439 89ff748d Thomas Thrainer
    This will either create or return an already-created parser for the
440 89ff748d Thomas Thrainer
    output of the command `drbd show`.
441 89ff748d Thomas Thrainer

442 89ff748d Thomas Thrainer
    """
443 89ff748d Thomas Thrainer
    if cls._PARSE_SHOW is not None:
444 89ff748d Thomas Thrainer
      return cls._PARSE_SHOW
445 89ff748d Thomas Thrainer
446 89ff748d Thomas Thrainer
    # pyparsing setup
447 89ff748d Thomas Thrainer
    lbrace = pyp.Literal("{").suppress()
448 89ff748d Thomas Thrainer
    rbrace = pyp.Literal("}").suppress()
449 89ff748d Thomas Thrainer
    lbracket = pyp.Literal("[").suppress()
450 89ff748d Thomas Thrainer
    rbracket = pyp.Literal("]").suppress()
451 89ff748d Thomas Thrainer
    semi = pyp.Literal(";").suppress()
452 89ff748d Thomas Thrainer
    colon = pyp.Literal(":").suppress()
453 89ff748d Thomas Thrainer
    # this also converts the value to an int
454 89ff748d Thomas Thrainer
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
455 89ff748d Thomas Thrainer
456 89ff748d Thomas Thrainer
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
457 89ff748d Thomas Thrainer
    defa = pyp.Literal("_is_default").suppress()
458 89ff748d Thomas Thrainer
    dbl_quote = pyp.Literal('"').suppress()
459 89ff748d Thomas Thrainer
460 89ff748d Thomas Thrainer
    keyword = pyp.Word(pyp.alphanums + "-")
461 89ff748d Thomas Thrainer
462 89ff748d Thomas Thrainer
    # value types
463 89ff748d Thomas Thrainer
    value = pyp.Word(pyp.alphanums + "_-/.:")
464 89ff748d Thomas Thrainer
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
465 89ff748d Thomas Thrainer
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
466 89ff748d Thomas Thrainer
                 pyp.Word(pyp.nums + ".") + colon + number)
467 89ff748d Thomas Thrainer
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
468 89ff748d Thomas Thrainer
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
469 89ff748d Thomas Thrainer
                 pyp.Optional(rbracket) + colon + number)
470 89ff748d Thomas Thrainer
    # meta device, extended syntax
471 89ff748d Thomas Thrainer
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
472 89ff748d Thomas Thrainer
    # device name, extended syntax
473 89ff748d Thomas Thrainer
    device_value = pyp.Literal("minor").suppress() + number
474 89ff748d Thomas Thrainer
475 89ff748d Thomas Thrainer
    # a statement
476 89ff748d Thomas Thrainer
    stmt = (~rbrace + keyword + ~lbrace +
477 89ff748d Thomas Thrainer
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
478 89ff748d Thomas Thrainer
                         device_value) +
479 89ff748d Thomas Thrainer
            pyp.Optional(defa) + semi +
480 89ff748d Thomas Thrainer
            pyp.Optional(pyp.restOfLine).suppress())
481 89ff748d Thomas Thrainer
482 89ff748d Thomas Thrainer
    # an entire section
483 89ff748d Thomas Thrainer
    section_name = pyp.Word(pyp.alphas + "_")
484 89ff748d Thomas Thrainer
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
485 89ff748d Thomas Thrainer
486 89ff748d Thomas Thrainer
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
487 89ff748d Thomas Thrainer
    bnf.ignore(comment)
488 89ff748d Thomas Thrainer
489 89ff748d Thomas Thrainer
    cls._PARSE_SHOW = bnf
490 89ff748d Thomas Thrainer
491 89ff748d Thomas Thrainer
    return bnf
492 89ff748d Thomas Thrainer
493 89ff748d Thomas Thrainer
  @classmethod
494 89ff748d Thomas Thrainer
  def _GetShowData(cls, minor):
495 89ff748d Thomas Thrainer
    """Return the `drbdsetup show` data for a minor.
496 89ff748d Thomas Thrainer

497 89ff748d Thomas Thrainer
    """
498 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
499 89ff748d Thomas Thrainer
    if result.failed:
500 89ff748d Thomas Thrainer
      logging.error("Can't display the drbd config: %s - %s",
501 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
502 89ff748d Thomas Thrainer
      return None
503 89ff748d Thomas Thrainer
    return result.stdout
504 89ff748d Thomas Thrainer
505 89ff748d Thomas Thrainer
  @classmethod
506 89ff748d Thomas Thrainer
  def _GetDevInfo(cls, out):
507 89ff748d Thomas Thrainer
    """Parse details about a given DRBD minor.
508 89ff748d Thomas Thrainer

509 89ff748d Thomas Thrainer
    This return, if available, the local backing device (as a path)
510 89ff748d Thomas Thrainer
    and the local and remote (ip, port) information from a string
511 89ff748d Thomas Thrainer
    containing the output of the `drbdsetup show` command as returned
512 89ff748d Thomas Thrainer
    by _GetShowData.
513 89ff748d Thomas Thrainer

514 89ff748d Thomas Thrainer
    """
515 89ff748d Thomas Thrainer
    data = {}
516 89ff748d Thomas Thrainer
    if not out:
517 89ff748d Thomas Thrainer
      return data
518 89ff748d Thomas Thrainer
519 89ff748d Thomas Thrainer
    bnf = cls._GetShowParser()
520 89ff748d Thomas Thrainer
    # run pyparse
521 89ff748d Thomas Thrainer
522 89ff748d Thomas Thrainer
    try:
523 89ff748d Thomas Thrainer
      results = bnf.parseString(out)
524 89ff748d Thomas Thrainer
    except pyp.ParseException, err:
525 89ff748d Thomas Thrainer
      base.ThrowError("Can't parse drbdsetup show output: %s", str(err))
526 89ff748d Thomas Thrainer
527 89ff748d Thomas Thrainer
    # and massage the results into our desired format
528 89ff748d Thomas Thrainer
    for section in results:
529 89ff748d Thomas Thrainer
      sname = section[0]
530 89ff748d Thomas Thrainer
      if sname == "_this_host":
531 89ff748d Thomas Thrainer
        for lst in section[1:]:
532 89ff748d Thomas Thrainer
          if lst[0] == "disk":
533 89ff748d Thomas Thrainer
            data["local_dev"] = lst[1]
534 89ff748d Thomas Thrainer
          elif lst[0] == "meta-disk":
535 89ff748d Thomas Thrainer
            data["meta_dev"] = lst[1]
536 89ff748d Thomas Thrainer
            data["meta_index"] = lst[2]
537 89ff748d Thomas Thrainer
          elif lst[0] == "address":
538 89ff748d Thomas Thrainer
            data["local_addr"] = tuple(lst[1:])
539 89ff748d Thomas Thrainer
      elif sname == "_remote_host":
540 89ff748d Thomas Thrainer
        for lst in section[1:]:
541 89ff748d Thomas Thrainer
          if lst[0] == "address":
542 89ff748d Thomas Thrainer
            data["remote_addr"] = tuple(lst[1:])
543 89ff748d Thomas Thrainer
    return data
544 89ff748d Thomas Thrainer
545 89ff748d Thomas Thrainer
  def _MatchesLocal(self, info):
546 89ff748d Thomas Thrainer
    """Test if our local config matches with an existing device.
547 89ff748d Thomas Thrainer

548 89ff748d Thomas Thrainer
    The parameter should be as returned from `_GetDevInfo()`. This
549 89ff748d Thomas Thrainer
    method tests if our local backing device is the same as the one in
550 89ff748d Thomas Thrainer
    the info parameter, in effect testing if we look like the given
551 89ff748d Thomas Thrainer
    device.
552 89ff748d Thomas Thrainer

553 89ff748d Thomas Thrainer
    """
554 89ff748d Thomas Thrainer
    if self._children:
555 89ff748d Thomas Thrainer
      backend, meta = self._children
556 89ff748d Thomas Thrainer
    else:
557 89ff748d Thomas Thrainer
      backend = meta = None
558 89ff748d Thomas Thrainer
559 89ff748d Thomas Thrainer
    if backend is not None:
560 89ff748d Thomas Thrainer
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
561 89ff748d Thomas Thrainer
    else:
562 89ff748d Thomas Thrainer
      retval = ("local_dev" not in info)
563 89ff748d Thomas Thrainer
564 89ff748d Thomas Thrainer
    if meta is not None:
565 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" in info and
566 89ff748d Thomas Thrainer
                           info["meta_dev"] == meta.dev_path)
567 89ff748d Thomas Thrainer
      retval = retval and ("meta_index" in info and
568 89ff748d Thomas Thrainer
                           info["meta_index"] == 0)
569 89ff748d Thomas Thrainer
    else:
570 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" not in info and
571 89ff748d Thomas Thrainer
                           "meta_index" not in info)
572 89ff748d Thomas Thrainer
    return retval
573 89ff748d Thomas Thrainer
574 89ff748d Thomas Thrainer
  def _MatchesNet(self, info):
575 89ff748d Thomas Thrainer
    """Test if our network config matches with an existing device.
576 89ff748d Thomas Thrainer

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

582 89ff748d Thomas Thrainer
    """
583 89ff748d Thomas Thrainer
    if (((self._lhost is None and not ("local_addr" in info)) and
584 89ff748d Thomas Thrainer
         (self._rhost is None and not ("remote_addr" in info)))):
585 89ff748d Thomas Thrainer
      return True
586 89ff748d Thomas Thrainer
587 89ff748d Thomas Thrainer
    if self._lhost is None:
588 89ff748d Thomas Thrainer
      return False
589 89ff748d Thomas Thrainer
590 89ff748d Thomas Thrainer
    if not ("local_addr" in info and
591 89ff748d Thomas Thrainer
            "remote_addr" in info):
592 89ff748d Thomas Thrainer
      return False
593 89ff748d Thomas Thrainer
594 89ff748d Thomas Thrainer
    retval = (info["local_addr"] == (self._lhost, self._lport))
595 89ff748d Thomas Thrainer
    retval = (retval and
596 89ff748d Thomas Thrainer
              info["remote_addr"] == (self._rhost, self._rport))
597 89ff748d Thomas Thrainer
    return retval
598 89ff748d Thomas Thrainer
599 89ff748d Thomas Thrainer
  def _AssembleLocal(self, minor, backend, meta, size):
600 89ff748d Thomas Thrainer
    """Configure the local part of a DRBD device.
601 89ff748d Thomas Thrainer

602 89ff748d Thomas Thrainer
    """
603 89ff748d Thomas Thrainer
    args = ["drbdsetup", self._DevPath(minor), "disk",
604 89ff748d Thomas Thrainer
            backend, meta, "0",
605 89ff748d Thomas Thrainer
            "-e", "detach",
606 89ff748d Thomas Thrainer
            "--create-device"]
607 89ff748d Thomas Thrainer
    if size:
608 89ff748d Thomas Thrainer
      args.extend(["-d", "%sm" % size])
609 89ff748d Thomas Thrainer
610 89ff748d Thomas Thrainer
    version = self._GetVersion(self._GetProcData())
611 89ff748d Thomas Thrainer
    vmaj = version["k_major"]
612 89ff748d Thomas Thrainer
    vmin = version["k_minor"]
613 89ff748d Thomas Thrainer
    vrel = version["k_point"]
614 89ff748d Thomas Thrainer
615 89ff748d Thomas Thrainer
    barrier_args = \
616 89ff748d Thomas Thrainer
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
617 89ff748d Thomas Thrainer
                                   self.params[constants.LDP_BARRIERS],
618 89ff748d Thomas Thrainer
                                   self.params[constants.LDP_NO_META_FLUSH])
619 89ff748d Thomas Thrainer
    args.extend(barrier_args)
620 89ff748d Thomas Thrainer
621 89ff748d Thomas Thrainer
    if self.params[constants.LDP_DISK_CUSTOM]:
622 89ff748d Thomas Thrainer
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
623 89ff748d Thomas Thrainer
624 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
625 89ff748d Thomas Thrainer
    if result.failed:
626 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach local disk: %s",
627 89ff748d Thomas Thrainer
                      minor, result.output)
628 89ff748d Thomas Thrainer
629 89ff748d Thomas Thrainer
  @classmethod
630 89ff748d Thomas Thrainer
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
631 89ff748d Thomas Thrainer
                              disable_meta_flush):
632 89ff748d Thomas Thrainer
    """Compute the DRBD command line parameters for disk barriers
633 89ff748d Thomas Thrainer

634 89ff748d Thomas Thrainer
    Returns a list of the disk barrier parameters as requested via the
635 89ff748d Thomas Thrainer
    disabled_barriers and disable_meta_flush arguments, and according to the
636 89ff748d Thomas Thrainer
    supported ones in the DRBD version vmaj.vmin.vrel
637 89ff748d Thomas Thrainer

638 89ff748d Thomas Thrainer
    If the desired option is unsupported, raises errors.BlockDeviceError.
639 89ff748d Thomas Thrainer

640 89ff748d Thomas Thrainer
    """
641 89ff748d Thomas Thrainer
    disabled_barriers_set = frozenset(disabled_barriers)
642 89ff748d Thomas Thrainer
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
643 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
644 89ff748d Thomas Thrainer
                                    " barriers" % disabled_barriers)
645 89ff748d Thomas Thrainer
646 89ff748d Thomas Thrainer
    args = []
647 89ff748d Thomas Thrainer
648 89ff748d Thomas Thrainer
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
649 89ff748d Thomas Thrainer
    # does not exist)
650 89ff748d Thomas Thrainer
    if not vmaj == 8 and vmin in (0, 2, 3):
651 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
652 89ff748d Thomas Thrainer
                                    (vmaj, vmin, vrel))
653 89ff748d Thomas Thrainer
654 89ff748d Thomas Thrainer
    def _AppendOrRaise(option, min_version):
655 89ff748d Thomas Thrainer
      """Helper for DRBD options"""
656 89ff748d Thomas Thrainer
      if min_version is not None and vrel >= min_version:
657 89ff748d Thomas Thrainer
        args.append(option)
658 89ff748d Thomas Thrainer
      else:
659 89ff748d Thomas Thrainer
        raise errors.BlockDeviceError("Could not use the option %s as the"
660 89ff748d Thomas Thrainer
                                      " DRBD version %d.%d.%d does not support"
661 89ff748d Thomas Thrainer
                                      " it." % (option, vmaj, vmin, vrel))
662 89ff748d Thomas Thrainer
663 89ff748d Thomas Thrainer
    # the minimum version for each feature is encoded via pairs of (minor
664 89ff748d Thomas Thrainer
    # version -> x) where x is version in which support for the option was
665 89ff748d Thomas Thrainer
    # introduced.
666 89ff748d Thomas Thrainer
    meta_flush_supported = disk_flush_supported = {
667 89ff748d Thomas Thrainer
      0: 12,
668 89ff748d Thomas Thrainer
      2: 7,
669 89ff748d Thomas Thrainer
      3: 0,
670 89ff748d Thomas Thrainer
      }
671 89ff748d Thomas Thrainer
672 89ff748d Thomas Thrainer
    disk_drain_supported = {
673 89ff748d Thomas Thrainer
      2: 7,
674 89ff748d Thomas Thrainer
      3: 0,
675 89ff748d Thomas Thrainer
      }
676 89ff748d Thomas Thrainer
677 89ff748d Thomas Thrainer
    disk_barriers_supported = {
678 89ff748d Thomas Thrainer
      3: 0,
679 89ff748d Thomas Thrainer
      }
680 89ff748d Thomas Thrainer
681 89ff748d Thomas Thrainer
    # meta flushes
682 89ff748d Thomas Thrainer
    if disable_meta_flush:
683 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
684 89ff748d Thomas Thrainer
                     meta_flush_supported.get(vmin, None))
685 89ff748d Thomas Thrainer
686 89ff748d Thomas Thrainer
    # disk flushes
687 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
688 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
689 89ff748d Thomas Thrainer
                     disk_flush_supported.get(vmin, None))
690 89ff748d Thomas Thrainer
691 89ff748d Thomas Thrainer
    # disk drain
692 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
693 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
694 89ff748d Thomas Thrainer
                     disk_drain_supported.get(vmin, None))
695 89ff748d Thomas Thrainer
696 89ff748d Thomas Thrainer
    # disk barriers
697 89ff748d Thomas Thrainer
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
698 89ff748d Thomas Thrainer
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
699 89ff748d Thomas Thrainer
                     disk_barriers_supported.get(vmin, None))
700 89ff748d Thomas Thrainer
701 89ff748d Thomas Thrainer
    return args
702 89ff748d Thomas Thrainer
703 89ff748d Thomas Thrainer
  def _AssembleNet(self, minor, net_info, protocol,
704 89ff748d Thomas Thrainer
                   dual_pri=False, hmac=None, secret=None):
705 89ff748d Thomas Thrainer
    """Configure the network part of the device.
706 89ff748d Thomas Thrainer

707 89ff748d Thomas Thrainer
    """
708 89ff748d Thomas Thrainer
    lhost, lport, rhost, rport = net_info
709 89ff748d Thomas Thrainer
    if None in net_info:
710 89ff748d Thomas Thrainer
      # we don't want network connection and actually want to make
711 89ff748d Thomas Thrainer
      # sure its shutdown
712 89ff748d Thomas Thrainer
      self._ShutdownNet(minor)
713 89ff748d Thomas Thrainer
      return
714 89ff748d Thomas Thrainer
715 89ff748d Thomas Thrainer
    # Workaround for a race condition. When DRBD is doing its dance to
716 89ff748d Thomas Thrainer
    # establish a connection with its peer, it also sends the
717 89ff748d Thomas Thrainer
    # synchronization speed over the wire. In some cases setting the
718 89ff748d Thomas Thrainer
    # sync speed only after setting up both sides can race with DRBD
719 89ff748d Thomas Thrainer
    # connecting, hence we set it here before telling DRBD anything
720 89ff748d Thomas Thrainer
    # about its peer.
721 89ff748d Thomas Thrainer
    sync_errors = self._SetMinorSyncParams(minor, self.params)
722 89ff748d Thomas Thrainer
    if sync_errors:
723 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
724 89ff748d Thomas Thrainer
                      (minor, utils.CommaJoin(sync_errors)))
725 89ff748d Thomas Thrainer
726 89ff748d Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
727 89ff748d Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
728 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
729 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
730 89ff748d Thomas Thrainer
      family = "ipv6"
731 89ff748d Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
732 89ff748d Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
733 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
734 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
735 89ff748d Thomas Thrainer
      family = "ipv4"
736 89ff748d Thomas Thrainer
    else:
737 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
738 89ff748d Thomas Thrainer
739 89ff748d Thomas Thrainer
    args = ["drbdsetup", self._DevPath(minor), "net",
740 89ff748d Thomas Thrainer
            "%s:%s:%s" % (family, lhost, lport),
741 89ff748d Thomas Thrainer
            "%s:%s:%s" % (family, rhost, rport), protocol,
742 89ff748d Thomas Thrainer
            "-A", "discard-zero-changes",
743 89ff748d Thomas Thrainer
            "-B", "consensus",
744 89ff748d Thomas Thrainer
            "--create-device",
745 89ff748d Thomas Thrainer
            ]
746 89ff748d Thomas Thrainer
    if dual_pri:
747 89ff748d Thomas Thrainer
      args.append("-m")
748 89ff748d Thomas Thrainer
    if hmac and secret:
749 89ff748d Thomas Thrainer
      args.extend(["-a", hmac, "-x", secret])
750 89ff748d Thomas Thrainer
751 89ff748d Thomas Thrainer
    if self.params[constants.LDP_NET_CUSTOM]:
752 89ff748d Thomas Thrainer
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
753 89ff748d Thomas Thrainer
754 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
755 89ff748d Thomas Thrainer
    if result.failed:
756 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't setup network: %s - %s",
757 89ff748d Thomas Thrainer
                      minor, result.fail_reason, result.output)
758 89ff748d Thomas Thrainer
759 89ff748d Thomas Thrainer
    def _CheckNetworkConfig():
760 89ff748d Thomas Thrainer
      info = self._GetDevInfo(self._GetShowData(minor))
761 89ff748d Thomas Thrainer
      if not "local_addr" in info or not "remote_addr" in info:
762 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
763 89ff748d Thomas Thrainer
764 89ff748d Thomas Thrainer
      if (info["local_addr"] != (lhost, lport) or
765 89ff748d Thomas Thrainer
          info["remote_addr"] != (rhost, rport)):
766 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
767 89ff748d Thomas Thrainer
768 89ff748d Thomas Thrainer
    try:
769 89ff748d Thomas Thrainer
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
770 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
771 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: timeout while configuring network", minor)
772 89ff748d Thomas Thrainer
773 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
774 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
775 89ff748d Thomas Thrainer

776 89ff748d Thomas Thrainer
    """
777 89ff748d Thomas Thrainer
    if self.minor is None:
778 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
779 89ff748d Thomas Thrainer
                      self._aminor)
780 89ff748d Thomas Thrainer
    if len(devices) != 2:
781 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
782 89ff748d Thomas Thrainer
    info = self._GetDevInfo(self._GetShowData(self.minor))
783 89ff748d Thomas Thrainer
    if "local_dev" in info:
784 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
785 89ff748d Thomas Thrainer
    backend, meta = devices
786 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
787 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
788 89ff748d Thomas Thrainer
                      self.minor)
789 89ff748d Thomas Thrainer
    backend.Open()
790 89ff748d Thomas Thrainer
    meta.Open()
791 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
792 89ff748d Thomas Thrainer
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
793 89ff748d Thomas Thrainer
794 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
795 89ff748d Thomas Thrainer
    self._children = devices
796 89ff748d Thomas Thrainer
797 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
798 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
799 89ff748d Thomas Thrainer

800 89ff748d Thomas Thrainer
    """
801 89ff748d Thomas Thrainer
    if self.minor is None:
802 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
803 89ff748d Thomas Thrainer
                      self._aminor)
804 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
805 89ff748d Thomas Thrainer
    info = self._GetDevInfo(self._GetShowData(self.minor))
806 89ff748d Thomas Thrainer
    if "local_dev" not in info:
807 89ff748d Thomas Thrainer
      return
808 89ff748d Thomas Thrainer
    if len(self._children) != 2:
809 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
810 89ff748d Thomas Thrainer
                      self._children)
811 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
812 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
813 89ff748d Thomas Thrainer
      return
814 89ff748d Thomas Thrainer
    if len(devices) != 2:
815 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
816 89ff748d Thomas Thrainer
                      self.minor)
817 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
818 89ff748d Thomas Thrainer
      if dev != child.dev_path:
819 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
820 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
821 89ff748d Thomas Thrainer
822 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
823 89ff748d Thomas Thrainer
    self._children = []
824 89ff748d Thomas Thrainer
825 89ff748d Thomas Thrainer
  @classmethod
826 89ff748d Thomas Thrainer
  def _SetMinorSyncParams(cls, minor, params):
827 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
828 89ff748d Thomas Thrainer

829 89ff748d Thomas Thrainer
    This is the low-level implementation.
830 89ff748d Thomas Thrainer

831 89ff748d Thomas Thrainer
    @type minor: int
832 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
833 89ff748d Thomas Thrainer
    @type params: dict
834 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
835 89ff748d Thomas Thrainer
    @rtype: list
836 89ff748d Thomas Thrainer
    @return: a list of error messages
837 89ff748d Thomas Thrainer

838 89ff748d Thomas Thrainer
    """
839 89ff748d Thomas Thrainer
840 89ff748d Thomas Thrainer
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
841 89ff748d Thomas Thrainer
    if params[constants.LDP_DYNAMIC_RESYNC]:
842 89ff748d Thomas Thrainer
      version = cls._GetVersion(cls._GetProcData())
843 89ff748d Thomas Thrainer
      vmin = version["k_minor"]
844 89ff748d Thomas Thrainer
      vrel = version["k_point"]
845 89ff748d Thomas Thrainer
846 89ff748d Thomas Thrainer
      # By definition we are using 8.x, so just check the rest of the version
847 89ff748d Thomas Thrainer
      # number
848 89ff748d Thomas Thrainer
      if vmin != 3 or vrel < 9:
849 89ff748d Thomas Thrainer
        msg = ("The current DRBD version (8.%d.%d) does not support the "
850 89ff748d Thomas Thrainer
               "dynamic resync speed controller" % (vmin, vrel))
851 89ff748d Thomas Thrainer
        logging.error(msg)
852 89ff748d Thomas Thrainer
        return [msg]
853 89ff748d Thomas Thrainer
854 89ff748d Thomas Thrainer
      if params[constants.LDP_PLAN_AHEAD] == 0:
855 89ff748d Thomas Thrainer
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
856 89ff748d Thomas Thrainer
               " controller at DRBD level. If you want to disable it, please"
857 89ff748d Thomas Thrainer
               " set the dynamic-resync disk parameter to False.")
858 89ff748d Thomas Thrainer
        logging.error(msg)
859 89ff748d Thomas Thrainer
        return [msg]
860 89ff748d Thomas Thrainer
861 89ff748d Thomas Thrainer
      # add the c-* parameters to args
862 89ff748d Thomas Thrainer
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
863 89ff748d Thomas Thrainer
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
864 89ff748d Thomas Thrainer
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
865 89ff748d Thomas Thrainer
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
866 89ff748d Thomas Thrainer
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
867 89ff748d Thomas Thrainer
                   ])
868 89ff748d Thomas Thrainer
869 89ff748d Thomas Thrainer
    else:
870 89ff748d Thomas Thrainer
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
871 89ff748d Thomas Thrainer
872 89ff748d Thomas Thrainer
    args.append("--create-device")
873 89ff748d Thomas Thrainer
    result = utils.RunCmd(args)
874 89ff748d Thomas Thrainer
    if result.failed:
875 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
876 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
877 89ff748d Thomas Thrainer
      logging.error(msg)
878 89ff748d Thomas Thrainer
      return [msg]
879 89ff748d Thomas Thrainer
880 89ff748d Thomas Thrainer
    return []
881 89ff748d Thomas Thrainer
882 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
883 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
884 89ff748d Thomas Thrainer

885 89ff748d Thomas Thrainer
    @type params: dict
886 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
887 89ff748d Thomas Thrainer
    @rtype: list
888 89ff748d Thomas Thrainer
    @return: a list of error messages, emitted both by the current node and by
889 89ff748d Thomas Thrainer
    children. An empty list means no errors
890 89ff748d Thomas Thrainer

891 89ff748d Thomas Thrainer
    """
892 89ff748d Thomas Thrainer
    if self.minor is None:
893 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
894 89ff748d Thomas Thrainer
      logging.info(err)
895 89ff748d Thomas Thrainer
      return [err]
896 89ff748d Thomas Thrainer
897 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).SetSyncParams(params)
898 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
899 89ff748d Thomas Thrainer
    return children_result
900 89ff748d Thomas Thrainer
901 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
902 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
903 89ff748d Thomas Thrainer

904 89ff748d Thomas Thrainer
    @param pause: Wether to pause or resume
905 89ff748d Thomas Thrainer
    @return: the success of the operation
906 89ff748d Thomas Thrainer

907 89ff748d Thomas Thrainer
    """
908 89ff748d Thomas Thrainer
    if self.minor is None:
909 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
910 89ff748d Thomas Thrainer
      return False
911 89ff748d Thomas Thrainer
912 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
913 89ff748d Thomas Thrainer
914 89ff748d Thomas Thrainer
    if pause:
915 89ff748d Thomas Thrainer
      cmd = "pause-sync"
916 89ff748d Thomas Thrainer
    else:
917 89ff748d Thomas Thrainer
      cmd = "resume-sync"
918 89ff748d Thomas Thrainer
919 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
920 89ff748d Thomas Thrainer
    if result.failed:
921 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
922 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
923 89ff748d Thomas Thrainer
    return not result.failed and children_result
924 89ff748d Thomas Thrainer
925 89ff748d Thomas Thrainer
  def GetProcStatus(self):
926 89ff748d Thomas Thrainer
    """Return device data from /proc.
927 89ff748d Thomas Thrainer

928 89ff748d Thomas Thrainer
    """
929 89ff748d Thomas Thrainer
    if self.minor is None:
930 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
931 89ff748d Thomas Thrainer
                      self._aminor)
932 38396ae2 Thomas Thrainer
    proc_info = self._JoinProcDataPerMinor(self._GetProcData())
933 89ff748d Thomas Thrainer
    if self.minor not in proc_info:
934 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
935 89ff748d Thomas Thrainer
    return DRBD8Status(proc_info[self.minor])
936 89ff748d Thomas Thrainer
937 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
938 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
939 89ff748d Thomas Thrainer

940 89ff748d Thomas Thrainer

941 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
942 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
943 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
944 89ff748d Thomas Thrainer

945 89ff748d Thomas Thrainer

946 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
947 89ff748d Thomas Thrainer
    network not connected or local disk missing.
948 89ff748d Thomas Thrainer

949 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
950 89ff748d Thomas Thrainer
    disk or not.
951 89ff748d Thomas Thrainer

952 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
953 89ff748d Thomas Thrainer

954 89ff748d Thomas Thrainer
    """
955 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
956 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
957 89ff748d Thomas Thrainer
958 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
959 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
960 89ff748d Thomas Thrainer
961 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
962 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
963 89ff748d Thomas Thrainer
    elif stats.is_diskless:
964 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
965 89ff748d Thomas Thrainer
    else:
966 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
967 89ff748d Thomas Thrainer
968 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
969 89ff748d Thomas Thrainer
                                  major=self.major,
970 89ff748d Thomas Thrainer
                                  minor=self.minor,
971 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
972 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
973 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
974 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
975 89ff748d Thomas Thrainer
976 89ff748d Thomas Thrainer
  def Open(self, force=False):
977 89ff748d Thomas Thrainer
    """Make the local state primary.
978 89ff748d Thomas Thrainer

979 89ff748d Thomas Thrainer
    If the 'force' parameter is given, the '-o' option is passed to
980 89ff748d Thomas Thrainer
    drbdsetup. Since this is a potentially dangerous operation, the
981 89ff748d Thomas Thrainer
    force flag should be only given after creation, when it actually
982 89ff748d Thomas Thrainer
    is mandatory.
983 89ff748d Thomas Thrainer

984 89ff748d Thomas Thrainer
    """
985 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
986 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
987 89ff748d Thomas Thrainer
      return False
988 89ff748d Thomas Thrainer
    cmd = ["drbdsetup", self.dev_path, "primary"]
989 89ff748d Thomas Thrainer
    if force:
990 89ff748d Thomas Thrainer
      cmd.append("-o")
991 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
992 89ff748d Thomas Thrainer
    if result.failed:
993 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
994 89ff748d Thomas Thrainer
                      result.output)
995 89ff748d Thomas Thrainer
996 89ff748d Thomas Thrainer
  def Close(self):
997 89ff748d Thomas Thrainer
    """Make the local state secondary.
998 89ff748d Thomas Thrainer

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

1001 89ff748d Thomas Thrainer
    """
1002 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
1003 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1004 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1005 89ff748d Thomas Thrainer
    if result.failed:
1006 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1007 89ff748d Thomas Thrainer
                      self.minor, result.output)
1008 89ff748d Thomas Thrainer
1009 89ff748d Thomas Thrainer
  def DisconnectNet(self):
1010 89ff748d Thomas Thrainer
    """Removes network configuration.
1011 89ff748d Thomas Thrainer

1012 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
1013 89ff748d Thomas Thrainer

1014 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
1015 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
1016 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
1017 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
1018 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
1019 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
1020 89ff748d Thomas Thrainer
    exception.
1021 89ff748d Thomas Thrainer

1022 89ff748d Thomas Thrainer
    """
1023 89ff748d Thomas Thrainer
    if self.minor is None:
1024 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: disk not attached in re-attach net",
1025 89ff748d Thomas Thrainer
                      self._aminor)
1026 89ff748d Thomas Thrainer
1027 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1028 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: DRBD disk missing network info in"
1029 89ff748d Thomas Thrainer
                      " DisconnectNet()", self.minor)
1030 89ff748d Thomas Thrainer
1031 89ff748d Thomas Thrainer
    class _DisconnectStatus:
1032 89ff748d Thomas Thrainer
      def __init__(self, ever_disconnected):
1033 89ff748d Thomas Thrainer
        self.ever_disconnected = ever_disconnected
1034 89ff748d Thomas Thrainer
1035 89ff748d Thomas Thrainer
    dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
1036 89ff748d Thomas Thrainer
1037 89ff748d Thomas Thrainer
    def _WaitForDisconnect():
1038 89ff748d Thomas Thrainer
      if self.GetProcStatus().is_standalone:
1039 89ff748d Thomas Thrainer
        return
1040 89ff748d Thomas Thrainer
1041 89ff748d Thomas Thrainer
      # retry the disconnect, it seems possible that due to a well-time
1042 89ff748d Thomas Thrainer
      # disconnect on the peer, my disconnect command might be ignored and
1043 89ff748d Thomas Thrainer
      # forgotten
1044 89ff748d Thomas Thrainer
      dstatus.ever_disconnected = \
1045 89ff748d Thomas Thrainer
        base.IgnoreError(self._ShutdownNet, self.minor) or \
1046 89ff748d Thomas Thrainer
        dstatus.ever_disconnected
1047 89ff748d Thomas Thrainer
1048 89ff748d Thomas Thrainer
      raise utils.RetryAgain()
1049 89ff748d Thomas Thrainer
1050 89ff748d Thomas Thrainer
    # Keep start time
1051 89ff748d Thomas Thrainer
    start_time = time.time()
1052 89ff748d Thomas Thrainer
1053 89ff748d Thomas Thrainer
    try:
1054 89ff748d Thomas Thrainer
      # Start delay at 100 milliseconds and grow up to 2 seconds
1055 89ff748d Thomas Thrainer
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1056 89ff748d Thomas Thrainer
                  self._NET_RECONFIG_TIMEOUT)
1057 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
1058 89ff748d Thomas Thrainer
      if dstatus.ever_disconnected:
1059 89ff748d Thomas Thrainer
        msg = ("drbd%d: device did not react to the"
1060 89ff748d Thomas Thrainer
               " 'disconnect' command in a timely manner")
1061 89ff748d Thomas Thrainer
      else:
1062 89ff748d Thomas Thrainer
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1063 89ff748d Thomas Thrainer
1064 89ff748d Thomas Thrainer
      base.ThrowError(msg, self.minor)
1065 89ff748d Thomas Thrainer
1066 89ff748d Thomas Thrainer
    reconfig_time = time.time() - start_time
1067 89ff748d Thomas Thrainer
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1068 89ff748d Thomas Thrainer
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1069 89ff748d Thomas Thrainer
                   self.minor, reconfig_time)
1070 89ff748d Thomas Thrainer
1071 89ff748d Thomas Thrainer
  def AttachNet(self, multimaster):
1072 89ff748d Thomas Thrainer
    """Reconnects the network.
1073 89ff748d Thomas Thrainer

1074 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
1075 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
1076 89ff748d Thomas Thrainer
    but have valid network configuration data.
1077 89ff748d Thomas Thrainer

1078 89ff748d Thomas Thrainer
    Args:
1079 89ff748d Thomas Thrainer
      - multimaster: init the network in dual-primary mode
1080 89ff748d Thomas Thrainer

1081 89ff748d Thomas Thrainer
    """
1082 89ff748d Thomas Thrainer
    if self.minor is None:
1083 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1084 89ff748d Thomas Thrainer
1085 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1086 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1087 89ff748d Thomas Thrainer
1088 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
1089 89ff748d Thomas Thrainer
1090 89ff748d Thomas Thrainer
    if not status.is_standalone:
1091 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
1092 89ff748d Thomas Thrainer
                      self.minor)
1093 89ff748d Thomas Thrainer
1094 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
1095 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
1096 89ff748d Thomas Thrainer
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1097 89ff748d Thomas Thrainer
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1098 89ff748d Thomas Thrainer
1099 89ff748d Thomas Thrainer
  def Attach(self):
1100 89ff748d Thomas Thrainer
    """Check if our minor is configured.
1101 89ff748d Thomas Thrainer

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

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

1109 89ff748d Thomas Thrainer
    """
1110 89ff748d Thomas Thrainer
    used_devs = self.GetUsedDevs()
1111 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
1112 89ff748d Thomas Thrainer
      minor = self._aminor
1113 89ff748d Thomas Thrainer
    else:
1114 89ff748d Thomas Thrainer
      minor = None
1115 89ff748d Thomas Thrainer
1116 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1117 89ff748d Thomas Thrainer
    return minor is not None
1118 89ff748d Thomas Thrainer
1119 89ff748d Thomas Thrainer
  def Assemble(self):
1120 89ff748d Thomas Thrainer
    """Assemble the drbd.
1121 89ff748d Thomas Thrainer

1122 89ff748d Thomas Thrainer
    Method:
1123 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
1124 89ff748d Thomas Thrainer
        our config
1125 89ff748d Thomas Thrainer
      - if not, we create it from zero
1126 89ff748d Thomas Thrainer
      - anyway, set the device parameters
1127 89ff748d Thomas Thrainer

1128 89ff748d Thomas Thrainer
    """
1129 89ff748d Thomas Thrainer
    super(DRBD8, self).Assemble()
1130 89ff748d Thomas Thrainer
1131 89ff748d Thomas Thrainer
    self.Attach()
1132 89ff748d Thomas Thrainer
    if self.minor is None:
1133 89ff748d Thomas Thrainer
      # local device completely unconfigured
1134 89ff748d Thomas Thrainer
      self._FastAssemble()
1135 89ff748d Thomas Thrainer
    else:
1136 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
1137 89ff748d Thomas Thrainer
      # the device
1138 89ff748d Thomas Thrainer
      self._SlowAssemble()
1139 89ff748d Thomas Thrainer
1140 89ff748d Thomas Thrainer
    sync_errors = self.SetSyncParams(self.params)
1141 89ff748d Thomas Thrainer
    if sync_errors:
1142 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1143 89ff748d Thomas Thrainer
                      (self.minor, utils.CommaJoin(sync_errors)))
1144 89ff748d Thomas Thrainer
1145 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
1146 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
1147 89ff748d Thomas Thrainer

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

1152 89ff748d Thomas Thrainer
    """
1153 89ff748d Thomas Thrainer
    # TODO: Rewrite to not use a for loop just because there is 'break'
1154 89ff748d Thomas Thrainer
    # pylint: disable=W0631
1155 89ff748d Thomas Thrainer
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1156 89ff748d Thomas Thrainer
    for minor in (self._aminor,):
1157 89ff748d Thomas Thrainer
      info = self._GetDevInfo(self._GetShowData(minor))
1158 89ff748d Thomas Thrainer
      match_l = self._MatchesLocal(info)
1159 89ff748d Thomas Thrainer
      match_r = self._MatchesNet(info)
1160 89ff748d Thomas Thrainer
1161 89ff748d Thomas Thrainer
      if match_l and match_r:
1162 89ff748d Thomas Thrainer
        # everything matches
1163 89ff748d Thomas Thrainer
        break
1164 89ff748d Thomas Thrainer
1165 89ff748d Thomas Thrainer
      if match_l and not match_r and "local_addr" not in info:
1166 89ff748d Thomas Thrainer
        # disk matches, but not attached to network, attach and recheck
1167 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1168 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1169 89ff748d Thomas Thrainer
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1170 89ff748d Thomas Thrainer
          break
1171 89ff748d Thomas Thrainer
        else:
1172 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1173 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1174 89ff748d Thomas Thrainer
1175 89ff748d Thomas Thrainer
      if match_r and "local_dev" not in info:
1176 89ff748d Thomas Thrainer
        # no local disk, but network attached and it matches
1177 89ff748d Thomas Thrainer
        self._AssembleLocal(minor, self._children[0].dev_path,
1178 89ff748d Thomas Thrainer
                            self._children[1].dev_path, self.size)
1179 89ff748d Thomas Thrainer
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1180 89ff748d Thomas Thrainer
          break
1181 89ff748d Thomas Thrainer
        else:
1182 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1183 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1184 89ff748d Thomas Thrainer
1185 89ff748d Thomas Thrainer
      # this case must be considered only if we actually have local
1186 89ff748d Thomas Thrainer
      # storage, i.e. not in diskless mode, because all diskless
1187 89ff748d Thomas Thrainer
      # devices are equal from the point of view of local
1188 89ff748d Thomas Thrainer
      # configuration
1189 89ff748d Thomas Thrainer
      if (match_l and "local_dev" in info and
1190 89ff748d Thomas Thrainer
          not match_r and "local_addr" in info):
1191 89ff748d Thomas Thrainer
        # strange case - the device network part points to somewhere
1192 89ff748d Thomas Thrainer
        # else, even though its local storage is ours; as we own the
1193 89ff748d Thomas Thrainer
        # drbd space, we try to disconnect from the remote peer and
1194 89ff748d Thomas Thrainer
        # reconnect to our correct one
1195 89ff748d Thomas Thrainer
        try:
1196 89ff748d Thomas Thrainer
          self._ShutdownNet(minor)
1197 89ff748d Thomas Thrainer
        except errors.BlockDeviceError, err:
1198 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: device has correct local storage, wrong"
1199 89ff748d Thomas Thrainer
                          " remote peer and is unable to disconnect in order"
1200 89ff748d Thomas Thrainer
                          " to attach to the correct peer: %s", minor, str(err))
1201 89ff748d Thomas Thrainer
        # note: _AssembleNet also handles the case when we don't want
1202 89ff748d Thomas Thrainer
        # local storage (i.e. one or more of the _[lr](host|port) is
1203 89ff748d Thomas Thrainer
        # None)
1204 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1205 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1206 89ff748d Thomas Thrainer
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1207 89ff748d Thomas Thrainer
          break
1208 89ff748d Thomas Thrainer
        else:
1209 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1210 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
1211 89ff748d Thomas Thrainer
1212 89ff748d Thomas Thrainer
    else:
1213 89ff748d Thomas Thrainer
      minor = None
1214 89ff748d Thomas Thrainer
1215 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1216 89ff748d Thomas Thrainer
    if minor is None:
1217 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1218 89ff748d Thomas Thrainer
                      self._aminor)
1219 89ff748d Thomas Thrainer
1220 89ff748d Thomas Thrainer
  def _FastAssemble(self):
1221 89ff748d Thomas Thrainer
    """Assemble the drbd device from zero.
1222 89ff748d Thomas Thrainer

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

1225 89ff748d Thomas Thrainer
    """
1226 89ff748d Thomas Thrainer
    minor = self._aminor
1227 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
1228 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
1229 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
1230 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
1231 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
1232 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
1233 89ff748d Thomas Thrainer
                        constants.DRBD_NET_PROTOCOL,
1234 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1235 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
1236 89ff748d Thomas Thrainer
1237 89ff748d Thomas Thrainer
  @classmethod
1238 89ff748d Thomas Thrainer
  def _ShutdownLocal(cls, minor):
1239 89ff748d Thomas Thrainer
    """Detach from the local device.
1240 89ff748d Thomas Thrainer

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

1244 89ff748d Thomas Thrainer
    """
1245 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1246 89ff748d Thomas Thrainer
    if result.failed:
1247 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
1248 89ff748d Thomas Thrainer
                      minor, result.output)
1249 89ff748d Thomas Thrainer
1250 89ff748d Thomas Thrainer
  @classmethod
1251 89ff748d Thomas Thrainer
  def _ShutdownNet(cls, minor):
1252 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
1253 89ff748d Thomas Thrainer

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

1256 89ff748d Thomas Thrainer
    """
1257 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1258 89ff748d Thomas Thrainer
    if result.failed:
1259 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
1260 89ff748d Thomas Thrainer
                      minor, result.output)
1261 89ff748d Thomas Thrainer
1262 89ff748d Thomas Thrainer
  @classmethod
1263 89ff748d Thomas Thrainer
  def _ShutdownAll(cls, minor):
1264 89ff748d Thomas Thrainer
    """Deactivate the device.
1265 89ff748d Thomas Thrainer

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

1268 89ff748d Thomas Thrainer
    """
1269 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1270 89ff748d Thomas Thrainer
    if result.failed:
1271 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown drbd device: %s",
1272 89ff748d Thomas Thrainer
                      minor, result.output)
1273 89ff748d Thomas Thrainer
1274 89ff748d Thomas Thrainer
  def Shutdown(self):
1275 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
1276 89ff748d Thomas Thrainer

1277 89ff748d Thomas Thrainer
    """
1278 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
1279 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1280 89ff748d Thomas Thrainer
      return
1281 89ff748d Thomas Thrainer
    minor = self.minor
1282 89ff748d Thomas Thrainer
    self.minor = None
1283 89ff748d Thomas Thrainer
    self.dev_path = None
1284 89ff748d Thomas Thrainer
    self._ShutdownAll(minor)
1285 89ff748d Thomas Thrainer
1286 89ff748d Thomas Thrainer
  def Remove(self):
1287 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
1288 89ff748d Thomas Thrainer

1289 89ff748d Thomas Thrainer
    """
1290 89ff748d Thomas Thrainer
    self.Shutdown()
1291 89ff748d Thomas Thrainer
1292 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
1293 fd300bc7 Thomas Thrainer
    """Rename a device.
1294 fd300bc7 Thomas Thrainer

1295 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
1296 fd300bc7 Thomas Thrainer

1297 fd300bc7 Thomas Thrainer
    """
1298 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
1299 fd300bc7 Thomas Thrainer
1300 89ff748d Thomas Thrainer
  @classmethod
1301 89ff748d Thomas Thrainer
  def Create(cls, unique_id, children, size, params, excl_stor):
1302 89ff748d Thomas Thrainer
    """Create a new DRBD8 device.
1303 89ff748d Thomas Thrainer

1304 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
1305 89ff748d Thomas Thrainer
    function only initializes the metadata.
1306 89ff748d Thomas Thrainer

1307 89ff748d Thomas Thrainer
    """
1308 89ff748d Thomas Thrainer
    if len(children) != 2:
1309 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1310 89ff748d Thomas Thrainer
    if excl_stor:
1311 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("DRBD device requested with"
1312 89ff748d Thomas Thrainer
                                   " exclusive_storage")
1313 89ff748d Thomas Thrainer
    # check that the minor is unused
1314 89ff748d Thomas Thrainer
    aminor = unique_id[4]
1315 38396ae2 Thomas Thrainer
    proc_info = cls._JoinProcDataPerMinor(cls._GetProcData())
1316 89ff748d Thomas Thrainer
    if aminor in proc_info:
1317 89ff748d Thomas Thrainer
      status = DRBD8Status(proc_info[aminor])
1318 89ff748d Thomas Thrainer
      in_use = status.is_in_use
1319 89ff748d Thomas Thrainer
    else:
1320 89ff748d Thomas Thrainer
      in_use = False
1321 89ff748d Thomas Thrainer
    if in_use:
1322 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: minor is already in use at Create() time",
1323 89ff748d Thomas Thrainer
                      aminor)
1324 89ff748d Thomas Thrainer
    meta = children[1]
1325 89ff748d Thomas Thrainer
    meta.Assemble()
1326 89ff748d Thomas Thrainer
    if not meta.Attach():
1327 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to meta device '%s'",
1328 89ff748d Thomas Thrainer
                      aminor, meta)
1329 89ff748d Thomas Thrainer
    cls._CheckMetaSize(meta.dev_path)
1330 89ff748d Thomas Thrainer
    cls._InitMeta(aminor, meta.dev_path)
1331 89ff748d Thomas Thrainer
    return cls(unique_id, children, size, params)
1332 89ff748d Thomas Thrainer
1333 89ff748d Thomas Thrainer
  def Grow(self, amount, dryrun, backingstore):
1334 89ff748d Thomas Thrainer
    """Resize the DRBD device and its backing storage.
1335 89ff748d Thomas Thrainer

1336 89ff748d Thomas Thrainer
    """
1337 89ff748d Thomas Thrainer
    if self.minor is None:
1338 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
1339 89ff748d Thomas Thrainer
    if len(self._children) != 2 or None in self._children:
1340 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
1341 89ff748d Thomas Thrainer
    self._children[0].Grow(amount, dryrun, backingstore)
1342 89ff748d Thomas Thrainer
    if dryrun or backingstore:
1343 89ff748d Thomas Thrainer
      # DRBD does not support dry-run mode and is not backing storage,
1344 89ff748d Thomas Thrainer
      # so we'll return here
1345 89ff748d Thomas Thrainer
      return
1346 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1347 89ff748d Thomas Thrainer
                           "%dm" % (self.size + amount)])
1348 89ff748d Thomas Thrainer
    if result.failed:
1349 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1350 89ff748d Thomas Thrainer
1351 89ff748d Thomas Thrainer
1352 89ff748d Thomas Thrainer
def _CanReadDevice(path):
1353 89ff748d Thomas Thrainer
  """Check if we can read from the given device.
1354 89ff748d Thomas Thrainer

1355 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
1356 89ff748d Thomas Thrainer

1357 89ff748d Thomas Thrainer
  """
1358 89ff748d Thomas Thrainer
  try:
1359 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1360 89ff748d Thomas Thrainer
    return True
1361 89ff748d Thomas Thrainer
  except EnvironmentError:
1362 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
1363 89ff748d Thomas Thrainer
    return False