Revision a2cfdea2 lib/bdev.py
b/lib/bdev.py | ||
---|---|---|
24 | 24 |
import re |
25 | 25 |
import time |
26 | 26 |
import errno |
27 |
import pyparsing as pyp |
|
27 | 28 |
|
28 | 29 |
from ganeti import utils |
29 | 30 |
from ganeti import logger |
... | ... | |
1549 | 1550 |
""" |
1550 | 1551 |
return self.Shutdown() |
1551 | 1552 |
|
1553 |
class DRBD8(BaseDRBD): |
|
1554 |
"""DRBD v8.x block device. |
|
1555 |
|
|
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. |
|
1559 |
|
|
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. |
|
1564 |
|
|
1565 |
""" |
|
1566 |
_DRBD_MAJOR = 147 |
|
1567 |
_MAX_MINORS = 255 |
|
1568 |
_PARSE_SHOW = None |
|
1569 |
|
|
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() |
|
1574 |
if kmaj != 8: |
|
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)) |
|
1578 |
|
|
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 |
|
1584 |
self.Attach() |
|
1585 |
|
|
1586 |
@classmethod |
|
1587 |
def _InitMeta(cls, minor, dev_path): |
|
1588 |
"""Initialize a meta device. |
|
1589 |
|
|
1590 |
This will not work if the given minor is in use. |
|
1591 |
|
|
1592 |
""" |
|
1593 |
result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor), |
|
1594 |
"v08", dev_path, "0", "create-md"]) |
|
1595 |
if result.failed: |
|
1596 |
raise errors.BlockDeviceError("Can't initialize meta device: %s" % |
|
1597 |
result.output) |
|
1598 |
|
|
1599 |
@classmethod |
|
1600 |
def _FindUnusedMinor(cls): |
|
1601 |
"""Find an unused DRBD device. |
|
1602 |
|
|
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. |
|
1605 |
|
|
1606 |
""" |
|
1607 |
data = cls._GetProcData() |
|
1608 |
|
|
1609 |
unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$") |
|
1610 |
used_line = re.compile("^ *([0-9]+): cs:") |
|
1611 |
highest = None |
|
1612 |
for line in data: |
|
1613 |
match = unused_line.match(line) |
|
1614 |
if match: |
|
1615 |
return int(match.group(1)) |
|
1616 |
match = used_line.match(line) |
|
1617 |
if match: |
|
1618 |
minor = int(match.group(1)) |
|
1619 |
highest = max(highest, minor) |
|
1620 |
if highest is None: # there are no minors in use at all |
|
1621 |
return 0 |
|
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") |
|
1625 |
return highest + 1 |
|
1626 |
|
|
1627 |
@classmethod |
|
1628 |
def _IsValidMeta(cls, meta_device): |
|
1629 |
"""Check if the given meta device looks like a valid one. |
|
1630 |
|
|
1631 |
""" |
|
1632 |
minor = cls._FindUnusedMinor() |
|
1633 |
minor_path = cls._DevPath(minor) |
|
1634 |
result = utils.RunCmd(["drbdmeta", minor_path, |
|
1635 |
"v08", meta_device, "0", |
|
1636 |
"dstate"]) |
|
1637 |
if result.failed: |
|
1638 |
logger.Error("Invalid meta device %s: %s" % (meta_device, result.output)) |
|
1639 |
return False |
|
1640 |
return True |
|
1641 |
|
|
1642 |
@classmethod |
|
1643 |
def _GetShowParser(cls): |
|
1644 |
"""Return a parser for `drbd show` output. |
|
1645 |
|
|
1646 |
This will either create or return an already-create parser for the |
|
1647 |
output of the command `drbd show`. |
|
1648 |
|
|
1649 |
""" |
|
1650 |
if cls._PARSE_SHOW is not None: |
|
1651 |
return cls._PARSE_SHOW |
|
1652 |
|
|
1653 |
# pyparsing setup |
|
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])])) |
|
1659 |
|
|
1660 |
comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine) |
|
1661 |
defa = pyp.Literal("_is_default").suppress() |
|
1662 |
dbl_quote = pyp.Literal('"').suppress() |
|
1663 |
|
|
1664 |
keyword = pyp.Word(pyp.alphanums + '-') |
|
1665 |
|
|
1666 |
# value types |
|
1667 |
value = pyp.Word(pyp.alphanums + '_-/.:') |
|
1668 |
quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote |
|
1669 |
addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() + |
|
1670 |
number) |
|
1671 |
# meta device, extended syntax |
|
1672 |
meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() + |
|
1673 |
number + pyp.Word(']').suppress()) |
|
1674 |
|
|
1675 |
# a statement |
|
1676 |
stmt = (~rbrace + keyword + ~lbrace + |
|
1677 |
(addr_port ^ value ^ quoted ^ meta_value) + |
|
1678 |
pyp.Optional(defa) + semi + |
|
1679 |
pyp.Optional(pyp.restOfLine).suppress()) |
|
1680 |
|
|
1681 |
# an entire section |
|
1682 |
section_name = pyp.Word(pyp.alphas + '_') |
|
1683 |
section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace |
|
1684 |
|
|
1685 |
bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt)) |
|
1686 |
bnf.ignore(comment) |
|
1687 |
|
|
1688 |
cls._PARSE_SHOW = bnf |
|
1689 |
|
|
1690 |
return bnf |
|
1691 |
|
|
1692 |
@classmethod |
|
1693 |
def _GetDevInfo(cls, minor): |
|
1694 |
"""Get details about a given DRBD minor. |
|
1695 |
|
|
1696 |
This return, if available, the local backing device (as a path) |
|
1697 |
and the local and remote (ip, port) information. |
|
1698 |
|
|
1699 |
""" |
|
1700 |
data = {} |
|
1701 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"]) |
|
1702 |
if result.failed: |
|
1703 |
logger.Error("Can't display the drbd config: %s" % result.fail_reason) |
|
1704 |
return data |
|
1705 |
out = result.stdout |
|
1706 |
if not out: |
|
1707 |
return data |
|
1708 |
|
|
1709 |
bnf = cls._GetShowParser() |
|
1710 |
# run pyparse |
|
1711 |
|
|
1712 |
try: |
|
1713 |
results = bnf.parseString(out) |
|
1714 |
except pyp.ParseException, err: |
|
1715 |
raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" % |
|
1716 |
str(err)) |
|
1717 |
|
|
1718 |
# and massage the results into our desired format |
|
1719 |
for section in results: |
|
1720 |
sname = section[0] |
|
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:]) |
|
1734 |
return data |
|
1735 |
|
|
1736 |
def _MatchesLocal(self, info): |
|
1737 |
"""Test if our local config matches with an existing device. |
|
1738 |
|
|
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 |
|
1742 |
device. |
|
1743 |
|
|
1744 |
""" |
|
1745 |
backend = self._children[0] |
|
1746 |
if backend is not None: |
|
1747 |
retval = (info["local_dev"] == backend.dev_path) |
|
1748 |
else: |
|
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) |
|
1754 |
else: |
|
1755 |
retval = retval and ("meta_dev" not in info and |
|
1756 |
"meta_index" not in info) |
|
1757 |
return retval |
|
1758 |
|
|
1759 |
def _MatchesNet(self, info): |
|
1760 |
"""Test if our network config matches with an existing device. |
|
1761 |
|
|
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 |
|
1765 |
device. |
|
1766 |
|
|
1767 |
""" |
|
1768 |
if (((self._lhost is None and not ("local_addr" in info)) and |
|
1769 |
(self._rhost is None and not ("remote_addr" in info)))): |
|
1770 |
return True |
|
1771 |
|
|
1772 |
if self._lhost is None: |
|
1773 |
return False |
|
1774 |
|
|
1775 |
if not ("local_addr" in info and |
|
1776 |
"remote_addr" in info): |
|
1777 |
return False |
|
1778 |
|
|
1779 |
retval = (info["local_addr"] == (self._lhost, self._lport)) |
|
1780 |
retval = (retval and |
|
1781 |
info["remote_addr"] == (self._rhost, self._rport)) |
|
1782 |
return retval |
|
1783 |
|
|
1784 |
@classmethod |
|
1785 |
def _AssembleLocal(cls, minor, backend, meta): |
|
1786 |
"""Configure the local part of a DRBD device. |
|
1787 |
|
|
1788 |
This is the first thing that must be done on an unconfigured DRBD |
|
1789 |
device. And it must be done only once. |
|
1790 |
|
|
1791 |
""" |
|
1792 |
if not cls._IsValidMeta(meta): |
|
1793 |
return False |
|
1794 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk", |
|
1795 |
backend, meta, "0", "-e", "detach", |
|
1796 |
"--create-device"]) |
|
1797 |
if result.failed: |
|
1798 |
logger.Error("Can't attach local disk: %s" % result.output) |
|
1799 |
return not result.failed |
|
1800 |
|
|
1801 |
@classmethod |
|
1802 |
def _AssembleNet(cls, minor, net_info, protocol, |
|
1803 |
dual_pri=False, hmac=None, secret=None): |
|
1804 |
"""Configure the network part of the device. |
|
1805 |
|
|
1806 |
""" |
|
1807 |
lhost, lport, rhost, rport = net_info |
|
1808 |
args = ["drbdsetup", cls._DevPath(minor), "net", |
|
1809 |
"%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), |
|
1810 |
protocol] |
|
1811 |
if dual_pri: |
|
1812 |
args.append("-m") |
|
1813 |
if hmac and secret: |
|
1814 |
args.extend(["-a", hmac, "-x", secret]) |
|
1815 |
result = utils.RunCmd(args) |
|
1816 |
if result.failed: |
|
1817 |
logger.Error("Can't setup network for dbrd device: %s" % |
|
1818 |
result.fail_reason) |
|
1819 |
return False |
|
1820 |
|
|
1821 |
timeout = time.time() + 10 |
|
1822 |
ok = False |
|
1823 |
while time.time() < timeout: |
|
1824 |
info = cls._GetDevInfo(minor) |
|
1825 |
if not "local_addr" in info or not "remote_addr" in info: |
|
1826 |
time.sleep(1) |
|
1827 |
continue |
|
1828 |
if (info["local_addr"] != (lhost, lport) or |
|
1829 |
info["remote_addr"] != (rhost, rport)): |
|
1830 |
time.sleep(1) |
|
1831 |
continue |
|
1832 |
ok = True |
|
1833 |
break |
|
1834 |
if not ok: |
|
1835 |
logger.Error("Timeout while configuring network") |
|
1836 |
return False |
|
1837 |
return True |
|
1838 |
|
|
1839 |
def SetSyncSpeed(self, kbytes): |
|
1840 |
"""Set the speed of the DRBD syncer. |
|
1841 |
|
|
1842 |
""" |
|
1843 |
children_result = super(DRBD8, self).SetSyncSpeed(kbytes) |
|
1844 |
if self.minor is None: |
|
1845 |
logger.Info("Instance not attached to a device") |
|
1846 |
return False |
|
1847 |
result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" % |
|
1848 |
kbytes]) |
|
1849 |
if result.failed: |
|
1850 |
logger.Error("Can't change syncer rate: %s " % result.fail_reason) |
|
1851 |
return not result.failed and children_result |
|
1852 |
|
|
1853 |
def GetSyncStatus(self): |
|
1854 |
"""Returns the sync status of the device. |
|
1855 |
|
|
1856 |
Returns: |
|
1857 |
(sync_percent, estimated_time) |
|
1858 |
|
|
1859 |
If sync_percent is None, it means all is ok |
|
1860 |
If estimated_time is None, it means we can't esimate |
|
1861 |
the time needed, otherwise it's the time left in seconds |
|
1862 |
|
|
1863 |
""" |
|
1864 |
if self.minor is None and not self.Attach(): |
|
1865 |
raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus") |
|
1866 |
proc_info = self._MassageProcData(self._GetProcData()) |
|
1867 |
if self.minor not in proc_info: |
|
1868 |
raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" % |
|
1869 |
self.minor) |
|
1870 |
line = proc_info[self.minor] |
|
1871 |
match = re.match("^.*sync'ed: *([0-9.]+)%.*" |
|
1872 |
" finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line) |
|
1873 |
if match: |
|
1874 |
sync_percent = float(match.group(1)) |
|
1875 |
hours = int(match.group(2)) |
|
1876 |
minutes = int(match.group(3)) |
|
1877 |
seconds = int(match.group(4)) |
|
1878 |
est_time = hours * 3600 + minutes * 60 + seconds |
|
1879 |
else: |
|
1880 |
sync_percent = None |
|
1881 |
est_time = None |
|
1882 |
match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line) |
|
1883 |
if not match: |
|
1884 |
raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" % |
|
1885 |
self.minor) |
|
1886 |
client_state = match.group(1) |
|
1887 |
is_degraded = client_state != "Connected" |
|
1888 |
return sync_percent, est_time, is_degraded |
|
1889 |
|
|
1890 |
def GetStatus(self): |
|
1891 |
"""Compute the status of the DRBD device |
|
1892 |
|
|
1893 |
Note that DRBD devices don't have the STATUS_EXISTING state. |
|
1894 |
|
|
1895 |
""" |
|
1896 |
if self.minor is None and not self.Attach(): |
|
1897 |
return self.STATUS_UNKNOWN |
|
1898 |
|
|
1899 |
data = self._GetProcData() |
|
1900 |
match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" % |
|
1901 |
self.minor) |
|
1902 |
for line in data: |
|
1903 |
mresult = match.match(line) |
|
1904 |
if mresult: |
|
1905 |
break |
|
1906 |
else: |
|
1907 |
logger.Error("Can't find myself!") |
|
1908 |
return self.STATUS_UNKNOWN |
|
1909 |
|
|
1910 |
state = mresult.group(2) |
|
1911 |
if state == "Primary": |
|
1912 |
result = self.STATUS_ONLINE |
|
1913 |
else: |
|
1914 |
result = self.STATUS_STANDBY |
|
1915 |
|
|
1916 |
return result |
|
1917 |
|
|
1918 |
def Open(self, force=False): |
|
1919 |
"""Make the local state primary. |
|
1920 |
|
|
1921 |
If the 'force' parameter is given, the '--do-what-I-say' parameter |
|
1922 |
is given. Since this is a pottentialy dangerous operation, the |
|
1923 |
force flag should be only given after creation, when it actually |
|
1924 |
has to be given. |
|
1925 |
|
|
1926 |
""" |
|
1927 |
if self.minor is None and not self.Attach(): |
|
1928 |
logger.Error("DRBD cannot attach to a device during open") |
|
1929 |
return False |
|
1930 |
cmd = ["drbdsetup", self.dev_path, "primary"] |
|
1931 |
if force: |
|
1932 |
cmd.append("-o") |
|
1933 |
result = utils.RunCmd(cmd) |
|
1934 |
if result.failed: |
|
1935 |
logger.Error("Can't make drbd device primary: %s" % result.output) |
|
1936 |
return False |
|
1937 |
return True |
|
1938 |
|
|
1939 |
def Close(self): |
|
1940 |
"""Make the local state secondary. |
|
1941 |
|
|
1942 |
This will, of course, fail if the device is in use. |
|
1943 |
|
|
1944 |
""" |
|
1945 |
if self.minor is None and not self.Attach(): |
|
1946 |
logger.Info("Instance not attached to a device") |
|
1947 |
raise errors.BlockDeviceError("Can't find device") |
|
1948 |
result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"]) |
|
1949 |
if result.failed: |
|
1950 |
logger.Error("Can't switch drbd device to secondary: %s" % result.output) |
|
1951 |
raise errors.BlockDeviceError("Can't switch drbd device to secondary") |
|
1952 |
|
|
1953 |
def Attach(self): |
|
1954 |
"""Find a DRBD device which matches our config and attach to it. |
|
1955 |
|
|
1956 |
In case of partially attached (local device matches but no network |
|
1957 |
setup), we perform the network attach. If successful, we re-test |
|
1958 |
the attach if can return success. |
|
1959 |
|
|
1960 |
""" |
|
1961 |
for minor in self._GetUsedDevs(): |
|
1962 |
info = self._GetDevInfo(minor) |
|
1963 |
match_l = self._MatchesLocal(info) |
|
1964 |
match_r = self._MatchesNet(info) |
|
1965 |
if match_l and match_r: |
|
1966 |
break |
|
1967 |
if match_l and not match_r and "local_addr" not in info: |
|
1968 |
res_r = self._AssembleNet(minor, |
|
1969 |
(self._lhost, self._lport, |
|
1970 |
self._rhost, self._rport), |
|
1971 |
"C") |
|
1972 |
if res_r and self._MatchesNet(self._GetDevInfo(minor)): |
|
1973 |
break |
|
1974 |
else: |
|
1975 |
minor = None |
|
1976 |
|
|
1977 |
self._SetFromMinor(minor) |
|
1978 |
return minor is not None |
|
1979 |
|
|
1980 |
def Assemble(self): |
|
1981 |
"""Assemble the drbd. |
|
1982 |
|
|
1983 |
Method: |
|
1984 |
- if we have a local backing device, we bind to it by: |
|
1985 |
- checking the list of used drbd devices |
|
1986 |
- check if the local minor use of any of them is our own device |
|
1987 |
- if yes, abort? |
|
1988 |
- if not, bind |
|
1989 |
- if we have a local/remote net info: |
|
1990 |
- redo the local backing device step for the remote device |
|
1991 |
- check if any drbd device is using the local port, |
|
1992 |
if yes abort |
|
1993 |
- check if any remote drbd device is using the remote |
|
1994 |
port, if yes abort (for now) |
|
1995 |
- bind our net port |
|
1996 |
- bind the remote net port |
|
1997 |
|
|
1998 |
""" |
|
1999 |
self.Attach() |
|
2000 |
if self.minor is not None: |
|
2001 |
logger.Info("Already assembled") |
|
2002 |
return True |
|
2003 |
|
|
2004 |
result = super(DRBD8, self).Assemble() |
|
2005 |
if not result: |
|
2006 |
return result |
|
2007 |
|
|
2008 |
minor = self._FindUnusedMinor() |
|
2009 |
need_localdev_teardown = False |
|
2010 |
if self._children[0]: |
|
2011 |
result = self._AssembleLocal(minor, self._children[0].dev_path, |
|
2012 |
self._children[1].dev_path) |
|
2013 |
if not result: |
|
2014 |
return False |
|
2015 |
need_localdev_teardown = True |
|
2016 |
if self._lhost and self._lport and self._rhost and self._rport: |
|
2017 |
result = self._AssembleNet(minor, |
|
2018 |
(self._lhost, self._lport, |
|
2019 |
self._rhost, self._rport), |
|
2020 |
"C") |
|
2021 |
if not result: |
|
2022 |
if need_localdev_teardown: |
|
2023 |
# we will ignore failures from this |
|
2024 |
logger.Error("net setup failed, tearing down local device") |
|
2025 |
self._ShutdownAll(minor) |
|
2026 |
return False |
|
2027 |
self._SetFromMinor(minor) |
|
2028 |
return True |
|
2029 |
|
|
2030 |
@classmethod |
|
2031 |
def _ShutdownAll(cls, minor): |
|
2032 |
"""Deactivate the device. |
|
2033 |
|
|
2034 |
This will, of course, fail if the device is in use. |
|
2035 |
|
|
2036 |
""" |
|
2037 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"]) |
|
2038 |
if result.failed: |
|
2039 |
logger.Error("Can't shutdown drbd device: %s" % result.output) |
|
2040 |
return not result.failed |
|
2041 |
|
|
2042 |
def Shutdown(self): |
|
2043 |
"""Shutdown the DRBD device. |
|
2044 |
|
|
2045 |
""" |
|
2046 |
if self.minor is None and not self.Attach(): |
|
2047 |
logger.Info("DRBD device not attached to a device during Shutdown") |
|
2048 |
return True |
|
2049 |
if not self._ShutdownAll(self.minor): |
|
2050 |
return False |
|
2051 |
self.minor = None |
|
2052 |
self.dev_path = None |
|
2053 |
return True |
|
2054 |
|
|
2055 |
def Remove(self): |
|
2056 |
"""Stub remove for DRBD devices. |
|
2057 |
|
|
2058 |
""" |
|
2059 |
return self.Shutdown() |
|
2060 |
|
|
2061 |
@classmethod |
|
2062 |
def Create(cls, unique_id, children, size): |
|
2063 |
"""Create a new DRBD8 device. |
|
2064 |
|
|
2065 |
Since DRBD devices are not created per se, just assembled, this |
|
2066 |
function only initializes the metadata. |
|
2067 |
|
|
2068 |
""" |
|
2069 |
if len(children) != 2: |
|
2070 |
raise errors.ProgrammerError("Invalid setup for the drbd device") |
|
2071 |
meta = children[1] |
|
2072 |
meta.Assemble() |
|
2073 |
if not meta.Attach(): |
|
2074 |
raise errors.BlockDeviceError("Can't attach to meta device") |
|
2075 |
if not cls._CheckMetaSize(meta.dev_path): |
|
2076 |
raise errors.BlockDeviceError("Invalid meta device size") |
|
2077 |
cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path) |
|
2078 |
if not cls._IsValidMeta(meta.dev_path): |
|
2079 |
raise errors.BlockDeviceError("Cannot initalize meta device") |
|
2080 |
return cls(unique_id, children) |
|
2081 |
|
|
1552 | 2082 |
|
1553 | 2083 |
DEV_MAP = { |
1554 | 2084 |
constants.LD_LV: LogicalVolume, |
Also available in: Unified diff