Revision bc5d0215

b/lib/bootstrap.py
286 286
                master_netmask, master_netdev, file_storage_dir,
287 287
                shared_file_storage_dir, candidate_pool_size, secondary_ip=None,
288 288
                vg_name=None, beparams=None, nicparams=None, ndparams=None,
289
                hvparams=None, enabled_hypervisors=None, modify_etc_hosts=True,
290
                modify_ssh_setup=True, maintain_node_health=False,
291
                drbd_helper=None, uid_pool=None, default_iallocator=None,
292
                primary_ip_version=None, prealloc_wipe_disks=False,
293
                use_external_mip_script=False):
289
                hvparams=None, diskparams=None, enabled_hypervisors=None,
290
                modify_etc_hosts=True, modify_ssh_setup=True,
291
                maintain_node_health=False, drbd_helper=None, uid_pool=None,
292
                default_iallocator=None, primary_ip_version=None,
293
                prealloc_wipe_disks=False, use_external_mip_script=False):
294 294
  """Initialise the cluster.
295 295

  
296 296
  @type candidate_pool_size: int
......
426 426
    hv_class = hypervisor.GetHypervisor(hv_name)
427 427
    hv_class.CheckParameterSyntax(hv_params)
428 428

  
429
  # diskparams is a mapping of disk-template->diskparams dict
430
  for template, dt_params in diskparams.items():
431
    param_keys = set(dt_params.keys())
432
    default_param_keys = set(constants.DISK_DT_DEFAULTS[template].keys())
433
    if not (param_keys <= default_param_keys):
434
      unknown_params = param_keys - default_param_keys
435
      raise errors.OpPrereqError("Invalid parameters for disk template %s:"
436
                                 " %s" % (template,
437
                                          utils.CommaJoin(unknown_params)))
438
    utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
439

  
429 440
  # set up ssh config and /etc/hosts
430 441
  sshline = utils.ReadFile(constants.SSH_HOST_RSA_PUB)
431 442
  sshkey = sshline.split(" ")[1]
......
473 484
    nicparams={constants.PP_DEFAULT: nicparams},
474 485
    ndparams=ndparams,
475 486
    hvparams=hvparams,
487
    diskparams=diskparams,
476 488
    candidate_pool_size=candidate_pool_size,
477 489
    modify_etc_hosts=modify_etc_hosts,
478 490
    modify_ssh_setup=modify_ssh_setup,
......
542 554
    uuid=uuid_generator.Generate([], utils.NewUUID, _INITCONF_ECID),
543 555
    name=constants.INITIAL_NODE_GROUP_NAME,
544 556
    members=[master_node_config.name],
557
    diskparams=cluster_config.diskparams,
545 558
    )
546 559
  nodegroups = {
547 560
    default_nodegroup.uuid: default_nodegroup,
b/lib/cli.py
69 69
  "DEBUG_SIMERR_OPT",
70 70
  "DISKIDX_OPT",
71 71
  "DISK_OPT",
72
  "DISK_PARAMS_OPT",
72 73
  "DISK_TEMPLATE_OPT",
73 74
  "DRAINED_OPT",
74 75
  "DRY_RUN_OPT",
......
752 753
                        default={}, dest="hvparams",
753 754
                        help="Hypervisor parameters")
754 755

  
756
DISK_PARAMS_OPT = cli_option("-D", "--disk-parameters", dest="diskparams",
757
                             help="Disk template parameters, in the format"
758
                             " template:option=value,option=value,...",
759
                             type="identkeyval", action="append", default=[])
760

  
755 761
HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
756 762
                            help="Hypervisor and hypervisor options, in the"
757 763
                            " format hypervisor:option=value,option=value,...",
b/lib/client/gnt_cluster.py
98 98
  beparams = opts.beparams
99 99
  nicparams = opts.nicparams
100 100

  
101
  diskparams = dict(opts.diskparams)
102

  
103
  # check the disk template types here, as we cannot rely on the type check done
104
  # by the opcode parameter types
105
  diskparams_keys = set(diskparams.keys())
106
  if not (diskparams_keys <= constants.DISK_TEMPLATES):
107
    unknown = utils.NiceSort(diskparams_keys - constants.DISK_TEMPLATES)
108
    ToStderr("Disk templates unknown: %s" % utils.CommaJoin(unknown))
109
    return 1
110

  
101 111
  # prepare beparams dict
102 112
  beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams)
103 113
  utils.ForceDictType(beparams, constants.BES_PARAMETER_COMPAT)
......
120 130
    hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv])
121 131
    utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
122 132

  
133
  # prepare diskparams dict
134
  for templ in constants.DISK_TEMPLATES:
135
    if templ not in diskparams:
136
      diskparams[templ] = {}
137
    diskparams[templ] = objects.FillDict(constants.DISK_DT_DEFAULTS[templ],
138
                                         diskparams[templ])
139
    utils.ForceDictType(diskparams[templ], constants.DISK_DT_TYPES)
140

  
123 141
  if opts.candidate_pool_size is None:
124 142
    opts.candidate_pool_size = constants.MASTER_POOL_SIZE_DEFAULT
125 143

  
......
164 182
                        beparams=beparams,
165 183
                        nicparams=nicparams,
166 184
                        ndparams=ndparams,
185
                        diskparams=diskparams,
167 186
                        candidate_pool_size=opts.candidate_pool_size,
168 187
                        modify_etc_hosts=opts.modify_etc_hosts,
169 188
                        modify_ssh_setup=opts.modify_ssh_setup,
......
876 895
  if not (not opts.lvm_storage or opts.vg_name or
877 896
          not opts.drbd_storage or opts.drbd_helper or
878 897
          opts.enabled_hypervisors or opts.hvparams or
879
          opts.beparams or opts.nicparams or opts.ndparams or
898
          opts.beparams or opts.nicparams or
899
          opts.ndparams or opts.diskparams or
880 900
          opts.candidate_pool_size is not None or
881 901
          opts.uid_pool is not None or
882 902
          opts.maintain_node_health is not None or
......
916 936
  for hv_params in hvparams.values():
917 937
    utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
918 938

  
939
  diskparams = dict(opts.diskparams)
940

  
941
  for dt_params in hvparams.values():
942
    utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
943

  
919 944
  beparams = opts.beparams
920 945
  utils.ForceDictType(beparams, constants.BES_PARAMETER_COMPAT)
921 946

  
......
963 988
                                  beparams=beparams,
964 989
                                  nicparams=nicparams,
965 990
                                  ndparams=ndparams,
991
                                  diskparams=diskparams,
966 992
                                  candidate_pool_size=opts.candidate_pool_size,
967 993
                                  maintain_node_health=mnh,
968 994
                                  uid_pool=uid_pool,
......
1376 1402
     NOMODIFY_SSH_SETUP_OPT, SECONDARY_IP_OPT, VG_NAME_OPT,
1377 1403
     MAINTAIN_NODE_HEALTH_OPT, UIDPOOL_OPT, DRBD_HELPER_OPT, NODRBD_STORAGE_OPT,
1378 1404
     DEFAULT_IALLOCATOR_OPT, PRIMARY_IP_VERSION_OPT, PREALLOC_WIPE_DISKS_OPT,
1379
     NODE_PARAMS_OPT, GLOBAL_SHARED_FILEDIR_OPT, USE_EXTERNAL_MIP_SCRIPT],
1405
     NODE_PARAMS_OPT, GLOBAL_SHARED_FILEDIR_OPT, USE_EXTERNAL_MIP_SCRIPT,
1406
     DISK_PARAMS_OPT],
1380 1407
    "[opts...] <cluster_name>", "Initialises a new cluster configuration"),
1381 1408
  "destroy": (
1382 1409
    DestroyCluster, ARGS_NONE, [YES_DOIT_OPT],
......
1453 1480
     MAINTAIN_NODE_HEALTH_OPT, UIDPOOL_OPT, ADD_UIDS_OPT, REMOVE_UIDS_OPT,
1454 1481
     DRBD_HELPER_OPT, NODRBD_STORAGE_OPT, DEFAULT_IALLOCATOR_OPT,
1455 1482
     RESERVED_LVS_OPT, DRY_RUN_OPT, PRIORITY_OPT, PREALLOC_WIPE_DISKS_OPT,
1456
     NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT],
1483
     NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT, DISK_PARAMS_OPT],
1457 1484
    "[opts...]",
1458 1485
    "Alters the parameters of the cluster"),
1459 1486
  "renew-crypto": (
b/lib/client/gnt_group.py
48 48

  
49 49
  """
