Commit a8469393 authored by Iustin Pop's avatar Iustin Pop
Browse files

Add two new options types for CLI usage

For the new 2.0-style command line options, we need to parse strings of
the type:
  ident:key=val[,...]
and
  key=val[,...]

This patch adds two new option builders for these two, which return
(ident, {key=val,}) and {key=val,} for the above two formats. It also
handles specially constructs of type “key” (val is set to True) and
“no_key” (val is set to False, and the ‘no_’ prefix is stripped).

Reviewed-by: imsnah
parent 2d8ed1fe
......@@ -42,7 +42,8 @@ from optparse import (OptionParser, make_option, TitledHelpFormatter,
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain",
"SubmitOpCode", "GetClient",
"cli_option", "GenerateTable", "AskUser",
"cli_option", "ikv_option", "keyval_option",
"GenerateTable", "AskUser",
"ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
"USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT", "SUBMIT_OPT",
"ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
......@@ -221,8 +222,88 @@ class CliOption(Option):
TYPE_CHECKER["unit"] = check_unit
def _SplitKeyVal(opt, data):
"""Convert a KeyVal string into a dict.
This function will convert a key=val[,...] string into a dict. Empty
values will be converted specially: keys which have the prefix 'no_'
will have the value=False and the prefix stripped, the others will
have value=True.
@type opt: string
@param opt: a string holding the option name for which we process the
data, used in building error messages
@type data: string
@param data: a string of the format key=val,key=val,...
@rtype: dict
@return: {key=val, key=val}
@raises errors.ParameterError: if there are duplicate keys
"""
NO_PREFIX = "no_"
kv_dict = {}
for elem in data.split(","):
if "=" in elem:
key, val = elem.split("=", 1)
else:
if elem.startswith(NO_PREFIX):
key, val = elem[len(NO_PREFIX):], False
else:
key, val = elem, True
if key in kv_dict:
raise errors.ParameterError("Duplicate key '%s' in option %s" %
(key, opt))
kv_dict[key] = val
return kv_dict
def check_ident_key_val(option, opt, value):
"""Custom parser for the IdentKeyVal option type.
"""
if ":" not in value:
retval = (value, {})
else:
ident, rest = value.split(":", 1)
kv_dict = _SplitKeyVal(opt, rest)
retval = (ident, kv_dict)
return retval
class IdentKeyValOption(Option):
"""Custom option class for ident:key=val,key=val options.
This will store the parsed values as a tuple (ident, {key: val}). As
such, multiple uses of this option via action=append is possible.
"""
TYPES = Option.TYPES + ("identkeyval",)
TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
TYPE_CHECKER["identkeyval"] = check_ident_key_val
def check_key_val(option, opt, value):
"""Custom parser for the KeyVal option type.
"""
return _SplitKeyVal(opt, value)
class KeyValOption(Option):
"""Custom option class for key=val,key=val options.
This will store the parsed values as a dict {key: val}.
"""
TYPES = Option.TYPES + ("keyval",)
TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
TYPE_CHECKER["keyval"] = check_key_val
# optparse.py sets make_option, so we do it for our own option class, too
cli_option = CliOption
ikv_option = IdentKeyValOption
keyval_option = KeyValOption
def _ParseArgs(argv, commands, aliases):
......
......@@ -27,7 +27,7 @@ import ganeti
import testutils
from ganeti import constants
from ganeti import cli
from ganeti.errors import OpPrereqError
from ganeti.errors import OpPrereqError, ParameterError
class TestParseTimespec(unittest.TestCase):
"""Testing case for ParseTimespec"""
......@@ -59,5 +59,21 @@ class TestParseTimespec(unittest.TestCase):
self.failUnlessRaises(OpPrereqError, cli.ParseTimespec, value)
class TestSplitKeyVal(unittest.TestCase):
"""Testing case for cli._SplitKeyVal"""
DATA = "a=b,c,no_d"
RESULT = {"a": "b", "c": True, "d": False}
def testSplitKeyVal(self):
"""Test splitting"""
self.failUnlessEqual(cli._SplitKeyVal("option", self.DATA), self.RESULT)
def testDuplicateParam(self):
"""Test duplicate parameters"""
for data in ("a=1,a=2", "a,no_a"):
self.failUnlessRaises(ParameterError, cli._SplitKeyVal,
"option", data)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment