Revision 391d0261
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 | ||
---|---|---|
6621 | 6621 |
disk_index)), |
6622 | 6622 |
mode=disk["mode"]) |
6623 | 6623 |
disks.append(disk_dev) |
6624 |
elif template_name == constants.DT_BLOCK: |
|
6625 |
if len(secondary_nodes) != 0: |
|
6626 |
raise errors.ProgrammerError("Wrong template configuration") |
|
6627 |
|
|
6628 |
for idx, disk in enumerate(disk_info): |
|
6629 |
disk_index = idx + base_index |
|
6630 |
disk_dev = objects.Disk(dev_type=constants.LD_BLOCKDEV, size=disk["size"], |
|
6631 |
logical_id=(constants.BLOCKDEV_DRIVER_MANUAL, |
|
6632 |
disk["adopt"]), |
|
6633 |
iv_name="disk/%d" % disk_index, |
|
6634 |
mode=disk["mode"]) |
|
6635 |
disks.append(disk_dev) |
|
6636 |
|
|
6624 | 6637 |
else: |
6625 | 6638 |
raise errors.ProgrammerError("Invalid disk template '%s'" % template_name) |
6626 | 6639 |
return disks |
... | ... | |
6847 | 6860 |
constants.DT_DRBD8: sum(d["size"] + 128 for d in disks), |
6848 | 6861 |
constants.DT_FILE: None, |
6849 | 6862 |
constants.DT_SHARED_FILE: 0, |
6863 |
constants.DT_BLOCK: 0, |
|
6850 | 6864 |
} |
6851 | 6865 |
|
6852 | 6866 |
if disk_template not in req_size_dict: |
... | ... | |
6982 | 6996 |
if self.op.mode == constants.INSTANCE_IMPORT: |
6983 | 6997 |
raise errors.OpPrereqError("Disk adoption not allowed for" |
6984 | 6998 |
" instance import", errors.ECODE_INVAL) |
6999 |
else: |
|
7000 |
if self.op.disk_template in constants.DTS_MUST_ADOPT: |
|
7001 |
raise errors.OpPrereqError("Disk template %s requires disk adoption," |
|
7002 |
" but no 'adopt' parameter given" % |
|
7003 |
self.op.disk_template, |
|
7004 |
errors.ECODE_INVAL) |
|
6985 | 7005 |
|
6986 | 7006 |
self.adopt_disks = has_adopt |
6987 | 7007 |
|
... | ... | |
7584 | 7604 |
req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks) |
7585 | 7605 |
_CheckNodesFreeDiskPerVG(self, nodenames, req_sizes) |
7586 | 7606 |
|
7587 |
else: # instead, we must check the adoption data
|
|
7607 |
elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
|
|
7588 | 7608 |
all_lvs = set([i["vg"] + "/" + i["adopt"] for i in self.disks]) |
7589 | 7609 |
if len(all_lvs) != len(self.disks): |
7590 | 7610 |
raise errors.OpPrereqError("Duplicate volume names given for adoption", |
... | ... | |
7620 | 7640 |
for dsk in self.disks: |
7621 | 7641 |
dsk["size"] = int(float(node_lvs[dsk["vg"] + "/" + dsk["adopt"]][0])) |
7622 | 7642 |
|
7643 |
elif self.op.disk_template == constants.DT_BLOCK: |
|
7644 |
# Normalize and de-duplicate device paths |
|
7645 |
all_disks = set([os.path.abspath(i["adopt"]) for i in self.disks]) |
|
7646 |
if len(all_disks) != len(self.disks): |
|
7647 |
raise errors.OpPrereqError("Duplicate disk names given for adoption", |
|
7648 |
errors.ECODE_INVAL) |
|
7649 |
baddisks = [d for d in all_disks |
|
7650 |
if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)] |
|
7651 |
if baddisks: |
|
7652 |
raise errors.OpPrereqError("Device node(s) %s lie outside %s and" |
|
7653 |
" cannot be adopted" % |
|
7654 |
(", ".join(baddisks), |
|
7655 |
constants.ADOPTABLE_BLOCKDEV_ROOT), |
|
7656 |
errors.ECODE_INVAL) |
|
7657 |
|
|
7658 |
node_disks = self.rpc.call_bdev_sizes([pnode.name], |
|
7659 |
list(all_disks))[pnode.name] |
|
7660 |
node_disks.Raise("Cannot get block device information from node %s" % |
|
7661 |
pnode.name) |
|
7662 |
node_disks = node_disks.payload |
|
7663 |
delta = all_disks.difference(node_disks.keys()) |
|
7664 |
if delta: |
|
7665 |
raise errors.OpPrereqError("Missing block device(s): %s" % |
|
7666 |
utils.CommaJoin(delta), |
|
7667 |
errors.ECODE_INVAL) |
|
7668 |
for dsk in self.disks: |
|
7669 |
dsk["size"] = int(float(node_disks[dsk["adopt"]])) |
|
7670 |
|
|
7623 | 7671 |
_CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams) |
7624 | 7672 |
|
7625 | 7673 |
_CheckNodeHasOS(self, pnode.name, self.op.os_type, self.op.force_variant) |
... | ... | |
7691 | 7739 |
) |
7692 | 7740 |
|
7693 | 7741 |
if self.adopt_disks: |
7694 |
# rename LVs to the newly-generated names; we need to construct |
|
7695 |
# 'fake' LV disks with the old data, plus the new unique_id |
|
7696 |
tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks] |
|
7697 |
rename_to = [] |
|
7698 |
for t_dsk, a_dsk in zip (tmp_disks, self.disks): |
|
7699 |
rename_to.append(t_dsk.logical_id) |
|
7700 |
t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"]) |
|
7701 |
self.cfg.SetDiskID(t_dsk, pnode_name) |
|
7702 |
result = self.rpc.call_blockdev_rename(pnode_name, |
|
7703 |
zip(tmp_disks, rename_to)) |
|
7704 |
result.Raise("Failed to rename adoped LVs") |
|
7742 |
if self.op.disk_template == constants.DT_PLAIN: |
|
7743 |
# rename LVs to the newly-generated names; we need to construct |
|
7744 |
# 'fake' LV disks with the old data, plus the new unique_id |
|
7745 |
tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks] |
|
7746 |
rename_to = [] |
|
7747 |
for t_dsk, a_dsk in zip (tmp_disks, self.disks): |
|
7748 |
rename_to.append(t_dsk.logical_id) |
|
7749 |
t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"]) |
|
7750 |
self.cfg.SetDiskID(t_dsk, pnode_name) |
|
7751 |
result = self.rpc.call_blockdev_rename(pnode_name, |
|
7752 |
zip(tmp_disks, rename_to)) |
|
7753 |
result.Raise("Failed to rename adoped LVs") |
|
7705 | 7754 |
else: |
7706 | 7755 |
feedback_fn("* creating instance disks...") |
7707 | 7756 |
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 |
|
... | ... | |
1279 | 1285 |
ALLOC_POLICY_LAST_RESORT, |
1280 | 1286 |
ALLOC_POLICY_UNALLOCABLE, |
1281 | 1287 |
] |
1288 |
|
|
1289 |
# Temporary external/shared storage parameters |
|
1290 |
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