50 50
  (group_name,) = args
51
  diskparams = dict(opts.diskparams)
51 52
  op = opcodes.OpGroupAdd(group_name=group_name, ndparams=opts.ndparams,
52
                          alloc_policy=opts.alloc_policy)
53
                          alloc_policy=opts.alloc_policy,
54
                          diskparams=diskparams)
53 55
  SubmitOpCode(op, opts=opts)
54 56

  
55 57

  
......
133 135
  @return: the desired exit code
134 136

  
135 137
  """
136
  if opts.ndparams is None and opts.alloc_policy is None:
138
  if (opts.ndparams is None and opts.alloc_policy is None
139
      and not opts.diskparams):
137 140
    ToStderr("Please give at least one of the parameters.")
138 141
    return 1
139 142

  
143
  diskparams = dict(opts.diskparams)
140 144
  op = opcodes.OpGroupSetParams(group_name=args[0],
141 145
                                ndparams=opts.ndparams,
142
                                alloc_policy=opts.alloc_policy)
146
                                alloc_policy=opts.alloc_policy,
147
                                diskparams=diskparams)
143 148
  result = SubmitOrSend(op, opts)
144 149

  
145 150
  if result:
......
214 219

  
215 220
commands = {
216 221
  "add": (
217
    AddGroup, ARGS_ONE_GROUP, [DRY_RUN_OPT, ALLOC_POLICY_OPT, NODE_PARAMS_OPT],
222
    AddGroup, ARGS_ONE_GROUP,
223
    [DRY_RUN_OPT, ALLOC_POLICY_OPT, NODE_PARAMS_OPT, DISK_PARAMS_OPT],
218 224
    "<group_name>", "Add a new node group to the cluster"),
219 225
  "assign-nodes": (
220 226
    AssignNodes, ARGS_ONE_GROUP + ARGS_MANY_NODES, [DRY_RUN_OPT, FORCE_OPT],
......
231 237
    "Lists all available fields for node groups"),
232 238
  "modify": (
233 239
    SetGroupParams, ARGS_ONE_GROUP,
234
    [DRY_RUN_OPT, SUBMIT_OPT, ALLOC_POLICY_OPT, NODE_PARAMS_OPT],
240
    [DRY_RUN_OPT, SUBMIT_OPT, ALLOC_POLICY_OPT, NODE_PARAMS_OPT,
241
     DISK_PARAMS_OPT],
235 242
    "<group_name>", "Alters the parameters of a node group"),
236 243
  "remove": (
237 244
    RemoveGroup, ARGS_ONE_GROUP, [DRY_RUN_OPT],
b/lib/cmdlib.py
3496 3496
    if self.op.master_netmask is not None:
3497 3497
      _ValidateNetmask(self.cfg, self.op.master_netmask)
3498 3498

  
3499
    if self.op.diskparams:
3500
      for dt_params in self.op.diskparams.values():
3501
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
3502

  
3499 3503
  def ExpandNames(self):
3500 3504
    # FIXME: in the future maybe other cluster params won't require checking on
3501 3505
    # all nodes to be modified.
......
3628 3632
        else:
3629 3633
          self.new_hvparams[hv_name].update(hv_dict)
3630 3634

  
3635
    # disk template parameters
3636
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
3637
    if self.op.diskparams:
3638
      for dt_name, dt_params in self.op.diskparams.items():
3639
        if dt_name not in self.op.diskparams:
3640
          self.new_diskparams[dt_name] = dt_params
3641
        else:
3642
          self.new_diskparams[dt_name].update(dt_params)
3643

  
3631 3644
    # os hypervisor parameters
3632 3645
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
3633 3646
    if self.op.os_hvp:
......
3746 3759
      self.cluster.osparams = self.new_osp
3747 3760
    if self.op.ndparams:
3748 3761
      self.cluster.ndparams = self.new_ndparams
3762
    if self.op.diskparams:
3763
      self.cluster.diskparams = self.new_diskparams
3749 3764

  
3750 3765
    if self.op.candidate_pool_size is not None:
3751 3766
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
......
12463 12478
    if self.op.ndparams:
12464 12479
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
12465 12480

  
12481
    if self.op.diskparams:
12482
      for templ in constants.DISK_TEMPLATES:
12483
        if templ not in self.op.diskparams:
12484
          self.op.diskparams[templ] = {}
12485
        utils.ForceDictType(self.op.diskparams[templ], constants.DISK_DT_TYPES)
12486
    else:
12487
      self.op.diskparams = self.cfg.GetClusterInfo().diskparams
12488

  
12466 12489
  def BuildHooksEnv(self):
12467 12490
    """Build hooks env.
