Merge lp:~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions into lp:~openstack-charmers-archive/charms/trusty/swift-storage/next

Proposed by Adam Collard on 2015-08-17
Status: Merged
Merged at revision: 74
Proposed branch: lp:~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions
Merge into: lp:~openstack-charmers-archive/charms/trusty/swift-storage/next
Prerequisite: lp:~adam-collard/charms/trusty/swift-storage/fix-service-status
Diff against target: 605 lines (+425/-42)
9 files modified
.coveragerc (+1/-0)
Makefile (+2/-0)
actions.yaml (+5/-0)
actions/actions.py (+93/-0)
lib/swift_storage_utils.py (+1/-15)
setup.cfg (+1/-1)
tests/00-setup (+1/-0)
tests/basic_deployment.py (+82/-26)
unit_tests/test_actions.py (+239/-0)
To merge this branch: bzr merge lp:~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions
Reviewer Review Type Date Requested Status
Liam Young 2015-08-17 Approve on 2015-08-18
Geoff Teale 2015-08-17 Pending
Review via email: mp+268233@code.launchpad.net

This proposal supersedes a proposal from 2015-08-11.

Description of the change

This branch adds actions for pausing and resuming services on swift-storage units.

In addition to just stopping the services, the actions also set the status of the unit to be "maintenance" with a message on how to resume - I'm not attached to the message, bikesheds welcome :).

Further refinements to the charm to support pause mode will be coming in follow-up branches, notably guarding calls to service_start and service_restart to prevent config-changed, *-relation-changed and other hooks from (re)-starting a unit which should be paused.

To post a comment you must log in.
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7869 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7869/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7290 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7290/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7871 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7871/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7292 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7292/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5759 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12057851/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5759/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7922 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7922/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7340 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7340/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5763 swift-storage-next for adam-collard mp267681
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/5763/

Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

RE: Amulet tests

Thank for your contribution and clean-up here. Backgrounder: I've not gotten down the list to swift-* charm amulet test updates, but intend to complete the vivid enablement in 15.08 and wily in 15.09. There are also a number of refactors and helper optimizations, which we're making across all of the os-charm amulet tests, so I'll be shifting these around a bit when I get down the list to the swift-*s.

1 easy change that I'd request: Please chmod -x tests/basic_deployment.py ... set not-executable, as it's just a module with class definitions called by the actual test executables (NNN-<ubuntu>-<openstack> files which are set +x for known-working combos).

Take note that juju test (and bundletester) will juju bootstrap then execute anything in the tests/ dir which is +x, and that any yamls in the tests/ dir will be assumed by bundletester to be a test definition. While we are prepping to switch completely to bundletester, our first priority with the amulet tests is in updating the horizon of u:os test coverage to catch up with charm development efforts. Then we will resume the bundletester effort.

Thanks again, holler with any ?s.

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7926 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7926/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7344 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7344/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5767 swift-storage-next for adam-collard mp267681
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/5767/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7927 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7927/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7345 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7345/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5768 swift-storage-next for adam-collard mp267681
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/5768/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7929 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7929/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7347 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7347/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5770 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12063967/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5770/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7983 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7983/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7397 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7397/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5775 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 124
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12070002/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5775/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7985 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7985/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7399 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7399/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5777 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12070240/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5777/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #7987 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7987/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7401 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7401/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5779 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12070492/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5779/

Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

Hi. Kicking in with some hopefully helpful hints. I see the proposed test is failing when trying to confirm a system service that doesn't appear to be present at a particular release combo.

Since the basic_deployment.py set of tests is expected to pass for all supported release combos, each test_ method may need to use release detection logic as needed. In some cases, openstack components have shifted options, sections, etc., in conf files. In other cases, some system services may or may not be present across that entire timeline. There was particularly a lot of upstream shifting-around introduced at >= Kilo ... and < Icehouse.

