Allow -P to be invoked multiple times
If --parameters is specified more than once it is assumed that each one contains only one parameter key=value. This means semicolons are ignored if there is more than one -P specified. This should be fully backwared compatible, a single -P will be assumed to be semicolon delimited. Change-Id: Ic202ff3e0596b786cd5b07c53509f97a95d41585 Closes-Bug: #1224825 Closes-Bug: #1229030
This commit is contained in:
@@ -147,17 +147,25 @@ def exit(msg=''):
|
|||||||
|
|
||||||
def format_parameters(params):
|
def format_parameters(params):
|
||||||
'''Reformat parameters into dict of format expected by the API.'''
|
'''Reformat parameters into dict of format expected by the API.'''
|
||||||
parameters = {}
|
|
||||||
if params:
|
|
||||||
for count, p in enumerate(params.split(';'), 1):
|
|
||||||
try:
|
|
||||||
(n, v) = p.split(('='), 1)
|
|
||||||
except ValueError:
|
|
||||||
msg = '%s(%s). %s.' % ('Malformed parameter', p,
|
|
||||||
'Use the key=value format')
|
|
||||||
raise exc.CommandError(msg)
|
|
||||||
|
|
||||||
parameters[n] = v
|
if not params:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# expect multiple invocations of --parameters but fall back
|
||||||
|
# to ; delimited if only one --parameters is specified
|
||||||
|
if len(params) == 1:
|
||||||
|
params = params[0].split(';')
|
||||||
|
|
||||||
|
parameters = {}
|
||||||
|
for p in params:
|
||||||
|
try:
|
||||||
|
(n, v) = p.split(('='), 1)
|
||||||
|
except ValueError:
|
||||||
|
msg = '%s(%s). %s.' % ('Malformed parameter', p,
|
||||||
|
'Use the key=value format')
|
||||||
|
raise exc.CommandError(msg)
|
||||||
|
|
||||||
|
parameters[n] = v
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ class shellTest(testtools.TestCase):
|
|||||||
self.assertEqual({}, utils.format_parameters(None))
|
self.assertEqual({}, utils.format_parameters(None))
|
||||||
|
|
||||||
def test_format_parameters(self):
|
def test_format_parameters(self):
|
||||||
p = utils.format_parameters(
|
p = utils.format_parameters([
|
||||||
'InstanceType=m1.large;DBUsername=wp;'
|
'InstanceType=m1.large;DBUsername=wp;'
|
||||||
'DBPassword=verybadpassword;KeyName=heat_key;'
|
'DBPassword=verybadpassword;KeyName=heat_key;'
|
||||||
'LinuxDistribution=F17')
|
'LinuxDistribution=F17'])
|
||||||
self.assertEqual({'InstanceType': 'm1.large',
|
self.assertEqual({'InstanceType': 'm1.large',
|
||||||
'DBUsername': 'wp',
|
'DBUsername': 'wp',
|
||||||
'DBPassword': 'verybadpassword',
|
'DBPassword': 'verybadpassword',
|
||||||
@@ -35,20 +35,50 @@ class shellTest(testtools.TestCase):
|
|||||||
}, p)
|
}, p)
|
||||||
|
|
||||||
def test_format_parameters_split(self):
|
def test_format_parameters_split(self):
|
||||||
p = utils.format_parameters(
|
p = utils.format_parameters([
|
||||||
'KeyName=heat_key;'
|
'KeyName=heat_key;'
|
||||||
'DnsSecKey=hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/wwC7a4oAONQ/fDV5ct'
|
'DnsSecKey=hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/wwC7a4oAONQ/fDV5ct'
|
||||||
'qrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==;'
|
'qrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==;'
|
||||||
'UpstreamDNS=8.8.8.8')
|
'UpstreamDNS=8.8.8.8'])
|
||||||
self.assertEqual({'KeyName': 'heat_key',
|
self.assertEqual({'KeyName': 'heat_key',
|
||||||
'DnsSecKey': 'hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/ww'
|
'DnsSecKey': 'hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/ww'
|
||||||
'C7a4oAONQ/fDV5ctqrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==',
|
'C7a4oAONQ/fDV5ctqrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==',
|
||||||
'UpstreamDNS': '8.8.8.8'}, p)
|
'UpstreamDNS': '8.8.8.8'}, p)
|
||||||
|
|
||||||
|
def test_format_parameters_multiple(self):
|
||||||
|
p = utils.format_parameters([
|
||||||
|
'KeyName=heat_key',
|
||||||
|
'DnsSecKey=hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/wwC7a4oAONQ/fDV5ct'
|
||||||
|
'qrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==',
|
||||||
|
'UpstreamDNS=8.8.8.8'])
|
||||||
|
self.assertEqual({'KeyName': 'heat_key',
|
||||||
|
'DnsSecKey': 'hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/ww'
|
||||||
|
'C7a4oAONQ/fDV5ctqrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==',
|
||||||
|
'UpstreamDNS': '8.8.8.8'}, p)
|
||||||
|
|
||||||
|
def test_format_parameters_multiple_semicolon_values(self):
|
||||||
|
p = utils.format_parameters([
|
||||||
|
'KeyName=heat_key',
|
||||||
|
'DnsSecKey=hsgx1m31;PbaNF4WEcHlwj;IlCGgfOdoB;58/ww7a4oAO;NQ/fD==',
|
||||||
|
'UpstreamDNS=8.8.8.8'])
|
||||||
|
self.assertEqual({'KeyName': 'heat_key',
|
||||||
|
'DnsSecKey': 'hsgx1m31;PbaNF4WEcHlwj;IlCGgfOdoB;58/'
|
||||||
|
'ww7a4oAO;NQ/fD==',
|
||||||
|
'UpstreamDNS': '8.8.8.8'}, p)
|
||||||
|
|
||||||
def test_format_parameter_bad_parameter(self):
|
def test_format_parameter_bad_parameter(self):
|
||||||
params = 'KeyName=heat_key;UpstreamDNS8.8.8.8'
|
params = ['KeyName=heat_key;UpstreamDNS8.8.8.8']
|
||||||
self.assertRaises(exc.CommandError,
|
ex = self.assertRaises(exc.CommandError,
|
||||||
utils.format_parameters, params)
|
utils.format_parameters, params)
|
||||||
|
self.assertEqual('Malformed parameter(UpstreamDNS8.8.8.8). '
|
||||||
|
'Use the key=value format.', str(ex))
|
||||||
|
|
||||||
|
def test_format_multiple_bad_parameter(self):
|
||||||
|
params = ['KeyName=heat_key', 'UpstreamDNS8.8.8.8']
|
||||||
|
ex = self.assertRaises(exc.CommandError,
|
||||||
|
utils.format_parameters, params)
|
||||||
|
self.assertEqual('Malformed parameter(UpstreamDNS8.8.8.8). '
|
||||||
|
'Use the key=value format.', str(ex))
|
||||||
|
|
||||||
def test_link_formatter(self):
|
def test_link_formatter(self):
|
||||||
self.assertEqual('', utils.link_formatter(None))
|
self.assertEqual('', utils.link_formatter(None))
|
||||||
|
|||||||
@@ -103,7 +103,10 @@ def _process_environment_and_files(hc, args, fields):
|
|||||||
@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
|
@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
|
||||||
help='Enable rollback on create/update failure')
|
help='Enable rollback on create/update failure')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values used to create the stack.')
|
help='Parameter values used to create the stack. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
@utils.arg('name', metavar='<STACK_NAME>',
|
@utils.arg('name', metavar='<STACK_NAME>',
|
||||||
help='Name of the stack to create.')
|
help='Name of the stack to create.')
|
||||||
def do_create(hc, args):
|
def do_create(hc, args):
|
||||||
@@ -125,7 +128,10 @@ def do_create(hc, args):
|
|||||||
@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
|
@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
|
||||||
help='Enable rollback on create/update failure')
|
help='Enable rollback on create/update failure')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values used to create the stack.')
|
help='Parameter values used to create the stack. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
@utils.arg('name', metavar='<STACK_NAME>',
|
@utils.arg('name', metavar='<STACK_NAME>',
|
||||||
help='Name of the stack to create.')
|
help='Name of the stack to create.')
|
||||||
def do_stack_create(hc, args):
|
def do_stack_create(hc, args):
|
||||||
@@ -221,7 +227,10 @@ def do_stack_show(hc, args):
|
|||||||
@utils.arg('-o', '--template-object', metavar='<URL>',
|
@utils.arg('-o', '--template-object', metavar='<URL>',
|
||||||
help='URL to retrieve template object (e.g from swift)')
|
help='URL to retrieve template object (e.g from swift)')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values used to create the stack.')
|
help='Parameter values used to create the stack. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
@utils.arg('id', metavar='<NAME or ID>',
|
@utils.arg('id', metavar='<NAME or ID>',
|
||||||
help='Name or ID of stack to update.')
|
help='Name or ID of stack to update.')
|
||||||
def do_update(hc, args):
|
def do_update(hc, args):
|
||||||
@@ -238,7 +247,10 @@ def do_update(hc, args):
|
|||||||
@utils.arg('-o', '--template-object', metavar='<URL>',
|
@utils.arg('-o', '--template-object', metavar='<URL>',
|
||||||
help='URL to retrieve template object (e.g from swift)')
|
help='URL to retrieve template object (e.g from swift)')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values used to create the stack.')
|
help='Parameter values used to create the stack. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
@utils.arg('id', metavar='<NAME or ID>',
|
@utils.arg('id', metavar='<NAME or ID>',
|
||||||
help='Name or ID of stack to update.')
|
help='Name or ID of stack to update.')
|
||||||
def do_stack_update(hc, args):
|
def do_stack_update(hc, args):
|
||||||
@@ -297,7 +309,10 @@ def do_template_show(hc, args):
|
|||||||
@utils.arg('-o', '--template-object', metavar='<URL>',
|
@utils.arg('-o', '--template-object', metavar='<URL>',
|
||||||
help='URL to retrieve template object (e.g from swift)')
|
help='URL to retrieve template object (e.g from swift)')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values to validate.')
|
help='Parameter values to validate. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
def do_validate(hc, args):
|
def do_validate(hc, args):
|
||||||
'''DEPRECATED! Use template-validate instead.'''
|
'''DEPRECATED! Use template-validate instead.'''
|
||||||
do_template_validate(hc, args)
|
do_template_validate(hc, args)
|
||||||
@@ -312,7 +327,10 @@ def do_validate(hc, args):
|
|||||||
@utils.arg('-o', '--template-object', metavar='<URL>',
|
@utils.arg('-o', '--template-object', metavar='<URL>',
|
||||||
help='URL to retrieve template object (e.g from swift)')
|
help='URL to retrieve template object (e.g from swift)')
|
||||||
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
|
||||||
help='Parameter values to validate.')
|
help='Parameter values to validate. '
|
||||||
|
'This can be specified multiple times, or once with parameters '
|
||||||
|
'separated by semicolon.',
|
||||||
|
action='append')
|
||||||
def do_template_validate(hc, args):
|
def do_template_validate(hc, args):
|
||||||
'''Validate a template with parameters.'''
|
'''Validate a template with parameters.'''
|
||||||
fields = {'parameters': utils.format_parameters(args.parameters)}
|
fields = {'parameters': utils.format_parameters(args.parameters)}
|
||||||
|
|||||||
Reference in New Issue
Block a user