12468 12491

  
......
12485 12508
    group_obj = objects.NodeGroup(name=self.op.group_name, members=[],
12486 12509
                                  uuid=self.group_uuid,
12487 12510
                                  alloc_policy=self.op.alloc_policy,
12488
                                  ndparams=self.op.ndparams)
12511
                                  ndparams=self.op.ndparams,
12512
                                  diskparams=self.op.diskparams)
12489 12513

  
12490 12514
    self.cfg.AddNodeGroup(group_obj, self.proc.GetECId(), check_uuid=False)
12491 12515
    del self.remove_locks[locking.LEVEL_NODEGROUP]
......
12732 12756
  def CheckArguments(self):
12733 12757
    all_changes = [
12734 12758
      self.op.ndparams,
12759
      self.op.diskparams,
12735 12760
      self.op.alloc_policy,
12736 12761
      ]
12737 12762

  
......
12762 12787
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
12763 12788
      self.new_ndparams = new_ndparams
12764 12789

  
12790
    if self.op.diskparams:
12791
      self.new_diskparams = dict()
12792
      for templ in constants.DISK_TEMPLATES:
12793
        if templ not in self.op.diskparams:
12794
          self.op.diskparams[templ] = {}
12795
        new_templ_params = _GetUpdatedParams(self.group.diskparams[templ],
12796
                                             self.op.diskparams[templ])
