Revision 255dcebd
b/daemons/ganeti-noded | ||
---|---|---|
608 | 608 |
"""Query detailed information about existing OSes. |
609 | 609 |
|
610 | 610 |
""" |
611 |
return True, [os_obj.ToDict() for os_obj in backend.DiagnoseOS()]
|
|
611 |
return backend.DiagnoseOS()
|
|
612 | 612 |
|
613 | 613 |
@staticmethod |
614 | 614 |
def perspective_os_get(params): |
... | ... | |
616 | 616 |
|
617 | 617 |
""" |
618 | 618 |
name = params[0] |
619 |
try: |
|
620 |
os_obj = backend.OSFromDisk(name) |
|
621 |
except errors.InvalidOS, err: |
|
622 |
os_obj = objects.OS.FromInvalidOS(err) |
|
623 |
return os_obj.ToDict() |
|
619 |
os_obj = backend.OSFromDisk(name) |
|
620 |
return True, os_obj.ToDict() |
|
624 | 621 |
|
625 | 622 |
# hooks ----------------------- |
626 | 623 |
|
b/lib/backend.py | ||
---|---|---|
718 | 718 |
@return: the success of the operation |
719 | 719 |
|
720 | 720 |
""" |
721 |
try: |
|
722 |
inst_os = OSFromDisk(instance.os) |
|
723 |
except errors.InvalidOS, err: |
|
724 |
os_name, os_dir, os_err = err.args |
|
725 |
if os_dir is None: |
|
726 |
return (False, "Can't find OS '%s': %s" % (os_name, os_err)) |
|
727 |
else: |
|
728 |
return (False, "Error parsing OS '%s' in directory %s: %s" % |
|
729 |
(os_name, os_dir, os_err)) |
|
721 |
inst_os = OSFromDisk(instance.os) |
|
722 |
|
|
730 | 723 |
|
731 | 724 |
create_env = OSEnvironment(instance) |
732 | 725 |
if reinstall: |
... | ... | |
1530 | 1523 |
try: |
1531 | 1524 |
st = os.stat(api_file) |
1532 | 1525 |
except EnvironmentError, err: |
1533 |
raise errors.InvalidOS(name, os_dir, "'ganeti_api_version' file not"
|
|
1534 |
" found (%s)" % _ErrnoOrStr(err))
|
|
1526 |
return False, ("Required file 'ganeti_api_version' file not"
|
|
1527 |
" found under path %s: %s" % (os_dir, _ErrnoOrStr(err)))
|
|
1535 | 1528 |
|
1536 | 1529 |
if not stat.S_ISREG(stat.S_IFMT(st.st_mode)): |
1537 |
raise errors.InvalidOS(name, os_dir, "'ganeti_api_version' file is not"
|
|
1538 |
" a regular file")
|
|
1530 |
return False, ("File 'ganeti_api_version' file at %s is not"
|
|
1531 |
" a regular file" % os_dir)
|
|
1539 | 1532 |
|
1540 | 1533 |
try: |
1541 | 1534 |
f = open(api_file) |
... | ... | |
1544 | 1537 |
finally: |
1545 | 1538 |
f.close() |
1546 | 1539 |
except EnvironmentError, err: |
1547 |
raise errors.InvalidOS(name, os_dir, "error while reading the"
|
|
1548 |
" API version (%s)" % _ErrnoOrStr(err))
|
|
1540 |
return False, ("Error while reading the API version file at %s: %s" %
|
|
1541 |
(api_file, _ErrnoOrStr(err)))
|
|
1549 | 1542 |
|
1550 | 1543 |
api_versions = [version.strip() for version in api_versions] |
1551 | 1544 |
try: |
1552 | 1545 |
api_versions = [int(version) for version in api_versions] |
1553 | 1546 |
except (TypeError, ValueError), err: |
1554 |
raise errors.InvalidOS(name, os_dir,
|
|
1555 |
"API version is not integer (%s)" % str(err))
|
|
1547 |
return False, ("API version(s) can't be converted to integer: %s" %
|
|
1548 |
str(err)) |
|
1556 | 1549 |
|
1557 |
return api_versions |
|
1550 |
return True, api_versions
|
|
1558 | 1551 |
|
1559 | 1552 |
|
1560 | 1553 |
def DiagnoseOS(top_dirs=None): |
... | ... | |
1565 | 1558 |
search (if not given defaults to |
1566 | 1559 |
L{constants.OS_SEARCH_PATH}) |
1567 | 1560 |
@rtype: list of L{objects.OS} |
1568 |
@return: an OS object for each name in all the given |
|
1569 |
directories |
|
1561 |
@return: a list of tuples (name, path, status, diagnose) |
|
1562 |
for all (potential) OSes under all search paths, where: |
|
1563 |
- name is the (potential) OS name |
|
1564 |
- path is the full path to the OS |
|
1565 |
- status True/False is the validity of the OS |
|
1566 |
- diagnose is the error message for an invalid OS, otherwise empty |
|
1570 | 1567 |
|
1571 | 1568 |
""" |
1572 | 1569 |
if top_dirs is None: |
... | ... | |
1581 | 1578 |
logging.exception("Can't list the OS directory %s", dir_name) |
1582 | 1579 |
break |
1583 | 1580 |
for name in f_names: |
1584 |
try: |
|
1585 |
os_inst = OSFromDisk(name, base_dir=dir_name) |
|
1586 |
result.append(os_inst) |
|
1587 |
except errors.InvalidOS, err: |
|
1588 |
result.append(objects.OS.FromInvalidOS(err)) |
|
1581 |
os_path = os.path.sep.join([dir_name, name]) |
|
1582 |
status, os_inst = _TryOSFromDisk(name, base_dir=dir_name) |
|
1583 |
if status: |
|
1584 |
diagnose = "" |
|
1585 |
else: |
|
1586 |
diagnose = os_inst |
|
1587 |
result.append((name, os_path, status, diagnose)) |
|
1589 | 1588 |
|
1590 |
return result |
|
1589 |
return True, result
|
|
1591 | 1590 |
|
1592 | 1591 |
|
1593 |
def OSFromDisk(name, base_dir=None): |
|
1592 |
def _TryOSFromDisk(name, base_dir=None):
|
|
1594 | 1593 |
"""Create an OS instance from disk. |
1595 | 1594 |
|
1596 | 1595 |
This function will return an OS instance if the given name is a |
... | ... | |
1600 | 1599 |
@type base_dir: string |
1601 | 1600 |
@keyword base_dir: Base directory containing OS installations. |
1602 | 1601 |
Defaults to a search in all the OS_SEARCH_PATH dirs. |
1603 |
@rtype: L{objects.OS}
|
|
1604 |
@return: the OS instance if we find a valid one
|
|
1605 |
@raise errors.InvalidOS: if we don't find a valid OS
|
|
1602 |
@rtype: tuple
|
|
1603 |
@return: success and either the OS instance if we find a valid one,
|
|
1604 |
or error message
|
|
1606 | 1605 |
|
1607 | 1606 |
""" |
1608 | 1607 |
if base_dir is None: |
1609 | 1608 |
os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir) |
1610 | 1609 |
if os_dir is None: |
1611 |
raise errors.InvalidOS(name, None, "OS dir not found in search path")
|
|
1610 |
return False, "Directory for OS %s not found in search path" % name
|
|
1612 | 1611 |
else: |
1613 | 1612 |
os_dir = os.path.sep.join([base_dir, name]) |
1614 | 1613 |
|
1615 |
api_versions = _OSOndiskVersion(name, os_dir) |
|
1614 |
status, api_versions = _OSOndiskVersion(name, os_dir) |
|
1615 |
if not status: |
|
1616 |
# push the error up |
|
1617 |
return status, api_versions |
|
1616 | 1618 |
|
1617 | 1619 |
if constants.OS_API_VERSION not in api_versions: |
1618 |
raise errors.InvalidOS(name, os_dir, "API version mismatch" |
|
1619 |
" (found %s want %s)" |
|
1620 |
% (api_versions, constants.OS_API_VERSION)) |
|
1620 |
return False, ("API version mismatch for path '%s': found %s, want %s." % |
|
1621 |
(os_dir, api_versions, constants.OS_API_VERSION)) |
|
1621 | 1622 |
|
1622 | 1623 |
# OS Scripts dictionary, we will populate it with the actual script names |
1623 | 1624 |
os_scripts = dict.fromkeys(constants.OS_SCRIPTS) |
... | ... | |
1628 | 1629 |
try: |
1629 | 1630 |
st = os.stat(os_scripts[script]) |
1630 | 1631 |
except EnvironmentError, err: |
1631 |
raise errors.InvalidOS(name, os_dir, "'%s' script missing (%s)" %
|
|
1632 |
(script, _ErrnoOrStr(err)))
|
|
1632 |
return False, ("Script '%s' under path '%s' is missing (%s)" %
|
|
1633 |
(script, os_dir, _ErrnoOrStr(err)))
|
|
1633 | 1634 |
|
1634 | 1635 |
if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR: |
1635 |
raise errors.InvalidOS(name, os_dir, "'%s' script not executable" %
|
|
1636 |
script)
|
|
1636 |
return False, ("Script '%s' under path '%s' is not executable" %
|
|
1637 |
(script, os_dir))
|
|
1637 | 1638 |
|
1638 | 1639 |
if not stat.S_ISREG(stat.S_IFMT(st.st_mode)): |
1639 |
raise errors.InvalidOS(name, os_dir, "'%s' is not a regular file" % |
|
1640 |
script) |
|
1640 |
return False, ("Script '%s' under path '%s' is not a regular file" % |
|
1641 |
(script, os_dir)) |
|
1642 |
|
|
1643 |
os_obj = objects.OS(name=name, path=os_dir, status=constants.OS_VALID_STATUS, |
|
1644 |
create_script=os_scripts[constants.OS_SCRIPT_CREATE], |
|
1645 |
export_script=os_scripts[constants.OS_SCRIPT_EXPORT], |
|
1646 |
import_script=os_scripts[constants.OS_SCRIPT_IMPORT], |
|
1647 |
rename_script=os_scripts[constants.OS_SCRIPT_RENAME], |
|
1648 |
api_versions=api_versions) |
|
1649 |
return True, os_obj |
|
1650 |
|
|
1651 |
|
|
1652 |
def OSFromDisk(name, base_dir=None): |
|
1653 |
"""Create an OS instance from disk. |
|
1654 |
|
|
1655 |
This function will return an OS instance if the given name is a |
|
1656 |
valid OS name. Otherwise, it will raise an appropriate |
|
1657 |
L{RPCFail} exception, detailing why this is not a valid OS. |
|
1658 |
|
|
1659 |
This is just a wrapper over L{_TryOSFromDisk}, which doesn't raise |
|
1660 |
an exception but returns true/false status data. |
|
1661 |
|
|
1662 |
@type base_dir: string |
|
1663 |
@keyword base_dir: Base directory containing OS installations. |
|
1664 |
Defaults to a search in all the OS_SEARCH_PATH dirs. |
|
1665 |
@rtype: L{objects.OS} |
|
1666 |
@return: the OS instance if we find a valid one |
|
1667 |
@raise RPCFail: if we don't find a valid OS |
|
1668 |
|
|
1669 |
""" |
|
1670 |
status, payload = _TryOSFromDisk(name, base_dir) |
|
1671 |
|
|
1672 |
if not status: |
|
1673 |
_Fail(payload) |
|
1641 | 1674 |
|
1675 |
return payload |
|
1642 | 1676 |
|
1643 |
return objects.OS(name=name, path=os_dir, status=constants.OS_VALID_STATUS, |
|
1644 |
create_script=os_scripts[constants.OS_SCRIPT_CREATE], |
|
1645 |
export_script=os_scripts[constants.OS_SCRIPT_EXPORT], |
|
1646 |
import_script=os_scripts[constants.OS_SCRIPT_IMPORT], |
|
1647 |
rename_script=os_scripts[constants.OS_SCRIPT_RENAME], |
|
1648 |
api_versions=api_versions) |
|
1649 | 1677 |
|
1650 | 1678 |
def OSEnvironment(instance, debug=0): |
1651 | 1679 |
"""Calculate the environment for an os script. |
b/lib/cmdlib.py | ||
---|---|---|
1801 | 1801 |
|
1802 | 1802 |
@rtype: dict |
1803 | 1803 |
@return: a dictionary with osnames as keys and as value another map, with |
1804 |
nodes as keys and list of OS objects as values, eg::
|
|
1804 |
nodes as keys and tuples of (path, status, diagnose) as values, eg::
|
|
1805 | 1805 |
|
1806 |
{"debian-etch": {"node1": [<object>,...], |
|
1807 |
"node2": [<object>,]} |
|
1806 |
{"debian-etch": {"node1": [(/usr/lib/..., True, ""), |
|
1807 |
(/srv/..., False, "invalid api")], |
|
1808 |
"node2": [(/srv/..., True, "")]} |
|
1808 | 1809 |
} |
1809 | 1810 |
|
1810 | 1811 |
""" |
... | ... | |
1817 | 1818 |
for node_name, nr in rlist.items(): |
1818 | 1819 |
if nr.RemoteFailMsg() or not nr.payload: |
1819 | 1820 |
continue |
1820 |
for os_serialized in nr.payload: |
|
1821 |
os_obj = objects.OS.FromDict(os_serialized) |
|
1822 |
if os_obj.name not in all_os: |
|
1821 |
for name, path, status, diagnose in nr.payload: |
|
1822 |
if name not in all_os: |
|
1823 | 1823 |
# build a list of nodes for this os containing empty lists |
1824 | 1824 |
# for each node in node_list |
1825 |
all_os[os_obj.name] = {}
|
|
1825 |
all_os[name] = {} |
|
1826 | 1826 |
for nname in good_nodes: |
1827 |
all_os[os_obj.name][nname] = []
|
|
1828 |
all_os[os_obj.name][node_name].append(os_obj)
|
|
1827 |
all_os[name][nname] = [] |
|
1828 |
all_os[name][node_name].append((path, status, diagnose))
|
|
1829 | 1829 |
return all_os |
1830 | 1830 |
|
1831 | 1831 |
def Exec(self, feedback_fn): |
... | ... | |
1842 | 1842 |
if field == "name": |
1843 | 1843 |
val = os_name |
1844 | 1844 |
elif field == "valid": |
1845 |
val = utils.all([osl and osl[0] for osl in os_data.values()]) |
|
1845 |
val = utils.all([osl and osl[0][1] for osl in os_data.values()])
|
|
1846 | 1846 |
elif field == "node_status": |
1847 |
# this is just a copy of the dict |
|
1847 | 1848 |
val = {} |
1848 |
for node_name, nos_list in os_data.iteritems():
|
|
1849 |
val[node_name] = [(v.status, v.path) for v in nos_list]
|
|
1849 |
for node_name, nos_list in os_data.items(): |
|
1850 |
val[node_name] = nos_list
|
|
1850 | 1851 |
else: |
1851 | 1852 |
raise errors.ParameterError(field) |
1852 | 1853 |
row.append(val) |
... | ... | |
3156 | 3157 |
raise errors.OpPrereqError("Primary node '%s' is unknown" % |
3157 | 3158 |
self.op.pnode) |
3158 | 3159 |
result = self.rpc.call_os_get(pnode.name, self.op.os_type) |
3159 |
result.Raise()
|
|
3160 |
if not isinstance(result.data, objects.OS):
|
|
3160 |
msg = result.RemoteFailMsg()
|
|
3161 |
if msg:
|
|
3161 | 3162 |
raise errors.OpPrereqError("OS '%s' not in supported OS list for" |
3162 |
" primary node" % self.op.os_type) |
|
3163 |
" primary node %s: %s" % |
|
3164 |
(self.op.os_type, pnode.pname, msg)) |
|
3163 | 3165 |
|
3164 | 3166 |
self.instance = instance |
3165 | 3167 |
|
... | ... | |
4843 | 4845 |
|
4844 | 4846 |
# os verification |
4845 | 4847 |
result = self.rpc.call_os_get(pnode.name, self.op.os_type) |
4846 |
result.Raise()
|
|
4847 |
if not isinstance(result.data, objects.OS):
|
|
4848 |
msg = result.RemoteFailMsg()
|
|
4849 |
if msg:
|
|
4848 | 4850 |
raise errors.OpPrereqError("OS '%s' not in supported os list for" |
4849 |
" primary node" % self.op.os_type) |
|
4851 |
" primary node %s: %s" % |
|
4852 |
(self.op.os_type, pnode.name, msg)) |
|
4850 | 4853 |
|
4851 | 4854 |
_CheckNicsBridgesExist(self, self.nics, self.pnode.name) |
4852 | 4855 |
|
b/scripts/gnt-os | ||
---|---|---|
64 | 64 |
return 0 |
65 | 65 |
|
66 | 66 |
|
67 |
def _OsStatus(status, diagnose): |
|
68 |
"""Beautifier function for OS status. |
|
69 |
|
|
70 |
@type status: boolean |
|
71 |
@param status: is the OS valid |
|
72 |
@type diagnose: string |
|
73 |
@param diagnose: the error message for invalid OSes |
|
74 |
@rtype: string |
|
75 |
@return: a formatted status |
|
76 |
|
|
77 |
""" |
|
78 |
if status: |
|
79 |
return "valid" |
|
80 |
else: |
|
81 |
return "invalid - %s" % diagnose |
|
82 |
|
|
67 | 83 |
def DiagnoseOS(opts, args): |
68 | 84 |
"""Analyse all OSes on this cluster. |
69 | 85 |
|
... | ... | |
91 | 107 |
for node_name, node_info in node_data.iteritems(): |
92 | 108 |
nodes_hidden[node_name] = [] |
93 | 109 |
if node_info: # at least one entry in the per-node list |
94 |
first_os_status, first_os_path = node_info.pop(0) |
|
95 |
first_os_msg = ("%s (path: %s)" % |
|
96 |
(first_os_status, first_os_path)) |
|
97 |
if first_os_status == constants.OS_VALID_STATUS: |
|
110 |
first_os_path, first_os_status, first_os_msg = node_info.pop(0) |
|
111 |
first_os_msg = ("%s (path: %s)" % (_OsStatus(first_os_status, |
|
112 |
first_os_msg), |
|
113 |
first_os_path)) |
|
114 |
if first_os_status: |
|
98 | 115 |
nodes_valid[node_name] = first_os_msg |
99 | 116 |
else: |
100 | 117 |
nodes_bad[node_name] = first_os_msg |
101 |
for hstatus, hpath in node_info:
|
|
118 |
for hpath, hstatus, hmsg in node_info:
|
|
102 | 119 |
nodes_hidden[node_name].append(" [hidden] path: %s, status: %s" % |
103 |
(hpath, hstatus))
|
|
120 |
(hpath, _OsStatus(hstatus, hmsg)))
|
|
104 | 121 |
else: |
105 | 122 |
nodes_bad[node_name] = "OS not found" |
106 | 123 |
|
Also available in: Unified diff