As a usage example, the heat amulet test detects and checks things slightly differently here:
http://bazaar.launchpad.net/~openstack-charmers/charms/trusty/heat/next/view/head:/tests/basic_deployment.py#L313

Which ultimately uses this for comparison:
http://bazaar.launchpad.net/~charm-helpers/charm-helpers/devel/view/head:/charmhelpers/contrib/openstack/amulet/deployment.py#L109

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #8042 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/8042/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7453 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7453/

Adam Collard (adam-collard) wrote : Posted in a previous version of this proposal

> Hi. Kicking in with some hopefully helpful hints. I see the proposed test is
> failing when trying to confirm a system service that doesn't appear to be
> present at a particular release combo.

That was my first guess too, fortunately (or unfortunately?) it wasn't that issue. The problem was the utils code for looking at what processes are running uses "pidof $name" but when $name is the name of a script it fails (using the -x option works as expected). Instead of yet another round of fixing charm-helpers, I opted to just roll my own for this test - there are comments in the test to further explain why.

In the absence of access to a testing environment that could satisfy the requirements, I was using UOSCI bot to run my tests for me. Now that's been resolved I have (hopefully!) got some green tests :)

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5786 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12078138/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5786/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #8044 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/8044/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7455 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7455/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5788 swift-storage-next for adam-collard mp267681
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/5788/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #8045 swift-storage-next for adam-collard mp267681
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/8045/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #7456 swift-storage-next for adam-collard mp267681
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7456/

uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #5789 swift-storage-next for adam-collard mp267681
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [test] Error 124
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12079074/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5789/

Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

RE: amulet tests

Thank you for this work!

Just a couple of things that will make future life easier:

_run_action and _wait_on_action:
Please consider landing these in charmhelpers/contrib/amulet/utils.py, as they will be increasingly-common needs.

