summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--heatclient/common/http.py2
-rw-r--r--heatclient/shell.py4
-rw-r--r--heatclient/tests/test_shell.py62
-rw-r--r--heatclient/v1/shell.py41
-rw-r--r--requirements.txt4
-rw-r--r--setup.cfg2
-rwxr-xr-xsetup.py8
-rw-r--r--test-requirements.txt2
-rw-r--r--tools/heat.bash_completion2
-rw-r--r--tox.ini5
10 files changed, 89 insertions, 43 deletions
diff --git a/heatclient/common/http.py b/heatclient/common/http.py
index 48c83f6..cbf4540 100644
--- a/heatclient/common/http.py
+++ b/heatclient/common/http.py
@@ -27,8 +27,6 @@ from heatclient.openstack.common import jsonutils
from heatclient.openstack.common import strutils
LOG = logging.getLogger(__name__)
-if not LOG.handlers:
- LOG.addHandler(logging.StreamHandler())
USER_AGENT = 'python-heatclient'
CHUNKSIZE = 1024 * 64 # 64kB
diff --git a/heatclient/shell.py b/heatclient/shell.py
index 22173f5..cd00ac6 100644
--- a/heatclient/shell.py
+++ b/heatclient/shell.py
@@ -283,8 +283,10 @@ class HeatShell(object):
def _setup_logging(self, debug):
log_lvl = logging.DEBUG if debug else logging.WARNING
logging.basicConfig(
- format="%(levelname)s (%(module)s:%(lineno)d) %(message)s",
+ format="%(levelname)s (%(module)s) %(message)s",
level=log_lvl)
+ logging.getLogger('iso8601').setLevel(logging.WARNING)
+ logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
def _setup_verbose(self, verbose):
if verbose:
diff --git a/heatclient/tests/test_shell.py b/heatclient/tests/test_shell.py
index d5808c2..088fd81 100644
--- a/heatclient/tests/test_shell.py
+++ b/heatclient/tests/test_shell.py
@@ -143,12 +143,6 @@ class ShellParamValidationTest(TestCase):
('stack-update', dict(
command='stack-update ts -P "a-b"',
err='Malformed parameter')),
- ('validate', dict(
- command='validate -P "a=b;c"',
- err='Malformed parameter')),
- ('template-validate', dict(
- command='template-validate -P "a$b"',
- err='Malformed parameter')),
]
def setUp(self):
@@ -397,6 +391,8 @@ class ShellTestUserPass(ShellBase):
'limit': 2,
'status': ['COMPLETE', 'FAILED'],
'marker': 'fake_id',
+ 'global_tenant': True,
+ 'show_deleted': 'True',
}, True)
fakes.script_heat_list(expected_url)
@@ -406,7 +402,9 @@ class ShellTestUserPass(ShellBase):
' --limit 2'
' --marker fake_id'
' --filters=status=COMPLETE'
- ' --filters=status=FAILED')
+ ' --filters=status=FAILED'
+ ' --global-tenant'
+ ' --show-deleted')
required = [
'teststack',
@@ -858,7 +856,8 @@ class ShellTestUserPass(ShellBase):
'LinuxDistribution': 'F17"',
'"InstanceType': 'm1.large',
'DBPassword': 'verybadpassword'},
- 'timeout_mins': 123}
+ 'timeout_mins': 123,
+ 'disable_rollback': True}
http.HTTPClient.json_request(
'PUT', '/stacks/teststack2/2',
data=expected_data,
@@ -1037,6 +1036,7 @@ class ShellTestUserPass(ShellBase):
update_text = self.shell(
'stack-update teststack2/2 '
'--template-file=%s '
+ '--enable-rollback '
'--parameters="InstanceType=m1.large;DBUsername=wp;'
'DBPassword=verybadpassword;KeyName=heat_key;'
'LinuxDistribution=F17"' % template_file)
@@ -1311,21 +1311,22 @@ class ShellTestResources(ShellBase):
def _script_keystone_client(self):
fakes.script_keystone_client()
- def test_resource_list(self):
+ def _test_resource_list(self, with_resource_name):
self._script_keystone_client()
resp_dict = {"resources": [
{"links": [{"href": "http://heat.example.com:8004/foo",
"rel": "self"},
{"href": "http://heat.example.com:8004/foo2",
"rel": "resource"}],
- "logical_resource_id": "aResource",
+ "logical_resource_id": "aLogicalResource",
"physical_resource_id":
"43b68bae-ed5d-4aed-a99f-0b3d39c2418a",
- "resource_name": "aResource",
"resource_status": "CREATE_COMPLETE",
"resource_status_reason": "state changed",
"resource_type": "OS::Nova::Server",
"updated_time": "2014-01-06T16:14:26Z"}]}
+ if with_resource_name:
+ resp_dict["resources"][0]["resource_name"] = "aResource"
resp = fakes.FakeHTTPResponse(
200,
'OK',
@@ -1341,18 +1342,53 @@ class ShellTestResources(ShellBase):
resource_list_text = self.shell('resource-list {0}'.format(stack_id))
required = [
- 'resource_name',
'resource_type',
'resource_status',
'updated_time',
- 'aResource',
'OS::Nova::Server',
'CREATE_COMPLETE',
'2014-01-06T16:14:26Z'
]
+ if with_resource_name:
+ required.append('resource_name')
+ required.append('aResource')
+ else:
+ required.append('logical_resource_id')
+ required.append("aLogicalResource")
+
for r in required:
self.assertRegexpMatches(resource_list_text, r)
+ def test_resource_list(self):
+ self._test_resource_list(True)
+
+ def test_resource_list_no_resource_name(self):
+ self._test_resource_list(False)
+
+ def test_resource_list_empty(self):
+ self._script_keystone_client()
+ resp_dict = {"resources": []}
+ resp = fakes.FakeHTTPResponse(
+ 200,
+ 'OK',
+ {'content-type': 'application/json'},
+ jsonutils.dumps(resp_dict))
+ stack_id = 'teststack/1'
+ http.HTTPClient.json_request(
+ 'GET', '/stacks/%s/resources' % (
+ stack_id)).AndReturn((resp, resp_dict))
+
+ self.m.ReplayAll()
+
+ resource_list_text = self.shell('resource-list {0}'.format(stack_id))
+
+ self.assertEqual('''\
++---------------+---------------+-----------------+--------------+
+| resource_name | resource_type | resource_status | updated_time |
++---------------+---------------+-----------------+--------------+
++---------------+---------------+-----------------+--------------+
+''', resource_list_text)
+
def test_resource_show(self):
self._script_keystone_client()
resp_dict = {"resource":
diff --git a/heatclient/v1/shell.py b/heatclient/v1/shell.py
index 18e9637..89ede40 100644
--- a/heatclient/v1/shell.py
+++ b/heatclient/v1/shell.py
@@ -329,6 +329,11 @@ def do_stack_show(hc, args):
help='URL of template.')
@utils.arg('-o', '--template-object', metavar='<URL>',
help='URL to retrieve template object (e.g. from swift).')
+@utils.arg('-t', '--timeout', metavar='<TIMEOUT>',
+ type=int,
+ help='Stack update timeout in minutes.')
+@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
+ help='Enable rollback on create/update failure.')
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
help='Parameter values used to create the stack. '
'This can be specified multiple times, or once with parameters '
@@ -353,6 +358,8 @@ def do_update(hc, args):
@utils.arg('-t', '--timeout', metavar='<TIMEOUT>',
type=int,
help='Stack update timeout in minutes.')
+@utils.arg('-r', '--enable-rollback', default=False, action="store_true",
+ help='Enable rollback on create/update failure.')
@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
help='Parameter values used to create the stack. '
'This can be specified multiple times, or once with parameters '
@@ -374,6 +381,7 @@ def do_stack_update(hc, args):
fields = {
'stack_id': args.id,
+ 'disable_rollback': not(args.enable_rollback),
'parameters': utils.format_parameters(args.parameters),
'template': template,
'files': dict(list(tpl_files.items()) + list(env_files.items())),
@@ -393,6 +401,8 @@ def do_list(hc, args=None):
do_stack_list(hc)
+@utils.arg('-s', '--show-deleted', default=False, action="store_true",
+ help='Include soft-deleted stacks in the stack listing.')
@utils.arg('-f', '--filters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
help='Filter parameters to apply on returned stacks. '
'This can be specified multiple times, or once with parameters '
@@ -402,13 +412,20 @@ def do_list(hc, args=None):
help='Limit the number of stacks returned.')
@utils.arg('-m', '--marker', metavar='<ID>',
help='Only return stacks that appear after the given stack ID.')
+@utils.arg('-g', '--global-tenant',
+ action='store_true',
+ default=False,
+ help='Display stacks from all tenants. Operation only authorized '
+ 'for users who match the policy in heat\'s policy.json.')
def do_stack_list(hc, args=None):
'''List the user's stacks.'''
kwargs = {}
if args:
kwargs = {'limit': args.limit,
'marker': args.marker,
- 'filters': utils.format_parameters(args.filters)}
+ 'filters': utils.format_parameters(args.filters),
+ 'global_tenant': args.global_tenant,
+ 'show_deleted': args.show_deleted}
stacks = hc.stacks.list(**kwargs)
fields = ['id', 'stack_name', 'stack_status', 'creation_time']
@@ -507,11 +524,6 @@ def do_template_show(hc, args):
help='Path to the environment.')
@utils.arg('-o', '--template-object', metavar='<URL>',
help='URL to retrieve template object (e.g. from swift).')
-@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
- help='Parameter values to validate. '
- 'This can be specified multiple times, or once with parameters '
- 'separated by a semicolon.',
- action='append')
def do_validate(hc, args):
'''DEPRECATED! Use template-validate instead.'''
logger.warning('DEPRECATED! Use template-validate instead.')
@@ -526,11 +538,6 @@ def do_validate(hc, args):
help='Path to the environment.')
@utils.arg('-o', '--template-object', metavar='<URL>',
help='URL to retrieve template object (e.g. from swift).')
-@utils.arg('-P', '--parameters', metavar='<KEY1=VALUE1;KEY2=VALUE2...>',
- help='Parameter values to validate. '
- 'This can be specified multiple times, or once with parameters '
- 'separated by a semicolon.',
- action='append')
def do_template_validate(hc, args):
'''Validate a template with parameters.'''
@@ -543,7 +550,6 @@ def do_template_validate(hc, args):
env_files, env = template_utils.process_environment_and_files(
env_path=args.environment_file)
fields = {
- 'parameters': utils.format_parameters(args.parameters),
'template': template,
'files': dict(list(tpl_files.items()) + list(env_files.items())),
'environment': env
@@ -564,11 +570,10 @@ def do_resource_list(hc, args):
raise exc.CommandError('Stack not found: %s' % args.id)
else:
fields = ['resource_type', 'resource_status', 'updated_time']
- if len(resources) >= 1:
- if hasattr(resources[0], 'resource_name'):
- fields.insert(0, 'resource_name')
- else:
- fields.insert(0, 'logical_resource_id')
+ if len(resources) >= 1 and not hasattr(resources[0], 'resource_name'):
+ fields.insert(0, 'logical_resource_id')
+ else:
+ fields.insert(0, 'resource_name')
utils.print_list(resources, fields, sortby_index=3)
@@ -698,7 +703,7 @@ def do_event_list(hc, args):
fields.insert(0, 'resource_name')
else:
fields.insert(0, 'logical_resource_id')
- utils.print_list(events, fields)
+ utils.print_list(events, fields, sortby_index=None)
@utils.arg('id', metavar='<NAME or ID>',
diff --git a/requirements.txt b/requirements.txt
index 93e29c6..cb74bf1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ argparse
iso8601>=0.1.9
pbr>=0.6,!=0.7,<1.0
PrettyTable>=0.7,<0.8
-python-keystoneclient>=0.7.0
+python-keystoneclient>=0.9.0
PyYAML>=3.1.0
requests>=1.1
-six>=1.5.2
+six>=1.7.0
diff --git a/setup.cfg b/setup.cfg
index 121b1d4..cae92c4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -39,3 +39,5 @@ all_files = 1
[upload_sphinx]
upload-dir = doc/build/html
+[wheel]
+universal = 1
diff --git a/setup.py b/setup.py
index 70c2b3f..7363757 100755
--- a/setup.py
+++ b/setup.py
@@ -17,6 +17,14 @@
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
+# In python < 2.7.4, a lazy loading of package `pbr` will break
+# setuptools if some other modules registered functions in `atexit`.
+# solution from: http://bugs.python.org/issue15881#msg170215
+try:
+ import multiprocessing # noqa
+except ImportError:
+ pass
+
setuptools.setup(
setup_requires=['pbr'],
pbr=True)
diff --git a/test-requirements.txt b/test-requirements.txt
index d85a7b9..0fe6988 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,7 +5,7 @@ fixtures>=0.3.14
hacking>=0.8.0,<0.9
mock>=1.0
mox3>=0.7.0
-sphinx>=1.1.2,<1.2
+sphinx>=1.1.2,!=1.2.0,<1.3
testrepository>=0.0.18
testscenarios>=0.4
testtools>=0.9.34
diff --git a/tools/heat.bash_completion b/tools/heat.bash_completion
index 457c305..a04de1a 100644
--- a/tools/heat.bash_completion
+++ b/tools/heat.bash_completion
@@ -24,4 +24,4 @@ _heat()
fi
return 0
}
-complete -o default -o nospace -F _heat heat
+complete -o default -F _heat heat
diff --git a/tox.ini b/tox.ini
index 36a8c0d..d18f1fd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,11 +24,6 @@ commands = {posargs}
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'
-[testenv:pypy]
-deps = setuptools<3.2
- -r{toxinidir}/requirements.txt
- -r{toxinidir}/test-requirements.txt
-
[tox:jenkins]
downloadcache = ~/cache/pip