diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded index 665b84c3c3d2892b6b36b212ea682fae38d5ead3..954010645386c84fb0a724e6b29d03e82ddb2620 100755 --- a/daemons/ganeti-noded +++ b/daemons/ganeti-noded @@ -112,6 +112,14 @@ class ServerObject(pb.Avatar): bdev = objects.Disk.FromDict(bdev_s) return backend.RemoveBlockDevice(bdev) + @staticmethod + def perspective_blockdev_rename(params): + """Remove a block device. + + """ + devlist = [(objects.Disk.FromDict(ds), uid) for ds, uid in params] + return backend.RenameBlockDevices(devlist) + @staticmethod def perspective_blockdev_assemble(params): """Assemble a block device. diff --git a/lib/backend.py b/lib/backend.py index 56dab650d82fb2fb0d159f4bf331f6be872c39ee..71d3330cfbeb1a3c9ac57280a1fda4f770a5c947 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1337,6 +1337,29 @@ def RemoveExport(export): return True +def RenameBlockDevices(devlist): + """Rename a list of block devices. + + The devlist argument is a list of tuples (disk, new_logical, + new_physical). The return value will be a combined boolean result + (True only if all renames succeeded). + + """ + result = True + for disk, unique_id in devlist: + dev = _RecursiveFindBD(disk) + if dev is None: + result = False + continue + try: + dev.Rename(unique_id) + except errors.BlockDeviceError, err: + logger.Error("Can't rename device '%s' to '%s': %s" % + (dev, unique_id, err)) + result = False + return result + + class HooksRunner(object): """Hook runner. diff --git a/lib/bdev.py b/lib/bdev.py index a01f813051ecfceb55eab2b547869ecfdc38978d..dfa153ee87b34360d514ab5360c7631d0dc3ca14 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -172,6 +172,15 @@ class BlockDev(object): raise NotImplementedError + def Rename(self, new_id): + """Rename this device. + + This may or may not make sense for a given device type. + + """ + raise NotImplementedError + + def GetStatus(self): """Return the status of the device. @@ -368,6 +377,21 @@ class LogicalVolume(BlockDev): return not result.failed + def Rename(self, new_id): + """Rename this logical volume. + + """ + if not isinstance(new_id, (tuple, list)) or len(new_id) != 2: + raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id) + new_vg, new_name = new_id + if new_vg != self._vg_name: + raise errors.ProgrammerError("Can't move a logical volume across" + " volume groups (from %s to to %s)" % + (self._vg_name, new_vg)) + result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name]) + if result.failed: + raise errors.BlockDeviceError("Failed to rename the logical volume: %s" % + result.output) def Attach(self): """Attach to an existing LV. @@ -700,6 +724,13 @@ class MDRaid1(BlockDev): #TODO: maybe zero superblock on child devices? return self.Shutdown() + def Rename(self, new_id): + """Rename a device. + + This is not supported for md raid1 devices. + + """ + raise errors.ProgrammerError("Can't rename a md raid1 device") def AddChildren(self, devices): """Add new member(s) to the md raid1. @@ -1056,6 +1087,14 @@ class BaseDRBD(BlockDev): return False return True + def Rename(self, new_id): + """Rename a device. + + This is not supported for drbd devices. + + """ + raise errors.ProgrammerError("Can't rename a drbd device") + class DRBDev(BaseDRBD): """DRBD block device. @@ -1562,6 +1601,7 @@ class DRBDev(BaseDRBD): """ return self.Shutdown() + class DRBD8(BaseDRBD): """DRBD v8.x block device. @@ -2041,6 +2081,17 @@ class DRBD8(BaseDRBD): self._SetFromMinor(minor) return True + @classmethod + def _ShutdownNet(cls, minor): + """Disconnect from the remote peer. + + This fails if we don't have a local device. + + """ + result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"]) + logger.Error("Can't shutdown network: %s" % result.output) + return not result.failed + @classmethod def _ShutdownAll(cls, minor): """Deactivate the device. @@ -2066,6 +2117,27 @@ class DRBD8(BaseDRBD): self.dev_path = None return True + def Rename(self, new_uid): + """Re-connect this device to another peer. + + """ + if self.minor is None: + raise errors.BlockDeviceError("Device not attached during rename") + if self._rhost is not None: + # this means we did have a host when we attached, so we are connected + if not self._ShutdownNet(self.minor): + raise errors.BlockDeviceError("Can't disconnect from remote peer") + old_id = self.unique_id + else: + old_id = None + self.unique_id = new_uid + if not self._AssembleNet(self.minor, self.unique_id, "C"): + logger.Error("Can't attach to new peer!") + if self.old_id is not None: + self._AssembleNet(self.minor, old_id, "C") + self.unique_id = old_id + raise errors.BlockDeviceError("Can't attach to new peer") + def Remove(self): """Stub remove for DRBD devices. diff --git a/lib/rpc.py b/lib/rpc.py index 4153da8f58d9628863275c54883a3de594a76309..790ad5e404ccb0c71d544973b364611b86f31043 100644 --- a/lib/rpc.py +++ b/lib/rpc.py @@ -514,6 +514,19 @@ def call_blockdev_remove(node, bdev): return c.getresult().get(node, False) +def call_blockdev_rename(node, devlist): + """Request rename of the given block devices. + + This is a single-node call. + + """ + params = [(d.ToDict(), uid) for d, uid in devlist] + c = Client("blockdev_rename", params) + c.connect(node) + c.run() + return c.getresult().get(node, False) + + def call_blockdev_assemble(node, disk, on_primary): """Request assembling of a given block device.