_assert_services:
Please consider enhancing the existing validate_unit_process_ids amulet helper to fit the needs here. I think it would be a fairly easy mod. We could do -x on pidof in all cases (or add a bool with default False to "check scripts too" if you want to be safer about it). Then add a boolean postive/negative switch to be able to invert its expectations (like you've done with should_run). fwiw, expect_success (default True) is such a bool name that I've used elsewhere for the same purpose in a different context (rmq wip).

Adam Collard (adam-collard) wrote : Posted in a previous version of this proposal

Hi Ryan,

Thanks for taking a look at this.

On Fri, 14 Aug 2015 at 17:10 Ryan Beisner <email address hidden>
wrote:

> RE: amulet tests
>
> Thank you for this work!
>
> Just a couple of things that will make future life easier:
>
> _run_action and _wait_on_action:
> Please consider landing these in charmhelpers/contrib/amulet/utils.py, as
> they will be increasingly-common needs.
>

Done. See charm-helpers r428

>
> _assert_services:
> Please consider enhancing the existing validate_unit_process_ids amulet
> helper to fit the needs here. I think it would be a fairly easy mod. We
> could do -x on pidof in all cases (or add a bool with default False to
> "check scripts too" if you want to be safer about it). Then add a boolean
> postive/negative switch to be able to invert its expectations (like you've
> done with should_run). fwiw, expect_success (default True) is such a bool
> name that I've used elsewhere for the same purpose in a different context
> (rmq wip).
>

I modified get_process_id_list and get_unit_process_ids but didn't end up
using validate_unit_process_ids because the validation is already done for
me in the previous two methods. See comment in the test itself.

I've split the branch up so that the new code is cleanly separated from the
fixes I made to existing code (see pre-req branch).

--
>
> https://code.launchpad.net/~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions/+merge/267681
> You are the owner of
> lp:~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions.
>
> Launchpad-Message-Rationale: Owner
> Launchpad-Notification-Type: code-review
> Launchpad-Branch:
> ~adam-collard/charms/trusty/swift-storage/add-pause-resume-actions
>

charm_lint_check #8196 swift-storage-next for adam-collard mp268233
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/8196/

charm_unit_test #7598 swift-storage-next for adam-collard mp268233
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/7598/

charm_amulet_test #5842 swift-storage-next for adam-collard mp268233
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/5842/

Liam Young (gnuoy) wrote :

LGTM, thanks for this excellent addition to the charm.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.coveragerc'
2--- .coveragerc 2015-08-11 08:09:29 +0000
3+++ .coveragerc 2015-08-17 13:45:32 +0000
4@@ -3,5 +3,6 @@
5 exclude_lines =
6 if __name__ == .__main__.:
7 include=
8+ actions/actions.py
9 hooks/swift_storage_*
10 lib/swift_storage_*
11
12=== modified file 'Makefile'
13--- Makefile 2015-08-04 11:48:35 +0000
14+++ Makefile 2015-08-17 13:45:32 +0000
15@@ -30,3 +30,5 @@
16 publish: lint test
17 bzr push lp:charms/swift-storage
18 bzr push lp:charms/trusty/swift-storage
19+
20+.PHONY: lint unit_test test sync publish
21
22=== added directory 'actions'
23=== added file 'actions.yaml'
24--- actions.yaml 1970-01-01 00:00:00 +0000
25+++ actions.yaml 2015-08-17 13:45:32 +0000
26@@ -0,0 +1,5 @@
27+pause:
28+ description: Pause the swift-storage unit. This action will stop Swift services.
29+resume:
30+ description: Resume the swift-storage unit. This action will start Swift services.
31+
32
33=== added file 'actions/__init__.py'
34=== added file 'actions/actions.py'
35--- actions/actions.py 1970-01-01 00:00:00 +0000
36+++ actions/actions.py 2015-08-17 13:45:32 +0000
37@@ -0,0 +1,93 @@
38+#!/usr/bin/python
39+
40+import argparse
41+import os
42+import sys
43+import yaml
44+
45+from charmhelpers.core.host import service_pause, service_resume
46+from charmhelpers.core.hookenv import action_fail, status_set
47+from charmhelpers.contrib.openstack.utils import get_os_codename_package
48+
49+from lib.swift_storage_utils import SWIFT_SVCS
50+
51+
52+def _get_services():
53+ """Return a list of services that need to be (un)paused."""
54+ services = SWIFT_SVCS[:]
55+ # Before Icehouse there was no swift-container-sync
56+ if get_os_codename_package("swift-container") < "icehouse":
57+ services.remove("swift-container-sync")
58+ return services
59+
60+
61+def get_action_parser(actions_yaml_path, action_name,
62+ get_services=_get_services):
63+ """Make an argparse.ArgumentParser seeded from actions.yaml definitions."""
64+ with open(actions_yaml_path) as fh:
65+ doc = yaml.load(fh)[action_name]["description"]
66+ parser = argparse.ArgumentParser(description=doc)
67+ parser.add_argument("--services", default=get_services())
68+ # TODO: Add arguments for params defined in the actions.yaml
69+ return parser
70+
71+
72+def pause(args):
73+ """Pause all the swift services.
74+
75+ @raises Exception if any services fail to stop
76+ """
77+ for service in args.services:
78+ stopped = service_pause(service)
79+ if not stopped:
80+ raise Exception("{} didn't stop cleanly.".format(service))
81+ status_set(
82+ "maintenance", "Paused. Use 'resume' action to resume normal service.")
83+
84+
85+def resume(args):
86+ """Resume all the swift services.
87+
88+ @raises Exception if any services fail to start
89+ """
90+ for service in args.services:
91+ started = service_resume(service)
92+ if not started:
93+ raise Exception("{} didn't start cleanly.".format(service))
94+ status_set("active", "")
95+
96+
97+# A dictionary of all the defined actions to callables (which take
98+# parsed arguments).
99+ACTIONS = {"pause": pause, "resume": resume}
100+
101+
102+def main(argv):
103+ action_name = _get_action_name()
104+ actions_yaml_path = _get_actions_yaml_path()
105+ parser = get_action_parser(actions_yaml_path, action_name)
106+ args = parser.parse_args(argv)
107+ try:
108+ action = ACTIONS[action_name]
109+ except KeyError:
110+ return "Action %s undefined" % action_name
111+ else:
112+ try:
113+ action(args)
114+ except Exception as e:
115+ action_fail(str(e))
116+
117+
118+def _get_action_name():
119+ """Return the name of the action."""
120+ return os.path.basename(__file__)
121+
122+
123+def _get_actions_yaml_path():
124+ """Return the path to actions.yaml"""
125+ cwd = os.path.dirname(__file__)
126+ return os.path.join(cwd, "..", "actions.yaml")
127+
128+
129+if __name__ == "__main__":
130+ sys.exit(main(sys.argv[1:]))
131
132=== added symlink 'actions/charmhelpers'
133=== target is u'../charmhelpers/'
134=== added symlink 'actions/lib'
135=== target is u'../lib'
136=== added symlink 'actions/pause'
137=== target is u'actions.py'
138=== added symlink 'actions/resume'
139=== target is u'actions.py'
140=== modified file 'lib/swift_storage_utils.py'
141--- lib/swift_storage_utils.py 2015-07-17 09:57:19 +0000
142+++ lib/swift_storage_utils.py 2015-08-17 13:45:32 +0000
143@@ -83,21 +83,7 @@
144 'swift-object-updater', 'swift-object-replicator'
145 ]
146
147-SWIFT_SVCS = [
148- 'swift-account-auditor',
149- 'swift-account-reaper',
150- 'swift-account-replicator',
151- 'swift-account-server',
152- 'swift-container-auditor',
153- 'swift-container-replicator',
154- 'swift-container-server',
155- 'swift-container-sync',
156- 'swift-container-updater',
157- 'swift-object-auditor',
158- 'swift-object-replicator',
159- 'swift-object-server',
160- 'swift-object-updater',
161- ]
162+SWIFT_SVCS = ACCOUNT_SVCS + CONTAINER_SVCS + OBJECT_SVCS
163
164 RESTART_MAP = {
165 '/etc/rsync-juju.d/050-swift-storage.conf': ['rsync'],
166
167=== modified file 'setup.cfg'
168--- setup.cfg 2015-08-11 08:09:29 +0000
169+++ setup.cfg 2015-08-17 13:45:32 +0000
170@@ -2,5 +2,5 @@
171 verbosity=2
172 with-coverage=1
173 cover-erase=1
174-cover-package=lib,hooks.swift_storage_hooks
175+cover-package=actions.actions,lib,hooks.swift_storage_hooks
176
177
178=== modified file 'tests/00-setup'
179--- tests/00-setup 2014-09-29 21:14:05 +0000
180+++ tests/00-setup 2015-08-17 13:45:32 +0000
181@@ -7,5 +7,6 @@
182 sudo apt-get install --yes python-amulet \
183 python-swiftclient \
184 python-glanceclient \
185+ python-heatclient \
186 python-keystoneclient \
187 python-novaclient
188
189=== modified file 'tests/basic_deployment.py'
190--- tests/basic_deployment.py 2015-08-17 13:45:32 +0000
191+++ tests/basic_deployment.py 2015-08-17 13:45:32 +0000
192@@ -7,8 +7,7 @@
193
194 from charmhelpers.contrib.openstack.amulet.utils import (
195 OpenStackAmuletUtils,
196- DEBUG, # flake8: noqa
197- ERROR
198+ DEBUG,
199 )
200
201 # Use DEBUG to turn on debug logging
202@@ -44,12 +43,12 @@
203 def _add_relations(self):
204 """Add all of the relations for the services."""
205 relations = {
206- 'keystone:shared-db': 'mysql:shared-db',
207- 'swift-proxy:identity-service': 'keystone:identity-service',
208- 'swift-storage:swift-storage': 'swift-proxy:swift-storage',
209- 'glance:identity-service': 'keystone:identity-service',
210- 'glance:shared-db': 'mysql:shared-db',
211- 'glance:object-store': 'swift-proxy:object-store'
212+ 'keystone:shared-db': 'mysql:shared-db',
213+ 'swift-proxy:identity-service': 'keystone:identity-service',
214+ 'swift-storage:swift-storage': 'swift-proxy:swift-storage',
215+ 'glance:identity-service': 'keystone:identity-service',
216+ 'glance:shared-db': 'mysql:shared-db',
217+ 'glance:object-store': 'swift-proxy:object-store'
218 }
219 super(SwiftStorageBasicDeployment, self)._add_relations(relations)
220
221@@ -57,9 +56,11 @@
222 """Configure all of the services."""
223 keystone_config = {'admin-password': 'openstack',
224 'admin-token': 'ubuntutesting'}
225- swift_proxy_config = {'zone-assignment': 'manual',
226- 'replicas': '1',
227- 'swift-hash': 'fdfef9d4-8b06-11e2-8ac0-531c923c8fae'}
228+ swift_proxy_config = {
229+ 'zone-assignment': 'manual',
230+ 'replicas': '1',
231+ 'swift-hash': 'fdfef9d4-8b06-11e2-8ac0-531c923c8fae'
232+ }
233 swift_storage_config = {'zone': '1',
234 'block-device': 'vdb',
235 'overwrite': 'true'}
236@@ -87,15 +88,16 @@
237 self.glance = u.authenticate_glance_admin(self.keystone)
238
239 # Authenticate swift user
240- keystone_relation = self.keystone_sentry.relation('identity-service',
241- 'swift-proxy:identity-service')
242+ keystone_relation = self.keystone_sentry.relation(
243+ 'identity-service', 'swift-proxy:identity-service')
244 ep = self.keystone.service_catalog.url_for(service_type='identity',
245 endpoint_type='publicURL')
246- self.swift = swiftclient.Connection(authurl=ep,
247- user=keystone_relation['service_username'],
248- key=keystone_relation['service_password'],
249- tenant_name=keystone_relation['service_tenant'],
250- auth_version='2.0')
251+ self.swift = swiftclient.Connection(
252+ authurl=ep,
253+ user=keystone_relation['service_username'],
254+ key=keystone_relation['service_password'],
255+ tenant_name=keystone_relation['service_tenant'],
256+ auth_version='2.0')
257
258 # Create a demo tenant/role/user
259 self.demo_tenant = 'demoTenant'
260@@ -302,8 +304,8 @@
261 file."""
262 unit = self.swift_storage_sentry
263 conf = '/etc/swift/swift.conf'
264- swift_proxy_relation = self.swift_proxy_sentry.relation('swift-storage',
265- 'swift-storage:swift-storage')
266+ swift_proxy_relation = self.swift_proxy_sentry.relation(
267+ 'swift-storage', 'swift-storage:swift-storage')
268 expected = {
269 'swift_hash_path_suffix': swift_proxy_relation['swift_hash']
270 }
271@@ -404,7 +406,8 @@
272
273 def test_image_create(self):
274 """Create an instance in glance, which is backed by swift, and validate
275- that some of the metadata for the image match in glance and swift."""
276+ that some of the metadata for the image match in glance and swift.
277+ """
278 # NOTE(coreycb): Skipping failing test on folsom until resolved. On
279 # folsom only, uploading an image to glance gets 400 Bad
280 # Request - Error uploading image: (error): [Errno 111]
281@@ -434,7 +437,8 @@
282 # Validate that swift object's checksum/size match that from glance
283 headers, containers = self.swift.get_account()
284 if len(containers) != 1:
285- msg = "Expected 1 swift container, found {}".format(len(containers))
286+ msg = "Expected 1 swift container, found {}".format(
287+ len(containers))
288 amulet.raise_status(amulet.FAIL, msg=msg)
289
290 container_name = containers[0].get('name')
291@@ -448,14 +452,66 @@
292 swift_object_md5 = objects[0].get('hash')
293
294 if glance_image_size != swift_object_size:
295- msg = "Glance image size {} != swift object size {}".format( \
296- glance_image_size, swift_object_size)
297+ msg = "Glance image size {} != swift object size {}".format(
298+ glance_image_size, swift_object_size)
299 amulet.raise_status(amulet.FAIL, msg=msg)
300
301 if glance_image_md5 != swift_object_md5:
302- msg = "Glance image hash {} != swift object hash {}".format( \
303- glance_image_md5, swift_object_md5)
304+ msg = "Glance image hash {} != swift object hash {}".format(
305+ glance_image_md5, swift_object_md5)
306 amulet.raise_status(amulet.FAIL, msg=msg)
307
308 # Cleanup
309 u.delete_image(self.glance, image)
310+
311+ def _assert_services(self, should_run):
312+ swift_storage_services = ['swift-account-auditor',
313+ 'swift-account-reaper',
314+ 'swift-account-replicator',
315+ 'swift-account-server',
316+ 'swift-container-auditor',
317+ 'swift-container-replicator',
318+ 'swift-container-server',
319+ 'swift-container-sync',
320+ 'swift-container-updater',
321+ 'swift-object-auditor',
322+ 'swift-object-replicator',
323+ 'swift-object-server',
324+ 'swift-object-updater']
325+ if self._get_openstack_release() < self.precise_icehouse:
326+ swift_storage_services.remove('swift-container-sync')
327+
328+ u.get_unit_process_ids(
329+ {self.swift_storage_sentry: swift_storage_services},
330+ expect_success=should_run)
331+ # No point using validate_unit_process_ids, since we don't
332+ # care about how many PIDs, merely that they're running, so
333+ # would populate expected with either True or False. This
334+ # validation is already performed in get_process_id_list
335+
336+ def _test_pause(self):
337+ u.log.info("Testing pause action")
338+ self._assert_services(should_run=True)
339+ pause_action_id = u.run_action(self.swift_storage_sentry, "pause")
340+ assert u.wait_on_action(pause_action_id), "Pause action failed."
341+
342+ self._assert_services(should_run=False)
343+
344+ def _test_resume(self):
345+ u.log.info("Testing resume action")
346+ # service is left paused by _test_pause
347+ self._assert_services(should_run=False)
348+ resume_action_id = u.run_action(self.swift_storage_sentry, "resume")
349+ assert u.wait_on_action(resume_action_id), "Resume action failed."
350+
351+ self._assert_services(should_run=True)
352+
353+ def test_z_actions(self):
354+ """Pause and then resume swift-storage.
355+
356+ Note(sparkiegeek): The method name with the _z_ is a little odd
357+ but it forces the test to run last. It just makes things
358+ easier because restarting services requires re-authorization.
359+ """
360+ self._test_pause()
361+ self._test_resume()
362
363=== added file 'unit_tests/test_actions.py'
364--- unit_tests/test_actions.py 1970-01-01 00:00:00 +0000
365+++ unit_tests/test_actions.py 2015-08-17 13:45:32 +0000
366@@ -0,0 +1,239 @@
367+import argparse
368+import tempfile
369+import unittest
370+
371+import mock
372+import yaml
373+
374+from test_utils import CharmTestCase
375+
376+import actions.actions
377+
378+
379+class PauseTestCase(CharmTestCase):
380+
381+ def setUp(self):
382+ super(PauseTestCase, self).setUp(
383+ actions.actions, ["service_pause", "status_set"])
384+
385+ class FakeArgs(object):
386+ services = ['swift-account',
387+ 'swift-account-auditor',
388+ 'swift-account-reaper',
389+ 'swift-account-replicator',
390+ 'swift-container',
391+ 'swift-container-auditor',
392+ 'swift-container-updater',
393+ 'swift-container-replicator',
394+ 'swift-container-sync',
395+ 'swift-object',
396+ 'swift-object-auditor',
397+ 'swift-object-updater',
398+ 'swift-object-replicator']
399+ self.args = FakeArgs()
400+
401+ def test_pauses_services(self):
402+ """Pause action pauses all of the Swift services."""
403+ pause_calls = []
404+
405+ def fake_service_pause(svc):
406+ pause_calls.append(svc)
407+ return True
408+
409+ self.service_pause.side_effect = fake_service_pause
410+
411+ actions.actions.pause(self.args)
412+ self.assertEqual(pause_calls, ['swift-account',
413+ 'swift-account-auditor',
414+ 'swift-account-reaper',
415+ 'swift-account-replicator',
416+ 'swift-container',
417+ 'swift-container-auditor',
418+ 'swift-container-updater',
419+ 'swift-container-replicator',
420+ 'swift-container-sync',
421+ 'swift-object',
422+ 'swift-object-auditor',
423+ 'swift-object-updater',
424+ 'swift-object-replicator'])
425+
426+ def test_bails_out_early_on_error(self):
427+ """Pause action fails early if there are errors stopping a service."""
428+ pause_calls = []
429+
430+ def maybe_kill(svc):
431+ if svc == "swift-container":
432+ return False
433+ else:
434+ pause_calls.append(svc)
435+ return True
436+
437+ self.service_pause.side_effect = maybe_kill
438+ self.assertRaisesRegexp(
439+ Exception, "swift-container didn't stop cleanly.",
440+ actions.actions.pause, self.args)
441+ self.assertEqual(pause_calls, ['swift-account',
442+ 'swift-account-auditor',
443+ 'swift-account-reaper',
444+ 'swift-account-replicator'])
445+
446+ def test_status_mode(self):
447+ """Pause action sets the status to maintenance."""
448+ status_calls = []
449+ self.status_set.side_effect = lambda state, msg: status_calls.append(
450+ state)
451+
452+ actions.actions.pause(self.args)
453+ self.assertEqual(status_calls, ["maintenance"])
454+
455+ def test_status_message(self):
456+ """Pause action sets a status message reflecting that it's paused."""
457+ status_calls = []
458+ self.status_set.side_effect = lambda state, msg: status_calls.append(
459+ msg)
460+
461+ actions.actions.pause(self.args)
462+ self.assertEqual(
463+ status_calls, ["Paused. "
464+ "Use 'resume' action to resume normal service."])
465+
466+
467+class ResumeTestCase(CharmTestCase):
468+
469+ def setUp(self):
470+ super(ResumeTestCase, self).setUp(
471+ actions.actions, ["service_resume", "status_set"])
472+
473+ class FakeArgs(object):
474+ services = ['swift-account',
475+ 'swift-account-auditor',
476+ 'swift-account-reaper',
477+ 'swift-account-replicator',
478+ 'swift-container',
479+ 'swift-container-auditor',
480+ 'swift-container-updater',
481+ 'swift-container-replicator',
482+ 'swift-container-sync',
483+ 'swift-object',
484+ 'swift-object-auditor',
485+ 'swift-object-updater',
486+ 'swift-object-replicator']
487+ self.args = FakeArgs()
488+
489+ def test_resumes_services(self):
490+ """Resume action resumes all of the Swift services."""
491+ resume_calls = []
492+
493+ def fake_service_resume(svc):
494+ resume_calls.append(svc)
495+ return True
496+
497+ self.service_resume.side_effect = fake_service_resume
498+ actions.actions.resume(self.args)
499+ self.assertEqual(resume_calls, ['swift-account',
500+ 'swift-account-auditor',
501+ 'swift-account-reaper',
502+ 'swift-account-replicator',
503+ 'swift-container',
504+ 'swift-container-auditor',
505+ 'swift-container-updater',
506+ 'swift-container-replicator',
507+ 'swift-container-sync',
508+ 'swift-object',
509+ 'swift-object-auditor',
510+ 'swift-object-updater',
511+ 'swift-object-replicator'])
512+
513+ def test_bails_out_early_on_error(self):
514+ """Resume action fails early if there are errors starting a service."""
515+ resume_calls = []
516+
517+ def maybe_kill(svc):
518+ if svc == "swift-container":
519+ return False
520+ else:
521+ resume_calls.append(svc)
522+ return True
523+
524+ self.service_resume.side_effect = maybe_kill
525+ self.assertRaisesRegexp(
526+ Exception, "swift-container didn't start cleanly.",
527+ actions.actions.resume, self.args)
528+ self.assertEqual(resume_calls, ['swift-account',
529+ 'swift-account-auditor',
530+ 'swift-account-reaper',
531+ 'swift-account-replicator'])
532+
533+ def test_status_mode(self):
534+ """Resume action sets the status to maintenance."""
535+ status_calls = []
536+ self.status_set.side_effect = lambda state, msg: status_calls.append(
537+ state)
538+
539+ actions.actions.resume(self.args)
540+ self.assertEqual(status_calls, ["active"])
541+
542+ def test_status_message(self):
543+ """Resume action sets an empty status message."""
544+ status_calls = []
545+ self.status_set.side_effect = lambda state, msg: status_calls.append(
546+ msg)
547+
548+ actions.actions.resume(self.args)
549+ self.assertEqual(status_calls, [""])
550+
551+
552+class GetActionParserTestCase(unittest.TestCase):
553+
554+ def test_definition_from_yaml(self):
555+ """ArgumentParser is seeded from actions.yaml."""
556+ actions_yaml = tempfile.NamedTemporaryFile(
557+ prefix="GetActionParserTestCase", suffix="yaml")
558+ actions_yaml.write(yaml.dump({"foo": {"description": "Foo is bar"}}))
559+ actions_yaml.seek(0)
560+ parser = actions.actions.get_action_parser(actions_yaml.name, "foo",
561+ get_services=lambda: [])
562+ self.assertEqual(parser.description, 'Foo is bar')
563+
564+
565+class MainTestCase(CharmTestCase):
566+
567+ def setUp(self):
568+ super(MainTestCase, self).setUp(
569+ actions.actions, ["_get_action_name",
570+ "get_action_parser",
571+ "action_fail"])
572+
573+ def test_invokes_pause(self):
574+ dummy_calls = []
575+
576+ def dummy_action(args):
577+ dummy_calls.append(True)
578+
579+ self._get_action_name.side_effect = lambda: "foo"
580+ self.get_action_parser = lambda: argparse.ArgumentParser()
581+ with mock.patch.dict(actions.actions.ACTIONS, {"foo": dummy_action}):
582+ actions.actions.main([])
583+ self.assertEqual(dummy_calls, [True])
584+
585+ def test_unknown_action(self):
586+ """Unknown actions aren't a traceback."""
587+ self._get_action_name.side_effect = lambda: "foo"
588+ self.get_action_parser = lambda: argparse.ArgumentParser()
589+ exit_string = actions.actions.main([])
590+ self.assertEqual("Action foo undefined", exit_string)
591+
592+ def test_failing_action(self):
593+ """Actions which traceback trigger action_fail() calls."""
594+ dummy_calls = []
595+
596+ self.action_fail.side_effect = dummy_calls.append
597+ self._get_action_name.side_effect = lambda: "foo"
598+
599+ def dummy_action(args):
600+ raise ValueError("uh oh")
601+
602+ self.get_action_parser = lambda: argparse.ArgumentParser()
603+ with mock.patch.dict(actions.actions.ACTIONS, {"foo": dummy_action}):
604+ actions.actions.main([])
605+ self.assertEqual(dummy_calls, ["uh oh"])

Subscribers

People subscribed via source and target branches