Revision 6c00b2c7

b/lib/cmdlib/node.py
1205 1205
      # filter out non-vm_capable nodes
1206 1206
      toquery_node_uuids = [node.uuid for node in all_info.values()
1207 1207
                            if node.vm_capable and node.uuid in node_uuids]
1208
      lvm_enabled = utils.storage.IsLvmEnabled(
1209
          lu.cfg.GetClusterInfo().enabled_disk_templates)
1210
      # FIXME: this per default asks for storage space information for all
1211
      # enabled disk templates. Fix this by making it possible to specify
1212
      # space report fields for specific disk templates.
1213
      raw_storage_units = utils.storage.GetStorageUnitsOfCluster(
1214
          lu.cfg, include_spindles=lvm_enabled)
1208
      default_template = lu.cfg.GetClusterInfo().enabled_disk_templates[0]
1209
      raw_storage_units = utils.storage.GetStorageUnits(
1210
          lu.cfg, [default_template])
1215 1211
      storage_units = rpc.PrepareStorageUnitsForNodes(
1216 1212
          lu.cfg, raw_storage_units, toquery_node_uuids)
1217 1213
      default_hypervisor = lu.cfg.GetHypervisorType()
......
1220 1216
      node_data = lu.rpc.call_node_info(toquery_node_uuids, storage_units,
1221 1217
                                        hvspecs)
1222 1218
      live_data = dict(
1223
          (uuid, rpc.MakeLegacyNodeInfo(nresult.payload,
1224
                                        require_spindles=lvm_enabled))
1219
          (uuid, rpc.MakeLegacyNodeInfo(nresult.payload, default_template))
1225 1220
          for (uuid, nresult) in node_data.items()
1226 1221
          if not nresult.fail_msg and nresult.payload)
1227 1222
    else:
b/lib/masterd/iallocator.py
415 415
    @return: the result of the node info RPC call
416 416

  
417 417
    """
418
    if disk_templates:
419
      storage_units_raw = utils.storage.GetStorageUnits(self.cfg,
420
                                                        disk_templates)
421
    else:
422
      # FIXME: eliminate this case
423
      storage_units_raw = utils.storage.GetStorageUnitsOfCluster(
424
         self.cfg, include_spindles=True)
418
    storage_units_raw = utils.storage.GetStorageUnits(self.cfg, disk_templates)
425 419
    storage_units = rpc.PrepareStorageUnitsForNodes(self.cfg, storage_units_raw,
426 420
                                                    node_list)
427 421
    hvspecs = [(hypervisor_name, cluster_info.hvparams[hypervisor_name])]
......
587 581
      total_disk = template_space_info["storage_size"]
588 582
      free_disk = template_space_info["storage_free"]
589 583

  
584
      total_spindles = 0
585
      free_spindles = 0
590 586
      if disk_template in constants.DTS_LVM:
591 587
        lvm_pv_info = utils.storage.LookupSpaceInfoByStorageType(
592 588
           space_info, constants.ST_LVM_PV)
593
        if not lvm_pv_info:
594
          raise errors.OpExecError("Node '%s' didn't return LVM pv space info."
595
                                   % (node_name))
596
        total_spindles = lvm_pv_info["storage_size"]
597
        free_spindles = lvm_pv_info["storage_free"]
598
      else:
599
        total_spindles = 0
600
        free_spindles = 0
589
        if lvm_pv_info:
590
          total_spindles = lvm_pv_info["storage_size"]
591
          free_spindles = lvm_pv_info["storage_free"]
601 592
    return (total_disk, free_disk, total_spindles, free_spindles)
602 593

  
603 594
  @staticmethod
b/lib/rpc.py
586 586
    result["spindles_free"] = lvm_pv_info["storage_free"]
587 587
    result["spindles_total"] = lvm_pv_info["storage_size"]
588 588
  else:
589
    raise errors.OpExecError("No spindle storage information available.")
589
    result["spindles_free"] = 0
590
    result["spindles_total"] = 0
590 591

  
591 592

  
592
def _AddDefaultStorageInfoToLegacyNodeInfo(result, space_info):
593
  """Extracts the storage space information of the default storage type from
