Revision 06fb92cf

b/lib/cmdlib/node.py
1173 1173
      es_flags = rpc.GetExclusiveStorageForNodeNames(lu.cfg, toquery_nodes)
1174 1174
      # FIXME: This currently maps everything to lvm, this should be more
1175 1175
      # flexible
1176
      storage_units = [(constants.ST_LVM_VG, lu.cfg.GetVGName())]
1177
      node_data = lu.rpc.call_node_info(toquery_nodes, storage_units,
1176
      vg_req = rpc.BuildVgInfoQuery(lu.cfg)
1177
      node_data = lu.rpc.call_node_info(toquery_nodes, vg_req,
1178 1178
                                        [lu.cfg.GetHypervisorType()], es_flags)
1179 1179
      live_data = dict((name, rpc.MakeLegacyNodeInfo(nresult.payload))
1180 1180
                       for (name, nresult) in node_data.items()
b/lib/masterd/iallocator.py
431 431
      node_whitelist = None
432 432

  
433 433
    es_flags = rpc.GetExclusiveStorageForNodeNames(cfg, node_list)
434
    vg_name = cfg.GetVGName()
435
    if vg_name is not None:
436
      has_lvm = True
437
      vg_req = [(constants.ST_LVM_VG, vg_name)]
438
    else:
439
      has_lvm = False
440
      vg_req = []
434
    vg_req = rpc.BuildVgInfoQuery(cfg)
435
    has_lvm = bool(vg_req)
441 436
    node_data = self.rpc.call_node_info(node_list, vg_req,
442 437
                                        [hypervisor_name], es_flags)
443 438
    node_iinfo = \
......
554 549
        if has_lvm:
555 550
          total_disk = get_attr("vg_size")
556 551
          free_disk = get_attr("vg_free")
552
          total_spindles = get_attr("spindles_total")
553
          free_spindles = get_attr("spindles_free")
557 554
        else:
558 555
          # we didn't even ask the node for VG status, so use zeros
559 556
          total_disk = free_disk = 0
557
          total_spindles = free_spindles = 0
560 558

  
561 559
        # compute memory used by instances
562 560
        pnr_dyn = {
......
565 563
          "free_memory": mem_free,
566 564
          "total_disk": total_disk,
567 565
          "free_disk": free_disk,
566
          "total_spindles": total_spindles,
567
          "free_spindles": free_spindles,
568 568
          "total_cpus": get_attr("cpu_total"),
569 569
          "i_pri_memory": i_p_mem,
570 570
          "i_pri_up_memory": i_p_up_mem,
b/lib/query.py
1160 1160
  "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1161 1161
             "Total disk space in volume group used for instance disk"
1162 1162
             " allocation"),
1163
  "spfree": ("SpFree", QFT_NUMBER, "spindles_free",
1164
             "Available spindles in volume group (exclusive storage only)"),
1165
  "sptotal": ("SpTotal", QFT_NUMBER, "spindles_total",
1166
              "Total spindles in volume group (exclusive storage only)"),
1163 1167
  "mfree": ("MFree", QFT_UNIT, "memory_free",
1164 1168
            "Memory available for instance allocations"),
1165 1169
  "mnode": ("MNode", QFT_UNIT, "memory_dom0",
b/lib/rapi/rlib2.py
81 81
            ] + _COMMON_FIELDS
82 82

  
83 83
N_FIELDS = ["name", "offline", "master_candidate", "drained",
84
            "dtotal", "dfree",
84
            "dtotal", "dfree", "sptotal", "spfree",
85 85
            "mtotal", "mnode", "mfree",
86 86
            "pinst_cnt", "sinst_cnt",
87 87
            "ctotal", "cnodes", "csockets",
b/lib/rpc.py
584 584
  return [(d.ToDict(), uid) for d, uid in value]
585 585

  
586 586

  
587
def BuildVgInfoQuery(cfg):
588
  """Build a query about the default VG for C{node_info}.
589

  
590
  The result of the RPC can be parsed with L{MakeLegacyNodeInfo}.
591

  
592
  @type cfg: L{config.ConfigWriter}
593
  @param cfg: Cluster configuration
594
  @rtype: list
595
  @return: argument suitable for L{rpc.RpcRunner.call_node_info}
596

  
597
  """
598
  vg_name = cfg.GetVGName()
599
  if vg_name:
600
    ret = [
601
      (constants.ST_LVM_VG, vg_name),
602
      (constants.ST_LVM_PV, vg_name),
603
      ]
604
  else:
605
    ret = []
606
  return ret
607

  
608

  
587 609
def MakeLegacyNodeInfo(data, require_vg_info=True):
588 610
  """Formats the data returned by L{rpc.RpcRunner.call_node_info}.
589 611

  
......
599 621
  ret = utils.JoinDisjointDicts(hv_info, {"bootid": bootid})
600 622

  
601 623
  if require_vg_info or vgs_info:
602
    (vg0_info, ) = vgs_info
624
    (vg0_info, vg0_spindles) = vgs_info
603 625
    ret = utils.JoinDisjointDicts(vg0_info, ret)
626
    ret["spindles_free"] = vg0_spindles["vg_free"]
627
    ret["spindles_total"] = vg0_spindles["vg_size"]
604 628

  
605 629
  return ret
606 630

  
b/qa/qa_rapi.py
110 110
                   "beparams", "hvparams",
111 111
                   "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
112 112

  
113
NODE_FIELDS = ("name", "dtotal", "dfree",
113
NODE_FIELDS = ("name", "dtotal", "dfree", "sptotal", "spfree",
114 114
               "mtotal", "mnode", "mfree",
115 115
               "pinst_cnt", "sinst_cnt", "tags")
116 116

  
b/src/Ganeti/JSON.hs
5 5

  
6 6
{-
7 7

  
8
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
8
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
9 9

  
10 10
This program is free software; you can redistribute it and/or modify
11 11
it under the terms of the GNU General Public License as published by
......
36 36
  , fromJVal
37 37
  , jsonHead
38 38
  , getMaybeJsonHead
39
  , getMaybeJsonElem
39 40
  , asJSObject
40 41
  , asObjectList
41 42
  , tryFromObj
......
186 187
getMaybeJsonHead [] _ = J.JSNull
187 188
getMaybeJsonHead (x:_) f = maybe J.JSNull J.showJSON (f x)
188 189

  
190
-- | Helper for extracting Maybe values from a list that might be too short.
191
getMaybeJsonElem :: (J.JSON b) => [a] -> Int -> (a -> Maybe b) -> J.JSValue
192
getMaybeJsonElem [] _ _ = J.JSNull
193
getMaybeJsonElem xs 0 f = getMaybeJsonHead xs f
194
getMaybeJsonElem (_:xs) n f
195
  | n < 0 = J.JSNull
196
  | otherwise = getMaybeJsonElem xs (n - 1) f
197

  
189 198
-- | Converts a JSON value into a JSON object.
190 199
asJSObject :: (Monad m) => J.JSValue -> m (J.JSObject J.JSValue)
191 200
asJSObject (J.JSObject a) = return a
b/src/Ganeti/Query/Node.hs
64 64
     "Available disk space in volume group")
65 65
  , ("dtotal", "DTotal", QFTUnit, "vg_size",
66 66
     "Total disk space in volume group used for instance disk allocation")
67
  , ("spfree", "SpFree", QFTNumber, "spindles_free",
68
     "Available spindles in volume group (exclusive storage only)")
69
  , ("sptotal", "SpTotal", QFTNumber, "spindles_total",
70
     "Total spindles in volume group (exclusive storage only)")
67 71
  , ("mfree", "MFree", QFTUnit, "memory_free",
68 72
     "Memory available for instance allocations")
69 73
  , ("mnode", "MNode", QFTUnit, "memory_dom0",
......
87 91
  getMaybeJsonHead (rpcResNodeInfoVgInfo res) vgInfoVgFree
88 92
nodeLiveFieldExtract "dtotal" res =
89 93
  getMaybeJsonHead (rpcResNodeInfoVgInfo res) vgInfoVgSize
94
nodeLiveFieldExtract "spfree" res =
95
  getMaybeJsonElem (rpcResNodeInfoVgInfo res) 1 vgInfoVgFree
96
nodeLiveFieldExtract "sptotal" res =
97
  getMaybeJsonElem (rpcResNodeInfoVgInfo res) 1 vgInfoVgSize
90 98
nodeLiveFieldExtract "mfree" res =
91 99
  jsonHead (rpcResNodeInfoHvInfo res) hvInfoMemoryFree
92 100
nodeLiveFieldExtract "mnode" res =
......
226 234
collectLiveData True cfg nodes = do
227 235
  let vgs = maybeToList . clusterVolumeGroupName $ configCluster cfg
228 236
      -- FIXME: This currently sets every storage unit to LVM
229
      storage_units = zip (repeat T.StorageLvmVg) vgs
237
      storage_units = zip (repeat T.StorageLvmVg) vgs ++
238
                      zip (repeat T.StorageLvmPv) vgs
230 239
      hvs = [getDefaultHypervisor cfg]
231 240
      step n (bn, gn, em) =
232 241
        let ndp' = getNodeNdParams cfg n
b/test/py/ganeti.query_unittest.py
436 436
      "mtotal": 4096,
437 437
      "dfree": 5 * 1024 * 1024,
438 438
      "dtotal": 100 * 1024 * 1024,
439
      "spfree": 0,
440
      "sptotal": 0,
439 441
      }
440 442

  
441 443
    assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
b/test/py/ganeti.rpc_unittest.py
891 891

  
892 892
class TestLegacyNodeInfo(unittest.TestCase):
893 893
  KEY_BOOT = "bootid"
894
  KEY_VG = "disk_free"
894
  KEY_VG0 = "name"
895
  KEY_VG1 = "vg_free"
896
  KEY_VG2 = "vg_size"
895 897
  KEY_HV = "cpu_count"
898
  KEY_SP1 = "spindles_free"
899
  KEY_SP2 = "spindles_total"
896 900
  VAL_BOOT = 0
897
  VAL_VG = 1
901
  VAL_VG0 = "xy"
902
  VAL_VG1 = 11
903
  VAL_VG2 = 12
898 904
  VAL_HV = 2
899
  DICT_VG = {KEY_VG: VAL_VG}
905
  VAL_SP0 = "ab"
906
  VAL_SP1 = 31
907
  VAL_SP2 = 32
908
  DICT_VG = {
909
    KEY_VG0: VAL_VG0,
910
    KEY_VG1: VAL_VG1,
911
    KEY_VG2: VAL_VG2,
912
    }
900 913
  DICT_HV = {KEY_HV: VAL_HV}
901
  STD_LST = [VAL_BOOT, [DICT_VG], [DICT_HV]]
914
  DICT_SP = {
915
    KEY_VG0: VAL_SP0,
916
    KEY_VG1: VAL_SP1,
917
    KEY_VG2: VAL_SP2,
918
    }
919
  STD_LST = [VAL_BOOT, [DICT_VG, DICT_SP], [DICT_HV]]
902 920
  STD_DICT = {
903 921
    KEY_BOOT: VAL_BOOT,
904
    KEY_VG: VAL_VG,
905
    KEY_HV: VAL_HV
922
    KEY_VG0: VAL_VG0,
923
    KEY_VG1: VAL_VG1,
924
    KEY_VG2: VAL_VG2,
925
    KEY_HV: VAL_HV,
926
    KEY_SP1: VAL_SP1,
927
    KEY_SP2: VAL_SP2,
906 928
    }
907 929

  
908 930
  def testStandard(self):

Also available in: Unified diff