12797
        utils.ForceDictType(new_templ_params, constants.DISK_DT_TYPES)
12798
        self.new_diskparams[templ] = new_templ_params
12799

  
12765 12800
  def BuildHooksEnv(self):
12766 12801
    """Build hooks env.
12767 12802

  
......
12788 12823
      self.group.ndparams = self.new_ndparams
12789 12824
      result.append(("ndparams", str(self.group.ndparams)))
12790 12825

  
12826
    if self.op.diskparams:
12827
      self.group.diskparams = self.new_diskparams
12828
      result.append(("diskparams", str(self.group.diskparams)))
12829

  
12791 12830
    if self.op.alloc_policy:
12792 12831
      self.group.alloc_policy = self.op.alloc_policy
12793 12832

  
b/lib/constants.py
460 460
LD_DRBD8 = "drbd8"
461 461
LD_FILE = "file"
462 462
LD_BLOCKDEV = "blockdev"
463
LOGICAL_DISK_TYPES = frozenset([
464
  LD_LV,
465
  LD_DRBD8,
466
  LD_FILE,
467
  LD_BLOCKDEV,
468
  ])
469

  
463 470
LDS_BLOCK = frozenset([LD_LV, LD_DRBD8, LD_BLOCKDEV])
464 471

  
465 472
# drbd constants
......
889 896

  
890 897
NDS_PARAMETERS = frozenset(NDS_PARAMETER_TYPES.keys())
891 898

  
899
# Logical Disks parameters
900
DISK_LD_TYPES = {
901
  }
902
DISK_LD_PARAMETERS = frozenset(DISK_LD_TYPES.keys())
903

  
904
# Disk template parameters
905
DISK_DT_TYPES = {
906
  }
907

  
908
DISK_DT_PARAMETERS = frozenset(DISK_DT_TYPES.keys())
909

  
892 910
# OOB supported commands
893 911
OOB_POWER_ON = "power-on"
894 912
OOB_POWER_OFF = "power-off"
......
1651 1669
  ND_OOB_PROGRAM: None,
1652 1670
  }
1653 1671

  
1672
DISK_LD_DEFAULTS = {
1673
  LD_DRBD8: {
1674
    },
1675
  LD_LV: {
1676
    },
1677
  LD_FILE: {
1678
    },
1679
  LD_BLOCKDEV: {
1680
    },
1681
  }
1682

  
1683
DISK_DT_DEFAULTS = {
1684
  DT_PLAIN: {
1685
    },
1686
  DT_DRBD8: {
1687
    },
1688
  DT_DISKLESS: {
1689
    },
1690
  DT_FILE: {
1691
    },
1692
  DT_SHARED_FILE: {
1693
    },
1694
  DT_BLOCK: {
1695
    },
1696
  }
1697

  
1654 1698
NICC_DEFAULTS = {
1655 1699
  NIC_MODE: NIC_MODE_BRIDGED,
1656 1700
  NIC_LINK: DEFAULT_BRIDGE,
b/lib/masterd/instance.py
1176 1176
                            " result '%s'", idx, src_node, result.payload)
1177 1177
      else:
1178 1178
        disk_id = tuple(result.payload)
1179
        disk_params = constants.DISK_LD_DEFAULTS[constants.LD_LV].copy()
1179 1180
        new_dev = objects.Disk(dev_type=constants.LD_LV, size=disk.size,
1180 1181
                               logical_id=disk_id, physical_id=disk_id,
1181
                               iv_name=disk.iv_name)
1182
                               iv_name=disk.iv_name,
1183
                               params=disk_params)
1182 1184

  
1183 1185
      self._snap_disks.append(new_dev)
1184 1186

  
b/lib/objects.py
110 110
    del target[constants.BE_MEMORY]
111 111

  
112 112

  
113
def UpgradeDiskParams(diskparams):
114
  """Upgrade the disk parameters.
