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