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