Expand Objects.hs definitions
[ganeti-local] / lib / cli.py
index 1a3926a..845c8a7 100644 (file)
@@ -195,6 +195,7 @@ __all__ = [
   "USE_REPL_NET_OPT",
   "VERBOSE_OPT",
   "VG_NAME_OPT",
+  "WFSYNC_OPT",
   "YES_DOIT_OPT",
   "DISK_STATE_OPT",
   "HV_STATE_OPT",
@@ -415,7 +416,8 @@ def _ExtractTagsObject(opts, args):
                 constants.TAG_NODE,
                 constants.TAG_INSTANCE):
     if not args:
-      raise errors.OpPrereqError("no arguments passed to the command")
+      raise errors.OpPrereqError("no arguments passed to the command",
+                                 errors.ECODE_INVAL)
     name = args.pop(0)
     retval = kind, name
   else:
@@ -462,7 +464,7 @@ def ListTags(opts, args):
 
   """
   kind, name = _ExtractTagsObject(opts, args)
-  cl = GetClient()
+  cl = GetClient(query=True)
   result = cl.QueryTags(kind, name)
   result = list(result)
   result.sort()
@@ -482,7 +484,7 @@ def AddTags(opts, args):
   kind, name = _ExtractTagsObject(opts, args)
   _ExtendTags(opts, args)
   if not args:
-    raise errors.OpPrereqError("No tags to be added")
+    raise errors.OpPrereqError("No tags to be added", errors.ECODE_INVAL)
   op = opcodes.OpTagsSet(kind=kind, name=name, tags=args)
   SubmitOrSend(op, opts)
 
@@ -499,7 +501,7 @@ def RemoveTags(opts, args):
   kind, name = _ExtractTagsObject(opts, args)
   _ExtendTags(opts, args)
   if not args:
-    raise errors.OpPrereqError("No tags to be removed")
+    raise errors.OpPrereqError("No tags to be removed", errors.ECODE_INVAL)
   op = opcodes.OpTagsDel(kind=kind, name=name, tags=args)
   SubmitOrSend(op, opts)
 
@@ -617,6 +619,18 @@ def check_list(option, opt, value): # pylint: disable=W0613
     return utils.UnescapeAndSplit(value)
 
 
+def check_maybefloat(option, opt, value): # pylint: disable=W0613
+  """Custom parser for float numbers which might be also defaults.
+
+  """
+  value = value.lower()
+
+  if value == constants.VALUE_DEFAULT:
+    return value
+  else:
+    return float(value)
+
+
 # completion_suggestion is normally a list. Using numeric values not evaluating
 # to False for dynamic completion.
 (OPT_COMPL_MANY_NODES,
@@ -651,6 +665,7 @@ class CliOption(Option):
     "unit",
     "bool",
     "list",
+    "maybefloat",
     )
   TYPE_CHECKER = Option.TYPE_CHECKER.copy()
   TYPE_CHECKER["identkeyval"] = check_ident_key_val
@@ -658,6 +673,7 @@ class CliOption(Option):
   TYPE_CHECKER["unit"] = check_unit
   TYPE_CHECKER["bool"] = check_bool
   TYPE_CHECKER["list"] = check_list
+  TYPE_CHECKER["maybefloat"] = check_maybefloat
 
 
 # optparse.py sets make_option, so we do it for our own option class, too
@@ -733,6 +749,10 @@ NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
                         default=True, action="store_false",
                         help="Don't wait for sync (DANGEROUS!)")
 
+WFSYNC_OPT = cli_option("--wait-for-sync", dest="wait_for_sync",
+                        default=False, action="store_true",
+                        help="Wait for disks to sync")
+
 ONLINE_INST_OPT = cli_option("--online", dest="online_inst",
                              action="store_true", default=False,
                              help="Enable offline instance")
@@ -768,18 +788,19 @@ IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
                             completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
 
 DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator",
-                            metavar="<NAME>",
-                            help="Set the default instance allocator plugin",
-                            default=None, type="string",
-                            completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
+                                    metavar="<NAME>",
+                                    help="Set the default instance"
+                                    " allocator plugin",
+                                    default=None, type="string",
+                                    completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
 
 OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
                     metavar="<os>",
                     completion_suggest=OPT_COMPL_ONE_OS)
 
 OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams",
-                         type="keyval", default={},
-                         help="OS parameters")
+                          type="keyval", default={},
+                          help="OS parameters")
 
 FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant",
                                action="store_true", default=False,
@@ -810,37 +831,49 @@ DISK_PARAMS_OPT = cli_option("-D", "--disk-parameters", dest="diskparams",
 
 SPECS_MEM_SIZE_OPT = cli_option("--specs-mem-size", dest="ispecs_mem_size",
                                  type="keyval", default={},
-                                 help="Memory count specs: min, max, std"
-                                 " (in MB)")
+                                 help="Memory size specs: list of key=value,"
+                                " where key is one of min, max, std"
+                                 " (in MB or using a unit)")
 
 SPECS_CPU_COUNT_OPT = cli_option("--specs-cpu-count", dest="ispecs_cpu_count",
                                  type="keyval", default={},
-                                 help="CPU count specs: min, max, std")
+                                 help="CPU count specs: list of key=value,"
+                                 " where key is one of min, max, std")
 
 SPECS_DISK_COUNT_OPT = cli_option("--specs-disk-count",
                                   dest="ispecs_disk_count",
                                   type="keyval", default={},
-                                  help="Disk count specs: min, max, std")
+                                  help="Disk count specs: list of key=value,"
+                                  " where key is one of min, max, std")
 
 SPECS_DISK_SIZE_OPT = cli_option("--specs-disk-size", dest="ispecs_disk_size",
                                  type="keyval", default={},
-                                 help="Disk size specs: min, max, std (in MB)")
+                                 help="Disk size specs: list of key=value,"
+                                 " where key is one of min, max, std"
+                                 " (in MB or using a unit)")
 
 SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count",
                                  type="keyval", default={},
-                                 help="NIC count specs: min, max, std")
+                                 help="NIC count specs: list of key=value,"
+                                 " where key is one of min, max, std")
 
 IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates",
-                                 dest="ipolicy_disk_templates",
-                                 type="list", default=None,
-                                 help="Comma-separated list of"
-                                 " enabled disk templates")
+                                    dest="ipolicy_disk_templates",
+                                    type="list", default=None,
+                                    help="Comma-separated list of"
+                                    " enabled disk templates")
 
 IPOLICY_VCPU_RATIO = cli_option("--ipolicy-vcpu-ratio",
                                  dest="ipolicy_vcpu_ratio",
-                                 type="float", default=None,
+                                 type="maybefloat", default=None,
                                  help="The maximum allowed vcpu-to-cpu ratio")
 
+IPOLICY_SPINDLE_RATIO = cli_option("--ipolicy-spindle-ratio",
+                                   dest="ipolicy_spindle_ratio",
+                                   type="maybefloat", default=None,
+                                   help=("The maximum allowed instances to"
+                                         " spindle ratio"))
+
 HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
                             help="Hypervisor and hypervisor options, in the"
                             " format hypervisor:option=value,option=value,...",
@@ -1055,12 +1088,12 @@ DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO,
                                " (excluded from allocation operations)"))
 
 CAPAB_MASTER_OPT = cli_option("--master-capable", dest="master_capable",
-                    type="bool", default=None, metavar=_YORNO,
-                    help="Set the master_capable flag on the node")
+                              type="bool", default=None, metavar=_YORNO,
+                              help="Set the master_capable flag on the node")
 
 CAPAB_VM_OPT = cli_option("--vm-capable", dest="vm_capable",
-                    type="bool", default=None, metavar=_YORNO,
-                    help="Set the vm_capable flag on the node")
+                          type="bool", default=None, metavar=_YORNO,
+                          help="Set the vm_capable flag on the node")
 
 ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
                              type="bool", default=None, metavar=_YORNO,
@@ -1117,11 +1150,12 @@ MASTER_NETMASK_OPT = cli_option("--master-netmask", dest="master_netmask",
                                 default=None)
 
 USE_EXTERNAL_MIP_SCRIPT = cli_option("--use-external-mip-script",
-                                dest="use_external_mip_script",
-                                help="Specify whether to run a user-provided"
-                                " script for the master IP address turnup and"
-                                " turndown operations",
-                                type="bool", metavar=_YORNO, default=None)
+                                     dest="use_external_mip_script",
+                                     help="Specify whether to run a"
+                                     " user-provided script for the master"
+                                     " IP address turnup and"
+                                     " turndown operations",
+                                     type="bool", metavar=_YORNO, default=None)
 
 GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
                                 help="Specify the default directory (cluster-"
@@ -1130,14 +1164,13 @@ GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
                                 metavar="DIR",
                                 default=constants.DEFAULT_FILE_STORAGE_DIR)
 
-GLOBAL_SHARED_FILEDIR_OPT = cli_option("--shared-file-storage-dir",
-                            dest="shared_file_storage_dir",
-                            help="Specify the default directory (cluster-"
-                            "wide) for storing the shared file-based"
-                            " disks [%s]" %
-                            constants.DEFAULT_SHARED_FILE_STORAGE_DIR,
-                            metavar="SHAREDDIR",
-                            default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR)
+GLOBAL_SHARED_FILEDIR_OPT = cli_option(
+  "--shared-file-storage-dir",
+  dest="shared_file_storage_dir",
+  help="Specify the default directory (cluster-wide) for storing the"
+  " shared file-based disks [%s]" %
+  constants.DEFAULT_SHARED_FILE_STORAGE_DIR,
+  metavar="SHAREDDIR", default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR)
 
 NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
                                    help="Don't modify /etc/hosts",
@@ -1175,9 +1208,10 @@ TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int",
                          help="Maximum time to wait")
 
 SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout",
-                         dest="shutdown_timeout", type="int",
-                         default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
-                         help="Maximum time to wait for instance shutdown")
+                                  dest="shutdown_timeout", type="int",
+                                  default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
+                                  help="Maximum time to wait for instance"
+                                  " shutdown")
 
 INTERVAL_OPT = cli_option("--interval", dest="interval", type="int",
                           default=None,
@@ -1205,19 +1239,19 @@ NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert",
                                      " certificate"))
 
 SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
-                           default=None,
-                           help="File containing new SPICE certificate")
+                            default=None,
+                            help="File containing new SPICE certificate")
 
 SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert",
-                           default=None,
-                           help="File containing the certificate of the CA"
-                                " which signed the SPICE certificate")
+                              default=None,
+                              help="File containing the certificate of the CA"
+                              " which signed the SPICE certificate")
 
 NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate",
-                               dest="new_spice_cert", default=None,
-                               action="store_true",
-                               help=("Generate a new self-signed SPICE"
-                                     " certificate"))
+                                dest="new_spice_cert", default=None,
+                                action="store_true",
+                                help=("Generate a new self-signed SPICE"
+                                      " certificate"))
 
 NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
                                     dest="new_confd_hmac_key",
@@ -1275,10 +1309,10 @@ REMOVE_UIDS_OPT = cli_option("--remove-uids", default=None,
                                    " removed from the user-id pool"))
 
 RESERVED_LVS_OPT = cli_option("--reserved-lvs", default=None,
-                             action="store", dest="reserved_lvs",
-                             help=("A comma-separated list of reserved"
-                                   " logical volumes names, that will be"
-                                   " ignored by cluster verify"))
+                              action="store", dest="reserved_lvs",
+                              help=("A comma-separated list of reserved"
+                                    " logical volumes names, that will be"
+                                    " ignored by cluster verify"))
 
 ROMAN_OPT = cli_option("--roman",
                        dest="roman_integers", default=False,
@@ -1333,8 +1367,8 @@ NODE_POWERED_OPT = cli_option("--node-powered", default=None,
                               help="Specify if the SoR for node is powered")
 
 OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int",
-                         default=constants.OOB_TIMEOUT,
-                         help="Maximum time to wait for out-of-band helper")
+                             default=constants.OOB_TIMEOUT,
+                             help="Maximum time to wait for out-of-band helper")
 
 POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float",
                              default=constants.OOB_POWER_DELAY,
@@ -1378,14 +1412,17 @@ IGNORE_ERRORS_OPT = cli_option("-I", "--ignore-errors", default=[],
 
 DISK_STATE_OPT = cli_option("--disk-state", default=[], dest="disk_state",
                             action="append",
-                            help=("Specify disk state information in the format"
-                                  " storage_type/identifier:option=value,..."),
+                            help=("Specify disk state information in the"
+                                  " format"
+                                  " storage_type/identifier:option=value,...;"
+                                  " note this is unused for now"),
                             type="identkeyval")
 
 HV_STATE_OPT = cli_option("--hypervisor-state", default=[], dest="hv_state",
                           action="append",
                           help=("Specify hypervisor state information in the"
-                                " format hypervisor:option=value,..."),
+                                " format hypervisor:option=value,...;"
+                                " note this is unused for now"),
                           type="identkeyval")
 
 IGNORE_IPOLICY_OPT = cli_option("--ignore-ipolicy", dest="ignore_ipolicy",
@@ -1438,6 +1475,7 @@ INSTANCE_POLICY_OPTS = [
   SPECS_NIC_COUNT_OPT,
   IPOLICY_DISK_TEMPLATES,
   IPOLICY_VCPU_RATIO,
+  IPOLICY_SPINDLE_RATIO,
   ]
 
 
@@ -2044,10 +2082,23 @@ def SetGenericOpcodeOpts(opcode_list, options):
       op.priority = _PRIONAME_TO_VALUE[options.priority]
 
 
-def GetClient():
+def GetClient(query=False):
+  """Connects to the a luxi socket and returns a client.
+
+  @type query: boolean
+  @param query: this signifies that the client will only be
+      used for queries; if the build-time parameter
+      enable-split-queries is enabled, then the client will be
+      connected to the query socket instead of the masterd socket
+
+  """
+  if query and constants.ENABLE_SPLIT_QUERY:
+    address = constants.QUERY_SOCKET
+  else:
+    address = None
   # TODO: Cache object?
   try:
-    client = luxi.Client()
+    client = luxi.Client(address=address)
   except luxi.NoMasterError:
     ss = ssconf.SimpleStore()
 
@@ -2056,13 +2107,14 @@ def GetClient():
       ss.GetMasterNode()
     except errors.ConfigurationError:
       raise errors.OpPrereqError("Cluster not initialized or this machine is"
-                                 " not part of a cluster")
+                                 " not part of a cluster",
+                                 errors.ECODE_INVAL)
 
     master, myself = ssconf.GetMasterAndMyself(ss=ss)
     if master != myself:
       raise errors.OpPrereqError("This is not the master node, please connect"
                                  " to node '%s' and rerun the command" %
-                                 master)
+                                 master, errors.ECODE_INVAL)
     raise
   return client
 
@@ -2106,7 +2158,7 @@ def FormatError(err):
   elif isinstance(err, errors.OpPrereqError):
     if len(err.args) == 2:
       obuf.write("Failure: prerequisites not met for this"
-               " operation:\nerror type: %s, error details:\n%s" %
+                 " operation:\nerror type: %s, error details:\n%s" %
                  (err.args[1], err.args[0]))
     else:
       obuf.write("Failure: prerequisites not met for this"
@@ -2236,7 +2288,8 @@ def ParseNicOption(optvalue):
   try:
     nic_max = max(int(nidx[0]) + 1 for nidx in optvalue)
   except (TypeError, ValueError), err:
-    raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
+    raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err),
+                               errors.ECODE_INVAL)
 
   nics = [{}] * nic_max
   for nidx, ndict in optvalue:
@@ -2244,7 +2297,7 @@ def ParseNicOption(optvalue):
 
     if not isinstance(ndict, dict):
       raise errors.OpPrereqError("Invalid nic/%d value: expected dict,"
-                                 " got %s" % (nidx, ndict))
+                                 " got %s" % (nidx, ndict), errors.ECODE_INVAL)
 
     utils.ForceDictType(ndict, constants.INIC_PARAMS_TYPES)
 
@@ -2288,15 +2341,16 @@ def GenericInstanceCreate(mode, opts, args):
   if opts.disk_template == constants.DT_DISKLESS:
     if opts.disks or opts.sd_size is not None:
       raise errors.OpPrereqError("Diskless instance but disk"
-                                 " information passed")
+                                 " information passed", errors.ECODE_INVAL)
     disks = []
   else:
     if (not opts.disks and not opts.sd_size
         and mode == constants.INSTANCE_CREATE):
-      raise errors.OpPrereqError("No disk information specified")
+      raise errors.OpPrereqError("No disk information specified",
+                                 errors.ECODE_INVAL)
     if opts.disks and opts.sd_size is not None:
       raise errors.OpPrereqError("Please use either the '--disk' or"
-                                 " '-s' option")
+                                 " '-s' option", errors.ECODE_INVAL)
     if opts.sd_size is not None:
       opts.disks = [(0, {constants.IDISK_SIZE: opts.sd_size})]
 
@@ -2304,7 +2358,8 @@ def GenericInstanceCreate(mode, opts, args):
       try:
         disk_max = max(int(didx[0]) + 1 for didx in opts.disks)
       except ValueError, err:
-        raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
+        raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err),
+                                   errors.ECODE_INVAL)
       disks = [{}] * disk_max
     else:
       disks = []
@@ -2312,25 +2367,25 @@ def GenericInstanceCreate(mode, opts, args):
       didx = int(didx)
       if not isinstance(ddict, dict):
         msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
-        raise errors.OpPrereqError(msg)
+        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
       elif constants.IDISK_SIZE in ddict:
         if constants.IDISK_ADOPT in ddict:
           raise errors.OpPrereqError("Only one of 'size' and 'adopt' allowed"
-                                     " (disk %d)" % didx)
+                                     " (disk %d)" % didx, errors.ECODE_INVAL)
         try:
           ddict[constants.IDISK_SIZE] = \
             utils.ParseUnit(ddict[constants.IDISK_SIZE])
         except ValueError, err:
           raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
-                                     (didx, err))
+                                     (didx, err), errors.ECODE_INVAL)
       elif constants.IDISK_ADOPT in ddict:
         if mode == constants.INSTANCE_IMPORT:
           raise errors.OpPrereqError("Disk adoption not allowed for instance"
-                                     " import")
+                                     " import", errors.ECODE_INVAL)
         ddict[constants.IDISK_SIZE] = 0
       else:
         raise errors.OpPrereqError("Missing size or adoption source for"
-                                   " disk %d" % didx)
+                                   " disk %d" % didx, errors.ECODE_INVAL)
       disks[didx] = ddict
 
   if opts.tags is not None:
@@ -2821,7 +2876,7 @@ def _WarnUnknownFields(fdefs):
 
 def GenericList(resource, fields, names, unit, separator, header, cl=None,
                 format_override=None, verbose=False, force_filter=False,
-                namefield=None, qfilter=None):
+                namefield=None, qfilter=None, isnumeric=False):
   """Generic implementation for listing all items of a resource.
 
   @param resource: One of L{constants.QR_VIA_LUXI}
@@ -2849,12 +2904,17 @@ def GenericList(resource, fields, names, unit, separator, header, cl=None,
     L{qlang.MakeFilter} for details)
   @type qfilter: list or None
   @param qfilter: Query filter (in addition to names)
+  @param isnumeric: bool
+  @param isnumeric: Whether the namefield's type is numeric, and therefore
+    any simple filters built by namefield should use integer values to
+    reflect that
 
   """
   if not names:
     names = None
 
-  namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield)
+  namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield,
+                                isnumeric=isnumeric)
 
   if qfilter is None:
     qfilter = namefilter
@@ -3039,7 +3099,8 @@ def ParseTimespec(value):
   """
   value = str(value)
   if not value:
-    raise errors.OpPrereqError("Empty time specification passed")
+    raise errors.OpPrereqError("Empty time specification passed",
+                               errors.ECODE_INVAL)
   suffix_map = {
     "s": 1,
     "m": 60,
@@ -3051,17 +3112,19 @@ def ParseTimespec(value):
     try:
       value = int(value)
     except (TypeError, ValueError):
-      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
+      raise errors.OpPrereqError("Invalid time specification '%s'" % value,
+                                 errors.ECODE_INVAL)
   else:
     multiplier = suffix_map[value[-1]]
     value = value[:-1]
     if not value: # no data left after stripping the suffix
       raise errors.OpPrereqError("Invalid time specification (only"
-                                 " suffix passed)")
+                                 " suffix passed)", errors.ECODE_INVAL)
     try:
       value = int(value) * multiplier
     except (TypeError, ValueError):