593
def _AddStorageInfoToLegacyNodeInfoByTemplate(
594
    result, space_info, disk_template):
595
  """Extracts the storage space information of the disk template from
594 596
  the space info and adds it to the result dictionary.
595 597

  
596 598
  @see: C{_AddSpindlesToLegacyNodeInfo} for parameter information.
597 599

  
598 600
  """
599
  # Check if there is at least one row for non-spindle storage info.
600
  no_defaults = (len(space_info) < 1) or \
601
      (space_info[0]["type"] == constants.ST_LVM_PV and len(space_info) == 1)
602

  
603
  default_space_info = None
604
  if no_defaults:
605
    logging.warning("No storage info provided for default storage type.")
601
  if utils.storage.DiskTemplateSupportsSpaceReporting(disk_template):
602
    disk_info = utils.storage.LookupSpaceInfoByDiskTemplate(
603
        space_info, disk_template)
604
    result["name"] = disk_info["name"]
605
    result["storage_free"] = disk_info["storage_free"]
606
    result["storage_size"] = disk_info["storage_size"]
606 607
  else:
607
    default_space_info = space_info[0]
608

  
609
  if default_space_info:
610
    result["name"] = default_space_info["name"]
611
    result["storage_free"] = default_space_info["storage_free"]
612
    result["storage_size"] = default_space_info["storage_size"]
608
    # FIXME: consider displaying '-' in this case
609
    result["storage_free"] = 0
610
    result["storage_size"] = 0
613 611

  
614 612

  
615
def MakeLegacyNodeInfo(data, require_spindles=False):
613
def MakeLegacyNodeInfo(data, disk_template):
616 614
  """Formats the data returned by L{rpc.RpcRunner.call_node_info}.
617 615

  
618 616
  Converts the data into a single dictionary. This is fine for most use cases,
619 617
  but some require information from more than one volume group or hypervisor.
620 618

  
621
  @param require_spindles: add spindle storage information to the legacy node
622
      info
623

  
624 619
  """
625 620
  (bootid, space_info, (hv_info, )) = data
626 621

  
627 622
  ret = utils.JoinDisjointDicts(hv_info, {"bootid": bootid})
628 623

  
629
  if require_spindles:
630
    _AddSpindlesToLegacyNodeInfo(ret, space_info)
631
  _AddDefaultStorageInfoToLegacyNodeInfo(ret, space_info)
624
  _AddSpindlesToLegacyNodeInfo(ret, space_info)
625
  _AddStorageInfoToLegacyNodeInfoByTemplate(ret, space_info, disk_template)
632 626

  
633 627
  return ret
634 628

  
b/lib/utils/storage.py
95 95
    return (storage_type, None)
96 96

  
97 97

  
98
def _GetDefaultStorageUnitForSpindles(cfg):
99
  """Creates a 'spindle' storage unit, by retrieving the volume group
100
  name and associating it to the lvm-pv storage type.
101

  
102
  @rtype: (string, string)
103
  @return: tuple (storage_type, storage_key), where storage type is
104
    'lvm-pv' and storage_key the name of the default volume group
105

  
106
  """
107
  return (constants.ST_LVM_PV, cfg.GetVGName())
108

  
109

  
110
def GetStorageUnitsOfCluster(cfg, include_spindles=False):
111
  """Examines the cluster's configuration and returns a list of storage
112
  units and their storage keys, ordered by the order in which they
113
  are enabled.
114

  
115
  @type cfg: L{config.ConfigWriter}
116
  @param cfg: Cluster configuration
117
  @type include_spindles: boolean
118
  @param include_spindles: flag to include an extra storage unit for physical
119
    volumes
120
  @rtype: list of tuples (string, string)
121
  @return: list of storage units, each storage unit being a tuple of
122
    (storage_type, storage_key); storage_type is in
123
    C{constants.STORAGE_TYPES} and the storage_key a string to
124
    identify an entity of that storage type, for example a volume group
125
    name for LVM storage or a file for file storage.
126

  
127
  """
128
  cluster_config = cfg.GetClusterInfo()