115

  
116
  @type diskparams: dict
117
  @param diskparams: disk parameters to upgrade
118
  @rtype: dict
119
  @return: the upgraded disk parameters dit
120

  
121
  """
122
  result = dict()
123
  if diskparams is None:
124
    result = constants.DISK_DT_DEFAULTS.copy()
125
  else:
126
    # Update the disk parameter values for each disk template.
127
    # The code iterates over constants.DISK_TEMPLATES because new templates
128
    # might have been added.
129
    for template in constants.DISK_TEMPLATES:
130
      if template not in diskparams:
131
        result[template] = constants.DISK_DT_DEFAULTS[template].copy()
132
      else:
133
        result[template] = FillDict(constants.DISK_DT_DEFAULTS[template],
134
                                    diskparams[template])
135

  
136
  return result
137

  
138

  
113 139
class ConfigObject(object):
114 140
  """A generic config object.
115 141

  
......
451 477
class Disk(ConfigObject):
452 478
  """Config object representing a block device."""
453 479
  __slots__ = ["dev_type", "logical_id", "physical_id",
454
               "children", "iv_name", "size", "mode"]
480
               "children", "iv_name", "size", "mode", "params"]
455 481

  
456 482
  def CreateOnSecondary(self):
457 483
    """Test if this device needs to be created on a secondary node."""
