Revision d2d3935a

b/lib/cli.py
188 188
  "SPECS_DISK_SIZE_OPT",
189 189
  "SPECS_MEM_SIZE_OPT",
190 190
  "SPECS_NIC_COUNT_OPT",
191
  "IPOLICY_STD_SPECS_OPT",
191 192
  "IPOLICY_DISK_TEMPLATES",
192 193
  "IPOLICY_VCPU_RATIO",
193 194
  "SPICE_CACERT_OPT",
......
957 958
                                 help="NIC count specs: list of key=value,"
958 959
                                 " where key is one of min, max, std")
959 960

  
961
IPOLICY_BOUNDS_SPECS_STR = "--ipolicy-bounds-specs"
962
IPOLICY_BOUNDS_SPECS_OPT = cli_option(IPOLICY_BOUNDS_SPECS_STR,
963
                                      dest="ipolicy_bounds_specs",
964
                                      type="listidentkeyval", default=None,
965
                                      help="Complete instance specs limits")
966

  
967
IPOLICY_STD_SPECS_STR = "--ipolicy-std-specs"
968
IPOLICY_STD_SPECS_OPT = cli_option(IPOLICY_STD_SPECS_STR,
969
                                   dest="ipolicy_std_specs",
970
                                   type="keyval", default=None,
971
                                   help="Complte standard instance specs")
972

  
960 973
IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates",
961 974
                                    dest="ipolicy_disk_templates",
962 975
                                    type="list", default=None,
......
1643 1656
  SPECS_DISK_SIZE_OPT,
1644 1657
  SPECS_MEM_SIZE_OPT,
1645 1658
  SPECS_NIC_COUNT_OPT,
1659
  IPOLICY_BOUNDS_SPECS_OPT,
1646 1660
  IPOLICY_DISK_TEMPLATES,
1647 1661
  IPOLICY_VCPU_RATIO,
1648 1662
  IPOLICY_SPINDLE_RATIO,
......
3789 3803
  return parsed
3790 3804

  
3791 3805

  
3792
def _InitIspecsFromOpts(ipolicy, ispecs_mem_size, ispecs_cpu_count,
3793
                        ispecs_disk_count, ispecs_disk_size, ispecs_nic_count,
3794
                        group_ipolicy, allowed_values):
3806
def _InitISpecsFromSplitOpts(ipolicy, ispecs_mem_size, ispecs_cpu_count,
3807
                             ispecs_disk_count, ispecs_disk_size,
3808
                             ispecs_nic_count, group_ipolicy, allowed_values):
3795 3809
  try:
3796 3810
    if ispecs_mem_size:
3797 3811
      ispecs_mem_size = _MaybeParseUnit(ispecs_mem_size)
......
3818 3832
  else:
3819 3833
    forced_type = TISPECS_CLUSTER_TYPES
3820 3834
  for specs in ispecs_transposed.values():
3835
    assert type(specs) is dict
3821 3836
    utils.ForceDictType(specs, forced_type, allowed_values=allowed_values)
3822 3837

  
3823 3838
  # then transpose
......
3836 3851
  ipolicy[constants.ISPECS_STD] = ispecs[constants.ISPECS_STD]
3837 3852

  
3838 3853

  
3854
def _ParseSpecUnit(spec, keyname):
3855
  ret = spec.copy()
3856
  for k in [constants.ISPEC_DISK_SIZE, constants.ISPEC_MEM_SIZE]:
3857
    if k in ret and ret[k] != constants.VALUE_DEFAULT:
3858
      try:
3859
        ret[k] = utils.ParseUnit(ret[k])
3860
      except (TypeError, ValueError, errors.UnitParseError), err:
3861
        raise errors.OpPrereqError(("Invalid parameter %s (%s) in %s instance"
3862
                                    " specs: %s" % (k, ret[k], keyname, err)),
3863
                                   errors.ECODE_INVAL)
3864
  return ret
3865

  
3866

  
3867
def _ParseISpec(spec, keyname, allowed_values):
3868
  ret = _ParseSpecUnit(spec, keyname)
3869
  utils.ForceDictType(ret, constants.ISPECS_PARAMETER_TYPES,
3870
                      allowed_values=allowed_values)
3871
  return ret
3872

  
3873

  
3874
def _InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs,
3875
                            group_ipolicy, allowed_values):
3876
  if minmax_ispecs is not None:
3877
    minmax_out = {}
3878
    for (key, spec) in minmax_ispecs.items():
3879
      if key not in constants.ISPECS_MINMAX_KEYS:
3880
        msg = "Invalid key in bounds instance specifications: %s" % key
3881
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3882
      minmax_out[key] = _ParseISpec(spec, key, allowed_values)
3883
    ipolicy_out[constants.ISPECS_MINMAX] = minmax_out
3884
  if std_ispecs is not None:
3885
    assert not group_ipolicy # This is not an option for gnt-group
3886
    ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std",
3887
                                                    allowed_values)
3888

  
3889

  
3839 3890
def CreateIPolicyFromOpts(ispecs_mem_size=None,
3840 3891
                          ispecs_cpu_count=None,
3841 3892
                          ispecs_disk_count=None,
3842 3893
                          ispecs_disk_size=None,
3843 3894
                          ispecs_nic_count=None,
3895
                          minmax_ispecs=None,
3896
                          std_ispecs=None,
3844 3897
                          ipolicy_disk_templates=None,
3845 3898
                          ipolicy_vcpu_ratio=None,
3846 3899
                          ipolicy_spindle_ratio=None,
......
3854 3907

  
3855 3908

  
3856 3909
  """
3910
  if ((ispecs_mem_size or ispecs_cpu_count or ispecs_disk_count or
3911
       ispecs_disk_size or ispecs_nic_count) and
3912
      (minmax_ispecs is not None or std_ispecs is not None)):
3913
    raise errors.OpPrereqError("A --specs-xxx option cannot be specified"
3914
                               " together with any --ipolicy-xxx-specs option",
3915
                               errors.ECODE_INVAL)
3857 3916

  
3858 3917
  ipolicy_out = objects.MakeEmptyIPolicy()
3859
  _InitIspecsFromOpts(ipolicy_out, ispecs_mem_size, ispecs_cpu_count,
3860
                      ispecs_disk_count, ispecs_disk_size, ispecs_nic_count,
3861
                      group_ipolicy, allowed_values)
3918
  if minmax_ispecs is None and std_ispecs is None:
3919
    _InitISpecsFromSplitOpts(ipolicy_out, ispecs_mem_size, ispecs_cpu_count,
3920
                             ispecs_disk_count, ispecs_disk_size,
3921
                             ispecs_nic_count, group_ipolicy, allowed_values)
3922
  else:
3923
    _InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs,
3924
                            group_ipolicy, allowed_values)
3862 3925

  
3863 3926
  if ipolicy_disk_templates is not None:
3864 3927
    if allowed_values and ipolicy_disk_templates in allowed_values:
b/lib/client/gnt_cluster.py
150 150
    ispecs_disk_count=opts.ispecs_disk_count,
151 151
    ispecs_disk_size=opts.ispecs_disk_size,
152 152
    ispecs_nic_count=opts.ispecs_nic_count,
153
    minmax_ispecs=opts.ipolicy_bounds_specs,
154
    std_ispecs=opts.ipolicy_std_specs,
153 155
    ipolicy_disk_templates=opts.ipolicy_disk_templates,
154 156
    ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
155 157
    ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
......
969 971
          opts.ispecs_disk_count or
970 972
          opts.ispecs_disk_size or
971 973
          opts.ispecs_nic_count or
974
          opts.ipolicy_bounds_specs is not None or
975
          opts.ipolicy_std_specs is not None or
972 976
          opts.ipolicy_disk_templates is not None or
973 977
          opts.ipolicy_vcpu_ratio is not None or
974 978
          opts.ipolicy_spindle_ratio is not None):
......
1025 1029
    ispecs_disk_count=opts.ispecs_disk_count,
1026 1030
    ispecs_disk_size=opts.ispecs_disk_size,
1027 1031
    ispecs_nic_count=opts.ispecs_nic_count,
1032
    minmax_ispecs=opts.ipolicy_bounds_specs,
1033
    std_ispecs=opts.ipolicy_std_specs,
1028 1034
    ipolicy_disk_templates=opts.ipolicy_disk_templates,
1029 1035
    ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
1030 1036
    ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
......
1495 1501
     MAINTAIN_NODE_HEALTH_OPT, UIDPOOL_OPT, DRBD_HELPER_OPT, NODRBD_STORAGE_OPT,
1496 1502
     DEFAULT_IALLOCATOR_OPT, PRIMARY_IP_VERSION_OPT, PREALLOC_WIPE_DISKS_OPT,
1497 1503
     NODE_PARAMS_OPT, GLOBAL_SHARED_FILEDIR_OPT, USE_EXTERNAL_MIP_SCRIPT,
1498
     DISK_PARAMS_OPT, HV_STATE_OPT, DISK_STATE_OPT, ENABLED_DISK_TEMPLATES_OPT]
1499
     + INSTANCE_POLICY_OPTS,
1504
     DISK_PARAMS_OPT, HV_STATE_OPT, DISK_STATE_OPT, ENABLED_DISK_TEMPLATES_OPT,
1505
     IPOLICY_STD_SPECS_OPT] + INSTANCE_POLICY_OPTS,
1500 1506
    "[opts...] <cluster_name>", "Initialises a new cluster configuration"),
1501 1507
  "destroy": (
1502 1508
    DestroyCluster, ARGS_NONE, [YES_DOIT_OPT],
......
1574 1580
     DRBD_HELPER_OPT, NODRBD_STORAGE_OPT, DEFAULT_IALLOCATOR_OPT,
1575 1581
     RESERVED_LVS_OPT, DRY_RUN_OPT, PRIORITY_OPT, PREALLOC_WIPE_DISKS_OPT,
1576 1582
     NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT, DISK_PARAMS_OPT, HV_STATE_OPT,
1577
     DISK_STATE_OPT, SUBMIT_OPT, ENABLED_DISK_TEMPLATES_OPT] +
1578
    INSTANCE_POLICY_OPTS,
1583
     DISK_STATE_OPT, SUBMIT_OPT, ENABLED_DISK_TEMPLATES_OPT,
1584
     IPOLICY_STD_SPECS_OPT] + INSTANCE_POLICY_OPTS,
1579 1585
    "[opts...]",
1580 1586
    "Alters the parameters of the cluster"),
1581 1587
  "renew-crypto": (
b/lib/client/gnt_group.py
53 53
    ispecs_disk_count=opts.ispecs_disk_count,
54 54
    ispecs_disk_size=opts.ispecs_disk_size,
55 55
    ispecs_nic_count=opts.ispecs_nic_count,
56
    minmax_ispecs=opts.ipolicy_bounds_specs,
56 57
    ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
57 58
    ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
58 59
    group_ipolicy=True)
......
161 162
  allmods = [opts.ndparams, opts.alloc_policy, opts.diskparams, opts.hv_state,
162 163
             opts.disk_state, opts.ispecs_mem_size, opts.ispecs_cpu_count,
163 164
             opts.ispecs_disk_count, opts.ispecs_disk_size,
164
             opts.ispecs_nic_count, opts.ipolicy_vcpu_ratio,
165
             opts.ipolicy_spindle_ratio, opts.diskparams]
165
             opts.ispecs_nic_count, opts.ipolicy_bounds_specs,
166
             opts.ipolicy_vcpu_ratio, opts.ipolicy_spindle_ratio,
167
             opts.diskparams]
166 168
  if allmods.count(None) == len(allmods):
167 169
    ToStderr("Please give at least one of the parameters.")
168 170
    return 1
......
196 198
    ispecs_disk_count=opts.ispecs_disk_count,
197 199
    ispecs_disk_size=opts.ispecs_disk_size,
198 200
    ispecs_nic_count=opts.ispecs_nic_count,
201
    minmax_ispecs=opts.ipolicy_bounds_specs,
199 202
    ipolicy_disk_templates=opts.ipolicy_disk_templates,
200 203
    ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio,
201 204
    ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio,
b/man/gnt-cluster.rst
183 183
| [\--specs-disk-size *spec-param*=*value* [,*spec-param*=*value*...]]
184 184
| [\--specs-mem-size *spec-param*=*value* [,*spec-param*=*value*...]]
185 185
| [\--specs-nic-count *spec-param*=*value* [,*spec-param*=*value*...]]
186
| [\--ipolicy-std-specs *spec*=*value* [,*spec*=*value*...]]
187
| [\--ipolicy-bounds-specs *bounds_ispecs*]
186 188
| [\--ipolicy-disk-templates *template* [,*template*...]]
187 189
| [\--ipolicy-spindle-ratio *ratio*]
188 190
| [\--ipolicy-vcpu-ratio *ratio*]
......
492 494
details about this role and other node roles, see the **ganeti**\(7).
493 495

  
494 496
The ``--specs-...`` and ``--ipolicy-...`` options specify the instance
495
policy on the cluster. For the ``--specs-...`` options, each option can
496
have three values: ``min``, ``max`` and ``std``, which can also be
497
modified on group level (except for ``std``, which is defined once for
498
the entire cluster). Please note, that ``std`` values are not the same
499
as defaults set by ``--beparams``, but they are used for the capacity
500
calculations.
501

  
502
The ``--ipolicy-disk-templates`` and ``--ipolicy-spindle-ratio`` options
503
take a decimal number. The ``--ipolicy-disk-templates`` option takes a
504
comma-separated list of disk templates.
497
policy on the cluster. The ``--ipolicy-bounds-specs`` option sets the
498
minimum and maximum specifications for instances. The format is:
499
min:*param*=*value*,.../max:*param*=*value*,... The
500
``--ipolicy-std-specs`` option takes a list of parameter/value pairs.
501
For both options, *param* can be:
502

  
503
- ``cpu-count``: number of VCPUs for an instance
504
- ``disk-count``: number of disk for an instance
505
- ``disk-size``: size of each disk
506
- ``memory-size``: instance memory
507
- ``nic-count``: number of network interface
508
- ``spindle-use``: spindle usage for an instance
509

  
510
For the ``--specs-...`` options, each option can have three values:
511
``min``, ``max`` and ``std``, which can also be modified on group level
512
(except for ``std``, which is defined once for the entire cluster).
513
Please note, that ``std`` values are not the same as defaults set by
514
``--beparams``, but they are used for the capacity calculations.
505 515

  
506 516
- ``--specs-cpu-count`` limits the number of VCPUs that can be used by an
507 517
  instance.
......
509 519
- ``--specs-disk-size`` limits the disk size for every disk used
510 520
- ``--specs-mem-size`` limits the amount of memory available
511 521
- ``--specs-nic-count`` sets limits on the number of NICs used
522

  
523
The ``--ipolicy-disk-templates`` and ``--ipolicy-spindle-ratio`` options
524
take a decimal number. The ``--ipolicy-disk-templates`` option takes a
525
comma-separated list of disk templates.
526

  
512 527
- ``--ipolicy-disk-templates`` limits the allowed disk templates
513 528
- ``--ipolicy-spindle-ratio`` limits the instances-spindles ratio
514 529
- ``--ipolicy-vcpu-ratio`` limits the vcpu-cpu ratio
......
589 604
| [\--specs-disk-size *spec-param*=*value* [,*spec-param*=*value*...]]
590 605
| [\--specs-mem-size *spec-param*=*value* [,*spec-param*=*value*...]]
591 606
| [\--specs-nic-count *spec-param*=*value* [,*spec-param*=*value*...]]
607
| [\--ipolicy-std-specs *spec*=*value* [,*spec*=*value*...]]
608
| [\--ipolicy-bounds-specs *bounds_ispecs*]
592 609
| [\--ipolicy-disk-templates *template* [,*template*...]]
593 610
| [\--ipolicy-spindle-ratio *ratio*]
594 611
| [\--ipolicy-vcpu-ratio *ratio*]
b/man/gnt-group.rst
32 32
| [\--specs-disk-size *spec-param*=*value* [,*spec-param*=*value*...]]
33 33
| [\--specs-mem-size *spec-param*=*value* [,*spec-param*=*value*...]]
34 34
| [\--specs-nic-count *spec-param*=*value* [,*spec-param*=*value*...]]
35
| [\--ipolicy-bounds-specs *bound_ispecs*]
35 36
| [\--ipolicy-disk-templates *template* [,*template*...]]
36 37
| [\--ipolicy-spindle-ratio *ratio*]
37 38
| [\--ipolicy-vcpu-ratio *ratio*]
......
109 110
| [\--specs-disk-size *spec-param*=*value* [,*spec-param*=*value*...]]
110 111
| [\--specs-mem-size *spec-param*=*value* [,*spec-param*=*value*...]]
111 112
| [\--specs-nic-count *spec-param*=*value* [,*spec-param*=*value*...]]
113
| [\--ipolicy-bounds-specs *bound_ispecs*]
112 114
| [\--ipolicy-disk-templates *template* [,*template*...]]
113 115
| [\--ipolicy-spindle-ratio *ratio*]
114 116
| [\--ipolicy-vcpu-ratio *ratio*]
b/test/py/ganeti.cli_unittest.py
1332 1332
                                     allowed_values=[allowedv])
1333 1333
    self.assertEqual(pol1, exp_pol1)
1334 1334

  
1335
  @staticmethod
1336
  def _ConvertSpecToStrings(spec):
1337
    ret = {}
1338
    for (par, val) in spec.items():
1339
      ret[par] = str(val)
1340
    return ret
1341

  
1342
  def _CheckNewStyleSpecsCall(self, exp_ipolicy, minmax_ispecs, std_ispecs,
1343
                              group_ipolicy, fill_all):
1344
    ipolicy = cli.CreateIPolicyFromOpts(minmax_ispecs=minmax_ispecs,
1345
                                        std_ispecs=std_ispecs,
1346
                                        group_ipolicy=group_ipolicy,
1347
                                        fill_all=fill_all)
1348
    self.assertEqual(ipolicy, exp_ipolicy)
1349

  
1350
  def _TestFullISpecsInner(self, skel_exp_ipol, exp_minmax, exp_std,
1351
                           group_ipolicy, fill_all):
1352
    exp_ipol = skel_exp_ipol.copy()
1353
    if exp_minmax is not None:
1354
      minmax_ispecs = {}
1355
      for (key, spec) in exp_minmax.items():
1356
        minmax_ispecs[key] = self._ConvertSpecToStrings(spec)
1357
      exp_ipol[constants.ISPECS_MINMAX] = exp_minmax
1358
    else:
1359
      minmax_ispecs = None
1360
      if constants.ISPECS_MINMAX not in exp_ipol:
1361
        empty_minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
1362
        exp_ipol[constants.ISPECS_MINMAX] = empty_minmax
1363
    if exp_std is not None:
1364
      std_ispecs = self._ConvertSpecToStrings(exp_std)
1365
      exp_ipol[constants.ISPECS_STD] = exp_std
1366
    else:
1367
      std_ispecs = None
1368
      if constants.ISPECS_STD not in exp_ipol:
1369
        exp_ipol[constants.ISPECS_STD] = {}
1370

  
1371
    self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1372
                                 group_ipolicy, fill_all)
1373
    if minmax_ispecs:
1374
      for (key, spec) in minmax_ispecs.items():
1375
        for par in [constants.ISPEC_MEM_SIZE, constants.ISPEC_DISK_SIZE]:
1376
          if par in spec:
1377
            spec[par] += "m"
1378
            self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1379
                                         group_ipolicy, fill_all)
1380
    if std_ispecs:
1381
      for par in [constants.ISPEC_MEM_SIZE, constants.ISPEC_DISK_SIZE]:
1382
        if par in std_ispecs:
1383
          std_ispecs[par] += "m"
1384
          self._CheckNewStyleSpecsCall(exp_ipol, minmax_ispecs, std_ispecs,
1385
                                       group_ipolicy, fill_all)
1386

  
1387
  def testFullISpecs(self):
1388
    exp_minmax1 = {
1389
      constants.ISPECS_MIN: {
1390
        constants.ISPEC_MEM_SIZE: 512,
1391
        constants.ISPEC_CPU_COUNT: 2,
1392
        constants.ISPEC_DISK_COUNT: 2,
1393
        constants.ISPEC_DISK_SIZE: 512,
1394
        constants.ISPEC_NIC_COUNT: 2,
1395
        constants.ISPEC_SPINDLE_USE: 2,
1396
        },
1397
      constants.ISPECS_MAX: {
1398
        constants.ISPEC_MEM_SIZE: 768*1024,
1399
        constants.ISPEC_CPU_COUNT: 7,
1400
        constants.ISPEC_DISK_COUNT: 6,
1401
        constants.ISPEC_DISK_SIZE: 2048*1024,
1402
        constants.ISPEC_NIC_COUNT: 3,
1403
        constants.ISPEC_SPINDLE_USE: 1,
1404
        },
1405
      }
1406
    exp_std1 = {
1407
      constants.ISPEC_MEM_SIZE: 768*1024,
1408
      constants.ISPEC_CPU_COUNT: 7,
1409
      constants.ISPEC_DISK_COUNT: 6,
1410
      constants.ISPEC_DISK_SIZE: 2048*1024,
1411
      constants.ISPEC_NIC_COUNT: 3,
1412
      constants.ISPEC_SPINDLE_USE: 1,
1413
      }
1414
    for fill_all in [False, True]:
1415
      if fill_all:
1416
        skel_ipolicy = constants.IPOLICY_DEFAULTS
1417
      else:
1418
        skel_ipolicy = {}
1419
      self._TestFullISpecsInner(skel_ipolicy, exp_minmax1, exp_std1,
1420
                                False, fill_all)
1421
      self._TestFullISpecsInner(skel_ipolicy, None, exp_std1,
1422
                                False, fill_all)
1423
      self._TestFullISpecsInner(skel_ipolicy, exp_minmax1, None,
1424
                                False, fill_all)
1335 1425

  
1336 1426
if __name__ == "__main__":
1337 1427
  testutils.GanetiTestProgram()

Also available in: Unified diff