129
  storage_units = []
130
  for disk_template in cluster_config.enabled_disk_templates:
131
    if constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template]\
132
        in constants.STS_REPORT:
133
      storage_units.append(
134
          _GetDefaultStorageUnitForDiskTemplate(cfg, disk_template))
135
  if include_spindles:
136
    included_storage_types = set([st for (st, _) in storage_units])
137
    if not constants.ST_LVM_PV in included_storage_types:
138
      storage_units.append(
139
          _GetDefaultStorageUnitForSpindles(cfg))
140

  
141
  return storage_units
98
def DiskTemplateSupportsSpaceReporting(disk_template):
99
  """Check whether the disk template supports storage space reporting."""
100
  return (constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template]
101
          in constants.STS_REPORT)
142 102

  
143 103

  
144 104
def GetStorageUnits(cfg, disk_templates):
......
162 122
  """
163 123
  storage_units = []
164 124
  for disk_template in disk_templates:
165
    if constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template]\
166
        in constants.STS_REPORT:
125
    if DiskTemplateSupportsSpaceReporting(disk_template):
167 126
      storage_units.append(
168 127
          _GetDefaultStorageUnitForDiskTemplate(cfg, disk_template))
169
  if len(set(disk_templates) & constants.DTS_LVM) > 0:
170
    storage_units.append(
171
        _GetDefaultStorageUnitForSpindles(cfg))
172

  
173 128
  return storage_units
174 129

  
175 130

  
b/test/py/ganeti.rpc_unittest.py
943 943
    KEY_NAME: VAL_VG_NAME,
944 944
    KEY_STORAGE_FREE: VAL_VG_FREE,
945 945
    KEY_STORAGE_TOTAL: VAL_VG_TOTAL,
946
    KEY_SPINDLES_FREE: VAL_PV_FREE,
947
    KEY_SPINDLES_TOTAL: VAL_PV_TOTAL,
946 948
    KEY_CPU_COUNT: VAL_CPU_COUNT,
947 949
    }
948 950

  
949
  def testStandard(self):
950
    result = rpc.MakeLegacyNodeInfo(self.STD_LST)
951
  def testWithSpindles(self):
952
    result = rpc.MakeLegacyNodeInfo(self.STD_LST, constants.DT_PLAIN)
951 953
    self.assertEqual(result, self.STD_DICT)
952 954

  
953
  def testSpindlesRequired(self):
954
    my_lst = [self.VAL_BOOT, [], [self.DICT_HV]]
955
    self.assertRaises(errors.OpExecError, rpc.MakeLegacyNodeInfo, my_lst,
956
        require_spindles=True)
957

  
958
  def testNoSpindlesRequired(self):
959
    my_lst = [self.VAL_BOOT, [], [self.DICT_HV]]
960
    result = rpc.MakeLegacyNodeInfo(my_lst, require_spindles = False)
961
    self.assertEqual(result, {self.KEY_BOOT: self.VAL_BOOT,
962
                              self.KEY_CPU_COUNT: self.VAL_CPU_COUNT})
963
    result = rpc.MakeLegacyNodeInfo(self.STD_LST, require_spindles = False)
964
    self.assertEqual(result, self.STD_DICT)
965

  
966

  
967
class TestAddDefaultStorageInfoToLegacyNodeInfo(unittest.TestCase):
968

  
969
  def setUp(self):
970
    self.free_storage_file = 23
971
    self.total_storage_file = 42
972
    self.free_storage_lvm = 69
973
    self.total_storage_lvm = 666
974
    self.node_info = [{"name": "myfile",
975
                       "type": constants.ST_FILE,
976
                       "storage_free": self.free_storage_file,
977
                       "storage_size": self.total_storage_file},
978
                      {"name": "myvg",
979
                       "type": constants.ST_LVM_VG,
980
                       "storage_free": self.free_storage_lvm,
981
                       "storage_size": self.total_storage_lvm},
982
                      {"name": "myspindle",
983
                       "type": constants.ST_LVM_PV,
984
                       "storage_free": 33,
985
                       "storage_size": 44}]
986

  
987
  def testAddDefaultStorageInfoToLegacyNodeInfo(self):
988
    result = {}
989
    rpc._AddDefaultStorageInfoToLegacyNodeInfo(result, self.node_info)
990
    self.assertEqual(self.free_storage_file, result["storage_free"])
991
    self.assertEqual(self.total_storage_file, result["storage_size"])
992

  
993
  def testAddDefaultStorageInfoToLegacyNodeInfoNoDefaults(self):
994
    result = {}
995
    rpc._AddDefaultStorageInfoToLegacyNodeInfo(result, self.node_info[-1:])
996
    self.assertFalse("storage_free" in result)
997
    self.assertFalse("storage_size" in result)
955
  def testNoSpindles(self):
956
    my_lst = [self.VAL_BOOT, [self.DICT_VG], [self.DICT_HV]]
957
    result = rpc.MakeLegacyNodeInfo(my_lst, constants.DT_PLAIN)
958
    expected_dict = dict((k,v) for k, v in self.STD_DICT.iteritems())
959
    expected_dict[self.KEY_SPINDLES_FREE] = 0
960
    expected_dict[self.KEY_SPINDLES_TOTAL] = 0
961
    self.assertEqual(result, expected_dict)
998 962

  
999 963

  
1000 964
if __name__ == "__main__":
b/test/py/ganeti.utils.storage_unittest.py
26 26
import unittest
27 27

  
28 28
from ganeti import constants
29
from ganeti import objects
30 29
from ganeti.utils import storage
31 30

  
32 31
import testutils
......
72 71
    self.assertEqual(storage_type, constants.ST_DISKLESS)
73 72
    self.assertEqual(storage_key, None)
74 73

  
75
  def testGetDefaultStorageUnitForSpindles(self):
76
    (storage_type, storage_key) = \
77
        storage._GetDefaultStorageUnitForSpindles(self._cfg)
78
    self.assertEqual(storage_type, constants.ST_LVM_PV)
79
    self.assertEqual(storage_key, self._default_vg_name)
80

  
81

  
82
class TestGetStorageUnitsOfCluster(unittest.TestCase):
83

  
84
  def setUp(self):
85
    storage._GetDefaultStorageUnitForDiskTemplate = \
86
        mock.Mock(return_value=("foo", "bar"))
87

  
88
    self._cluster_cfg = objects.Cluster()
89
    self._enabled_disk_templates = \
90
        [constants.DT_DRBD8, constants.DT_PLAIN, constants.DT_FILE,
91
         constants.DT_SHARED_FILE]
92
    self._cluster_cfg.enabled_disk_templates = \
93
        self._enabled_disk_templates
94
    self._cfg = mock.Mock()
95
    self._cfg.GetClusterInfo = mock.Mock(return_value=self._cluster_cfg)
96
    self._cfg.GetVGName = mock.Mock(return_value="some_vg_name")
97

  
98
  def testGetStorageUnitsOfCluster(self):
99
    storage_units = storage.GetStorageUnitsOfCluster(self._cfg)
100
    self.assertEqual(len(storage_units), len(self._enabled_disk_templates))
101

  
102
  def testGetStorageUnitsOfClusterWithSpindles(self):
103
    storage_units = storage.GetStorageUnitsOfCluster(
104
        self._cfg, include_spindles=True)
105
    self.assertEqual(len(storage_units), len(self._enabled_disk_templates) + 1)
106
    self.assertTrue(constants.ST_LVM_PV in [st for (st, sk) in storage_units])
107

  
108 74

  
109 75
class TestGetStorageUnits(unittest.TestCase):
110 76

  
......
121 87
  def testGetStorageUnitsLvm(self):
122 88
    disk_templates = [constants.DT_PLAIN, constants.DT_DRBD8]
123 89
    storage_units = storage.GetStorageUnits(self._cfg, disk_templates)
124
    self.assertEqual(len(storage_units), len(disk_templates) + 1)
90
    self.assertEqual(len(storage_units), len(disk_templates))
125 91

  
126 92

  
127 93
class TestLookupSpaceInfoByStorageType(unittest.TestCase):

Also available in: Unified diff