-      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
+      raise errors.OpPrereqError("Invalid time specification '%s'" % value,
+                                 errors.ECODE_INVAL)
   return value
 
 
@@ -3410,6 +3473,19 @@ def ConfirmOperation(names, list_type, text, extra=""):
   return choice
 
 
+def _MaybeParseUnit(elements):
+  """Parses and returns an array of potential values with units.
+
+  """
+  parsed = {}
+  for k, v in elements.items():
+    if v == constants.VALUE_DEFAULT:
+      parsed[k] = v
+    else:
+      parsed[k] = utils.ParseUnit(v)
+  return parsed
+
+
 def CreateIPolicyFromOpts(ispecs_mem_size=None,
                           ispecs_cpu_count=None,
                           ispecs_disk_count=None,
@@ -3417,6 +3493,7 @@ def CreateIPolicyFromOpts(ispecs_mem_size=None,
                           ispecs_nic_count=None,
                           ipolicy_disk_templates=None,
                           ipolicy_vcpu_ratio=None,
+                          ipolicy_spindle_ratio=None,
                           group_ipolicy=False,
                           allowed_values=None,
                           fill_all=False):
@@ -3427,6 +3504,17 @@ def CreateIPolicyFromOpts(ispecs_mem_size=None,
 
 
   """
+  try:
+    if ispecs_mem_size:
+      ispecs_mem_size = _MaybeParseUnit(ispecs_mem_size)
+    if ispecs_disk_size:
+      ispecs_disk_size = _MaybeParseUnit(ispecs_disk_size)
+  except (TypeError, ValueError, errors.UnitParseError), err:
+    raise errors.OpPrereqError("Invalid disk (%s) or memory (%s) size"
+                               " in policy: %s" %
+                               (ispecs_disk_size, ispecs_mem_size, err),
+                               errors.ECODE_INVAL)
+
   # prepare ipolicy dict
   ipolicy_transposed = {
     constants.ISPEC_MEM_SIZE: ispecs_mem_size,
@@ -3459,10 +3547,15 @@ def CreateIPolicyFromOpts(ispecs_mem_size=None,
     if ipolicy_vcpu_ratio is None:
       ipolicy_vcpu_ratio = \
         constants.IPOLICY_DEFAULTS[constants.IPOLICY_VCPU_RATIO]
+    if ipolicy_spindle_ratio is None:
+      ipolicy_spindle_ratio = \
+        constants.IPOLICY_DEFAULTS[constants.IPOLICY_SPINDLE_RATIO]
   if ipolicy_disk_templates is not None:
     ipolicy_out[constants.IPOLICY_DTS] = list(ipolicy_disk_templates)
   if ipolicy_vcpu_ratio is not None:
     ipolicy_out[constants.IPOLICY_VCPU_RATIO] = ipolicy_vcpu_ratio
+  if ipolicy_spindle_ratio is not None:
+    ipolicy_out[constants.IPOLICY_SPINDLE_RATIO] = ipolicy_spindle_ratio
 
   assert not (frozenset(ipolicy_out.keys()) - constants.IPOLICY_ALL_KEYS)