......
745 771
    if self.children:
746 772
      for child in self.children:
747 773
        child.UpgradeConfig()
774

  
775
    if not self.params:
776
      self.params = constants.DISK_LD_DEFAULTS[self.dev_type].copy()
777
    else:
778
      self.params = FillDict(constants.DISK_LD_DEFAULTS[self.dev_type],
779
                             self.params)
748 780
    # add here config upgrade for this disk
749 781

  
750 782

  
......
1111 1143
    "name",
1112 1144
    "members",
1113 1145
    "ndparams",
1146
    "diskparams",
1114 1147
    "serial_no",
1115 1148
    "alloc_policy",
1116 1149
    ] + _TIMESTAMPS + _UUID
......
1155 1188
    if self.mtime is None:
1156 1189
      self.mtime = time.time()
1157 1190

  
1191
    self.diskparams = UpgradeDiskParams(self.diskparams)
1192

  
1158 1193
  def FillND(self, node):
1159 1194
    """Return filled out ndparams for L{objects.Node}
1160 1195

  
......
1206 1241
    "osparams",
1207 1242
    "nicparams",
1208 1243
    "ndparams",
1244
    "diskparams",
1209 1245
    "candidate_pool_size",
1210 1246
    "modify_etc_hosts",
1211 1247
    "modify_ssh_setup",
......
1312 1348
    if self.use_external_mip_script is None:
1313 1349
      self.use_external_mip_script = False
1314 1350

  
1351
    self.diskparams = UpgradeDiskParams(self.diskparams)
1352

  
1315 1353
  def ToDict(self):
1316 1354
    """Custom function for cluster.
1317 1355

  
b/lib/opcodes.py
141 141
                  ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
142 142
                  "List of error codes that should be treated as warnings")
143 143

  
144
# Disk parameters
145
_PDiskParams = ("diskparams", None,
146
                ht.TOr(
147
                  ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict),
148
                  ht.TNone),
149
                "Disk templates' parameter defaults")
150

  
144 151
#: OP_ID conversion regular expression
145 152
_OPID_RE = re.compile("([a-z])([A-Z])")
146 153

  
......
762 769
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
763 770
                              ht.TNone),
764 771
     "Cluster-wide OS parameter defaults"),
772
    _PDiskParams,
765 773
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone),
766 774
     "Master candidate pool size"),
767 775
    ("uid_pool", None, ht.NoType,
......
1394 1402
    _PGroupName,
1395 1403
    _PNodeGroupAllocPolicy,
1396 1404
    _PGroupNodeParams,
1405
    _PDiskParams,
1397 1406
    ]
1398 1407

  
1399 1408

  
......
1424 1433
    _PGroupName,
1425 1434
    _PNodeGroupAllocPolicy,
1426 1435
    _PGroupNodeParams,
1436
    _PDiskParams,
1427 1437
    ]
1428 1438
  OP_RESULT = _TSetParamsResult
1429 1439

  
b/test/ganeti.constants_unittest.py
79 79
    self.failUnless(constants.OP_PRIO_NORMAL > constants.OP_PRIO_HIGH)
80 80
    self.failUnless(constants.OP_PRIO_HIGH > constants.OP_PRIO_HIGHEST)
81 81

  
82
  def testDiskDefaults(self):
83
    self.failUnless(set(constants.DISK_LD_DEFAULTS.keys()) ==
84
                    constants.LOGICAL_DISK_TYPES)
85
    self.failUnless(set(constants.DISK_DT_DEFAULTS.keys()) ==
86
                    constants.DISK_TEMPLATES)
87

  
82 88

  
83 89
class TestExportedNames(unittest.TestCase):
84 90
  _VALID_NAME_RE = re.compile(r"^[A-Z][A-Z0-9_]+$")

Also available in: Unified diff