Revision b0d85178 lib/cmdlib.py
b/lib/cmdlib.py | ||
---|---|---|
1115 | 1115 |
ENODELVM = (TNODE, "ENODELVM") |
1116 | 1116 |
ENODEN1 = (TNODE, "ENODEN1") |
1117 | 1117 |
ENODENET = (TNODE, "ENODENET") |
1118 |
ENODEOS = (TNODE, "ENODEOS") |
|
1118 | 1119 |
ENODEORPHANINSTANCE = (TNODE, "ENODEORPHANINSTANCE") |
1119 | 1120 |
ENODEORPHANLV = (TNODE, "ENODEORPHANLV") |
1120 | 1121 |
ENODERPC = (TNODE, "ENODERPC") |
... | ... | |
1130 | 1131 |
class NodeImage(object): |
1131 | 1132 |
"""A class representing the logical and physical status of a node. |
1132 | 1133 |
|
1134 |
@type name: string |
|
1135 |
@ivar name: the node name to which this object refers |
|
1133 | 1136 |
@ivar volumes: a structure as returned from |
1134 | 1137 |
L{ganeti.backend.GetVolumeList} (runtime) |
1135 | 1138 |
@ivar instances: a list of running instances (runtime) |
... | ... | |
1149 | 1152 |
@ivar hyp_fail: whether the RPC call didn't return the instance list |
1150 | 1153 |
@type ghost: boolean |
1151 | 1154 |
@ivar ghost: whether this is a known node or not (config) |
1155 |
@type os_fail: boolean |
|
1156 |
@ivar os_fail: whether the RPC call didn't return valid OS data |
|
1157 |
@type oslist: list |
|
1158 |
@ivar oslist: list of OSes as diagnosed by DiagnoseOS |
|
1152 | 1159 |
|
1153 | 1160 |
""" |
1154 |
def __init__(self, offline=False): |
|
1161 |
def __init__(self, offline=False, name=None): |
|
1162 |
self.name = name |
|
1155 | 1163 |
self.volumes = {} |
1156 | 1164 |
self.instances = [] |
1157 | 1165 |
self.pinst = [] |
... | ... | |
1164 | 1172 |
self.lvm_fail = False |
1165 | 1173 |
self.hyp_fail = False |
1166 | 1174 |
self.ghost = False |
1175 |
self.os_fail = False |
|
1176 |
self.oslist = {} |
|
1167 | 1177 |
|
1168 | 1178 |
def ExpandNames(self): |
1169 | 1179 |
self.needed_locks = { |
... | ... | |
1574 | 1584 |
_ErrorIf(test, self.ENODEDRBD, node, |
1575 | 1585 |
"unallocated drbd minor %d is in use", minor) |
1576 | 1586 |
|
1587 |
def _UpdateNodeOS(self, ninfo, nresult, nimg): |
|
1588 |
"""Builds the node OS structures. |
|
1589 |
|
|
1590 |
@type ninfo: L{objects.Node} |
|
1591 |
@param ninfo: the node to check |
|
1592 |
@param nresult: the remote results for the node |
|
1593 |
@param nimg: the node image object |
|
1594 |
|
|
1595 |
""" |
|
1596 |
node = ninfo.name |
|
1597 |
_ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 |
|
1598 |
|
|
1599 |
remote_os = nresult.get(constants.NV_OSLIST, None) |
|
1600 |
test = (not isinstance(remote_os, list) or |
|
1601 |
not compat.all(remote_os, |
|
1602 |
lambda v: isinstance(v, list) and len(v) == 7)) |
|
1603 |
|
|
1604 |
_ErrorIf(test, self.ENODEOS, node, |
|
1605 |
"node hasn't returned valid OS data") |
|
1606 |
|
|
1607 |
nimg.os_fail = test |
|
1608 |
|
|
1609 |
if test: |
|
1610 |
return |
|
1611 |
|
|
1612 |
os_dict = {} |
|
1613 |
|
|
1614 |
for (name, os_path, status, diagnose, |
|
1615 |
variants, parameters, api_ver) in nresult[constants.NV_OSLIST]: |
|
1616 |
|
|
1617 |
if name not in os_dict: |
|
1618 |
os_dict[name] = [] |
|
1619 |
|
|
1620 |
# parameters is a list of lists instead of list of tuples due to |
|
1621 |
# JSON lacking a real tuple type, fix it: |
|
1622 |
parameters = [tuple(v) for v in parameters] |
|
1623 |
os_dict[name].append((os_path, status, diagnose, |
|
1624 |
set(variants), set(parameters), set(api_ver))) |
|
1625 |
|
|
1626 |
nimg.oslist = os_dict |
|
1627 |
|
|
1628 |
def _VerifyNodeOS(self, ninfo, nimg, base): |
|
1629 |
"""Verifies the node OS list. |
|
1630 |
|
|
1631 |
@type ninfo: L{objects.Node} |
|
1632 |
@param ninfo: the node to check |
|
1633 |
@param nimg: the node image object |
|
1634 |
@param base: the 'template' node we match against (e.g. from the master) |
|
1635 |
|
|
1636 |
""" |
|
1637 |
node = ninfo.name |
|
1638 |
_ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 |
|
1639 |
|
|
1640 |
assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?" |
|
1641 |
|
|
1642 |
for os_name, os_data in nimg.oslist.items(): |
|
1643 |
assert os_data, "Empty OS status for OS %s?!" % os_name |
|
1644 |
f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0] |
|
1645 |
_ErrorIf(not f_status, self.ENODEOS, node, |
|
1646 |
"Invalid OS %s (located at %s): %s", os_name, f_path, f_diag) |
|
1647 |
_ErrorIf(len(os_data) > 1, self.ENODEOS, node, |
|
1648 |
"OS '%s' has multiple entries (first one shadows the rest): %s", |
|
1649 |
os_name, utils.CommaJoin([v[0] for v in os_data])) |
|
1650 |
# this will catched in backend too |
|
1651 |
_ErrorIf(compat.any(f_api, lambda v: v >= constants.OS_API_V15) |
|
1652 |
and not f_var, self.ENODEOS, node, |
|
1653 |
"OS %s with API at least %d does not declare any variant", |
|
1654 |
os_name, constants.OS_API_V15) |
|
1655 |
# comparisons with the 'base' image |
|
1656 |
test = os_name not in base.oslist |
|
1657 |
_ErrorIf(test, self.ENODEOS, node, |
|
1658 |
"Extra OS %s not present on reference node (%s)", |
|
1659 |
os_name, base.name) |
|
1660 |
if test: |
|
1661 |
continue |
|
1662 |
assert base.oslist[os_name], "Base node has empty OS status?" |
|
1663 |
_, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0] |
|
1664 |
if not b_status: |
|
1665 |
# base OS is invalid, skipping |
|
1666 |
continue |
|
1667 |
for kind, a, b in [("API version", f_api, b_api), |
|
1668 |
("variants list", f_var, b_var), |
|
1669 |
("parameters", f_param, b_param)]: |
|
1670 |
_ErrorIf(a != b, self.ENODEOS, node, |
|
1671 |
"OS %s %s differs from reference node %s: %s vs. %s", |
|
1672 |
kind, os_name, base.name, |
|
1673 |
utils.CommaJoin(a), utils.CommaJoin(a)) |
|
1674 |
|
|
1675 |
# check any missing OSes |
|
1676 |
missing = set(base.oslist.keys()).difference(nimg.oslist.keys()) |
|
1677 |
_ErrorIf(missing, self.ENODEOS, node, |
|
1678 |
"OSes present on reference node %s but missing on this node: %s", |
|
1679 |
base.name, utils.CommaJoin(missing)) |
|
1680 |
|
|
1577 | 1681 |
def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name): |
1578 | 1682 |
"""Verifies and updates the node volume data. |
1579 | 1683 |
|
... | ... | |
1751 | 1855 |
constants.NV_NODESETUP: None, |
1752 | 1856 |
constants.NV_TIME: None, |
1753 | 1857 |
constants.NV_MASTERIP: (master_node, master_ip), |
1858 |
constants.NV_OSLIST: None, |
|
1754 | 1859 |
} |
1755 | 1860 |
|
1756 | 1861 |
if vg_name is not None: |
... | ... | |
1760 | 1865 |
node_verify_param[constants.NV_DRBDLIST] = None |
1761 | 1866 |
|
1762 | 1867 |
# Build our expected cluster state |
1763 |
node_image = dict((node.name, self.NodeImage(offline=node.offline)) |
|
1868 |
node_image = dict((node.name, self.NodeImage(offline=node.offline, |
|
1869 |
name=node.name)) |
|
1764 | 1870 |
for node in nodeinfo) |
1765 | 1871 |
|
1766 | 1872 |
for instance in instancelist: |
... | ... | |
1769 | 1875 |
for nname in inst_config.all_nodes: |
1770 | 1876 |
if nname not in node_image: |
1771 | 1877 |
# ghost node |
1772 |
gnode = self.NodeImage() |
|
1878 |
gnode = self.NodeImage(name=nname)
|
|
1773 | 1879 |
gnode.ghost = True |
1774 | 1880 |
node_image[nname] = gnode |
1775 | 1881 |
|
... | ... | |
1800 | 1906 |
all_drbd_map = self.cfg.ComputeDRBDMap() |
1801 | 1907 |
|
1802 | 1908 |
feedback_fn("* Verifying node status") |
1909 |
|
|
1910 |
refos_img = None |
|
1911 |
|
|
1803 | 1912 |
for node_i in nodeinfo: |
1804 | 1913 |
node = node_i.name |
1805 | 1914 |
nimg = node_image[node] |
... | ... | |
1841 | 1950 |
self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name) |
1842 | 1951 |
self._UpdateNodeInstances(node_i, nresult, nimg) |
1843 | 1952 |
self._UpdateNodeInfo(node_i, nresult, nimg, vg_name) |
1953 |
self._UpdateNodeOS(node_i, nresult, nimg) |
|
1954 |
if not nimg.os_fail: |
|
1955 |
if refos_img is None: |
|
1956 |
refos_img = nimg |
|
1957 |
self._VerifyNodeOS(node_i, nimg, refos_img) |
|
1844 | 1958 |
|
1845 | 1959 |
feedback_fn("* Verifying instance status") |
1846 | 1960 |
for instance in instancelist: |
Also available in: Unified diff