Revision be3a4b14
b/doc/design-query2.rst | ||
---|---|---|
100 | 100 |
Jobs |
101 | 101 |
``lock`` |
102 | 102 |
Locks |
103 |
``os`` |
|
104 |
Operating systems |
|
103 | 105 |
|
104 | 106 |
.. _data-query: |
105 | 107 |
|
b/lib/cmdlib.py | ||
---|---|---|
3410 | 3410 |
raise errors.OpExecError("Check of out-of-band payload failed due to %s" % |
3411 | 3411 |
utils.CommaJoin(errs)) |
3412 | 3412 |
|
3413 |
class _OsQuery(_QueryBase): |
|
3414 |
FIELDS = query.OS_FIELDS |
|
3413 | 3415 |
|
3414 |
|
|
3415 |
class LUOsDiagnose(NoHooksLU): |
|
3416 |
"""Logical unit for OS diagnose/query. |
|
3417 |
|
|
3418 |
""" |
|
3419 |
REQ_BGL = False |
|
3420 |
_HID = "hidden" |
|
3421 |
_BLK = "blacklisted" |
|
3422 |
_VLD = "valid" |
|
3423 |
_FIELDS_STATIC = utils.FieldSet() |
|
3424 |
_FIELDS_DYNAMIC = utils.FieldSet("name", _VLD, "node_status", "variants", |
|
3425 |
"parameters", "api_versions", _HID, _BLK) |
|
3426 |
|
|
3427 |
def CheckArguments(self): |
|
3428 |
if self.op.names: |
|
3429 |
raise errors.OpPrereqError("Selective OS query not supported", |
|
3430 |
errors.ECODE_INVAL) |
|
3431 |
|
|
3432 |
_CheckOutputFields(static=self._FIELDS_STATIC, |
|
3433 |
dynamic=self._FIELDS_DYNAMIC, |
|
3434 |
selected=self.op.output_fields) |
|
3435 |
|
|
3436 |
def ExpandNames(self): |
|
3437 |
# Lock all nodes, in shared mode |
|
3416 |
def ExpandNames(self, lu): |
|
3417 |
# Lock all nodes in shared mode |
|
3438 | 3418 |
# Temporary removal of locks, should be reverted later |
3439 | 3419 |
# TODO: reintroduce locks when they are lighter-weight |
3440 |
self.needed_locks = {}
|
|
3420 |
lu.needed_locks = {}
|
|
3441 | 3421 |
#self.share_locks[locking.LEVEL_NODE] = 1 |
3442 | 3422 |
#self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET |
3443 | 3423 |
|
3424 |
# The following variables interact with _QueryBase._GetNames |
|
3425 |
if self.names: |
|
3426 |
self.wanted = self.names |
|
3427 |
else: |
|
3428 |
self.wanted = locking.ALL_SET |
|
3429 |
|
|
3430 |
self.do_locking = self.use_locking |
|
3431 |
|
|
3432 |
def DeclareLocks(self, lu, level): |
|
3433 |
pass |
|
3434 |
|
|
3444 | 3435 |
@staticmethod |
3445 | 3436 |
def _DiagnoseByOS(rlist): |
3446 | 3437 |
"""Remaps a per-node return list into an a per-os per-node dictionary |
... | ... | |
3481 | 3472 |
variants, params, api_versions)) |
3482 | 3473 |
return all_os |
3483 | 3474 |
|
3484 |
def Exec(self, feedback_fn):
|
|
3485 |
"""Compute the list of OSes.
|
|
3475 |
def _GetQueryData(self, lu):
|
|
3476 |
"""Computes the list of nodes and their attributes.
|
|
3486 | 3477 |
|
3487 | 3478 |
""" |
3479 |
# Locking is not used |
|
3480 |
assert not (lu.acquired_locks or self.do_locking or self.use_locking) |
|
3481 |
|
|
3482 |
# Used further down |
|
3483 |
assert "valid" in self.FIELDS |
|
3484 |
assert "hidden" in self.FIELDS |
|
3485 |
assert "blacklisted" in self.FIELDS |
|
3486 |
|
|
3488 | 3487 |
valid_nodes = [node.name |
3489 |
for node in self.cfg.GetAllNodesInfo().values()
|
|
3488 |
for node in lu.cfg.GetAllNodesInfo().values()
|
|
3490 | 3489 |
if not node.offline and node.vm_capable] |
3491 |
node_data = self.rpc.call_os_diagnose(valid_nodes) |
|
3492 |
pol = self._DiagnoseByOS(node_data) |
|
3493 |
output = [] |
|
3494 |
cluster = self.cfg.GetClusterInfo() |
|
3490 |
pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes)) |
|
3491 |
cluster = lu.cfg.GetClusterInfo() |
|
3492 |
|
|
3493 |
# Build list of used field names |
|
3494 |
fields = [fdef.name for fdef in self.query.GetFields()] |
|
3495 |
|
|
3496 |
data = {} |
|
3497 |
|
|
3498 |
for (os_name, os_data) in pol.items(): |
|
3499 |
info = query.OsInfo(name=os_name, valid=True, node_status=os_data, |
|
3500 |
hidden=(os_name in cluster.hidden_os), |
|
3501 |
blacklisted=(os_name in cluster.blacklisted_os)) |
|
3502 |
|
|
3503 |
variants = set() |
|
3504 |
parameters = set() |
|
3505 |
api_versions = set() |
|
3495 | 3506 |
|
3496 |
for os_name in utils.NiceSort(pol.keys()): |
|
3497 |
os_data = pol[os_name] |
|
3498 |
row = [] |
|
3499 |
valid = True |
|
3500 |
(variants, params, api_versions) = null_state = (set(), set(), set()) |
|
3501 | 3507 |
for idx, osl in enumerate(os_data.values()): |
3502 |
valid = bool(valid and osl and osl[0][1]) |
|
3503 |
if not valid: |
|
3504 |
(variants, params, api_versions) = null_state |
|
3508 |
info.valid = bool(info.valid and osl and osl[0][1]) |
|
3509 |
if not info.valid: |
|
3505 | 3510 |
break |
3506 |
node_variants, node_params, node_api = osl[0][3:6] |
|
3507 |
if idx == 0: # first entry |
|
3508 |
variants = set(node_variants) |
|
3509 |
params = set(node_params) |
|
3510 |
api_versions = set(node_api) |
|
3511 |
else: # keep consistency |
|
3511 |
|
|
3512 |
(node_variants, node_params, node_api) = osl[0][3:6] |
|
3513 |
if idx == 0: |
|
3514 |
# First entry |
|
3515 |
variants.update(node_variants) |
|
3516 |
parameters.update(node_params) |
|
3517 |
api_versions.update(node_api) |
|
3518 |
else: |
|
3519 |
# Filter out inconsistent values |
|
3512 | 3520 |
variants.intersection_update(node_variants) |
3513 |
params.intersection_update(node_params) |
|
3521 |
parameters.intersection_update(node_params)
|
|
3514 | 3522 |
api_versions.intersection_update(node_api) |
3515 | 3523 |
|
3516 |
is_hid = os_name in cluster.hidden_os |
|
3517 |
is_blk = os_name in cluster.blacklisted_os |
|
3518 |
if ((self._HID not in self.op.output_fields and is_hid) or |
|
3519 |
(self._BLK not in self.op.output_fields and is_blk) or |
|
3520 |
(self._VLD not in self.op.output_fields and not valid)): |
|
3524 |
info.variants = list(variants) |
|
3525 |
info.parameters = list(parameters) |
|
3526 |
info.api_versions = list(api_versions) |
|
3527 |
|
|
3528 |
# TODO: Move this to filters provided by the client |
|
3529 |
if (("hidden" not in fields and info.hidden) or |
|
3530 |
("blacklisted" not in fields and info.blacklisted) or |
|
3531 |
("valid" not in fields and not info.valid)): |
|
3521 | 3532 |
continue |
3522 | 3533 |
|
3523 |
for field in self.op.output_fields: |
|
3524 |
if field == "name": |
|
3525 |
val = os_name |
|
3526 |
elif field == self._VLD: |
|
3527 |
val = valid |
|
3528 |
elif field == "node_status": |
|
3529 |
# this is just a copy of the dict |
|
3530 |
val = {} |
|
3531 |
for node_name, nos_list in os_data.items(): |
|
3532 |
val[node_name] = nos_list |
|
3533 |
elif field == "variants": |
|
3534 |
val = utils.NiceSort(list(variants)) |
|
3535 |
elif field == "parameters": |
|
3536 |
val = list(params) |
|
3537 |
elif field == "api_versions": |
|
3538 |
val = list(api_versions) |
|
3539 |
elif field == self._HID: |
|
3540 |
val = is_hid |
|
3541 |
elif field == self._BLK: |
|
3542 |
val = is_blk |
|
3543 |
else: |
|
3544 |
raise errors.ParameterError(field) |
|
3545 |
row.append(val) |
|
3546 |
output.append(row) |
|
3534 |
data[os_name] = info |
|
3547 | 3535 |
|
3548 |
return output |
|
3536 |
# Prepare data in requested order |
|
3537 |
return [data[name] for name in self._GetNames(lu, pol.keys(), None) |
|
3538 |
if name in data] |
|
3539 |
|
|
3540 |
|
|
3541 |
class LUOsDiagnose(NoHooksLU): |
|
3542 |
"""Logical unit for OS diagnose/query. |
|
3543 |
|
|
3544 |
""" |
|
3545 |
REQ_BGL = False |
|
3546 |
|
|
3547 |
def CheckArguments(self): |
|
3548 |
self.oq = _OsQuery(qlang.MakeSimpleFilter("name", self.op.names), |
|
3549 |
self.op.output_fields, False) |
|
3550 |
|
|
3551 |
def ExpandNames(self): |
|
3552 |
self.oq.ExpandNames(self) |
|
3553 |
|
|
3554 |
def Exec(self, feedback_fn): |
|
3555 |
return self.oq.OldStyleQuery(self) |
|
3549 | 3556 |
|
3550 | 3557 |
|
3551 | 3558 |
class LUNodeRemove(LogicalUnit): |
... | ... | |
11643 | 11650 |
constants.QR_INSTANCE: _InstanceQuery, |
11644 | 11651 |
constants.QR_NODE: _NodeQuery, |
11645 | 11652 |
constants.QR_GROUP: _GroupQuery, |
11653 |
constants.QR_OS: _OsQuery, |
|
11646 | 11654 |
} |
11647 | 11655 |
|
11656 |
assert set(_QUERY_IMPL.keys()) == constants.QR_OP_QUERY |
|
11657 |
|
|
11648 | 11658 |
|
11649 | 11659 |
def _GetQueryImplementation(name): |
11650 | 11660 |
"""Returns the implemtnation for a query type. |
b/lib/constants.py | ||
---|---|---|
1027 | 1027 |
QR_NODE = "node" |
1028 | 1028 |
QR_LOCK = "lock" |
1029 | 1029 |
QR_GROUP = "group" |
1030 |
QR_OS = "os" |
|
1030 | 1031 |
|
1031 | 1032 |
#: List of resources which can be queried using L{opcodes.OpQuery} |
1032 |
QR_OP_QUERY = frozenset([QR_INSTANCE, QR_NODE, QR_GROUP]) |
|
1033 |
QR_OP_QUERY = frozenset([QR_INSTANCE, QR_NODE, QR_GROUP, QR_OS])
|
|
1033 | 1034 |
|
1034 | 1035 |
#: List of resources which can be queried using Local UniX Interface |
1035 | 1036 |
QR_OP_LUXI = QR_OP_QUERY.union([ |
b/lib/query.py | ||
---|---|---|
1888 | 1888 |
return _PrepareFieldList(fields, []) |
1889 | 1889 |
|
1890 | 1890 |
|
1891 |
class OsInfo(objects.ConfigObject): |
|
1892 |
__slots__ = [ |
|
1893 |
"name", |
|
1894 |
"valid", |
|
1895 |
"hidden", |
|
1896 |
"blacklisted", |
|
1897 |
"variants", |
|
1898 |
"api_versions", |
|
1899 |
"parameters", |
|
1900 |
"node_status", |
|
1901 |
] |
|
1902 |
|
|
1903 |
|
|
1904 |
def _BuildOsFields(): |
|
1905 |
"""Builds list of fields for operating system queries. |
|
1906 |
|
|
1907 |
""" |
|
1908 |
fields = [ |
|
1909 |
(_MakeField("name", "Name", QFT_TEXT, "Operating system name"), |
|
1910 |
None, 0, _GetItemAttr("name")), |
|
1911 |
(_MakeField("valid", "Valid", QFT_BOOL, |
|
1912 |
"Whether operating system definition is valid"), |
|
1913 |
None, 0, _GetItemAttr("valid")), |
|
1914 |
(_MakeField("hidden", "Hidden", QFT_BOOL, |
|
1915 |
"Whether operating system is hidden"), |
|
1916 |
None, 0, _GetItemAttr("hidden")), |
|
1917 |
(_MakeField("blacklisted", "Blacklisted", QFT_BOOL, |
|
1918 |
"Whether operating system is blacklisted"), |
|
1919 |
None, 0, _GetItemAttr("blacklisted")), |
|
1920 |
(_MakeField("variants", "Variants", QFT_OTHER, |
|
1921 |
"Operating system variants"), |
|
1922 |
None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))), |
|
1923 |
(_MakeField("api_versions", "ApiVersions", QFT_OTHER, |
|
1924 |
"Operating system API versions"), |
|
1925 |
None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))), |
|
1926 |
(_MakeField("parameters", "Parameters", QFT_OTHER, |
|
1927 |
"Operating system parameters"), |
|
1928 |
None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("parameters"))), |
|
1929 |
(_MakeField("node_status", "NodeStatus", QFT_OTHER, |
|
1930 |
"Status from node"), |
|
1931 |
None, 0, _GetItemAttr("node_status")), |
|
1932 |
] |
|
1933 |
|
|
1934 |
return _PrepareFieldList(fields, []) |
|
1935 |
|
|
1936 |
|
|
1891 | 1937 |
#: Fields available for node queries |
1892 | 1938 |
NODE_FIELDS = _BuildNodeFields() |
1893 | 1939 |
|
... | ... | |
1900 | 1946 |
#: Fields available for node group queries |
1901 | 1947 |
GROUP_FIELDS = _BuildGroupFields() |
1902 | 1948 |
|
1949 |
#: Fields available for operating system queries |
|
1950 |
OS_FIELDS = _BuildOsFields() |
|
1951 |
|
|
1903 | 1952 |
#: All available resources |
1904 | 1953 |
ALL_FIELDS = { |
1905 | 1954 |
constants.QR_INSTANCE: INSTANCE_FIELDS, |
1906 | 1955 |
constants.QR_NODE: NODE_FIELDS, |
1907 | 1956 |
constants.QR_LOCK: LOCK_FIELDS, |
1908 | 1957 |
constants.QR_GROUP: GROUP_FIELDS, |
1958 |
constants.QR_OS: OS_FIELDS, |
|
1909 | 1959 |
} |
1910 | 1960 |
|
1911 | 1961 |
#: All available field lists |
b/test/ganeti.query_unittest.py | ||
---|---|---|
941 | 941 |
]) |
942 | 942 |
|
943 | 943 |
|
944 |
class TestOsQuery(unittest.TestCase): |
|
945 |
def _Create(self, selected): |
|
946 |
return query.Query(query.OS_FIELDS, selected) |
|
947 |
|
|
948 |
def test(self): |
|
949 |
variants = ["v00", "plain", "v3", "var0", "v33", "v20"] |
|
950 |
api_versions = [10, 0, 15, 5] |
|
951 |
parameters = ["zpar3", "apar9"] |
|
952 |
|
|
953 |
assert variants != sorted(variants) and variants != utils.NiceSort(variants) |
|
954 |
assert (api_versions != sorted(api_versions) and |
|
955 |
api_versions != utils.NiceSort(variants)) |
|
956 |
assert (parameters != sorted(parameters) and |
|
957 |
parameters != utils.NiceSort(parameters)) |
|
958 |
|
|
959 |
data = [ |
|
960 |
query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False, |
|
961 |
variants=set(), api_versions=set(), parameters=set(), |
|
962 |
node_status={ "some": "status", }), |
|
963 |
query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True, |
|
964 |
variants=set(variants), |
|
965 |
api_versions=set(api_versions), |
|
966 |
parameters=set(parameters), |
|
967 |
node_status={ "some": "other", "status": None, }), |
|
968 |
] |
|
969 |
|
|
970 |
|
|
971 |
q = self._Create(["name", "valid", "hidden", "blacklisted", "variants", |
|
972 |
"api_versions", "parameters", "node_status"]) |
|
973 |
self.assertEqual(q.RequestedData(), set([])) |
|
974 |
self.assertEqual(q.Query(data), |
|
975 |
[[(constants.RS_NORMAL, "debian"), |
|
976 |
(constants.RS_NORMAL, False), |
|
977 |
(constants.RS_NORMAL, False), |
|
978 |
(constants.RS_NORMAL, False), |
|
979 |
(constants.RS_NORMAL, []), |
|
980 |
(constants.RS_NORMAL, []), |
|
981 |
(constants.RS_NORMAL, []), |
|
982 |
(constants.RS_NORMAL, {"some": "status"})], |
|
983 |
[(constants.RS_NORMAL, "dos"), |
|
984 |
(constants.RS_NORMAL, True), |
|
985 |
(constants.RS_NORMAL, False), |
|
986 |
(constants.RS_NORMAL, True), |
|
987 |
(constants.RS_NORMAL, |
|
988 |
["plain", "v00", "v3", "v20", "v33", "var0"]), |
|
989 |
(constants.RS_NORMAL, [0, 5, 10, 15]), |
|
990 |
(constants.RS_NORMAL, ["apar9", "zpar3"]), |
|
991 |
(constants.RS_NORMAL, |
|
992 |
{ "some": "other", "status": None, }) |
|
993 |
]]) |
|
994 |
|
|
995 |
|
|
944 | 996 |
class TestQueryFields(unittest.TestCase): |
945 | 997 |
def testAllFields(self): |
946 | 998 |
for fielddefs in query.ALL_FIELD_LISTS: |
Also available in: Unified diff