Merge lp:~corey.bryant/charms/trusty/cinder/action-managed-upgrade into lp:~openstack-charmers-archive/charms/trusty/cinder/next
- Trusty Tahr (14.04)
- action-managed-upgrade
- Merge into next
Status: | Merged |
---|---|
Merged at revision: | 124 |
Proposed branch: | lp:~corey.bryant/charms/trusty/cinder/action-managed-upgrade |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/cinder/next |
Diff against target: |
945 lines (+584/-164) 9 files modified
actions/openstack_upgrade.py (+13/-40) hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+20/-5) hooks/charmhelpers/contrib/openstack/amulet/utils.py (+359/-0) hooks/charmhelpers/contrib/openstack/utils.py (+51/-0) hooks/charmhelpers/contrib/storage/linux/ceph.py (+2/-11) hooks/charmhelpers/core/host.py (+32/-16) hooks/charmhelpers/core/kernel.py (+68/-0) tests/charmhelpers/contrib/amulet/utils.py (+9/-0) unit_tests/test_actions_openstack_upgrade.py (+30/-92) |
To merge this branch: | bzr merge lp:~corey.bryant/charms/trusty/cinder/action-managed-upgrade |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Billy Olsen | Approve | ||
Review via email: mp+270999@code.launchpad.net |
Commit message
Description of the change
uosci-testing-bot (uosci-testing-bot) wrote : | # |
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #9216 cinder-next for corey.bryant mp270999
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6436 cinder-next for corey.bryant mp270999
AMULET OK: passed
Build: http://
- 134. By Corey Bryant
-
Update unit tests
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #10056 cinder-next for corey.bryant mp270999
LINT FAIL: lint-test failed
LINT Results (max last 2 lines):
make: *** [lint] Error 1
ERROR:root:Make target returned non-zero.
Full lint test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #9222 cinder-next for corey.bryant mp270999
UNIT OK: passed
- 135. By Corey Bryant
-
Fix unit test
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #10058 cinder-next for corey.bryant mp270999
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #9226 cinder-next for corey.bryant mp270999
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6443 cinder-next for corey.bryant mp270999
AMULET FAIL: amulet-test failed
AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.
Full amulet test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6445 cinder-next for corey.bryant mp270999
AMULET OK: passed
Build: http://
Billy Olsen (billy-olsen) wrote : | # |
LGTM. Approved.
Preview Diff
1 | === modified file 'actions/openstack_upgrade.py' | |||
2 | --- actions/openstack_upgrade.py 2015-09-01 15:13:23 +0000 | |||
3 | +++ actions/openstack_upgrade.py 2015-09-15 18:42:15 +0000 | |||
4 | @@ -1,26 +1,20 @@ | |||
5 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
6 | 2 | import sys | 2 | import sys |
7 | 3 | import traceback | ||
8 | 4 | import uuid | 3 | import uuid |
9 | 5 | 4 | ||
10 | 6 | sys.path.append('hooks/') | 5 | sys.path.append('hooks/') |
11 | 7 | 6 | ||
12 | 7 | from charmhelpers.contrib.openstack.utils import ( | ||
13 | 8 | do_action_openstack_upgrade, | ||
14 | 9 | ) | ||
15 | 10 | |||
16 | 8 | from charmhelpers.core.hookenv import ( | 11 | from charmhelpers.core.hookenv import ( |
17 | 9 | action_set, | ||
18 | 10 | action_fail, | ||
19 | 11 | config, | ||
20 | 12 | relation_ids, | 12 | relation_ids, |
22 | 13 | relation_set | 13 | relation_set, |
23 | 14 | ) | 14 | ) |
24 | 15 | 15 | ||
25 | 16 | from cinder_hooks import config_changed | 16 | from cinder_hooks import config_changed |
26 | 17 | 17 | ||
27 | 18 | from charmhelpers.contrib.openstack.utils import ( | ||
28 | 19 | juju_log, | ||
29 | 20 | git_install_requested, | ||
30 | 21 | openstack_upgrade_available | ||
31 | 22 | ) | ||
32 | 23 | |||
33 | 24 | from cinder_utils import ( | 18 | from cinder_utils import ( |
34 | 25 | do_openstack_upgrade, | 19 | do_openstack_upgrade, |
35 | 26 | register_configs | 20 | register_configs |
36 | @@ -38,35 +32,14 @@ | |||
37 | 38 | code to run, otherwise a full service level upgrade will fire | 32 | code to run, otherwise a full service level upgrade will fire |
38 | 39 | on config-changed.""" | 33 | on config-changed.""" |
39 | 40 | 34 | ||
69 | 41 | if git_install_requested(): | 35 | if (do_action_openstack_upgrade('cinder-common', |
70 | 42 | action_set({'outcome': 'installed from source, skipped upgrade.'}) | 36 | do_openstack_upgrade, |
71 | 43 | else: | 37 | CONFIGS)): |
72 | 44 | if openstack_upgrade_available('cinder-common'): | 38 | # tell any storage-backends we just upgraded |
73 | 45 | if config('action-managed-upgrade'): | 39 | for rid in relation_ids('storage-backend'): |
74 | 46 | juju_log('Upgrading OpenStack release') | 40 | relation_set(relation_id=rid, |
75 | 47 | 41 | upgrade_nonce=uuid.uuid4()) | |
76 | 48 | try: | 42 | config_changed() |
48 | 49 | do_openstack_upgrade(configs=CONFIGS) | ||
49 | 50 | |||
50 | 51 | # NOTE(jamespage) tell any storage-backends we just | ||
51 | 52 | # upgraded | ||
52 | 53 | for rid in relation_ids('storage-backend'): | ||
53 | 54 | relation_set(relation_id=rid, | ||
54 | 55 | upgrade_nonce=uuid.uuid4()) | ||
55 | 56 | |||
56 | 57 | action_set({'outcome': 'success, upgrade completed.'}) | ||
57 | 58 | except: | ||
58 | 59 | action_set({'outcome': 'upgrade failed, see traceback.'}) | ||
59 | 60 | action_set({'traceback': traceback.format_exc()}) | ||
60 | 61 | action_fail('do_openstack_upgrade resulted in an ' | ||
61 | 62 | 'unexpected error') | ||
62 | 63 | |||
63 | 64 | config_changed() | ||
64 | 65 | else: | ||
65 | 66 | action_set({'outcome': 'action-managed-upgrade config is ' | ||
66 | 67 | 'False, skipped upgrade.'}) | ||
67 | 68 | else: | ||
68 | 69 | action_set({'outcome': 'no upgrade available.'}) | ||
77 | 70 | 43 | ||
78 | 71 | if __name__ == '__main__': | 44 | if __name__ == '__main__': |
79 | 72 | openstack_upgrade() | 45 | openstack_upgrade() |
80 | 73 | 46 | ||
81 | === added directory 'bin' | |||
82 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
83 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-08-18 17:34:33 +0000 | |||
84 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-15 18:42:15 +0000 | |||
85 | @@ -44,8 +44,15 @@ | |||
86 | 44 | Determine if the local branch being tested is derived from its | 44 | Determine if the local branch being tested is derived from its |
87 | 45 | stable or next (dev) branch, and based on this, use the corresonding | 45 | stable or next (dev) branch, and based on this, use the corresonding |
88 | 46 | stable or next branches for the other_services.""" | 46 | stable or next branches for the other_services.""" |
89 | 47 | |||
90 | 48 | # Charms outside the lp:~openstack-charmers namespace | ||
91 | 47 | base_charms = ['mysql', 'mongodb', 'nrpe'] | 49 | base_charms = ['mysql', 'mongodb', 'nrpe'] |
92 | 48 | 50 | ||
93 | 51 | # Force these charms to current series even when using an older series. | ||
94 | 52 | # ie. Use trusty/nrpe even when series is precise, as the P charm | ||
95 | 53 | # does not possess the necessary external master config and hooks. | ||
96 | 54 | force_series_current = ['nrpe'] | ||
97 | 55 | |||
98 | 49 | if self.series in ['precise', 'trusty']: | 56 | if self.series in ['precise', 'trusty']: |
99 | 50 | base_series = self.series | 57 | base_series = self.series |
100 | 51 | else: | 58 | else: |
101 | @@ -53,11 +60,17 @@ | |||
102 | 53 | 60 | ||
103 | 54 | if self.stable: | 61 | if self.stable: |
104 | 55 | for svc in other_services: | 62 | for svc in other_services: |
105 | 63 | if svc['name'] in force_series_current: | ||
106 | 64 | base_series = self.current_next | ||
107 | 65 | |||
108 | 56 | temp = 'lp:charms/{}/{}' | 66 | temp = 'lp:charms/{}/{}' |
109 | 57 | svc['location'] = temp.format(base_series, | 67 | svc['location'] = temp.format(base_series, |
110 | 58 | svc['name']) | 68 | svc['name']) |
111 | 59 | else: | 69 | else: |
112 | 60 | for svc in other_services: | 70 | for svc in other_services: |
113 | 71 | if svc['name'] in force_series_current: | ||
114 | 72 | base_series = self.current_next | ||
115 | 73 | |||
116 | 61 | if svc['name'] in base_charms: | 74 | if svc['name'] in base_charms: |
117 | 62 | temp = 'lp:charms/{}/{}' | 75 | temp = 'lp:charms/{}/{}' |
118 | 63 | svc['location'] = temp.format(base_series, | 76 | svc['location'] = temp.format(base_series, |
119 | @@ -77,21 +90,23 @@ | |||
120 | 77 | 90 | ||
121 | 78 | services = other_services | 91 | services = other_services |
122 | 79 | services.append(this_service) | 92 | services.append(this_service) |
123 | 93 | |||
124 | 94 | # Charms which should use the source config option | ||
125 | 80 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', | 95 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
126 | 81 | 'ceph-osd', 'ceph-radosgw'] | 96 | 'ceph-osd', 'ceph-radosgw'] |
130 | 82 | # Most OpenStack subordinate charms do not expose an origin option | 97 | |
131 | 83 | # as that is controlled by the principle. | 98 | # Charms which can not use openstack-origin, ie. many subordinates |
132 | 84 | ignore = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe'] | 99 | no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe'] |
133 | 85 | 100 | ||
134 | 86 | if self.openstack: | 101 | if self.openstack: |
135 | 87 | for svc in services: | 102 | for svc in services: |
137 | 88 | if svc['name'] not in use_source + ignore: | 103 | if svc['name'] not in use_source + no_origin: |
138 | 89 | config = {'openstack-origin': self.openstack} | 104 | config = {'openstack-origin': self.openstack} |
139 | 90 | self.d.configure(svc['name'], config) | 105 | self.d.configure(svc['name'], config) |
140 | 91 | 106 | ||
141 | 92 | if self.source: | 107 | if self.source: |
142 | 93 | for svc in services: | 108 | for svc in services: |
144 | 94 | if svc['name'] in use_source and svc['name'] not in ignore: | 109 | if svc['name'] in use_source and svc['name'] not in no_origin: |
145 | 95 | config = {'source': self.source} | 110 | config = {'source': self.source} |
146 | 96 | self.d.configure(svc['name'], config) | 111 | self.d.configure(svc['name'], config) |
147 | 97 | 112 | ||
148 | 98 | 113 | ||
149 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py' | |||
150 | --- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-07-16 20:17:07 +0000 | |||
151 | +++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-09-15 18:42:15 +0000 | |||
152 | @@ -27,6 +27,7 @@ | |||
153 | 27 | import heatclient.v1.client as heat_client | 27 | import heatclient.v1.client as heat_client |
154 | 28 | import keystoneclient.v2_0 as keystone_client | 28 | import keystoneclient.v2_0 as keystone_client |
155 | 29 | import novaclient.v1_1.client as nova_client | 29 | import novaclient.v1_1.client as nova_client |
156 | 30 | import pika | ||
157 | 30 | import swiftclient | 31 | import swiftclient |
158 | 31 | 32 | ||
159 | 32 | from charmhelpers.contrib.amulet.utils import ( | 33 | from charmhelpers.contrib.amulet.utils import ( |
160 | @@ -602,3 +603,361 @@ | |||
161 | 602 | self.log.debug('Ceph {} samples (OK): ' | 603 | self.log.debug('Ceph {} samples (OK): ' |
162 | 603 | '{}'.format(sample_type, samples)) | 604 | '{}'.format(sample_type, samples)) |
163 | 604 | return None | 605 | return None |
164 | 606 | |||
165 | 607 | # rabbitmq/amqp specific helpers: | ||
166 | 608 | def add_rmq_test_user(self, sentry_units, | ||
167 | 609 | username="testuser1", password="changeme"): | ||
168 | 610 | """Add a test user via the first rmq juju unit, check connection as | ||
169 | 611 | the new user against all sentry units. | ||
170 | 612 | |||
171 | 613 | :param sentry_units: list of sentry unit pointers | ||
172 | 614 | :param username: amqp user name, default to testuser1 | ||
173 | 615 | :param password: amqp user password | ||
174 | 616 | :returns: None if successful. Raise on error. | ||
175 | 617 | """ | ||
176 | 618 | self.log.debug('Adding rmq user ({})...'.format(username)) | ||
177 | 619 | |||
178 | 620 | # Check that user does not already exist | ||
179 | 621 | cmd_user_list = 'rabbitmqctl list_users' | ||
180 | 622 | output, _ = self.run_cmd_unit(sentry_units[0], cmd_user_list) | ||
181 | 623 | if username in output: | ||
182 | 624 | self.log.warning('User ({}) already exists, returning ' | ||
183 | 625 | 'gracefully.'.format(username)) | ||
184 | 626 | return | ||
185 | 627 | |||
186 | 628 | perms = '".*" ".*" ".*"' | ||
187 | 629 | cmds = ['rabbitmqctl add_user {} {}'.format(username, password), | ||
188 | 630 | 'rabbitmqctl set_permissions {} {}'.format(username, perms)] | ||
189 | 631 | |||
190 | 632 | # Add user via first unit | ||
191 | 633 | for cmd in cmds: | ||
192 | 634 | output, _ = self.run_cmd_unit(sentry_units[0], cmd) | ||
193 | 635 | |||
194 | 636 | # Check connection against the other sentry_units | ||
195 | 637 | self.log.debug('Checking user connect against units...') | ||
196 | 638 | for sentry_unit in sentry_units: | ||
197 | 639 | connection = self.connect_amqp_by_unit(sentry_unit, ssl=False, | ||
198 | 640 | username=username, | ||
199 | 641 | password=password) | ||
200 | 642 | connection.close() | ||
201 | 643 | |||
202 | 644 | def delete_rmq_test_user(self, sentry_units, username="testuser1"): | ||
203 | 645 | """Delete a rabbitmq user via the first rmq juju unit. | ||
204 | 646 | |||
205 | 647 | :param sentry_units: list of sentry unit pointers | ||
206 | 648 | :param username: amqp user name, default to testuser1 | ||
207 | 649 | :param password: amqp user password | ||
208 | 650 | :returns: None if successful or no such user. | ||
209 | 651 | """ | ||
210 | 652 | self.log.debug('Deleting rmq user ({})...'.format(username)) | ||
211 | 653 | |||
212 | 654 | # Check that the user exists | ||
213 | 655 | cmd_user_list = 'rabbitmqctl list_users' | ||
214 | 656 | output, _ = self.run_cmd_unit(sentry_units[0], cmd_user_list) | ||
215 | 657 | |||
216 | 658 | if username not in output: | ||
217 | 659 | self.log.warning('User ({}) does not exist, returning ' | ||
218 | 660 | 'gracefully.'.format(username)) | ||
219 | 661 | return | ||
220 | 662 | |||
221 | 663 | # Delete the user | ||
222 | 664 | cmd_user_del = 'rabbitmqctl delete_user {}'.format(username) | ||
223 | 665 | output, _ = self.run_cmd_unit(sentry_units[0], cmd_user_del) | ||
224 | 666 | |||
225 | 667 | def get_rmq_cluster_status(self, sentry_unit): | ||
226 | 668 | """Execute rabbitmq cluster status command on a unit and return | ||
227 | 669 | the full output. | ||
228 | 670 | |||
229 | 671 | :param unit: sentry unit | ||
230 | 672 | :returns: String containing console output of cluster status command | ||
231 | 673 | """ | ||
232 | 674 | cmd = 'rabbitmqctl cluster_status' | ||
233 | 675 | output, _ = self.run_cmd_unit(sentry_unit, cmd) | ||
234 | 676 | self.log.debug('{} cluster_status:\n{}'.format( | ||
235 | 677 | sentry_unit.info['unit_name'], output)) | ||
236 | 678 | return str(output) | ||
237 | 679 | |||
238 | 680 | def get_rmq_cluster_running_nodes(self, sentry_unit): | ||
239 | 681 | """Parse rabbitmqctl cluster_status output string, return list of | ||
240 | 682 | running rabbitmq cluster nodes. | ||
241 | 683 | |||
242 | 684 | :param unit: sentry unit | ||
243 | 685 | :returns: List containing node names of running nodes | ||
244 | 686 | """ | ||
245 | 687 | # NOTE(beisner): rabbitmqctl cluster_status output is not | ||
246 | 688 | # json-parsable, do string chop foo, then json.loads that. | ||
247 | 689 | str_stat = self.get_rmq_cluster_status(sentry_unit) | ||
248 | 690 | if 'running_nodes' in str_stat: | ||
249 | 691 | pos_start = str_stat.find("{running_nodes,") + 15 | ||
250 | 692 | pos_end = str_stat.find("]},", pos_start) + 1 | ||
251 | 693 | str_run_nodes = str_stat[pos_start:pos_end].replace("'", '"') | ||
252 | 694 | run_nodes = json.loads(str_run_nodes) | ||
253 | 695 | return run_nodes | ||
254 | 696 | else: | ||
255 | 697 | return [] | ||
256 | 698 | |||
257 | 699 | def validate_rmq_cluster_running_nodes(self, sentry_units): | ||
258 | 700 | """Check that all rmq unit hostnames are represented in the | ||
259 | 701 | cluster_status output of all units. | ||
260 | 702 | |||
261 | 703 | :param host_names: dict of juju unit names to host names | ||
262 | 704 | :param units: list of sentry unit pointers (all rmq units) | ||
263 | 705 | :returns: None if successful, otherwise return error message | ||
264 | 706 | """ | ||
265 | 707 | host_names = self.get_unit_hostnames(sentry_units) | ||
266 | 708 | errors = [] | ||
267 | 709 | |||
268 | 710 | # Query every unit for cluster_status running nodes | ||
269 | 711 | for query_unit in sentry_units: | ||
270 | 712 | query_unit_name = query_unit.info['unit_name'] | ||
271 | 713 | running_nodes = self.get_rmq_cluster_running_nodes(query_unit) | ||
272 | 714 | |||
273 | 715 | # Confirm that every unit is represented in the queried unit's | ||
274 | 716 | # cluster_status running nodes output. | ||
275 | 717 | for validate_unit in sentry_units: | ||
276 | 718 | val_host_name = host_names[validate_unit.info['unit_name']] | ||
277 | 719 | val_node_name = 'rabbit@{}'.format(val_host_name) | ||
278 | 720 | |||
279 | 721 | if val_node_name not in running_nodes: | ||
280 | 722 | errors.append('Cluster member check failed on {}: {} not ' | ||
281 | 723 | 'in {}\n'.format(query_unit_name, | ||
282 | 724 | val_node_name, | ||
283 | 725 | running_nodes)) | ||
284 | 726 | if errors: | ||
285 | 727 | return ''.join(errors) | ||
286 | 728 | |||
287 | 729 | def rmq_ssl_is_enabled_on_unit(self, sentry_unit, port=None): | ||
288 | 730 | """Check a single juju rmq unit for ssl and port in the config file.""" | ||
289 | 731 | host = sentry_unit.info['public-address'] | ||
290 | 732 | unit_name = sentry_unit.info['unit_name'] | ||
291 | 733 | |||
292 | 734 | conf_file = '/etc/rabbitmq/rabbitmq.config' | ||
293 | 735 | conf_contents = str(self.file_contents_safe(sentry_unit, | ||
294 | 736 | conf_file, max_wait=16)) | ||
295 | 737 | # Checks | ||
296 | 738 | conf_ssl = 'ssl' in conf_contents | ||
297 | 739 | conf_port = str(port) in conf_contents | ||
298 | 740 | |||
299 | 741 | # Port explicitly checked in config | ||
300 | 742 | if port and conf_port and conf_ssl: | ||
301 | 743 | self.log.debug('SSL is enabled @{}:{} ' | ||
302 | 744 | '({})'.format(host, port, unit_name)) | ||
303 | 745 | return True | ||
304 | 746 | elif port and not conf_port and conf_ssl: | ||
305 | 747 | self.log.debug('SSL is enabled @{} but not on port {} ' | ||
306 | 748 | '({})'.format(host, port, unit_name)) | ||
307 | 749 | return False | ||
308 | 750 | # Port not checked (useful when checking that ssl is disabled) | ||
309 | 751 | elif not port and conf_ssl: | ||
310 | 752 | self.log.debug('SSL is enabled @{}:{} ' | ||
311 | 753 | '({})'.format(host, port, unit_name)) | ||
312 | 754 | return True | ||
313 | 755 | elif not port and not conf_ssl: | ||
314 | 756 | self.log.debug('SSL not enabled @{}:{} ' | ||
315 | 757 | '({})'.format(host, port, unit_name)) | ||
316 | 758 | return False | ||
317 | 759 | else: | ||
318 | 760 | msg = ('Unknown condition when checking SSL status @{}:{} ' | ||
319 | 761 | '({})'.format(host, port, unit_name)) | ||
320 | 762 | amulet.raise_status(amulet.FAIL, msg) | ||
321 | 763 | |||
322 | 764 | def validate_rmq_ssl_enabled_units(self, sentry_units, port=None): | ||
323 | 765 | """Check that ssl is enabled on rmq juju sentry units. | ||
324 | 766 | |||
325 | 767 | :param sentry_units: list of all rmq sentry units | ||
326 | 768 | :param port: optional ssl port override to validate | ||
327 | 769 | :returns: None if successful, otherwise return error message | ||
328 | 770 | """ | ||
329 | 771 | for sentry_unit in sentry_units: | ||
330 | 772 | if not self.rmq_ssl_is_enabled_on_unit(sentry_unit, port=port): | ||
331 | 773 | return ('Unexpected condition: ssl is disabled on unit ' | ||
332 | 774 | '({})'.format(sentry_unit.info['unit_name'])) | ||
333 | 775 | return None | ||
334 | 776 | |||
335 | 777 | def validate_rmq_ssl_disabled_units(self, sentry_units): | ||
336 | 778 | """Check that ssl is enabled on listed rmq juju sentry units. | ||
337 | 779 | |||
338 | 780 | :param sentry_units: list of all rmq sentry units | ||
339 | 781 | :returns: True if successful. Raise on error. | ||
340 | 782 | """ | ||
341 | 783 | for sentry_unit in sentry_units: | ||
342 | 784 | if self.rmq_ssl_is_enabled_on_unit(sentry_unit): | ||
343 | 785 | return ('Unexpected condition: ssl is enabled on unit ' | ||
344 | 786 | '({})'.format(sentry_unit.info['unit_name'])) | ||
345 | 787 | return None | ||
346 | 788 | |||
347 | 789 | def configure_rmq_ssl_on(self, sentry_units, deployment, | ||
348 | 790 | port=None, max_wait=60): | ||
349 | 791 | """Turn ssl charm config option on, with optional non-default | ||
350 | 792 | ssl port specification. Confirm that it is enabled on every | ||
351 | 793 | unit. | ||
352 | 794 | |||
353 | 795 | :param sentry_units: list of sentry units | ||
354 | 796 | :param deployment: amulet deployment object pointer | ||
355 | 797 | :param port: amqp port, use defaults if None | ||
356 | 798 | :param max_wait: maximum time to wait in seconds to confirm | ||
357 | 799 | :returns: None if successful. Raise on error. | ||
358 | 800 | """ | ||
359 | 801 | self.log.debug('Setting ssl charm config option: on') | ||
360 | 802 | |||
361 | 803 | # Enable RMQ SSL | ||
362 | 804 | config = {'ssl': 'on'} | ||
363 | 805 | if port: | ||
364 | 806 | config['ssl_port'] = port | ||
365 | 807 | |||
366 | 808 | deployment.configure('rabbitmq-server', config) | ||
367 | 809 | |||
368 | 810 | # Confirm | ||
369 | 811 | tries = 0 | ||
370 | 812 | ret = self.validate_rmq_ssl_enabled_units(sentry_units, port=port) | ||
371 | 813 | while ret and tries < (max_wait / 4): | ||
372 | 814 | time.sleep(4) | ||
373 | 815 | self.log.debug('Attempt {}: {}'.format(tries, ret)) | ||
374 | 816 | ret = self.validate_rmq_ssl_enabled_units(sentry_units, port=port) | ||
375 | 817 | tries += 1 | ||
376 | 818 | |||
377 | 819 | if ret: | ||
378 | 820 | amulet.raise_status(amulet.FAIL, ret) | ||
379 | 821 | |||
380 | 822 | def configure_rmq_ssl_off(self, sentry_units, deployment, max_wait=60): | ||
381 | 823 | """Turn ssl charm config option off, confirm that it is disabled | ||
382 | 824 | on every unit. | ||
383 | 825 | |||
384 | 826 | :param sentry_units: list of sentry units | ||
385 | 827 | :param deployment: amulet deployment object pointer | ||
386 | 828 | :param max_wait: maximum time to wait in seconds to confirm | ||
387 | 829 | :returns: None if successful. Raise on error. | ||
388 | 830 | """ | ||
389 | 831 | self.log.debug('Setting ssl charm config option: off') | ||
390 | 832 | |||
391 | 833 | # Disable RMQ SSL | ||
392 | 834 | config = {'ssl': 'off'} | ||
393 | 835 | deployment.configure('rabbitmq-server', config) | ||
394 | 836 | |||
395 | 837 | # Confirm | ||
396 | 838 | tries = 0 | ||
397 | 839 | ret = self.validate_rmq_ssl_disabled_units(sentry_units) | ||
398 | 840 | while ret and tries < (max_wait / 4): | ||
399 | 841 | time.sleep(4) | ||
400 | 842 | self.log.debug('Attempt {}: {}'.format(tries, ret)) | ||
401 | 843 | ret = self.validate_rmq_ssl_disabled_units(sentry_units) | ||
402 | 844 | tries += 1 | ||
403 | 845 | |||
404 | 846 | if ret: | ||
405 | 847 | amulet.raise_status(amulet.FAIL, ret) | ||
406 | 848 | |||
407 | 849 | def connect_amqp_by_unit(self, sentry_unit, ssl=False, | ||
408 | 850 | port=None, fatal=True, | ||
409 | 851 | username="testuser1", password="changeme"): | ||
410 | 852 | """Establish and return a pika amqp connection to the rabbitmq service | ||
411 | 853 | running on a rmq juju unit. | ||
412 | 854 | |||
413 | 855 | :param sentry_unit: sentry unit pointer | ||
414 | 856 | :param ssl: boolean, default to False | ||
415 | 857 | :param port: amqp port, use defaults if None | ||
416 | 858 | :param fatal: boolean, default to True (raises on connect error) | ||
417 | 859 | :param username: amqp user name, default to testuser1 | ||
418 | 860 | :param password: amqp user password | ||
419 | 861 | :returns: pika amqp connection pointer or None if failed and non-fatal | ||
420 | 862 | """ | ||
421 | 863 | host = sentry_unit.info['public-address'] | ||
422 | 864 | unit_name = sentry_unit.info['unit_name'] | ||
423 | 865 | |||
424 | 866 | # Default port logic if port is not specified | ||
425 | 867 | if ssl and not port: | ||
426 | 868 | port = 5671 | ||
427 | 869 | elif not ssl and not port: | ||
428 | 870 | port = 5672 | ||
429 | 871 | |||
430 | 872 | self.log.debug('Connecting to amqp on {}:{} ({}) as ' | ||
431 | 873 | '{}...'.format(host, port, unit_name, username)) | ||
432 | 874 | |||
433 | 875 | try: | ||
434 | 876 | credentials = pika.PlainCredentials(username, password) | ||
435 | 877 | parameters = pika.ConnectionParameters(host=host, port=port, | ||
436 | 878 | credentials=credentials, | ||
437 | 879 | ssl=ssl, | ||
438 | 880 | connection_attempts=3, | ||
439 | 881 | retry_delay=5, | ||
440 | 882 | socket_timeout=1) | ||
441 | 883 | connection = pika.BlockingConnection(parameters) | ||
442 | 884 | assert connection.server_properties['product'] == 'RabbitMQ' | ||
443 | 885 | self.log.debug('Connect OK') | ||
444 | 886 | return connection | ||
445 | 887 | except Exception as e: | ||
446 | 888 | msg = ('amqp connection failed to {}:{} as ' | ||
447 | 889 | '{} ({})'.format(host, port, username, str(e))) | ||
448 | 890 | if fatal: | ||
449 | 891 | amulet.raise_status(amulet.FAIL, msg) | ||
450 | 892 | else: | ||
451 | 893 | self.log.warn(msg) | ||
452 | 894 | return None | ||
453 | 895 | |||
454 | 896 | def publish_amqp_message_by_unit(self, sentry_unit, message, | ||
455 | 897 | queue="test", ssl=False, | ||
456 | 898 | username="testuser1", | ||
457 | 899 | password="changeme", | ||
458 | 900 | port=None): | ||
459 | 901 | """Publish an amqp message to a rmq juju unit. | ||
460 | 902 | |||
461 | 903 | :param sentry_unit: sentry unit pointer | ||
462 | 904 | :param message: amqp message string | ||
463 | 905 | :param queue: message queue, default to test | ||
464 | 906 | :param username: amqp user name, default to testuser1 | ||
465 | 907 | :param password: amqp user password | ||
466 | 908 | :param ssl: boolean, default to False | ||
467 | 909 | :param port: amqp port, use defaults if None | ||
468 | 910 | :returns: None. Raises exception if publish failed. | ||
469 | 911 | """ | ||
470 | 912 | self.log.debug('Publishing message to {} queue:\n{}'.format(queue, | ||
471 | 913 | message)) | ||
472 | 914 | connection = self.connect_amqp_by_unit(sentry_unit, ssl=ssl, | ||
473 | 915 | port=port, | ||
474 | 916 | username=username, | ||
475 | 917 | password=password) | ||
476 | 918 | |||
477 | 919 | # NOTE(beisner): extra debug here re: pika hang potential: | ||
478 | 920 | # https://github.com/pika/pika/issues/297 | ||
479 | 921 | # https://groups.google.com/forum/#!topic/rabbitmq-users/Ja0iyfF0Szw | ||
480 | 922 | self.log.debug('Defining channel...') | ||
481 | 923 | channel = connection.channel() | ||
482 | 924 | self.log.debug('Declaring queue...') | ||
483 | 925 | channel.queue_declare(queue=queue, auto_delete=False, durable=True) | ||
484 | 926 | self.log.debug('Publishing message...') | ||
485 | 927 | channel.basic_publish(exchange='', routing_key=queue, body=message) | ||
486 | 928 | self.log.debug('Closing channel...') | ||
487 | 929 | channel.close() | ||
488 | 930 | self.log.debug('Closing connection...') | ||
489 | 931 | connection.close() | ||
490 | 932 | |||
491 | 933 | def get_amqp_message_by_unit(self, sentry_unit, queue="test", | ||
492 | 934 | username="testuser1", | ||
493 | 935 | password="changeme", | ||
494 | 936 | ssl=False, port=None): | ||
495 | 937 | """Get an amqp message from a rmq juju unit. | ||
496 | 938 | |||
497 | 939 | :param sentry_unit: sentry unit pointer | ||
498 | 940 | :param queue: message queue, default to test | ||
499 | 941 | :param username: amqp user name, default to testuser1 | ||
500 | 942 | :param password: amqp user password | ||
501 | 943 | :param ssl: boolean, default to False | ||
502 | 944 | :param port: amqp port, use defaults if None | ||
503 | 945 | :returns: amqp message body as string. Raise if get fails. | ||
504 | 946 | """ | ||
505 | 947 | connection = self.connect_amqp_by_unit(sentry_unit, ssl=ssl, | ||
506 | 948 | port=port, | ||
507 | 949 | username=username, | ||
508 | 950 | password=password) | ||
509 | 951 | channel = connection.channel() | ||
510 | 952 | method_frame, _, body = channel.basic_get(queue) | ||
511 | 953 | |||
512 | 954 | if method_frame: | ||
513 | 955 | self.log.debug('Retreived message from {} queue:\n{}'.format(queue, | ||
514 | 956 | body)) | ||
515 | 957 | channel.basic_ack(method_frame.delivery_tag) | ||
516 | 958 | channel.close() | ||
517 | 959 | connection.close() | ||
518 | 960 | return body | ||
519 | 961 | else: | ||
520 | 962 | msg = 'No message retrieved.' | ||
521 | 963 | amulet.raise_status(amulet.FAIL, msg) | ||
522 | 605 | 964 | ||
523 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
524 | --- hooks/charmhelpers/contrib/openstack/utils.py 2015-09-03 09:43:34 +0000 | |||
525 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2015-09-15 18:42:15 +0000 | |||
526 | @@ -25,6 +25,7 @@ | |||
527 | 25 | import re | 25 | import re |
528 | 26 | 26 | ||
529 | 27 | import six | 27 | import six |
530 | 28 | import traceback | ||
531 | 28 | import yaml | 29 | import yaml |
532 | 29 | 30 | ||
533 | 30 | from charmhelpers.contrib.network import ip | 31 | from charmhelpers.contrib.network import ip |
534 | @@ -34,6 +35,8 @@ | |||
535 | 34 | ) | 35 | ) |
536 | 35 | 36 | ||
537 | 36 | from charmhelpers.core.hookenv import ( | 37 | from charmhelpers.core.hookenv import ( |
538 | 38 | action_fail, | ||
539 | 39 | action_set, | ||
540 | 37 | config, | 40 | config, |
541 | 38 | log as juju_log, | 41 | log as juju_log, |
542 | 39 | charm_dir, | 42 | charm_dir, |
543 | @@ -114,6 +117,7 @@ | |||
544 | 114 | ('2.2.1', 'kilo'), | 117 | ('2.2.1', 'kilo'), |
545 | 115 | ('2.2.2', 'kilo'), | 118 | ('2.2.2', 'kilo'), |
546 | 116 | ('2.3.0', 'liberty'), | 119 | ('2.3.0', 'liberty'), |
547 | 120 | ('2.4.0', 'liberty'), | ||
548 | 117 | ]) | 121 | ]) |
549 | 118 | 122 | ||
550 | 119 | # >= Liberty version->codename mapping | 123 | # >= Liberty version->codename mapping |
551 | @@ -142,6 +146,9 @@ | |||
552 | 142 | 'glance-common': OrderedDict([ | 146 | 'glance-common': OrderedDict([ |
553 | 143 | ('11.0.0', 'liberty'), | 147 | ('11.0.0', 'liberty'), |
554 | 144 | ]), | 148 | ]), |
555 | 149 | 'openstack-dashboard': OrderedDict([ | ||
556 | 150 | ('8.0.0', 'liberty'), | ||
557 | 151 | ]), | ||
558 | 145 | } | 152 | } |
559 | 146 | 153 | ||
560 | 147 | DEFAULT_LOOPBACK_SIZE = '5G' | 154 | DEFAULT_LOOPBACK_SIZE = '5G' |
561 | @@ -745,3 +752,47 @@ | |||
562 | 745 | return projects[key] | 752 | return projects[key] |
563 | 746 | 753 | ||
564 | 747 | return None | 754 | return None |
565 | 755 | |||
566 | 756 | |||
567 | 757 | def do_action_openstack_upgrade(package, upgrade_callback, configs): | ||
568 | 758 | """Perform action-managed OpenStack upgrade. | ||
569 | 759 | |||
570 | 760 | Upgrades packages to the configured openstack-origin version and sets | ||
571 | 761 | the corresponding action status as a result. | ||
572 | 762 | |||
573 | 763 | If the charm was installed from source we cannot upgrade it. | ||
574 | 764 | For backwards compatibility a config flag (action-managed-upgrade) must | ||
575 | 765 | be set for this code to run, otherwise a full service level upgrade will | ||
576 | 766 | fire on config-changed. | ||
577 | 767 | |||
578 | 768 | @param package: package name for determining if upgrade available | ||
579 | 769 | @param upgrade_callback: function callback to charm's upgrade function | ||
580 | 770 | @param configs: templating object derived from OSConfigRenderer class | ||
581 | 771 | |||
582 | 772 | @return: True if upgrade successful; False if upgrade failed or skipped | ||
583 | 773 | """ | ||
584 | 774 | ret = False | ||
585 | 775 | |||
586 | 776 | if git_install_requested(): | ||
587 | 777 | action_set({'outcome': 'installed from source, skipped upgrade.'}) | ||
588 | 778 | else: | ||
589 | 779 | if openstack_upgrade_available(package): | ||
590 | 780 | if config('action-managed-upgrade'): | ||
591 | 781 | juju_log('Upgrading OpenStack release') | ||
592 | 782 | |||
593 | 783 | try: | ||
594 | 784 | upgrade_callback(configs=configs) | ||
595 | 785 | action_set({'outcome': 'success, upgrade completed.'}) | ||
596 | 786 | ret = True | ||
597 | 787 | except: | ||
598 | 788 | action_set({'outcome': 'upgrade failed, see traceback.'}) | ||
599 | 789 | action_set({'traceback': traceback.format_exc()}) | ||
600 | 790 | action_fail('do_openstack_upgrade resulted in an ' | ||
601 | 791 | 'unexpected error') | ||
602 | 792 | else: | ||
603 | 793 | action_set({'outcome': 'action-managed-upgrade config is ' | ||
604 | 794 | 'False, skipped upgrade.'}) | ||
605 | 795 | else: | ||
606 | 796 | action_set({'outcome': 'no upgrade available.'}) | ||
607 | 797 | |||
608 | 798 | return ret | ||
609 | 748 | 799 | ||
610 | === modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py' | |||
611 | --- hooks/charmhelpers/contrib/storage/linux/ceph.py 2015-09-10 09:30:00 +0000 | |||
612 | +++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2015-09-15 18:42:15 +0000 | |||
613 | @@ -59,6 +59,8 @@ | |||
614 | 59 | apt_install, | 59 | apt_install, |
615 | 60 | ) | 60 | ) |
616 | 61 | 61 | ||
617 | 62 | from charmhelpers.core.kernel import modprobe | ||
618 | 63 | |||
619 | 62 | KEYRING = '/etc/ceph/ceph.client.{}.keyring' | 64 | KEYRING = '/etc/ceph/ceph.client.{}.keyring' |
620 | 63 | KEYFILE = '/etc/ceph/ceph.client.{}.key' | 65 | KEYFILE = '/etc/ceph/ceph.client.{}.key' |
621 | 64 | 66 | ||
622 | @@ -291,17 +293,6 @@ | |||
623 | 291 | os.chown(data_src_dst, uid, gid) | 293 | os.chown(data_src_dst, uid, gid) |
624 | 292 | 294 | ||
625 | 293 | 295 | ||
626 | 294 | # TODO: re-use | ||
627 | 295 | def modprobe(module): | ||
628 | 296 | """Load a kernel module and configure for auto-load on reboot.""" | ||
629 | 297 | log('Loading kernel module', level=INFO) | ||
630 | 298 | cmd = ['modprobe', module] | ||
631 | 299 | check_call(cmd) | ||
632 | 300 | with open('/etc/modules', 'r+') as modules: | ||
633 | 301 | if module not in modules.read(): | ||
634 | 302 | modules.write(module) | ||
635 | 303 | |||
636 | 304 | |||
637 | 305 | def copy_files(src, dst, symlinks=False, ignore=None): | 296 | def copy_files(src, dst, symlinks=False, ignore=None): |
638 | 306 | """Copy files from src to dst.""" | 297 | """Copy files from src to dst.""" |
639 | 307 | for item in os.listdir(src): | 298 | for item in os.listdir(src): |
640 | 308 | 299 | ||
641 | === modified file 'hooks/charmhelpers/core/host.py' | |||
642 | --- hooks/charmhelpers/core/host.py 2015-08-19 13:51:56 +0000 | |||
643 | +++ hooks/charmhelpers/core/host.py 2015-09-15 18:42:15 +0000 | |||
644 | @@ -63,32 +63,48 @@ | |||
645 | 63 | return service_result | 63 | return service_result |
646 | 64 | 64 | ||
647 | 65 | 65 | ||
649 | 66 | def service_pause(service_name, init_dir=None): | 66 | def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d"): |
650 | 67 | """Pause a system service. | 67 | """Pause a system service. |
651 | 68 | 68 | ||
652 | 69 | Stop it, and prevent it from starting again at boot.""" | 69 | Stop it, and prevent it from starting again at boot.""" |
653 | 70 | if init_dir is None: | ||
654 | 71 | init_dir = "/etc/init" | ||
655 | 72 | stopped = service_stop(service_name) | 70 | stopped = service_stop(service_name) |
661 | 73 | # XXX: Support systemd too | 71 | upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) |
662 | 74 | override_path = os.path.join( | 72 | sysv_file = os.path.join(initd_dir, service_name) |
663 | 75 | init_dir, '{}.override'.format(service_name)) | 73 | if os.path.exists(upstart_file): |
664 | 76 | with open(override_path, 'w') as fh: | 74 | override_path = os.path.join( |
665 | 77 | fh.write("manual\n") | 75 | init_dir, '{}.override'.format(service_name)) |
666 | 76 | with open(override_path, 'w') as fh: | ||
667 | 77 | fh.write("manual\n") | ||
668 | 78 | elif os.path.exists(sysv_file): | ||
669 | 79 | subprocess.check_call(["update-rc.d", service_name, "disable"]) | ||
670 | 80 | else: | ||
671 | 81 | # XXX: Support SystemD too | ||
672 | 82 | raise ValueError( | ||
673 | 83 | "Unable to detect {0} as either Upstart {1} or SysV {2}".format( | ||
674 | 84 | service_name, upstart_file, sysv_file)) | ||
675 | 78 | return stopped | 85 | return stopped |
676 | 79 | 86 | ||
677 | 80 | 87 | ||
679 | 81 | def service_resume(service_name, init_dir=None): | 88 | def service_resume(service_name, init_dir="/etc/init", |
680 | 89 | initd_dir="/etc/init.d"): | ||
681 | 82 | """Resume a system service. | 90 | """Resume a system service. |
682 | 83 | 91 | ||
683 | 84 | Reenable starting again at boot. Start the service""" | 92 | Reenable starting again at boot. Start the service""" |
691 | 85 | # XXX: Support systemd too | 93 | upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) |
692 | 86 | if init_dir is None: | 94 | sysv_file = os.path.join(initd_dir, service_name) |
693 | 87 | init_dir = "/etc/init" | 95 | if os.path.exists(upstart_file): |
694 | 88 | override_path = os.path.join( | 96 | override_path = os.path.join( |
695 | 89 | init_dir, '{}.override'.format(service_name)) | 97 | init_dir, '{}.override'.format(service_name)) |
696 | 90 | if os.path.exists(override_path): | 98 | if os.path.exists(override_path): |
697 | 91 | os.unlink(override_path) | 99 | os.unlink(override_path) |
698 | 100 | elif os.path.exists(sysv_file): | ||
699 | 101 | subprocess.check_call(["update-rc.d", service_name, "enable"]) | ||
700 | 102 | else: | ||
701 | 103 | # XXX: Support SystemD too | ||
702 | 104 | raise ValueError( | ||
703 | 105 | "Unable to detect {0} as either Upstart {1} or SysV {2}".format( | ||
704 | 106 | service_name, upstart_file, sysv_file)) | ||
705 | 107 | |||
706 | 92 | started = service_start(service_name) | 108 | started = service_start(service_name) |
707 | 93 | return started | 109 | return started |
708 | 94 | 110 | ||
709 | 95 | 111 | ||
710 | === added file 'hooks/charmhelpers/core/kernel.py' | |||
711 | --- hooks/charmhelpers/core/kernel.py 1970-01-01 00:00:00 +0000 | |||
712 | +++ hooks/charmhelpers/core/kernel.py 2015-09-15 18:42:15 +0000 | |||
713 | @@ -0,0 +1,68 @@ | |||
714 | 1 | #!/usr/bin/env python | ||
715 | 2 | # -*- coding: utf-8 -*- | ||
716 | 3 | |||
717 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
718 | 5 | # | ||
719 | 6 | # This file is part of charm-helpers. | ||
720 | 7 | # | ||
721 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
722 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
723 | 10 | # published by the Free Software Foundation. | ||
724 | 11 | # | ||
725 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
726 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
727 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
728 | 15 | # GNU Lesser General Public License for more details. | ||
729 | 16 | # | ||
730 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
731 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
732 | 19 | |||
733 | 20 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
734 | 21 | |||
735 | 22 | from charmhelpers.core.hookenv import ( | ||
736 | 23 | log, | ||
737 | 24 | INFO | ||
738 | 25 | ) | ||
739 | 26 | |||
740 | 27 | from subprocess import check_call, check_output | ||
741 | 28 | import re | ||
742 | 29 | |||
743 | 30 | |||
744 | 31 | def modprobe(module, persist=True): | ||
745 | 32 | """Load a kernel module and configure for auto-load on reboot.""" | ||
746 | 33 | cmd = ['modprobe', module] | ||
747 | 34 | |||
748 | 35 | log('Loading kernel module %s' % module, level=INFO) | ||
749 | 36 | |||
750 | 37 | check_call(cmd) | ||
751 | 38 | if persist: | ||
752 | 39 | with open('/etc/modules', 'r+') as modules: | ||
753 | 40 | if module not in modules.read(): | ||
754 | 41 | modules.write(module) | ||
755 | 42 | |||
756 | 43 | |||
757 | 44 | def rmmod(module, force=False): | ||
758 | 45 | """Remove a module from the linux kernel""" | ||
759 | 46 | cmd = ['rmmod'] | ||
760 | 47 | if force: | ||
761 | 48 | cmd.append('-f') | ||
762 | 49 | cmd.append(module) | ||
763 | 50 | log('Removing kernel module %s' % module, level=INFO) | ||
764 | 51 | return check_call(cmd) | ||
765 | 52 | |||
766 | 53 | |||
767 | 54 | def lsmod(): | ||
768 | 55 | """Shows what kernel modules are currently loaded""" | ||
769 | 56 | return check_output(['lsmod'], | ||
770 | 57 | universal_newlines=True) | ||
771 | 58 | |||
772 | 59 | |||
773 | 60 | def is_module_loaded(module): | ||
774 | 61 | """Checks if a kernel module is already loaded""" | ||
775 | 62 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) | ||
776 | 63 | return len(matches) > 0 | ||
777 | 64 | |||
778 | 65 | |||
779 | 66 | def update_initramfs(version='all'): | ||
780 | 67 | """Updates an initramfs image""" | ||
781 | 68 | return check_call(["update-initramfs", "-k", version, "-u"]) | ||
782 | 0 | 69 | ||
783 | === modified file 'tests/charmhelpers/contrib/amulet/utils.py' | |||
784 | --- tests/charmhelpers/contrib/amulet/utils.py 2015-09-10 09:30:00 +0000 | |||
785 | +++ tests/charmhelpers/contrib/amulet/utils.py 2015-09-15 18:42:15 +0000 | |||
786 | @@ -776,3 +776,12 @@ | |||
787 | 776 | output = _check_output(command, universal_newlines=True) | 776 | output = _check_output(command, universal_newlines=True) |
788 | 777 | data = json.loads(output) | 777 | data = json.loads(output) |
789 | 778 | return data.get(u"status") == "completed" | 778 | return data.get(u"status") == "completed" |
790 | 779 | |||
791 | 780 | def status_get(self, unit): | ||
792 | 781 | """Return the current service status of this unit.""" | ||
793 | 782 | raw_status, return_code = unit.run( | ||
794 | 783 | "status-get --format=json --include-data") | ||
795 | 784 | if return_code != 0: | ||
796 | 785 | return ("unknown", "") | ||
797 | 786 | status = json.loads(raw_status) | ||
798 | 787 | return (status["status"], status["message"]) | ||
799 | 779 | 788 | ||
800 | === modified file 'unit_tests/test_actions_openstack_upgrade.py' | |||
801 | --- unit_tests/test_actions_openstack_upgrade.py 2015-09-01 15:13:23 +0000 | |||
802 | +++ unit_tests/test_actions_openstack_upgrade.py 2015-09-15 18:42:15 +0000 | |||
803 | @@ -11,8 +11,9 @@ | |||
804 | 11 | ) | 11 | ) |
805 | 12 | 12 | ||
806 | 13 | TO_PATCH = [ | 13 | TO_PATCH = [ |
809 | 14 | 'config', | 14 | 'config_changed', |
810 | 15 | 'juju_log', | 15 | 'do_openstack_upgrade', |
811 | 16 | 'register_configs', | ||
812 | 16 | 'relation_set', | 17 | 'relation_set', |
813 | 17 | 'relation_ids', | 18 | 'relation_ids', |
814 | 18 | 'uuid' | 19 | 'uuid' |
815 | @@ -24,103 +25,40 @@ | |||
816 | 24 | def setUp(self): | 25 | def setUp(self): |
817 | 25 | super(TestCinderUpgradeActions, self).setUp(openstack_upgrade, | 26 | super(TestCinderUpgradeActions, self).setUp(openstack_upgrade, |
818 | 26 | TO_PATCH) | 27 | TO_PATCH) |
819 | 27 | self.config.side_effect = self.test_config.get | ||
820 | 28 | 28 | ||
821 | 29 | @patch.object(openstack_upgrade, 'action_set') | ||
822 | 30 | @patch.object(openstack_upgrade, 'action_fail') | ||
823 | 31 | @patch.object(openstack_upgrade, 'do_openstack_upgrade') | ||
824 | 32 | @patch.object(openstack_upgrade, 'openstack_upgrade_available') | ||
825 | 33 | @patch.object(openstack_upgrade, 'config_changed') | ||
826 | 34 | @patch('charmhelpers.contrib.openstack.utils.config') | 29 | @patch('charmhelpers.contrib.openstack.utils.config') |
833 | 35 | def test_openstack_upgrade(self, _config, config_changed, | 30 | @patch('charmhelpers.contrib.openstack.utils.action_set') |
834 | 36 | openstack_upgrade_available, | 31 | @patch('charmhelpers.contrib.openstack.utils.git_install_requested') |
835 | 37 | do_openstack_upgrade, action_fail, | 32 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') |
836 | 38 | action_set): | 33 | def test_openstack_upgrade_true(self, upgrade_avail, git_requested, |
837 | 39 | _config.return_value = None | 34 | action_set, config): |
838 | 40 | openstack_upgrade_available.return_value = True | 35 | git_requested.return_value = False |
839 | 36 | upgrade_avail.return_value = True | ||
840 | 37 | config.return_value = True | ||
841 | 41 | self.relation_ids.return_value = ['relid1'] | 38 | self.relation_ids.return_value = ['relid1'] |
842 | 42 | self.uuid.uuid4.return_value = 12345 | 39 | self.uuid.uuid4.return_value = 12345 |
843 | 43 | 40 | ||
844 | 44 | self.test_config.set('action-managed-upgrade', True) | ||
845 | 45 | |||
846 | 46 | openstack_upgrade.openstack_upgrade() | 41 | openstack_upgrade.openstack_upgrade() |
847 | 47 | 42 | ||
850 | 48 | self.assertTrue(do_openstack_upgrade.called) | 43 | self.assertTrue(self.do_openstack_upgrade.called) |
849 | 49 | self.assertTrue(config_changed.called) | ||
851 | 50 | self.assertTrue(self.relation_ids.called) | 44 | self.assertTrue(self.relation_ids.called) |
852 | 51 | self.relation_set.assert_called_with(relation_id='relid1', | 45 | self.relation_set.assert_called_with(relation_id='relid1', |
853 | 52 | upgrade_nonce=12345) | 46 | upgrade_nonce=12345) |
928 | 53 | self.assertFalse(action_fail.called) | 47 | self.assertTrue(self.config_changed.called) |
929 | 54 | 48 | ||
930 | 55 | @patch.object(openstack_upgrade, 'action_set') | 49 | @patch('charmhelpers.contrib.openstack.utils.config') |
931 | 56 | @patch.object(openstack_upgrade, 'do_openstack_upgrade') | 50 | @patch('charmhelpers.contrib.openstack.utils.action_set') |
932 | 57 | @patch.object(openstack_upgrade, 'openstack_upgrade_available') | 51 | @patch('charmhelpers.contrib.openstack.utils.git_install_requested') |
933 | 58 | @patch.object(openstack_upgrade, 'config_changed') | 52 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') |
934 | 59 | @patch('charmhelpers.contrib.openstack.utils.config') | 53 | def test_openstack_upgrade_false(self, upgrade_avail, git_requested, |
935 | 60 | def test_openstack_upgrade_not_configured(self, _config, config_changed, | 54 | action_set, config): |
936 | 61 | openstack_upgrade_available, | 55 | git_requested.return_value = False |
937 | 62 | do_openstack_upgrade, | 56 | upgrade_avail.return_value = True |
938 | 63 | action_set): | 57 | config.return_value = False |
939 | 64 | _config.return_value = None | 58 | |
940 | 65 | openstack_upgrade_available.return_value = True | 59 | openstack_upgrade.openstack_upgrade() |
941 | 66 | 60 | ||
942 | 67 | openstack_upgrade.openstack_upgrade() | 61 | self.assertFalse(self.do_openstack_upgrade.called) |
943 | 68 | 62 | self.assertFalse(self.relation_ids.called) | |
944 | 69 | msg = ('action-managed-upgrade config is False, skipped upgrade.') | 63 | self.assertFalse(self.relation_set.called) |
945 | 70 | 64 | self.assertFalse(self.config_changed.called) | |
872 | 71 | action_set.assert_called_with({'outcome': msg}) | ||
873 | 72 | self.assertFalse(do_openstack_upgrade.called) | ||
874 | 73 | |||
875 | 74 | @patch.object(openstack_upgrade, 'action_set') | ||
876 | 75 | @patch.object(openstack_upgrade, 'do_openstack_upgrade') | ||
877 | 76 | @patch.object(openstack_upgrade, 'openstack_upgrade_available') | ||
878 | 77 | @patch.object(openstack_upgrade, 'config_changed') | ||
879 | 78 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
880 | 79 | def test_openstack_upgrade_git_install(self, _config, config_changed, | ||
881 | 80 | openstack_upgrade_available, | ||
882 | 81 | do_openstack_upgrade, | ||
883 | 82 | action_set): | ||
884 | 83 | |||
885 | 84 | self.test_config.set('action-managed-upgrade', True) | ||
886 | 85 | self.test_config.set('openstack-origin-git', True) | ||
887 | 86 | |||
888 | 87 | openstack_upgrade.openstack_upgrade() | ||
889 | 88 | |||
890 | 89 | msg = ('installed from source, skipped upgrade.') | ||
891 | 90 | action_set.assert_called_with({'outcome': msg}) | ||
892 | 91 | self.assertFalse(do_openstack_upgrade.called) | ||
893 | 92 | |||
894 | 93 | @patch.object(openstack_upgrade, 'action_set') | ||
895 | 94 | @patch.object(openstack_upgrade, 'action_fail') | ||
896 | 95 | @patch.object(openstack_upgrade, 'do_openstack_upgrade') | ||
897 | 96 | @patch.object(openstack_upgrade, 'openstack_upgrade_available') | ||
898 | 97 | @patch.object(openstack_upgrade, 'config_changed') | ||
899 | 98 | @patch('traceback.format_exc') | ||
900 | 99 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
901 | 100 | def test_openstack_upgrade_exception(self, _config, format_exc, | ||
902 | 101 | config_changed, | ||
903 | 102 | openstack_upgrade_available, | ||
904 | 103 | do_openstack_upgrade, | ||
905 | 104 | action_fail, action_set): | ||
906 | 105 | _config.return_value = None | ||
907 | 106 | self.test_config.set('action-managed-upgrade', True) | ||
908 | 107 | openstack_upgrade_available.return_value = True | ||
909 | 108 | |||
910 | 109 | e = OSError('something bad happened') | ||
911 | 110 | do_openstack_upgrade.side_effect = e | ||
912 | 111 | traceback = ( | ||
913 | 112 | "Traceback (most recent call last):\n" | ||
914 | 113 | " File \"actions/openstack_upgrade.py\", line 37, in openstack_upgrade\n" # noqa | ||
915 | 114 | " openstack_upgrade(config(\'openstack-origin-git\'))\n" | ||
916 | 115 | " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa | ||
917 | 116 | " return _mock_self._mock_call(*args, **kwargs)\n" | ||
918 | 117 | " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa | ||
919 | 118 | " raise effect\n" | ||
920 | 119 | "OSError: something bad happened\n") | ||
921 | 120 | format_exc.return_value = traceback | ||
922 | 121 | |||
923 | 122 | openstack_upgrade.openstack_upgrade() | ||
924 | 123 | |||
925 | 124 | msg = 'do_openstack_upgrade resulted in an unexpected error' | ||
926 | 125 | action_fail.assert_called_with(msg) | ||
927 | 126 | action_set.assert_called_with({'traceback': traceback}) |
charm_lint_check #10051 cinder-next for corey.bryant mp270999
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/10051/