Revision b6135bbc
b/lib/bdev.py | ||
---|---|---|
24 | 24 |
import re |
25 | 25 |
import time |
26 | 26 |
import errno |
27 |
import stat |
|
27 | 28 |
import pyparsing as pyp |
28 | 29 |
import os |
29 | 30 |
import logging |
... | ... | |
2069 | 2070 |
return FileStorage(unique_id, children, size) |
2070 | 2071 |
|
2071 | 2072 |
|
2073 |
class PersistentBlockDevice(BlockDev): |
|
2074 |
"""A block device with persistent node |
|
2075 |
|
|
2076 |
May be either directly attached, or exposed through DM (e.g. dm-multipath). |
|
2077 |
udev helpers are probably required to give persistent, human-friendly |
|
2078 |
names. |
|
2079 |
|
|
2080 |
For the time being, pathnames are required to lie under /dev. |
|
2081 |
|
|
2082 |
""" |
|
2083 |
def __init__(self, unique_id, children, size): |
|
2084 |
"""Attaches to a static block device. |
|
2085 |
|
|
2086 |
The unique_id is a path under /dev. |
|
2087 |
|
|
2088 |
""" |
|
2089 |
super(PersistentBlockDevice, self).__init__(unique_id, children, size) |
|
2090 |
if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
|
2091 |
raise ValueError("Invalid configuration data %s" % str(unique_id)) |
|
2092 |
self.dev_path = unique_id[1] |
|
2093 |
if not os.path.realpath(self.dev_path).startswith('/dev/'): |
|
2094 |
raise ValueError("Full path '%s' lies outside /dev" % |
|
2095 |
os.path.realpath(self.dev_path)) |
|
2096 |
# TODO: this is just a safety guard checking that we only deal with devices |
|
2097 |
# we know how to handle. In the future this will be integrated with |
|
2098 |
# external storage backends and possible values will probably be collected |
|
2099 |
# from the cluster configuration. |
|
2100 |
if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL: |
|
2101 |
raise ValueError("Got persistent block device of invalid type: %s" % |
|
2102 |
unique_id[0]) |
|
2103 |
|
|
2104 |
self.major = self.minor = None |
|
2105 |
self.Attach() |
|
2106 |
|
|
2107 |
@classmethod |
|
2108 |
def Create(cls, unique_id, children, size): |
|
2109 |
"""Create a new device |
|
2110 |
|
|
2111 |
This is a noop, we only return a PersistentBlockDevice instance |
|
2112 |
|
|
2113 |
""" |
|
2114 |
return PersistentBlockDevice(unique_id, children, 0) |
|
2115 |
|
|
2116 |
def Remove(self): |
|
2117 |
"""Remove a device |
|
2118 |
|
|
2119 |
This is a noop |
|
2120 |
|
|
2121 |
""" |
|
2122 |
pass |
|
2123 |
|
|
2124 |
def Rename(self, new_id): |
|
2125 |
"""Rename this device. |
|
2126 |
|
|
2127 |
""" |
|
2128 |
_ThrowError("Rename is not supported for PersistentBlockDev storage") |
|
2129 |
|
|
2130 |
def Attach(self): |
|
2131 |
"""Attach to an existing block device. |
|
2132 |
|
|
2133 |
|
|
2134 |
""" |
|
2135 |
self.attached = False |
|
2136 |
try: |
|
2137 |
st = os.stat(self.dev_path) |
|
2138 |
except OSError, err: |
|
2139 |
logging.error("Error stat()'ing %s: %s", self.dev_path, str(err)) |
|
2140 |
return False |
|
2141 |
|
|
2142 |
if not stat.S_ISBLK(st.st_mode): |
|
2143 |
logging.error("%s is not a block device", self.dev_path) |
|
2144 |
return False |
|
2145 |
|
|
2146 |
self.major = os.major(st.st_rdev) |
|
2147 |
self.minor = os.minor(st.st_rdev) |
|
2148 |
self.attached = True |
|
2149 |
|
|
2150 |
return True |
|
2151 |
|
|
2152 |
def Assemble(self): |
|
2153 |
"""Assemble the device. |
|
2154 |
|
|
2155 |
""" |
|
2156 |
pass |
|
2157 |
|
|
2158 |
def Shutdown(self): |
|
2159 |
"""Shutdown the device. |
|
2160 |
|
|
2161 |
""" |
|
2162 |
pass |
|
2163 |
|
|
2164 |
def Open(self, force=False): |
|
2165 |
"""Make the device ready for I/O. |
|
2166 |
|
|
2167 |
""" |
|
2168 |
pass |
|
2169 |
|
|
2170 |
def Close(self): |
|
2171 |
"""Notifies that the device will no longer be used for I/O. |
|
2172 |
|
|
2173 |
""" |
|
2174 |
pass |
|
2175 |
|
|
2176 |
def Grow(self, amount): |
|
2177 |
"""Grow the logical volume. |
|
2178 |
|
|
2179 |
""" |
|
2180 |
_ThrowError("Grow is not supported for PersistentBlockDev storage") |
|
2181 |
|
|
2182 |
|
|
2072 | 2183 |
DEV_MAP = { |
2073 | 2184 |
constants.LD_LV: LogicalVolume, |
2074 | 2185 |
constants.LD_DRBD8: DRBD8, |
2186 |
constants.LD_BLOCKDEV: PersistentBlockDevice, |
|
2075 | 2187 |
} |
2076 | 2188 |
|
2077 | 2189 |
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE: |
b/lib/cmdlib.py | ||
---|---|---|
6661 | 6661 |
disk_index)), |
6662 | 6662 |
mode=disk["mode"]) |
6663 | 6663 |
disks.append(disk_dev) |
6664 |
elif template_name == constants.DT_BLOCK: |
|
6665 |
if len(secondary_nodes) != 0: |
|
6666 |
raise errors.ProgrammerError("Wrong template configuration") |
|
6667 |
|
|
6668 |
for idx, disk in enumerate(disk_info): |
|
6669 |
disk_index = idx + base_index |
|
6670 |
disk_dev = objects.Disk(dev_type=constants.LD_BLOCKDEV, size=disk["size"], |
|
6671 |
logical_id=(constants.BLOCKDEV_DRIVER_MANUAL, |
|
6672 |
disk["adopt"]), |
|
6673 |
iv_name="disk/%d" % disk_index, |
|
6674 |
mode=disk["mode"]) |
|
6675 |
disks.append(disk_dev) |
|
6676 |
|
|
6664 | 6677 |
else: |
6665 | 6678 |
raise errors.ProgrammerError("Invalid disk template '%s'" % template_name) |
6666 | 6679 |
return disks |
... | ... | |
6887 | 6900 |
constants.DT_DRBD8: sum(d["size"] + 128 for d in disks), |
6888 | 6901 |
constants.DT_FILE: None, |
6889 | 6902 |
constants.DT_SHARED_FILE: 0, |
6903 |
constants.DT_BLOCK: 0, |
|
6890 | 6904 |
} |
6891 | 6905 |
|
6892 | 6906 |
if disk_template not in req_size_dict: |
... | ... | |
7022 | 7036 |
if self.op.mode == constants.INSTANCE_IMPORT: |
7023 | 7037 |
raise errors.OpPrereqError("Disk adoption not allowed for" |
7024 | 7038 |
" instance import", errors.ECODE_INVAL) |
7039 |
else: |
|
7040 |
if self.op.disk_template in constants.DTS_MUST_ADOPT: |
|
7041 |
raise errors.OpPrereqError("Disk template %s requires disk adoption," |
|
7042 |
" but no 'adopt' parameter given" % |
|
7043 |
self.op.disk_template, |
|
7044 |
errors.ECODE_INVAL) |
|
7025 | 7045 |
|
7026 | 7046 |
self.adopt_disks = has_adopt |
7027 | 7047 |
|
... | ... | |
7614 | 7634 |
req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks) |
7615 | 7635 |
_CheckNodesFreeDiskPerVG(self, nodenames, req_sizes) |
7616 | 7636 |
|
7617 |
else: # instead, we must check the adoption data
|
|
7637 |
elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
|
|
7618 | 7638 |
all_lvs = set([i["vg"] + "/" + i["adopt"] for i in self.disks]) |
7619 | 7639 |
if len(all_lvs) != len(self.disks): |
7620 | 7640 |
raise errors.OpPrereqError("Duplicate volume names given for adoption", |
... | ... | |
7650 | 7670 |
for dsk in self.disks: |
7651 | 7671 |
dsk["size"] = int(float(node_lvs[dsk["vg"] + "/" + dsk["adopt"]][0])) |
7652 | 7672 |
|
7673 |
elif self.op.disk_template == constants.DT_BLOCK: |
|
7674 |
# Normalize and de-duplicate device paths |
|
7675 |
all_disks = set([os.path.abspath(i["adopt"]) for i in self.disks]) |
|
7676 |
if len(all_disks) != len(self.disks): |
|
7677 |
raise errors.OpPrereqError("Duplicate disk names given for adoption", |
|
7678 |
errors.ECODE_INVAL) |
|
7679 |
baddisks = [d for d in all_disks |
|
7680 |
if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)] |
|
7681 |
if baddisks: |
|
7682 |
raise errors.OpPrereqError("Device node(s) %s lie outside %s and" |
|
7683 |
" cannot be adopted" % |
|
7684 |
(", ".join(baddisks), |
|
7685 |
constants.ADOPTABLE_BLOCKDEV_ROOT), |
|
7686 |
errors.ECODE_INVAL) |
|
7687 |
|
|
7688 |
node_disks = self.rpc.call_bdev_sizes([pnode.name], |
|
7689 |
list(all_disks))[pnode.name] |
|
7690 |
node_disks.Raise("Cannot get block device information from node %s" % |
|
7691 |
pnode.name) |
|
7692 |
node_disks = node_disks.payload |
|
7693 |
delta = all_disks.difference(node_disks.keys()) |
|
7694 |
if delta: |
|
7695 |
raise errors.OpPrereqError("Missing block device(s): %s" % |
|
7696 |
utils.CommaJoin(delta), |
|
7697 |
errors.ECODE_INVAL) |
|
7698 |
for dsk in self.disks: |
|
7699 |
dsk["size"] = int(float(node_disks[dsk["adopt"]])) |
|
7700 |
|
|
7653 | 7701 |
_CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams) |
7654 | 7702 |
|
7655 | 7703 |
_CheckNodeHasOS(self, pnode.name, self.op.os_type, self.op.force_variant) |
... | ... | |
7721 | 7769 |
) |
7722 | 7770 |
|
7723 | 7771 |
if self.adopt_disks: |
7724 |
# rename LVs to the newly-generated names; we need to construct |
|
7725 |
# 'fake' LV disks with the old data, plus the new unique_id |
|
7726 |
tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks] |
|
7727 |
rename_to = [] |
|
7728 |
for t_dsk, a_dsk in zip (tmp_disks, self.disks): |
|
7729 |
rename_to.append(t_dsk.logical_id) |
|
7730 |
t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"]) |
|
7731 |
self.cfg.SetDiskID(t_dsk, pnode_name) |
|
7732 |
result = self.rpc.call_blockdev_rename(pnode_name, |
|
7733 |
zip(tmp_disks, rename_to)) |
|
7734 |
result.Raise("Failed to rename adoped LVs") |
|
7772 |
if self.op.disk_template == constants.DT_PLAIN: |
|
7773 |
# rename LVs to the newly-generated names; we need to construct |
|
7774 |
# 'fake' LV disks with the old data, plus the new unique_id |
|
7775 |
tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks] |
|
7776 |
rename_to = [] |
|
7777 |
for t_dsk, a_dsk in zip (tmp_disks, self.disks): |
|
7778 |
rename_to.append(t_dsk.logical_id) |
|
7779 |
t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"]) |
|
7780 |
self.cfg.SetDiskID(t_dsk, pnode_name) |
|
7781 |
result = self.rpc.call_blockdev_rename(pnode_name, |
|
7782 |
zip(tmp_disks, rename_to)) |
|
7783 |
result.Raise("Failed to rename adoped LVs") |
|
7735 | 7784 |
else: |
7736 | 7785 |
feedback_fn("* creating instance disks...") |
7737 | 7786 |
try: |
b/lib/constants.py | ||
---|---|---|
120 | 120 |
CRYPTO_KEYS_DIR_MODE = SECURE_DIR_MODE |
121 | 121 |
IMPORT_EXPORT_DIR = RUN_GANETI_DIR + "/import-export" |
122 | 122 |
IMPORT_EXPORT_DIR_MODE = 0755 |
123 |
ADOPTABLE_BLOCKDEV_ROOT = "/dev/disk/" |
|
123 | 124 |
# keep RUN_GANETI_DIR first here, to make sure all get created when the node |
124 | 125 |
# daemon is started (this takes care of RUN_DIR being tmpfs) |
125 | 126 |
SUB_RUN_DIRS = [ RUN_GANETI_DIR, BDEV_CACHE_DIR, DISK_LINKS_DIR ] |
... | ... | |
363 | 364 |
DT_DRBD8 = "drbd" |
364 | 365 |
DT_FILE = "file" |
365 | 366 |
DT_SHARED_FILE = "sharedfile" |
367 |
DT_BLOCK = "blockdev" |
|
366 | 368 |
|
367 | 369 |
# the set of network-mirrored disk templates |
368 | 370 |
DTS_NET_MIRROR = frozenset([DT_DRBD8]) |
369 | 371 |
|
370 |
# the set of externally mirrored disk templates
|
|
371 |
DTS_EXT_MIRROR = frozenset([DT_SHARED_FILE]) |
|
372 |
# the set of externally-mirrored disk templates (e.g. SAN, NAS)
|
|
373 |
DTS_EXT_MIRROR = frozenset([DT_SHARED_FILE, DT_BLOCK])
|
|
372 | 374 |
|
373 | 375 |
# the set of non-lvm-based disk templates |
374 |
DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE, DT_SHARED_FILE]) |
|
376 |
DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE, DT_SHARED_FILE, DT_BLOCK])
|
|
375 | 377 |
|
376 | 378 |
# the set of disk templates which can be grown |
377 | 379 |
DTS_GROWABLE = frozenset([DT_PLAIN, DT_DRBD8, DT_FILE, DT_SHARED_FILE]) |
378 | 380 |
|
379 | 381 |
# the set of disk templates that allow adoption |
380 |
DTS_MAY_ADOPT = frozenset([DT_PLAIN]) |
|
382 |
DTS_MAY_ADOPT = frozenset([DT_PLAIN, DT_BLOCK]) |
|
383 |
|
|
384 |
# the set of disk templates that *must* use adoption |
|
385 |
DTS_MUST_ADOPT = frozenset([DT_BLOCK]) |
|
381 | 386 |
|
382 | 387 |
# the set of disk templates that allow migrations |
383 | 388 |
DTS_MIRRORED = frozenset.union(DTS_NET_MIRROR, DTS_EXT_MIRROR) |
... | ... | |
387 | 392 |
LD_LV = "lvm" |
388 | 393 |
LD_DRBD8 = "drbd8" |
389 | 394 |
LD_FILE = "file" |
390 |
LDS_BLOCK = frozenset([LD_LV, LD_DRBD8]) |
|
395 |
LD_BLOCKDEV = "blockdev" |
|
396 |
LDS_BLOCK = frozenset([LD_LV, LD_DRBD8, LD_BLOCKDEV]) |
|
391 | 397 |
|
392 | 398 |
# drbd constants |
393 | 399 |
DRBD_HMAC_ALG = "md5" |
... | ... | |
460 | 466 |
CHILD_LINGER_TIMEOUT = 5.0 |
461 | 467 |
|
462 | 468 |
DISK_TEMPLATES = frozenset([DT_DISKLESS, DT_PLAIN, DT_DRBD8, |
463 |
DT_FILE, DT_SHARED_FILE]) |
|
469 |
DT_FILE, DT_SHARED_FILE, DT_BLOCK])
|
|
464 | 470 |
|
465 | 471 |
FILE_DRIVER = frozenset([FD_LOOP, FD_BLKTAP]) |
466 | 472 |
|
... | ... | |
1312 | 1318 |
ALLOC_POLICY_LAST_RESORT, |
1313 | 1319 |
ALLOC_POLICY_UNALLOCABLE, |
1314 | 1320 |
] |
1321 |
|
|
1322 |
# Temporary external/shared storage parameters |
|
1323 |
BLOCKDEV_DRIVER_MANUAL = "manual" |
b/lib/objects.py | ||
---|---|---|
441 | 441 |
""" |
442 | 442 |
if self.dev_type == constants.LD_LV: |
443 | 443 |
return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1]) |
444 |
elif self.dev_type == constants.LD_BLOCKDEV: |
|
445 |
return self.logical_id[1] |
|
444 | 446 |
return None |
445 | 447 |
|
446 | 448 |
def ChildrenNeeded(self): |
... | ... | |
483 | 485 |
devices needs to (or can) be assembled. |
484 | 486 |
|
485 | 487 |
""" |
486 |
if self.dev_type in [constants.LD_LV, constants.LD_FILE]: |
|
488 |
if self.dev_type in [constants.LD_LV, constants.LD_FILE, |
|
489 |
constants.LD_BLOCKDEV]: |
|
487 | 490 |
result = [node] |
488 | 491 |
elif self.dev_type in constants.LDS_DRBD: |
489 | 492 |
result = [self.logical_id[0], self.logical_id[1]] |
Also available in: Unified diff