4 # Copyright (C) 2006, 2007 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Block device abstraction"""
27 import pyparsing as pyp
29 from ganeti import utils
30 from ganeti import logger
31 from ganeti import errors
32 from ganeti import constants
35 class BlockDev(object):
36 """Block device abstract class.
38 A block device can be in the following states:
39 - not existing on the system, and by `Create()` it goes into:
40 - existing but not setup/not active, and by `Assemble()` goes into:
41 - active read-write and by `Open()` it goes into
42 - online (=used, or ready for use)
44 A device can also be online but read-only, however we are not using
45 the readonly state (MD and LV have it, if needed in the future)
46 and we are usually looking at this like at a stack, so it's easier
47 to conceptualise the transition from not-existing to online and back
50 The many different states of the device are due to the fact that we
51 need to cover many device types:
52 - logical volumes are created, lvchange -a y $lv, and used
53 - md arrays are created or assembled and used
54 - drbd devices are attached to a local disk/remote peer and made primary
56 The status of the device can be examined by `GetStatus()`, which
57 returns a numerical value, depending on the position in the
58 transition stack of the device.
60 A block device is identified by three items:
61 - the /dev path of the device (dynamic)
62 - a unique ID of the device (static)
63 - it's major/minor pair (dynamic)
65 Not all devices implement both the first two as distinct items. LVM
66 logical volumes have their unique ID (the pair volume group, logical
67 volume name) in a 1-to-1 relation to the dev path. For MD devices,
68 the /dev path is dynamic and the unique ID is the UUID generated at
69 array creation plus the slave list. For DRBD devices, the /dev path
70 is again dynamic and the unique id is the pair (host1, dev1),
73 You can get to a device in two ways:
74 - creating the (real) device, which returns you
75 an attached instance (lvcreate, mdadm --create)
76 - attaching of a python instance to an existing (real) device
78 The second point, the attachement to a device, is different
79 depending on whether the device is assembled or not. At init() time,
80 we search for a device with the same unique_id as us. If found,
81 good. It also means that the device is already assembled. If not,
82 after assembly we'll have our correct major/minor.
91 STATUS_UNKNOWN: "unknown",
92 STATUS_EXISTING: "existing",
93 STATUS_STANDBY: "ready for use",
94 STATUS_ONLINE: "online",
98 def __init__(self, unique_id, children):
99 self._children = children
101 self.unique_id = unique_id
107 """Assemble the device from its components.
109 If this is a plain block device (e.g. LVM) than assemble does
110 nothing, as the LVM has no children and we don't put logical
113 One guarantee is that after the device has been assembled, it
114 knows its major/minor numbers. This allows other devices (usually
115 parents) to probe correctly for their children.
119 for child in self._children:
120 if not isinstance(child, BlockDev):
121 raise TypeError("Invalid child passed of type '%s'" % type(child))
124 status = status and child.Assemble()
127 status = status and child.Open()
130 for child in self._children:
136 """Find a device which matches our config and attach to it.
139 raise NotImplementedError
143 """Notifies that the device will no longer be used for I/O.
146 raise NotImplementedError
150 def Create(cls, unique_id, children, size):
151 """Create the device.
153 If the device cannot be created, it will return None
154 instead. Error messages go to the logging system.
156 Note that for some devices, the unique_id is used, and for other,
157 the children. The idea is that these two, taken together, are
158 enough for both creation and assembly (later).
161 raise NotImplementedError
165 """Remove this device.
167 This makes sense only for some of the device types: LV and to a
168 lesser degree, md devices. Also note that if the device can't
169 attach, the removal can't be completed.
172 raise NotImplementedError
176 """Return the status of the device.
179 raise NotImplementedError
182 def Open(self, force=False):
183 """Make the device ready for use.
185 This makes the device ready for I/O. For now, just the DRBD
188 The force parameter signifies that if the device has any kind of
189 --force thing, it should be used, we know what we are doing.
192 raise NotImplementedError
196 """Shut down the device, freeing its children.
198 This undoes the `Assemble()` work, except for the child
199 assembling; as such, the children on the device are still
200 assembled after this call.
203 raise NotImplementedError
206 def SetSyncSpeed(self, speed):
207 """Adjust the sync speed of the mirror.
209 In case this is not a mirroring device, this is no-op.
214 for child in self._children:
215 result = result and child.SetSyncSpeed(speed)
219 def GetSyncStatus(self):
220 """Returns the sync status of the device.
222 If this device is a mirroring device, this function returns the
223 status of the mirror.
226 (sync_percent, estimated_time, is_degraded)
228 If sync_percent is None, it means all is ok
229 If estimated_time is None, it means we can't estimate
230 the time needed, otherwise it's the time left in seconds
231 If is_degraded is True, it means the device is missing
232 redundancy. This is usually a sign that something went wrong in
233 the device setup, if sync_percent is None.
236 return None, None, False
239 def CombinedSyncStatus(self):
240 """Calculate the mirror status recursively for our children.
242 The return value is the same as for `GetSyncStatus()` except the
243 minimum percent and maximum time are calculated across our
247 min_percent, max_time, is_degraded = self.GetSyncStatus()
249 for child in self._children:
250 c_percent, c_time, c_degraded = child.GetSyncStatus()
251 if min_percent is None:
252 min_percent = c_percent
253 elif c_percent is not None:
254 min_percent = min(min_percent, c_percent)
257 elif c_time is not None:
258 max_time = max(max_time, c_time)
259 is_degraded = is_degraded or c_degraded
260 return min_percent, max_time, is_degraded
263 def SetInfo(self, text):
264 """Update metadata with info text.
266 Only supported for some device types.
269 for child in self._children:
274 return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
275 (self.__class__, self.unique_id, self._children,
276 self.major, self.minor, self.dev_path))
279 class LogicalVolume(BlockDev):
280 """Logical Volume block device.
283 def __init__(self, unique_id, children):
284 """Attaches to a LV device.
286 The unique_id is a tuple (vg_name, lv_name)
289 super(LogicalVolume, self).__init__(unique_id, children)
290 if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
291 raise ValueError("Invalid configuration data %s" % str(unique_id))
292 self._vg_name, self._lv_name = unique_id
293 self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
298 def Create(cls, unique_id, children, size):
299 """Create a new logical volume.
302 if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
303 raise ValueError("Invalid configuration data %s" % str(unique_id))
304 vg_name, lv_name = unique_id
305 pvs_info = cls.GetPVInfo(vg_name)
307 raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
312 pvlist = [ pv[1] for pv in pvs_info ]
313 free_size = sum([ pv[0] for pv in pvs_info ])
315 # The size constraint should have been checked from the master before
316 # calling the create function.
318 raise errors.BlockDeviceError("Not enough free space: required %s,"
319 " available %s" % (size, free_size))
320 result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
323 raise errors.BlockDeviceError(result.fail_reason)
324 return LogicalVolume(unique_id, children)
327 def GetPVInfo(vg_name):
328 """Get the free space info for PVs in a volume group.
331 vg_name: the volume group name
334 list of (free_space, name) with free_space in mebibytes
337 command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
338 "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
340 result = utils.RunCmd(command)
342 logger.Error("Can't get the PV information: %s" % result.fail_reason)
345 for line in result.stdout.splitlines():
346 fields = line.strip().split(':')
348 logger.Error("Can't parse pvs output: line '%s'" % line)
350 # skip over pvs from another vg or ones which are not allocatable
351 if fields[1] != vg_name or fields[3][0] != 'a':
353 data.append((float(fields[2]), fields[0]))
358 """Remove this logical volume.
361 if not self.minor and not self.Attach():
362 # the LV does not exist
364 result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
365 (self._vg_name, self._lv_name)])
367 logger.Error("Can't lvremove: %s" % result.fail_reason)
369 return not result.failed
373 """Attach to an existing LV.
375 This method will try to see if an existing and active LV exists
376 which matches the our name. If so, its major/minor will be
380 result = utils.RunCmd(["lvdisplay", self.dev_path])
382 logger.Error("Can't find LV %s: %s" %
383 (self.dev_path, result.fail_reason))
385 match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
386 for line in result.stdout.splitlines():
387 match_result = match.match(line)
389 self.major = int(match_result.group(1))
390 self.minor = int(match_result.group(2))
396 """Assemble the device.
398 This is a no-op for the LV device type. Eventually, we could
399 lvchange -ay here if we see that the LV is not active.
406 """Shutdown the device.
408 This is a no-op for the LV device type, as we don't deactivate the
416 """Return the status of the device.
418 Logical volumes will can be in all four states, although we don't
419 deactivate (lvchange -an) them when shutdown, so STATUS_EXISTING
420 should not be seen for our devices.
423 result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
425 logger.Error("Can't display lv: %s" % result.fail_reason)
426 return self.STATUS_UNKNOWN
427 out = result.stdout.strip()
428 # format: type/permissions/alloc/fixed_minor/state/open
430 return self.STATUS_UNKNOWN
431 #writable = (out[1] == "w")
432 active = (out[4] == "a")
433 online = (out[5] == "o")
435 retval = self.STATUS_ONLINE
437 retval = self.STATUS_STANDBY
439 retval = self.STATUS_EXISTING
444 def Open(self, force=False):
445 """Make the device ready for I/O.
447 This is a no-op for the LV device type.
454 """Notifies that the device will no longer be used for I/O.
456 This is a no-op for the LV device type.
462 def Snapshot(self, size):
463 """Create a snapshot copy of an lvm block device.
466 snap_name = self._lv_name + ".snap"
468 # remove existing snapshot if found
469 snap = LogicalVolume((self._vg_name, snap_name), None)
472 pvs_info = self.GetPVInfo(self._vg_name)
474 raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
478 free_size, pv_name = pvs_info[0]
480 raise errors.BlockDeviceError("Not enough free space: required %s,"
481 " available %s" % (size, free_size))
483 result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
484 "-n%s" % snap_name, self.dev_path])
486 raise errors.BlockDeviceError("command: %s error: %s" %
487 (result.cmd, result.fail_reason))
492 def SetInfo(self, text):
493 """Update metadata with info text.
496 BlockDev.SetInfo(self, text)
498 # Replace invalid characters
499 text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
500 text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
502 # Only up to 128 characters are allowed
505 result = utils.RunCmd(["lvchange", "--addtag", text,
508 raise errors.BlockDeviceError("Command: %s error: %s" %
509 (result.cmd, result.fail_reason))
512 class MDRaid1(BlockDev):
513 """raid1 device implemented via md.
516 def __init__(self, unique_id, children):
517 super(MDRaid1, self).__init__(unique_id, children)
523 """Find an array which matches our config and attach to it.
525 This tries to find a MD array which has the same UUID as our own.
528 minor = self._FindMDByUUID(self.unique_id)
529 if minor is not None:
530 self._SetFromMinor(minor)
535 return (minor is not None)
540 """Compute the list of in-use MD devices.
542 It doesn't matter if the used device have other raid level, just
543 that they are in use.
546 mdstat = open("/proc/mdstat", "r")
547 data = mdstat.readlines()
551 valid_line = re.compile("^md([0-9]+) : .*$")
553 match = valid_line.match(line)
555 md_no = int(match.group(1))
556 used_md[md_no] = line
562 def _GetDevInfo(minor):
563 """Get info about a MD device.
565 Currently only uuid is returned.
568 result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
570 logger.Error("Can't display md: %s" % result.fail_reason)
573 for line in result.stdout.splitlines():
575 kv = line.split(" : ", 1)
578 retval["uuid"] = kv[1].split()[0]
579 elif kv[0] == "State":
580 retval["state"] = kv[1].split(", ")
585 def _FindUnusedMinor():
586 """Compute an unused MD minor.
588 This code assumes that there are 256 minors only.
591 used_md = MDRaid1._GetUsedDevs()
598 logger.Error("Critical: Out of md minor numbers.")
599 raise errors.BlockDeviceError("Can't find a free MD minor")
604 def _FindMDByUUID(cls, uuid):
605 """Find the minor of an MD array with a given UUID.
608 md_list = cls._GetUsedDevs()
609 for minor in md_list:
610 info = cls._GetDevInfo(minor)
611 if info and info["uuid"] == uuid:
617 def _ZeroSuperblock(dev_path):
618 """Zero the possible locations for an MD superblock.
620 The zero-ing can't be done via ``mdadm --zero-superblock`` as that
621 fails in versions 2.x with the same error code as non-writable
624 The superblocks are located at (negative values are relative to
625 the end of the block device):
626 - -128k to end for version 0.90 superblock
627 - -8k to -12k for version 1.0 superblock (included in the above)
628 - 0k to 4k for version 1.1 superblock
629 - 4k to 8k for version 1.2 superblock
631 To cover all situations, the zero-ing will be:
635 As such, the minimum device size must be 128k, otherwise we'll get
638 Note that this function depends on the fact that one can open,
639 read and write block devices normally.
642 overwrite_size = 128 * 1024
643 empty_buf = '\0' * overwrite_size
644 fd = open(dev_path, "r+")
650 logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
651 fd.seek(-overwrite_size, 2)
655 logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
660 def Create(cls, unique_id, children, size):
661 """Create a new MD raid1 array.
664 if not isinstance(children, (tuple, list)):
665 raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
668 if not isinstance(i, BlockDev):
669 raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
672 cls._ZeroSuperblock(i.dev_path)
673 except EnvironmentError, err:
674 logger.Error("Can't zero superblock for %s: %s" %
675 (i.dev_path, str(err)))
677 minor = cls._FindUnusedMinor()
678 result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
679 "--auto=yes", "--force", "-l1",
680 "-n%d" % len(children)] +
681 [dev.dev_path for dev in children])
684 logger.Error("Can't create md: %s: %s" % (result.fail_reason,
687 info = cls._GetDevInfo(minor)
688 if not info or not "uuid" in info:
689 logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
691 return MDRaid1(info["uuid"], children)
695 """Stub remove function for MD RAID 1 arrays.
697 We don't remove the superblock right now. Mark a to do.
700 #TODO: maybe zero superblock on child devices?
701 return self.Shutdown()
704 def AddChild(self, device):
705 """Add a new member to the md raid1.
708 if self.minor is None and not self.Attach():
709 raise errors.BlockDeviceError("Can't attach to device")
710 if device.dev_path is None:
711 raise errors.BlockDeviceError("New child is not initialised")
712 result = utils.RunCmd(["mdadm", "-a", self.dev_path, device.dev_path])
714 raise errors.BlockDeviceError("Failed to add new device to array: %s" %
716 new_len = len(self._children) + 1
717 result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
719 raise errors.BlockDeviceError("Can't grow md array: %s" %
721 self._children.append(device)
724 def RemoveChild(self, dev_path):
725 """Remove member from the md raid1.
728 if self.minor is None and not self.Attach():
729 raise errors.BlockDeviceError("Can't attach to device")
730 if len(self._children) == 1:
731 raise errors.BlockDeviceError("Can't reduce member when only one"
733 for device in self._children:
734 if device.dev_path == dev_path:
737 raise errors.BlockDeviceError("Can't find child with this path")
738 new_len = len(self._children) - 1
739 result = utils.RunCmd(["mdadm", "-f", self.dev_path, dev_path])
741 raise errors.BlockDeviceError("Failed to mark device as failed: %s" %
744 # it seems here we need a short delay for MD to update its
747 result = utils.RunCmd(["mdadm", "-r", self.dev_path, dev_path])
749 raise errors.BlockDeviceError("Failed to remove device from array:"
750 " %s" % result.output)
751 result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
754 raise errors.BlockDeviceError("Can't shrink md array: %s" %
756 self._children.remove(device)
760 """Return the status of the device.
764 if self.minor is None:
765 retval = self.STATUS_UNKNOWN
767 retval = self.STATUS_ONLINE
771 def _SetFromMinor(self, minor):
772 """Set our parameters based on the given minor.
774 This sets our minor variable and our dev_path.
778 self.dev_path = "/dev/md%d" % minor
782 """Assemble the MD device.
784 At this point we should have:
785 - list of children devices
789 result = super(MDRaid1, self).Assemble()
792 md_list = self._GetUsedDevs()
793 for minor in md_list:
794 info = self._GetDevInfo(minor)
795 if info and info["uuid"] == self.unique_id:
796 self._SetFromMinor(minor)
797 logger.Info("MD array %s already started" % str(self))
799 free_minor = self._FindUnusedMinor()
800 result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
801 self.unique_id, "/dev/md%d" % free_minor] +
802 [bdev.dev_path for bdev in self._children])
804 logger.Error("Can't assemble MD array: %s: %s" %
805 (result.fail_reason, result.output))
808 self.minor = free_minor
809 return not result.failed
813 """Tear down the MD array.
815 This does a 'mdadm --stop' so after this command, the array is no
819 if self.minor is None and not self.Attach():
820 logger.Info("MD object not attached to a device")
823 result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
825 logger.Error("Can't stop MD array: %s" % result.fail_reason)
832 def SetSyncSpeed(self, kbytes):
833 """Set the maximum sync speed for the MD array.
836 result = super(MDRaid1, self).SetSyncSpeed(kbytes)
837 if self.minor is None:
838 logger.Error("MD array not attached to a device")
840 f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
842 f.write("%d" % kbytes)
845 f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
847 f.write("%d" % (kbytes/2))
853 def GetSyncStatus(self):
854 """Returns the sync status of the device.
857 (sync_percent, estimated_time)
859 If sync_percent is None, it means all is ok
860 If estimated_time is None, it means we can't esimate
861 the time needed, otherwise it's the time left in seconds
864 if self.minor is None and not self.Attach():
865 raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
866 dev_info = self._GetDevInfo(self.minor)
867 is_clean = ("state" in dev_info and
868 len(dev_info["state"]) == 1 and
869 dev_info["state"][0] in ("clean", "active"))
870 sys_path = "/sys/block/md%s/md/" % self.minor
871 f = file(sys_path + "sync_action")
872 sync_status = f.readline().strip()
874 if sync_status == "idle":
875 return None, None, not is_clean
876 f = file(sys_path + "sync_completed")
877 sync_completed = f.readline().strip().split(" / ")
879 if len(sync_completed) != 2:
880 return 0, None, not is_clean
881 sync_done, sync_total = [float(i) for i in sync_completed]
882 sync_percent = 100.0*sync_done/sync_total
883 f = file(sys_path + "sync_speed")
884 sync_speed_k = int(f.readline().strip())
885 if sync_speed_k == 0:
888 time_est = (sync_total - sync_done) / 2 / sync_speed_k
889 return sync_percent, time_est, not is_clean
892 def Open(self, force=False):
893 """Make the device ready for I/O.
895 This is a no-op for the MDRaid1 device type, although we could use
896 the 2.6.18's new array_state thing.
903 """Notifies that the device will no longer be used for I/O.
905 This is a no-op for the MDRaid1 device type, but see comment for
912 class BaseDRBD(BlockDev):
915 This class contains a few bits of common functionality between the
916 0.7 and 8.x versions of DRBD.
919 _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
920 r" \(api:(\d+)/proto:(\d+)\)")
922 _ST_UNCONFIGURED = "Unconfigured"
923 _ST_WFCONNECTION = "WFConnection"
924 _ST_CONNECTED = "Connected"
928 """Return data from /proc/drbd.
931 stat = open("/proc/drbd", "r")
933 data = stat.read().splitlines()
937 raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
941 def _MassageProcData(data):
942 """Transform the output of _GetProdData into a nicer form.
945 a dictionary of minor: joined lines from /proc/drbd for that minor
948 lmatch = re.compile("^ *([0-9]+):.*$")
950 old_minor = old_line = None
952 lresult = lmatch.match(line)
953 if lresult is not None:
954 if old_minor is not None:
955 results[old_minor] = old_line
956 old_minor = int(lresult.group(1))
959 if old_minor is not None:
960 old_line += " " + line.strip()
962 if old_minor is not None:
963 results[old_minor] = old_line
967 def _GetVersion(cls):
968 """Return the DRBD version.
970 This will return a list [k_major, k_minor, k_point, api, proto].
973 proc_data = cls._GetProcData()
974 first_line = proc_data[0].strip()
975 version = cls._VERSION_RE.match(first_line)
977 raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
979 return [int(val) for val in version.groups()]
983 """Return the path to a drbd device for a given minor.
986 return "/dev/drbd%d" % minor
989 def _GetUsedDevs(cls):
990 """Compute the list of used DRBD devices.
993 data = cls._GetProcData()
996 valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
998 match = valid_line.match(line)
1001 minor = int(match.group(1))
1002 state = match.group(2)
1003 if state == cls._ST_UNCONFIGURED:
1005 used_devs[minor] = state, line
1009 def _SetFromMinor(self, minor):
1010 """Set our parameters based on the given minor.
1012 This sets our minor variable and our dev_path.
1016 self.minor = self.dev_path = None
1019 self.dev_path = self._DevPath(minor)
1022 def _CheckMetaSize(meta_device):
1023 """Check if the given meta device looks like a valid one.
1025 This currently only check the size, which must be around
1029 result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1031 logger.Error("Failed to get device size: %s" % result.fail_reason)
1034 sectors = int(result.stdout)
1036 logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1038 bytes = sectors * 512
1039 if bytes < 128 * 1024 * 1024: # less than 128MiB
1040 logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
1042 if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
1043 logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
1048 class DRBDev(BaseDRBD):
1049 """DRBD block device.
1051 This implements the local host part of the DRBD device, i.e. it
1052 doesn't do anything to the supposed peer. If you need a fully
1053 connected DRBD pair, you need to use this class on both hosts.
1055 The unique_id for the drbd device is the (local_ip, local_port,
1056 remote_ip, remote_port) tuple, and it must have two children: the
1057 data device and the meta_device. The meta device is checked for
1058 valid size and is zeroed on create.
1061 def __init__(self, unique_id, children):
1062 super(DRBDev, self).__init__(unique_id, children)
1063 self.major = self._DRBD_MAJOR
1064 [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1065 if kmaj != 0 and kmin != 7:
1066 raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1067 " requested ganeti usage: kernel is"
1068 " %s.%s, ganeti wants 0.7" % (kmaj, kmin))
1070 if len(children) != 2:
1071 raise ValueError("Invalid configuration data %s" % str(children))
1072 if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1073 raise ValueError("Invalid configuration data %s" % str(unique_id))
1074 self._lhost, self._lport, self._rhost, self._rport = unique_id
1078 def _FindUnusedMinor(cls):
1079 """Find an unused DRBD device.
1082 data = cls._GetProcData()
1084 valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1086 match = valid_line.match(line)
1088 return int(match.group(1))
1089 logger.Error("Error: no free drbd minors!")
1090 raise errors.BlockDeviceError("Can't find a free DRBD minor")
1093 def _GetDevInfo(cls, minor):
1094 """Get details about a given DRBD minor.
1096 This return, if available, the local backing device in (major,
1097 minor) formant and the local and remote (ip, port) information.
1101 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1103 logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1106 if out == "Not configured\n":
1108 for line in out.splitlines():
1109 if "local_dev" not in data:
1110 match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1112 data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1114 if "meta_dev" not in data:
1115 match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1117 if match.group(2) is not None and match.group(3) is not None:
1118 # matched on the major/minor
1119 data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1121 # matched on the "internal" string
1122 data["meta_dev"] = match.group(1)
1123 # in this case, no meta_index is in the output
1124 data["meta_index"] = -1
1126 if "meta_index" not in data:
1127 match = re.match("^Meta index: ([0-9]+).*$", line)
1129 data["meta_index"] = int(match.group(1))
1131 if "local_addr" not in data:
1132 match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1134 data["local_addr"] = (match.group(1), int(match.group(2)))
1136 if "remote_addr" not in data:
1137 match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1139 data["remote_addr"] = (match.group(1), int(match.group(2)))
1144 def _MatchesLocal(self, info):
1145 """Test if our local config matches with an existing device.
1147 The parameter should be as returned from `_GetDevInfo()`. This
1148 method tests if our local backing device is the same as the one in
1149 the info parameter, in effect testing if we look like the given
1153 if not ("local_dev" in info and "meta_dev" in info and
1154 "meta_index" in info):
1157 backend = self._children[0]
1158 if backend is not None:
1159 retval = (info["local_dev"] == (backend.major, backend.minor))
1161 retval = (info["local_dev"] == (0, 0))
1162 meta = self._children[1]
1163 if meta is not None:
1164 retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1165 retval = retval and (info["meta_index"] == 0)
1167 retval = retval and (info["meta_dev"] == "internal" and
1168 info["meta_index"] == -1)
1172 def _MatchesNet(self, info):
1173 """Test if our network config matches with an existing device.
1175 The parameter should be as returned from `_GetDevInfo()`. This
1176 method tests if our network configuration is the same as the one
1177 in the info parameter, in effect testing if we look like the given
1181 if (((self._lhost is None and not ("local_addr" in info)) and
1182 (self._rhost is None and not ("remote_addr" in info)))):
1185 if self._lhost is None:
1188 if not ("local_addr" in info and
1189 "remote_addr" in info):
1192 retval = (info["local_addr"] == (self._lhost, self._lport))
1193 retval = (retval and
1194 info["remote_addr"] == (self._rhost, self._rport))
1199 def _AssembleLocal(cls, minor, backend, meta):
1200 """Configure the local part of a DRBD device.
1202 This is the first thing that must be done on an unconfigured DRBD
1203 device. And it must be done only once.
1206 if not cls._CheckMetaSize(meta):
1208 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1209 backend, meta, "0", "-e", "detach"])
1211 logger.Error("Can't attach local disk: %s" % result.output)
1212 return not result.failed
1216 def _ShutdownLocal(cls, minor):
1217 """Detach from the local device.
1219 I/Os will continue to be served from the remote device. If we
1220 don't have a remote device, this operation will fail.
1223 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1225 logger.Error("Can't detach local device: %s" % result.output)
1226 return not result.failed
1230 def _ShutdownAll(minor):
1231 """Deactivate the device.
1233 This will, of course, fail if the device is in use.
1236 result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1238 logger.Error("Can't shutdown drbd device: %s" % result.output)
1239 return not result.failed
1243 def _AssembleNet(cls, minor, net_info, protocol):
1244 """Configure the network part of the device.
1246 This operation can be, in theory, done multiple times, but there
1247 have been cases (in lab testing) in which the network part of the
1248 device had become stuck and couldn't be shut down because activity
1249 from the new peer (also stuck) triggered a timer re-init and
1250 needed remote peer interface shutdown in order to clear. So please
1251 don't change online the net config.
1254 lhost, lport, rhost, rport = net_info
1255 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1256 "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1259 logger.Error("Can't setup network for dbrd device: %s" %
1263 timeout = time.time() + 10
1265 while time.time() < timeout:
1266 info = cls._GetDevInfo(minor)
1267 if not "local_addr" in info or not "remote_addr" in info:
1270 if (info["local_addr"] != (lhost, lport) or
1271 info["remote_addr"] != (rhost, rport)):
1277 logger.Error("Timeout while configuring network")
1283 def _ShutdownNet(cls, minor):
1284 """Disconnect from the remote peer.
1286 This fails if we don't have a local device.
1289 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1290 logger.Error("Can't shutdown network: %s" % result.output)
1291 return not result.failed
1295 """Assemble the drbd.
1298 - if we have a local backing device, we bind to it by:
1299 - checking the list of used drbd devices
1300 - check if the local minor use of any of them is our own device
1303 - if we have a local/remote net info:
1304 - redo the local backing device step for the remote device
1305 - check if any drbd device is using the local port,
1307 - check if any remote drbd device is using the remote
1308 port, if yes abort (for now)
1310 - bind the remote net port
1314 if self.minor is not None:
1315 logger.Info("Already assembled")
1318 result = super(DRBDev, self).Assemble()
1322 minor = self._FindUnusedMinor()
1323 need_localdev_teardown = False
1324 if self._children[0]:
1325 result = self._AssembleLocal(minor, self._children[0].dev_path,
1326 self._children[1].dev_path)
1329 need_localdev_teardown = True
1330 if self._lhost and self._lport and self._rhost and self._rport:
1331 result = self._AssembleNet(minor,
1332 (self._lhost, self._lport,
1333 self._rhost, self._rport),
1336 if need_localdev_teardown:
1337 # we will ignore failures from this
1338 logger.Error("net setup failed, tearing down local device")
1339 self._ShutdownAll(minor)
1341 self._SetFromMinor(minor)
1346 """Shutdown the DRBD device.
1349 if self.minor is None and not self.Attach():
1350 logger.Info("DRBD device not attached to a device during Shutdown")
1352 if not self._ShutdownAll(self.minor):
1355 self.dev_path = None
1360 """Find a DRBD device which matches our config and attach to it.
1362 In case of partially attached (local device matches but no network
1363 setup), we perform the network attach. If successful, we re-test
1364 the attach if can return success.
1367 for minor in self._GetUsedDevs():
1368 info = self._GetDevInfo(minor)
1369 match_l = self._MatchesLocal(info)
1370 match_r = self._MatchesNet(info)
1371 if match_l and match_r:
1373 if match_l and not match_r and "local_addr" not in info:
1374 res_r = self._AssembleNet(minor,
1375 (self._lhost, self._lport,
1376 self._rhost, self._rport),
1378 if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1383 self._SetFromMinor(minor)
1384 return minor is not None
1387 def Open(self, force=False):
1388 """Make the local state primary.
1390 If the 'force' parameter is given, the '--do-what-I-say' parameter
1391 is given. Since this is a pottentialy dangerous operation, the
1392 force flag should be only given after creation, when it actually
1396 if self.minor is None and not self.Attach():
1397 logger.Error("DRBD cannot attach to a device during open")
1399 cmd = ["drbdsetup", self.dev_path, "primary"]
1401 cmd.append("--do-what-I-say")
1402 result = utils.RunCmd(cmd)
1404 logger.Error("Can't make drbd device primary: %s" % result.output)
1410 """Make the local state secondary.
1412 This will, of course, fail if the device is in use.
1415 if self.minor is None and not self.Attach():
1416 logger.Info("Instance not attached to a device")
1417 raise errors.BlockDeviceError("Can't find device")
1418 result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1420 logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1421 raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1424 def SetSyncSpeed(self, kbytes):
1425 """Set the speed of the DRBD syncer.
1428 children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1429 if self.minor is None:
1430 logger.Info("Instance not attached to a device")
1432 result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1435 logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1436 return not result.failed and children_result
1439 def GetSyncStatus(self):
1440 """Returns the sync status of the device.
1443 (sync_percent, estimated_time)
1445 If sync_percent is None, it means all is ok
1446 If estimated_time is None, it means we can't esimate
1447 the time needed, otherwise it's the time left in seconds
1450 if self.minor is None and not self.Attach():
1451 raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1452 proc_info = self._MassageProcData(self._GetProcData())
1453 if self.minor not in proc_info:
1454 raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1456 line = proc_info[self.minor]
1457 match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1458 " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1460 sync_percent = float(match.group(1))
1461 hours = int(match.group(2))
1462 minutes = int(match.group(3))
1463 seconds = int(match.group(4))
1464 est_time = hours * 3600 + minutes * 60 + seconds
1468 match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1470 raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1472 client_state = match.group(1)
1473 is_degraded = client_state != "Connected"
1474 return sync_percent, est_time, is_degraded
1479 def GetStatus(self):
1480 """Compute the status of the DRBD device
1482 Note that DRBD devices don't have the STATUS_EXISTING state.
1485 if self.minor is None and not self.Attach():
1486 return self.STATUS_UNKNOWN
1488 data = self._GetProcData()
1489 match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1492 mresult = match.match(line)
1496 logger.Error("Can't find myself!")
1497 return self.STATUS_UNKNOWN
1499 state = mresult.group(2)
1500 if state == "Primary":
1501 result = self.STATUS_ONLINE
1503 result = self.STATUS_STANDBY
1509 def _ZeroDevice(device):
1512 This writes until we get ENOSPC.
1515 f = open(device, "w")
1516 buf = "\0" * 1048576
1520 except IOError, err:
1521 if err.errno != errno.ENOSPC:
1526 def Create(cls, unique_id, children, size):
1527 """Create a new DRBD device.
1529 Since DRBD devices are not created per se, just assembled, this
1530 function just zeroes the meta device.
1533 if len(children) != 2:
1534 raise errors.ProgrammerError("Invalid setup for the drbd device")
1537 if not meta.Attach():
1538 raise errors.BlockDeviceError("Can't attach to meta device")
1539 if not cls._CheckMetaSize(meta.dev_path):
1540 raise errors.BlockDeviceError("Invalid meta device")
1541 logger.Info("Started zeroing device %s" % meta.dev_path)
1542 cls._ZeroDevice(meta.dev_path)
1543 logger.Info("Done zeroing device %s" % meta.dev_path)
1544 return cls(unique_id, children)
1548 """Stub remove for DRBD devices.
1551 return self.Shutdown()
1553 class DRBD8(BaseDRBD):
1554 """DRBD v8.x block device.
1556 This implements the local host part of the DRBD device, i.e. it
1557 doesn't do anything to the supposed peer. If you need a fully
1558 connected DRBD pair, you need to use this class on both hosts.
1560 The unique_id for the drbd device is the (local_ip, local_port,
1561 remote_ip, remote_port) tuple, and it must have two children: the
1562 data device and the meta_device. The meta device is checked for
1563 valid size and is zeroed on create.
1570 def __init__(self, unique_id, children):
1571 super(DRBD8, self).__init__(unique_id, children)
1572 self.major = self._DRBD_MAJOR
1573 [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1575 raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1576 " requested ganeti usage: kernel is"
1577 " %s.%s, ganeti wants 8.x" % (kmaj, kmin))
1579 if len(children) != 2:
1580 raise ValueError("Invalid configuration data %s" % str(children))
1581 if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1582 raise ValueError("Invalid configuration data %s" % str(unique_id))
1583 self._lhost, self._lport, self._rhost, self._rport = unique_id
1587 def _InitMeta(cls, minor, dev_path):
1588 """Initialize a meta device.
1590 This will not work if the given minor is in use.
1593 result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1594 "v08", dev_path, "0", "create-md"])
1596 raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1600 def _FindUnusedMinor(cls):
1601 """Find an unused DRBD device.
1603 This is specific to 8.x as the minors are allocated dynamically,
1604 so non-existing numbers up to a max minor count are actually free.
1607 data = cls._GetProcData()
1609 unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1610 used_line = re.compile("^ *([0-9]+): cs:")
1613 match = unused_line.match(line)
1615 return int(match.group(1))
1616 match = used_line.match(line)
1618 minor = int(match.group(1))
1619 highest = max(highest, minor)
1620 if highest is None: # there are no minors in use at all
1622 if highest >= cls._MAX_MINORS:
1623 logger.Error("Error: no free drbd minors!")
1624 raise errors.BlockDeviceError("Can't find a free DRBD minor")
1628 def _IsValidMeta(cls, meta_device):
1629 """Check if the given meta device looks like a valid one.
1632 minor = cls._FindUnusedMinor()
1633 minor_path = cls._DevPath(minor)
1634 result = utils.RunCmd(["drbdmeta", minor_path,
1635 "v08", meta_device, "0",
1638 logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
1643 def _GetShowParser(cls):
1644 """Return a parser for `drbd show` output.
1646 This will either create or return an already-create parser for the
1647 output of the command `drbd show`.
1650 if cls._PARSE_SHOW is not None:
1651 return cls._PARSE_SHOW
1654 lbrace = pyp.Literal("{").suppress()
1655 rbrace = pyp.Literal("}").suppress()
1656 semi = pyp.Literal(";").suppress()
1657 # this also converts the value to an int
1658 number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t:(l, [int(t[0])]))
1660 comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1661 defa = pyp.Literal("_is_default").suppress()
1662 dbl_quote = pyp.Literal('"').suppress()
1664 keyword = pyp.Word(pyp.alphanums + '-')
1667 value = pyp.Word(pyp.alphanums + '_-/.:')
1668 quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1669 addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
1671 # meta device, extended syntax
1672 meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1673 number + pyp.Word(']').suppress())
1676 stmt = (~rbrace + keyword + ~lbrace +
1677 (addr_port ^ value ^ quoted ^ meta_value) +
1678 pyp.Optional(defa) + semi +
1679 pyp.Optional(pyp.restOfLine).suppress())
1682 section_name = pyp.Word(pyp.alphas + '_')
1683 section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1685 bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1688 cls._PARSE_SHOW = bnf
1693 def _GetDevInfo(cls, minor):
1694 """Get details about a given DRBD minor.
1696 This return, if available, the local backing device (as a path)
1697 and the local and remote (ip, port) information.
1701 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1703 logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1709 bnf = cls._GetShowParser()
1713 results = bnf.parseString(out)
1714 except pyp.ParseException, err:
1715 raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1718 # and massage the results into our desired format
1719 for section in results:
1721 if sname == "_this_host":
1722 for lst in section[1:]:
1723 if lst[0] == "disk":
1724 data["local_dev"] = lst[1]
1725 elif lst[0] == "meta-disk":
1726 data["meta_dev"] = lst[1]
1727 data["meta_index"] = lst[2]
1728 elif lst[0] == "address":
1729 data["local_addr"] = tuple(lst[1:])
1730 elif sname == "_remote_host":
1731 for lst in section[1:]:
1732 if lst[0] == "address":
1733 data["remote_addr"] = tuple(lst[1:])
1736 def _MatchesLocal(self, info):
1737 """Test if our local config matches with an existing device.
1739 The parameter should be as returned from `_GetDevInfo()`. This
1740 method tests if our local backing device is the same as the one in
1741 the info parameter, in effect testing if we look like the given
1745 backend = self._children[0]
1746 if backend is not None:
1747 retval = (info["local_dev"] == backend.dev_path)
1749 retval = ("local_dev" not in info)
1750 meta = self._children[1]
1751 if meta is not None:
1752 retval = retval and (info["meta_dev"] == meta.dev_path)
1753 retval = retval and (info["meta_index"] == 0)
1755 retval = retval and ("meta_dev" not in info and
1756 "meta_index" not in info)
1759 def _MatchesNet(self, info):
1760 """Test if our network config matches with an existing device.
1762 The parameter should be as returned from `_GetDevInfo()`. This
1763 method tests if our network configuration is the same as the one
1764 in the info parameter, in effect testing if we look like the given
1768 if (((self._lhost is None and not ("local_addr" in info)) and
1769 (self._rhost is None and not ("remote_addr" in info)))):
1772 if self._lhost is None:
1775 if not ("local_addr" in info and
1776 "remote_addr" in info):
1779 retval = (info["local_addr"] == (self._lhost, self._lport))
1780 retval = (retval and
1781 info["remote_addr"] == (self._rhost, self._rport))
1785 def _AssembleLocal(cls, minor, backend, meta):
1786 """Configure the local part of a DRBD device.
1788 This is the first thing that must be done on an unconfigured DRBD
1789 device. And it must be done only once.
1792 if not cls._IsValidMeta(meta):
1794 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1795 backend, meta, "0", "-e", "detach",
1798 logger.Error("Can't attach local disk: %s" % result.output)
1799 return not result.failed
1802 def _AssembleNet(cls, minor, net_info, protocol,
1803 dual_pri=False, hmac=None, secret=None):
1804 """Configure the network part of the device.
1807 lhost, lport, rhost, rport = net_info
1808 args = ["drbdsetup", cls._DevPath(minor), "net",
1809 "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1810 "-A", "discard-zero-changes",
1816 args.extend(["-a", hmac, "-x", secret])
1817 result = utils.RunCmd(args)
1819 logger.Error("Can't setup network for dbrd device: %s" %
1823 timeout = time.time() + 10
1825 while time.time() < timeout:
1826 info = cls._GetDevInfo(minor)
1827 if not "local_addr" in info or not "remote_addr" in info:
1830 if (info["local_addr"] != (lhost, lport) or
1831 info["remote_addr"] != (rhost, rport)):
1837 logger.Error("Timeout while configuring network")
1841 def SetSyncSpeed(self, kbytes):
1842 """Set the speed of the DRBD syncer.
1845 children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1846 if self.minor is None:
1847 logger.Info("Instance not attached to a device")
1849 result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1852 logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1853 return not result.failed and children_result
1855 def GetSyncStatus(self):
1856 """Returns the sync status of the device.
1859 (sync_percent, estimated_time)
1861 If sync_percent is None, it means all is ok
1862 If estimated_time is None, it means we can't esimate
1863 the time needed, otherwise it's the time left in seconds
1866 if self.minor is None and not self.Attach():
1867 raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1868 proc_info = self._MassageProcData(self._GetProcData())
1869 if self.minor not in proc_info:
1870 raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1872 line = proc_info[self.minor]
1873 match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1874 " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1876 sync_percent = float(match.group(1))
1877 hours = int(match.group(2))
1878 minutes = int(match.group(3))
1879 seconds = int(match.group(4))
1880 est_time = hours * 3600 + minutes * 60 + seconds
1884 match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1886 raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1888 client_state = match.group(1)
1889 is_degraded = client_state != "Connected"
1890 return sync_percent, est_time, is_degraded
1892 def GetStatus(self):
1893 """Compute the status of the DRBD device
1895 Note that DRBD devices don't have the STATUS_EXISTING state.
1898 if self.minor is None and not self.Attach():
1899 return self.STATUS_UNKNOWN
1901 data = self._GetProcData()
1902 match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1905 mresult = match.match(line)
1909 logger.Error("Can't find myself!")
1910 return self.STATUS_UNKNOWN
1912 state = mresult.group(2)
1913 if state == "Primary":
1914 result = self.STATUS_ONLINE
1916 result = self.STATUS_STANDBY
1920 def Open(self, force=False):
1921 """Make the local state primary.
1923 If the 'force' parameter is given, the '--do-what-I-say' parameter
1924 is given. Since this is a pottentialy dangerous operation, the
1925 force flag should be only given after creation, when it actually
1929 if self.minor is None and not self.Attach():
1930 logger.Error("DRBD cannot attach to a device during open")
1932 cmd = ["drbdsetup", self.dev_path, "primary"]
1935 result = utils.RunCmd(cmd)
1937 logger.Error("Can't make drbd device primary: %s" % result.output)
1942 """Make the local state secondary.
1944 This will, of course, fail if the device is in use.
1947 if self.minor is None and not self.Attach():
1948 logger.Info("Instance not attached to a device")
1949 raise errors.BlockDeviceError("Can't find device")
1950 result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1952 logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1953 raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1956 """Find a DRBD device which matches our config and attach to it.
1958 In case of partially attached (local device matches but no network
1959 setup), we perform the network attach. If successful, we re-test
1960 the attach if can return success.
1963 for minor in self._GetUsedDevs():
1964 info = self._GetDevInfo(minor)
1965 match_l = self._MatchesLocal(info)
1966 match_r = self._MatchesNet(info)
1967 if match_l and match_r:
1969 if match_l and not match_r and "local_addr" not in info:
1970 res_r = self._AssembleNet(minor,
1971 (self._lhost, self._lport,
1972 self._rhost, self._rport),
1974 if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1979 self._SetFromMinor(minor)
1980 return minor is not None
1983 """Assemble the drbd.
1986 - if we have a local backing device, we bind to it by:
1987 - checking the list of used drbd devices
1988 - check if the local minor use of any of them is our own device
1991 - if we have a local/remote net info:
1992 - redo the local backing device step for the remote device
1993 - check if any drbd device is using the local port,
1995 - check if any remote drbd device is using the remote
1996 port, if yes abort (for now)
1998 - bind the remote net port
2002 if self.minor is not None:
2003 logger.Info("Already assembled")
2006 result = super(DRBD8, self).Assemble()
2010 minor = self._FindUnusedMinor()
2011 need_localdev_teardown = False
2012 if self._children[0]:
2013 result = self._AssembleLocal(minor, self._children[0].dev_path,
2014 self._children[1].dev_path)
2017 need_localdev_teardown = True
2018 if self._lhost and self._lport and self._rhost and self._rport:
2019 result = self._AssembleNet(minor,
2020 (self._lhost, self._lport,
2021 self._rhost, self._rport),
2024 if need_localdev_teardown:
2025 # we will ignore failures from this
2026 logger.Error("net setup failed, tearing down local device")
2027 self._ShutdownAll(minor)
2029 self._SetFromMinor(minor)
2033 def _ShutdownAll(cls, minor):
2034 """Deactivate the device.
2036 This will, of course, fail if the device is in use.
2039 result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2041 logger.Error("Can't shutdown drbd device: %s" % result.output)
2042 return not result.failed
2045 """Shutdown the DRBD device.
2048 if self.minor is None and not self.Attach():
2049 logger.Info("DRBD device not attached to a device during Shutdown")
2051 if not self._ShutdownAll(self.minor):
2054 self.dev_path = None
2058 """Stub remove for DRBD devices.
2061 return self.Shutdown()
2064 def Create(cls, unique_id, children, size):
2065 """Create a new DRBD8 device.
2067 Since DRBD devices are not created per se, just assembled, this
2068 function only initializes the metadata.
2071 if len(children) != 2:
2072 raise errors.ProgrammerError("Invalid setup for the drbd device")
2075 if not meta.Attach():
2076 raise errors.BlockDeviceError("Can't attach to meta device")
2077 if not cls._CheckMetaSize(meta.dev_path):
2078 raise errors.BlockDeviceError("Invalid meta device size")
2079 cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2080 if not cls._IsValidMeta(meta.dev_path):
2081 raise errors.BlockDeviceError("Cannot initalize meta device")
2082 return cls(unique_id, children)
2086 constants.LD_LV: LogicalVolume,
2087 constants.LD_MD_R1: MDRaid1,
2088 constants.LD_DRBD7: DRBDev,
2089 constants.LD_DRBD8: DRBD8,
2093 def FindDevice(dev_type, unique_id, children):
2094 """Search for an existing, assembled device.
2096 This will succeed only if the device exists and is assembled, but it
2097 does not do any actions in order to activate the device.
2100 if dev_type not in DEV_MAP:
2101 raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2102 device = DEV_MAP[dev_type](unique_id, children)
2103 if not device.Attach():
2108 def AttachOrAssemble(dev_type, unique_id, children):
2109 """Try to attach or assemble an existing device.
2111 This will attach to an existing assembled device or will assemble
2112 the device, as needed, to bring it fully up.
2115 if dev_type not in DEV_MAP:
2116 raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2117 device = DEV_MAP[dev_type](unique_id, children)
2118 if not device.Attach():
2120 if not device.Attach():
2121 raise errors.BlockDeviceError("Can't find a valid block device for"
2123 (dev_type, unique_id, children))
2127 def Create(dev_type, unique_id, children, size):
2131 if dev_type not in DEV_MAP:
2132 raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2133 device = DEV_MAP[dev_type].Create(unique_id, children, size)