Revision 1be6b00e lib/cmdlib/__init__.py
b/lib/cmdlib/__init__.py | ||
---|---|---|
82 | 82 |
from ganeti.cmdlib.backup import LUBackupQuery, LUBackupPrepare, \ |
83 | 83 |
LUBackupExport, LUBackupRemove |
84 | 84 |
from ganeti.cmdlib.query import LUQuery, LUQueryFields |
85 |
from ganeti.cmdlib.operating_system import LUOsDiagnose |
|
85 | 86 |
from ganeti.cmdlib.tags import LUTagsGet, LUTagsSearch, LUTagsSet, LUTagsDel |
86 | 87 |
from ganeti.cmdlib.network import LUNetworkAdd, LUNetworkRemove, \ |
87 | 88 |
LUNetworkSetParams, LUNetworkQuery, LUNetworkConnect, LUNetworkDisconnect |
... | ... | |
274 | 275 |
utils.CommaJoin(errs)) |
275 | 276 |
|
276 | 277 |
|
277 |
class _OsQuery(_QueryBase): |
|
278 |
FIELDS = query.OS_FIELDS |
|
279 |
|
|
280 |
def ExpandNames(self, lu): |
|
281 |
# Lock all nodes in shared mode |
|
282 |
# Temporary removal of locks, should be reverted later |
|
283 |
# TODO: reintroduce locks when they are lighter-weight |
|
284 |
lu.needed_locks = {} |
|
285 |
#self.share_locks[locking.LEVEL_NODE] = 1 |
|
286 |
#self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET |
|
287 |
|
|
288 |
# The following variables interact with _QueryBase._GetNames |
|
289 |
if self.names: |
|
290 |
self.wanted = self.names |
|
291 |
else: |
|
292 |
self.wanted = locking.ALL_SET |
|
293 |
|
|
294 |
self.do_locking = self.use_locking |
|
295 |
|
|
296 |
def DeclareLocks(self, lu, level): |
|
297 |
pass |
|
298 |
|
|
299 |
@staticmethod |
|
300 |
def _DiagnoseByOS(rlist): |
|
301 |
"""Remaps a per-node return list into an a per-os per-node dictionary |
|
302 |
|
|
303 |
@param rlist: a map with node names as keys and OS objects as values |
|
304 |
|
|
305 |
@rtype: dict |
|
306 |
@return: a dictionary with osnames as keys and as value another |
|
307 |
map, with nodes as keys and tuples of (path, status, diagnose, |
|
308 |
variants, parameters, api_versions) as values, eg:: |
|
309 |
|
|
310 |
{"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []), |
|
311 |
(/srv/..., False, "invalid api")], |
|
312 |
"node2": [(/srv/..., True, "", [], [])]} |
|
313 |
} |
|
314 |
|
|
315 |
""" |
|
316 |
all_os = {} |
|
317 |
# we build here the list of nodes that didn't fail the RPC (at RPC |
|
318 |
# level), so that nodes with a non-responding node daemon don't |
|
319 |
# make all OSes invalid |
|
320 |
good_nodes = [node_name for node_name in rlist |
|
321 |
if not rlist[node_name].fail_msg] |
|
322 |
for node_name, nr in rlist.items(): |
|
323 |
if nr.fail_msg or not nr.payload: |
|
324 |
continue |
|
325 |
for (name, path, status, diagnose, variants, |
|
326 |
params, api_versions) in nr.payload: |
|
327 |
if name not in all_os: |
|
328 |
# build a list of nodes for this os containing empty lists |
|
329 |
# for each node in node_list |
|
330 |
all_os[name] = {} |
|
331 |
for nname in good_nodes: |
|
332 |
all_os[name][nname] = [] |
|
333 |
# convert params from [name, help] to (name, help) |
|
334 |
params = [tuple(v) for v in params] |
|
335 |
all_os[name][node_name].append((path, status, diagnose, |
|
336 |
variants, params, api_versions)) |
|
337 |
return all_os |
|
338 |
|
|
339 |
def _GetQueryData(self, lu): |
|
340 |
"""Computes the list of nodes and their attributes. |
|
341 |
|
|
342 |
""" |
|
343 |
# Locking is not used |
|
344 |
assert not (compat.any(lu.glm.is_owned(level) |
|
345 |
for level in locking.LEVELS |
|
346 |
if level != locking.LEVEL_CLUSTER) or |
|
347 |
self.do_locking or self.use_locking) |
|
348 |
|
|
349 |
valid_nodes = [node.name |
|
350 |
for node in lu.cfg.GetAllNodesInfo().values() |
|
351 |
if not node.offline and node.vm_capable] |
|
352 |
pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes)) |
|
353 |
cluster = lu.cfg.GetClusterInfo() |
|
354 |
|
|
355 |
data = {} |
|
356 |
|
|
357 |
for (os_name, os_data) in pol.items(): |
|
358 |
info = query.OsInfo(name=os_name, valid=True, node_status=os_data, |
|
359 |
hidden=(os_name in cluster.hidden_os), |
|
360 |
blacklisted=(os_name in cluster.blacklisted_os)) |
|
361 |
|
|
362 |
variants = set() |
|
363 |
parameters = set() |
|
364 |
api_versions = set() |
|
365 |
|
|
366 |
for idx, osl in enumerate(os_data.values()): |
|
367 |
info.valid = bool(info.valid and osl and osl[0][1]) |
|
368 |
if not info.valid: |
|
369 |
break |
|
370 |
|
|
371 |
(node_variants, node_params, node_api) = osl[0][3:6] |
|
372 |
if idx == 0: |
|
373 |
# First entry |
|
374 |
variants.update(node_variants) |
|
375 |
parameters.update(node_params) |
|
376 |
api_versions.update(node_api) |
|
377 |
else: |
|
378 |
# Filter out inconsistent values |
|
379 |
variants.intersection_update(node_variants) |
|
380 |
parameters.intersection_update(node_params) |
|
381 |
api_versions.intersection_update(node_api) |
|
382 |
|
|
383 |
info.variants = list(variants) |
|
384 |
info.parameters = list(parameters) |
|
385 |
info.api_versions = list(api_versions) |
|
386 |
|
|
387 |
data[os_name] = info |
|
388 |
|
|
389 |
# Prepare data in requested order |
|
390 |
return [data[name] for name in self._GetNames(lu, pol.keys(), None) |
|
391 |
if name in data] |
|
392 |
|
|
393 |
|
|
394 |
class LUOsDiagnose(NoHooksLU): |
|
395 |
"""Logical unit for OS diagnose/query. |
|
396 |
|
|
397 |
""" |
|
398 |
REQ_BGL = False |
|
399 |
|
|
400 |
@staticmethod |
|
401 |
def _BuildFilter(fields, names): |
|
402 |
"""Builds a filter for querying OSes. |
|
403 |
|
|
404 |
""" |
|
405 |
name_filter = qlang.MakeSimpleFilter("name", names) |
|
406 |
|
|
407 |
# Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the |
|
408 |
# respective field is not requested |
|
409 |
status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]] |
|
410 |
for fname in ["hidden", "blacklisted"] |
|
411 |
if fname not in fields] |
|
412 |
if "valid" not in fields: |
|
413 |
status_filter.append([qlang.OP_TRUE, "valid"]) |
|
414 |
|
|
415 |
if status_filter: |
|
416 |
status_filter.insert(0, qlang.OP_AND) |
|
417 |
else: |
|
418 |
status_filter = None |
|
419 |
|
|
420 |
if name_filter and status_filter: |
|
421 |
return [qlang.OP_AND, name_filter, status_filter] |
|
422 |
elif name_filter: |
|
423 |
return name_filter |
|
424 |
else: |
|
425 |
return status_filter |
|
426 |
|
|
427 |
def CheckArguments(self): |
|
428 |
self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names), |
|
429 |
self.op.output_fields, False) |
|
430 |
|
|
431 |
def ExpandNames(self): |
|
432 |
self.oq.ExpandNames(self) |
|
433 |
|
|
434 |
def Exec(self, feedback_fn): |
|
435 |
return self.oq.OldStyleQuery(self) |
|
436 |
|
|
437 |
|
|
438 | 278 |
class _ExtStorageQuery(_QueryBase): |
439 | 279 |
FIELDS = query.EXTSTORAGE_FIELDS |
440 | 280 |
|
Also available in: Unified diff