Merge lp:~ddellav/charms/trusty/nova-compute/upgrade-action into lp:~openstack-charmers-archive/charms/trusty/nova-compute/next
- Trusty Tahr (14.04)
- upgrade-action
- Merge into next
Status: | Merged |
---|---|
Merged at revision: | 174 |
Proposed branch: | lp:~ddellav/charms/trusty/nova-compute/upgrade-action |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/nova-compute/next |
Diff against target: |
482 lines (+222/-33) 14 files modified
actions.yaml (+2/-0) actions/openstack_upgrade.py (+32/-0) config.yaml (+10/-0) hooks/charmhelpers/contrib/network/ip.py (+5/-3) hooks/charmhelpers/contrib/openstack/amulet/utils.py (+1/-1) hooks/charmhelpers/contrib/openstack/context.py (+15/-4) hooks/charmhelpers/contrib/openstack/neutron.py (+3/-3) hooks/charmhelpers/core/hookenv.py (+32/-0) hooks/nova_compute_hooks.py (+2/-2) hooks/nova_compute_utils.py (+1/-3) tests/charmhelpers/contrib/amulet/utils.py (+47/-16) tests/charmhelpers/contrib/openstack/amulet/utils.py (+1/-1) unit_tests/test_actions_openstack_upgrade.py (+62/-0) unit_tests/test_nova_compute_hooks.py (+9/-0) |
To merge this branch: | bzr merge lp:~ddellav/charms/trusty/nova-compute/upgrade-action |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Corey Bryant (community) | Approve | ||
Review via email: mp+273560@code.launchpad.net |
Commit message
Description of the change
Adding charmhelpers-based upgrade action to nova-compute.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10584 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10585 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #11394 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7141 nova-compute-next for ddellav mp273560
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7140 nova-compute-next for ddellav mp273560
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://
Build: http://
Corey Bryant (corey.bryant) wrote : | # |
Reviewed and tested successfully. I approve. Just waiting on amulet tests to pass before landing.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #11425 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10616 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7168 nova-compute-next for ddellav mp273560
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10733 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #11542 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7243 nova-compute-next for ddellav mp273560
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7258 nova-compute-next for ddellav mp273560
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10797 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #11608 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #11618 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #10807 nova-compute-next for ddellav mp273560
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #7266 nova-compute-next for ddellav mp273560
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://
Build: http://
Corey Bryant (corey.bryant) wrote : | # |
The amulet failure is not related. Something is wrong with kilo deploying from source. So I'm going to merge this.
Preview Diff
1 | === modified file 'actions.yaml' | |||
2 | --- actions.yaml 2015-04-15 14:21:42 +0000 | |||
3 | +++ actions.yaml 2015-10-08 15:08:00 +0000 | |||
4 | @@ -1,2 +1,4 @@ | |||
5 | 1 | git-reinstall: | 1 | git-reinstall: |
6 | 2 | description: Reinstall nova-compute from the openstack-origin-git repositories. | 2 | description: Reinstall nova-compute from the openstack-origin-git repositories. |
7 | 3 | openstack-upgrade: | ||
8 | 4 | description: Perform openstack upgrades. Config option action-managed-upgrade must be set to True. | ||
9 | 3 | 5 | ||
10 | === added symlink 'actions/openstack-upgrade' | |||
11 | === target is u'openstack_upgrade.py' | |||
12 | === added file 'actions/openstack_upgrade.py' | |||
13 | --- actions/openstack_upgrade.py 1970-01-01 00:00:00 +0000 | |||
14 | +++ actions/openstack_upgrade.py 2015-10-08 15:08:00 +0000 | |||
15 | @@ -0,0 +1,32 @@ | |||
16 | 1 | #!/usr/bin/python | ||
17 | 2 | import sys | ||
18 | 3 | |||
19 | 4 | sys.path.append('hooks/') | ||
20 | 5 | |||
21 | 6 | from charmhelpers.contrib.openstack.utils import ( | ||
22 | 7 | do_action_openstack_upgrade, | ||
23 | 8 | ) | ||
24 | 9 | |||
25 | 10 | from nova_compute_utils import do_openstack_upgrade | ||
26 | 11 | |||
27 | 12 | from nova_compute_hooks import ( | ||
28 | 13 | config_changed, | ||
29 | 14 | CONFIGS | ||
30 | 15 | ) | ||
31 | 16 | |||
32 | 17 | |||
33 | 18 | def openstack_upgrade(): | ||
34 | 19 | """Upgrade packages to config-set Openstack version. | ||
35 | 20 | |||
36 | 21 | If the charm was installed from source we cannot upgrade it. | ||
37 | 22 | For backwards compatibility a config flag must be set for this | ||
38 | 23 | code to run, otherwise a full service level upgrade will fire | ||
39 | 24 | on config-changed.""" | ||
40 | 25 | |||
41 | 26 | if (do_action_openstack_upgrade('nova-common', | ||
42 | 27 | do_openstack_upgrade, | ||
43 | 28 | CONFIGS)): | ||
44 | 29 | config_changed() | ||
45 | 30 | |||
46 | 31 | if __name__ == '__main__': | ||
47 | 32 | openstack_upgrade() | ||
48 | 0 | 33 | ||
49 | === modified file 'config.yaml' | |||
50 | --- config.yaml 2015-10-02 09:40:38 +0000 | |||
51 | +++ config.yaml 2015-10-08 15:08:00 +0000 | |||
52 | @@ -265,3 +265,13 @@ | |||
53 | 265 | description: | | 265 | description: | |
54 | 266 | The pecentage of system memory to use for hugepages eg '10%' or the total | 266 | The pecentage of system memory to use for hugepages eg '10%' or the total |
55 | 267 | number of 2M hugepages - eg "1024". | 267 | number of 2M hugepages - eg "1024". |
56 | 268 | action-managed-upgrade: | ||
57 | 269 | type: boolean | ||
58 | 270 | default: False | ||
59 | 271 | description: | | ||
60 | 272 | If True enables openstack upgrades for this charm via juju actions. | ||
61 | 273 | You will still need to set openstack-origin to the new repository but | ||
62 | 274 | instead of an upgrade running automatically across all units, it will | ||
63 | 275 | wait for you to execute the openstack-upgrade action for this charm on | ||
64 | 276 | each unit. If False it will revert to existing behavior of upgrading | ||
65 | 277 | all units on config change. | ||
66 | 268 | 278 | ||
67 | === modified file 'hooks/charmhelpers/contrib/network/ip.py' | |||
68 | --- hooks/charmhelpers/contrib/network/ip.py 2015-09-02 15:03:16 +0000 | |||
69 | +++ hooks/charmhelpers/contrib/network/ip.py 2015-10-08 15:08:00 +0000 | |||
70 | @@ -23,7 +23,7 @@ | |||
71 | 23 | from functools import partial | 23 | from functools import partial |
72 | 24 | 24 | ||
73 | 25 | from charmhelpers.core.hookenv import unit_get | 25 | from charmhelpers.core.hookenv import unit_get |
75 | 26 | from charmhelpers.fetch import apt_install | 26 | from charmhelpers.fetch import apt_install, apt_update |
76 | 27 | from charmhelpers.core.hookenv import ( | 27 | from charmhelpers.core.hookenv import ( |
77 | 28 | log, | 28 | log, |
78 | 29 | WARNING, | 29 | WARNING, |
79 | @@ -32,13 +32,15 @@ | |||
80 | 32 | try: | 32 | try: |
81 | 33 | import netifaces | 33 | import netifaces |
82 | 34 | except ImportError: | 34 | except ImportError: |
84 | 35 | apt_install('python-netifaces') | 35 | apt_update(fatal=True) |
85 | 36 | apt_install('python-netifaces', fatal=True) | ||
86 | 36 | import netifaces | 37 | import netifaces |
87 | 37 | 38 | ||
88 | 38 | try: | 39 | try: |
89 | 39 | import netaddr | 40 | import netaddr |
90 | 40 | except ImportError: | 41 | except ImportError: |
92 | 41 | apt_install('python-netaddr') | 42 | apt_update(fatal=True) |
93 | 43 | apt_install('python-netaddr', fatal=True) | ||
94 | 42 | import netaddr | 44 | import netaddr |
95 | 43 | 45 | ||
96 | 44 | 46 | ||
97 | 45 | 47 | ||
98 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py' | |||
99 | --- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-09-28 07:56:16 +0000 | |||
100 | +++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-08 15:08:00 +0000 | |||
101 | @@ -752,7 +752,7 @@ | |||
102 | 752 | self.log.debug('SSL is enabled @{}:{} ' | 752 | self.log.debug('SSL is enabled @{}:{} ' |
103 | 753 | '({})'.format(host, port, unit_name)) | 753 | '({})'.format(host, port, unit_name)) |
104 | 754 | return True | 754 | return True |
106 | 755 | elif not port and not conf_ssl: | 755 | elif not conf_ssl: |
107 | 756 | self.log.debug('SSL not enabled @{}:{} ' | 756 | self.log.debug('SSL not enabled @{}:{} ' |
108 | 757 | '({})'.format(host, port, unit_name)) | 757 | '({})'.format(host, port, unit_name)) |
109 | 758 | return False | 758 | return False |
110 | 759 | 759 | ||
111 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' | |||
112 | --- hooks/charmhelpers/contrib/openstack/context.py 2015-09-28 07:56:16 +0000 | |||
113 | +++ hooks/charmhelpers/contrib/openstack/context.py 2015-10-08 15:08:00 +0000 | |||
114 | @@ -14,6 +14,7 @@ | |||
115 | 14 | # You should have received a copy of the GNU Lesser General Public License | 14 | # You should have received a copy of the GNU Lesser General Public License |
116 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
117 | 16 | 16 | ||
118 | 17 | import glob | ||
119 | 17 | import json | 18 | import json |
120 | 18 | import os | 19 | import os |
121 | 19 | import re | 20 | import re |
122 | @@ -1363,7 +1364,7 @@ | |||
123 | 1363 | normalized.update({port: port for port in resolved | 1364 | normalized.update({port: port for port in resolved |
124 | 1364 | if port in ports}) | 1365 | if port in ports}) |
125 | 1365 | if resolved: | 1366 | if resolved: |
127 | 1366 | return {bridge: normalized[port] for port, bridge in | 1367 | return {normalized[port]: bridge for port, bridge in |
128 | 1367 | six.iteritems(portmap) if port in normalized.keys()} | 1368 | six.iteritems(portmap) if port in normalized.keys()} |
129 | 1368 | 1369 | ||
130 | 1369 | return None | 1370 | return None |
131 | @@ -1374,12 +1375,22 @@ | |||
132 | 1374 | def __call__(self): | 1375 | def __call__(self): |
133 | 1375 | ctxt = {} | 1376 | ctxt = {} |
134 | 1376 | mappings = super(PhyNICMTUContext, self).__call__() | 1377 | mappings = super(PhyNICMTUContext, self).__call__() |
137 | 1377 | if mappings and mappings.values(): | 1378 | if mappings and mappings.keys(): |
138 | 1378 | ports = mappings.values() | 1379 | ports = sorted(mappings.keys()) |
139 | 1379 | napi_settings = NeutronAPIContext()() | 1380 | napi_settings = NeutronAPIContext()() |
140 | 1380 | mtu = napi_settings.get('network_device_mtu') | 1381 | mtu = napi_settings.get('network_device_mtu') |
141 | 1382 | all_ports = set() | ||
142 | 1383 | # If any of ports is a vlan device, its underlying device must have | ||
143 | 1384 | # mtu applied first. | ||
144 | 1385 | for port in ports: | ||
145 | 1386 | for lport in glob.glob("/sys/class/net/%s/lower_*" % port): | ||
146 | 1387 | lport = os.path.basename(lport) | ||
147 | 1388 | all_ports.add(lport.split('_')[1]) | ||
148 | 1389 | |||
149 | 1390 | all_ports = list(all_ports) | ||
150 | 1391 | all_ports.extend(ports) | ||
151 | 1381 | if mtu: | 1392 | if mtu: |
153 | 1382 | ctxt["devs"] = '\\n'.join(ports) | 1393 | ctxt["devs"] = '\\n'.join(all_ports) |
154 | 1383 | ctxt['mtu'] = mtu | 1394 | ctxt['mtu'] = mtu |
155 | 1384 | 1395 | ||
156 | 1385 | return ctxt | 1396 | return ctxt |
157 | 1386 | 1397 | ||
158 | === modified file 'hooks/charmhelpers/contrib/openstack/neutron.py' | |||
159 | --- hooks/charmhelpers/contrib/openstack/neutron.py 2015-08-25 17:40:00 +0000 | |||
160 | +++ hooks/charmhelpers/contrib/openstack/neutron.py 2015-10-08 15:08:00 +0000 | |||
161 | @@ -310,10 +310,10 @@ | |||
162 | 310 | def parse_data_port_mappings(mappings, default_bridge='br-data'): | 310 | def parse_data_port_mappings(mappings, default_bridge='br-data'): |
163 | 311 | """Parse data port mappings. | 311 | """Parse data port mappings. |
164 | 312 | 312 | ||
166 | 313 | Mappings must be a space-delimited list of port:bridge mappings. | 313 | Mappings must be a space-delimited list of bridge:port. |
167 | 314 | 314 | ||
170 | 315 | Returns dict of the form {port:bridge} where port may be an mac address or | 315 | Returns dict of the form {port:bridge} where ports may be mac addresses or |
171 | 316 | interface name. | 316 | interface names. |
172 | 317 | """ | 317 | """ |
173 | 318 | 318 | ||
174 | 319 | # NOTE(dosaboy): we use rvalue for key to allow multiple values to be | 319 | # NOTE(dosaboy): we use rvalue for key to allow multiple values to be |
175 | 320 | 320 | ||
176 | === modified file 'hooks/charmhelpers/core/hookenv.py' | |||
177 | --- hooks/charmhelpers/core/hookenv.py 2015-09-28 07:56:16 +0000 | |||
178 | +++ hooks/charmhelpers/core/hookenv.py 2015-10-08 15:08:00 +0000 | |||
179 | @@ -623,6 +623,38 @@ | |||
180 | 623 | return unit_get('private-address') | 623 | return unit_get('private-address') |
181 | 624 | 624 | ||
182 | 625 | 625 | ||
183 | 626 | @cached | ||
184 | 627 | def storage_get(attribute="", storage_id=""): | ||
185 | 628 | """Get storage attributes""" | ||
186 | 629 | _args = ['storage-get', '--format=json'] | ||
187 | 630 | if storage_id: | ||
188 | 631 | _args.extend(('-s', storage_id)) | ||
189 | 632 | if attribute: | ||
190 | 633 | _args.append(attribute) | ||
191 | 634 | try: | ||
192 | 635 | return json.loads(subprocess.check_output(_args).decode('UTF-8')) | ||
193 | 636 | except ValueError: | ||
194 | 637 | return None | ||
195 | 638 | |||
196 | 639 | |||
197 | 640 | @cached | ||
198 | 641 | def storage_list(storage_name=""): | ||
199 | 642 | """List the storage IDs for the unit""" | ||
200 | 643 | _args = ['storage-list', '--format=json'] | ||
201 | 644 | if storage_name: | ||
202 | 645 | _args.append(storage_name) | ||
203 | 646 | try: | ||
204 | 647 | return json.loads(subprocess.check_output(_args).decode('UTF-8')) | ||
205 | 648 | except ValueError: | ||
206 | 649 | return None | ||
207 | 650 | except OSError as e: | ||
208 | 651 | import errno | ||
209 | 652 | if e.errno == errno.ENOENT: | ||
210 | 653 | # storage-list does not exist | ||
211 | 654 | return [] | ||
212 | 655 | raise | ||
213 | 656 | |||
214 | 657 | |||
215 | 626 | class UnregisteredHookError(Exception): | 658 | class UnregisteredHookError(Exception): |
216 | 627 | """Raised when an undefined hook is called""" | 659 | """Raised when an undefined hook is called""" |
217 | 628 | pass | 660 | pass |
218 | 629 | 661 | ||
219 | === modified file 'hooks/nova_compute_hooks.py' | |||
220 | --- hooks/nova_compute_hooks.py 2015-10-07 20:45:02 +0000 | |||
221 | +++ hooks/nova_compute_hooks.py 2015-10-08 15:08:00 +0000 | |||
222 | @@ -117,10 +117,10 @@ | |||
223 | 117 | if config_value_changed('openstack-origin-git'): | 117 | if config_value_changed('openstack-origin-git'): |
224 | 118 | status_set('maintenance', 'Running Git install') | 118 | status_set('maintenance', 'Running Git install') |
225 | 119 | git_install(config('openstack-origin-git')) | 119 | git_install(config('openstack-origin-git')) |
227 | 120 | else: | 120 | elif not config('action-managed-upgrade'): |
228 | 121 | if openstack_upgrade_available('nova-common'): | 121 | if openstack_upgrade_available('nova-common'): |
229 | 122 | status_set('maintenance', 'Running openstack upgrade') | 122 | status_set('maintenance', 'Running openstack upgrade') |
231 | 123 | CONFIGS = do_openstack_upgrade() | 123 | do_openstack_upgrade(CONFIGS) |
232 | 124 | 124 | ||
233 | 125 | sysctl_dict = config('sysctl') | 125 | sysctl_dict = config('sysctl') |
234 | 126 | if sysctl_dict: | 126 | if sysctl_dict: |
235 | 127 | 127 | ||
236 | === modified file 'hooks/nova_compute_utils.py' | |||
237 | --- hooks/nova_compute_utils.py 2015-10-03 00:00:02 +0000 | |||
238 | +++ hooks/nova_compute_utils.py 2015-10-08 15:08:00 +0000 | |||
239 | @@ -537,7 +537,7 @@ | |||
240 | 537 | _keys.write('{}\n'.format(authorized_keys[index])) | 537 | _keys.write('{}\n'.format(authorized_keys[index])) |
241 | 538 | 538 | ||
242 | 539 | 539 | ||
244 | 540 | def do_openstack_upgrade(): | 540 | def do_openstack_upgrade(configs): |
245 | 541 | # NOTE(jamespage) horrible hack to make utils forget a cached value | 541 | # NOTE(jamespage) horrible hack to make utils forget a cached value |
246 | 542 | import charmhelpers.contrib.openstack.utils as utils | 542 | import charmhelpers.contrib.openstack.utils as utils |
247 | 543 | utils.os_rel = None | 543 | utils.os_rel = None |
248 | @@ -557,10 +557,8 @@ | |||
249 | 557 | apt_install(determine_packages(), fatal=True) | 557 | apt_install(determine_packages(), fatal=True) |
250 | 558 | 558 | ||
251 | 559 | # Regenerate configs in full for new release | 559 | # Regenerate configs in full for new release |
252 | 560 | configs = register_configs() | ||
253 | 561 | configs.write_all() | 560 | configs.write_all() |
254 | 562 | [service_restart(s) for s in services()] | 561 | [service_restart(s) for s in services()] |
255 | 563 | return configs | ||
256 | 564 | 562 | ||
257 | 565 | 563 | ||
258 | 566 | def import_keystone_ca_cert(): | 564 | def import_keystone_ca_cert(): |
259 | 567 | 565 | ||
260 | === modified file 'tests/charmhelpers/contrib/amulet/utils.py' | |||
261 | --- tests/charmhelpers/contrib/amulet/utils.py 2015-09-28 07:56:16 +0000 | |||
262 | +++ tests/charmhelpers/contrib/amulet/utils.py 2015-10-08 15:08:00 +0000 | |||
263 | @@ -326,7 +326,7 @@ | |||
264 | 326 | 326 | ||
265 | 327 | def service_restarted_since(self, sentry_unit, mtime, service, | 327 | def service_restarted_since(self, sentry_unit, mtime, service, |
266 | 328 | pgrep_full=None, sleep_time=20, | 328 | pgrep_full=None, sleep_time=20, |
268 | 329 | retry_count=2, retry_sleep_time=30): | 329 | retry_count=30, retry_sleep_time=10): |
269 | 330 | """Check if service was been started after a given time. | 330 | """Check if service was been started after a given time. |
270 | 331 | 331 | ||
271 | 332 | Args: | 332 | Args: |
272 | @@ -334,8 +334,9 @@ | |||
273 | 334 | mtime (float): The epoch time to check against | 334 | mtime (float): The epoch time to check against |
274 | 335 | service (string): service name to look for in process table | 335 | service (string): service name to look for in process table |
275 | 336 | pgrep_full: [Deprecated] Use full command line search mode with pgrep | 336 | pgrep_full: [Deprecated] Use full command line search mode with pgrep |
278 | 337 | sleep_time (int): Seconds to sleep before looking for process | 337 | sleep_time (int): Initial sleep time (s) before looking for file |
279 | 338 | retry_count (int): If service is not found, how many times to retry | 338 | retry_sleep_time (int): Time (s) to sleep between retries |
280 | 339 | retry_count (int): If file is not found, how many times to retry | ||
281 | 339 | 340 | ||
282 | 340 | Returns: | 341 | Returns: |
283 | 341 | bool: True if service found and its start time it newer than mtime, | 342 | bool: True if service found and its start time it newer than mtime, |
284 | @@ -359,11 +360,12 @@ | |||
285 | 359 | pgrep_full) | 360 | pgrep_full) |
286 | 360 | self.log.debug('Attempt {} to get {} proc start time on {} ' | 361 | self.log.debug('Attempt {} to get {} proc start time on {} ' |
287 | 361 | 'OK'.format(tries, service, unit_name)) | 362 | 'OK'.format(tries, service, unit_name)) |
289 | 362 | except IOError: | 363 | except IOError as e: |
290 | 363 | # NOTE(beisner) - race avoidance, proc may not exist yet. | 364 | # NOTE(beisner) - race avoidance, proc may not exist yet. |
291 | 364 | # https://bugs.launchpad.net/charm-helpers/+bug/1474030 | 365 | # https://bugs.launchpad.net/charm-helpers/+bug/1474030 |
292 | 365 | self.log.debug('Attempt {} to get {} proc start time on {} ' | 366 | self.log.debug('Attempt {} to get {} proc start time on {} ' |
294 | 366 | 'failed'.format(tries, service, unit_name)) | 367 | 'failed\n{}'.format(tries, service, |
295 | 368 | unit_name, e)) | ||
296 | 367 | time.sleep(retry_sleep_time) | 369 | time.sleep(retry_sleep_time) |
297 | 368 | tries += 1 | 370 | tries += 1 |
298 | 369 | 371 | ||
299 | @@ -383,35 +385,62 @@ | |||
300 | 383 | return False | 385 | return False |
301 | 384 | 386 | ||
302 | 385 | def config_updated_since(self, sentry_unit, filename, mtime, | 387 | def config_updated_since(self, sentry_unit, filename, mtime, |
304 | 386 | sleep_time=20): | 388 | sleep_time=20, retry_count=30, |
305 | 389 | retry_sleep_time=10): | ||
306 | 387 | """Check if file was modified after a given time. | 390 | """Check if file was modified after a given time. |
307 | 388 | 391 | ||
308 | 389 | Args: | 392 | Args: |
309 | 390 | sentry_unit (sentry): The sentry unit to check the file mtime on | 393 | sentry_unit (sentry): The sentry unit to check the file mtime on |
310 | 391 | filename (string): The file to check mtime of | 394 | filename (string): The file to check mtime of |
311 | 392 | mtime (float): The epoch time to check against | 395 | mtime (float): The epoch time to check against |
313 | 393 | sleep_time (int): Seconds to sleep before looking for process | 396 | sleep_time (int): Initial sleep time (s) before looking for file |
314 | 397 | retry_sleep_time (int): Time (s) to sleep between retries | ||
315 | 398 | retry_count (int): If file is not found, how many times to retry | ||
316 | 394 | 399 | ||
317 | 395 | Returns: | 400 | Returns: |
318 | 396 | bool: True if file was modified more recently than mtime, False if | 401 | bool: True if file was modified more recently than mtime, False if |
320 | 397 | file was modified before mtime, | 402 | file was modified before mtime, or if file not found. |
321 | 398 | """ | 403 | """ |
323 | 399 | self.log.debug('Checking %s updated since %s' % (filename, mtime)) | 404 | unit_name = sentry_unit.info['unit_name'] |
324 | 405 | self.log.debug('Checking that %s updated since %s on ' | ||
325 | 406 | '%s' % (filename, mtime, unit_name)) | ||
326 | 400 | time.sleep(sleep_time) | 407 | time.sleep(sleep_time) |
328 | 401 | file_mtime = self._get_file_mtime(sentry_unit, filename) | 408 | file_mtime = None |
329 | 409 | tries = 0 | ||
330 | 410 | while tries <= retry_count and not file_mtime: | ||
331 | 411 | try: | ||
332 | 412 | file_mtime = self._get_file_mtime(sentry_unit, filename) | ||
333 | 413 | self.log.debug('Attempt {} to get {} file mtime on {} ' | ||
334 | 414 | 'OK'.format(tries, filename, unit_name)) | ||
335 | 415 | except IOError as e: | ||
336 | 416 | # NOTE(beisner) - race avoidance, file may not exist yet. | ||
337 | 417 | # https://bugs.launchpad.net/charm-helpers/+bug/1474030 | ||
338 | 418 | self.log.debug('Attempt {} to get {} file mtime on {} ' | ||
339 | 419 | 'failed\n{}'.format(tries, filename, | ||
340 | 420 | unit_name, e)) | ||
341 | 421 | time.sleep(retry_sleep_time) | ||
342 | 422 | tries += 1 | ||
343 | 423 | |||
344 | 424 | if not file_mtime: | ||
345 | 425 | self.log.warn('Could not determine file mtime, assuming ' | ||
346 | 426 | 'file does not exist') | ||
347 | 427 | return False | ||
348 | 428 | |||
349 | 402 | if file_mtime >= mtime: | 429 | if file_mtime >= mtime: |
350 | 403 | self.log.debug('File mtime is newer than provided mtime ' | 430 | self.log.debug('File mtime is newer than provided mtime ' |
352 | 404 | '(%s >= %s)' % (file_mtime, mtime)) | 431 | '(%s >= %s) on %s (OK)' % (file_mtime, |
353 | 432 | mtime, unit_name)) | ||
354 | 405 | return True | 433 | return True |
355 | 406 | else: | 434 | else: |
358 | 407 | self.log.warn('File mtime %s is older than provided mtime %s' | 435 | self.log.warn('File mtime is older than provided mtime' |
359 | 408 | % (file_mtime, mtime)) | 436 | '(%s < on %s) on %s' % (file_mtime, |
360 | 437 | mtime, unit_name)) | ||
361 | 409 | return False | 438 | return False |
362 | 410 | 439 | ||
363 | 411 | def validate_service_config_changed(self, sentry_unit, mtime, service, | 440 | def validate_service_config_changed(self, sentry_unit, mtime, service, |
364 | 412 | filename, pgrep_full=None, | 441 | filename, pgrep_full=None, |
367 | 413 | sleep_time=20, retry_count=2, | 442 | sleep_time=20, retry_count=30, |
368 | 414 | retry_sleep_time=30): | 443 | retry_sleep_time=10): |
369 | 415 | """Check service and file were updated after mtime | 444 | """Check service and file were updated after mtime |
370 | 416 | 445 | ||
371 | 417 | Args: | 446 | Args: |
372 | @@ -456,7 +485,9 @@ | |||
373 | 456 | sentry_unit, | 485 | sentry_unit, |
374 | 457 | filename, | 486 | filename, |
375 | 458 | mtime, | 487 | mtime, |
377 | 459 | sleep_time=0) | 488 | sleep_time=sleep_time, |
378 | 489 | retry_count=retry_count, | ||
379 | 490 | retry_sleep_time=retry_sleep_time) | ||
380 | 460 | 491 | ||
381 | 461 | return service_restart and config_update | 492 | return service_restart and config_update |
382 | 462 | 493 | ||
383 | 463 | 494 | ||
384 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py' | |||
385 | --- tests/charmhelpers/contrib/openstack/amulet/utils.py 2015-09-28 07:56:16 +0000 | |||
386 | +++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-08 15:08:00 +0000 | |||
387 | @@ -752,7 +752,7 @@ | |||
388 | 752 | self.log.debug('SSL is enabled @{}:{} ' | 752 | self.log.debug('SSL is enabled @{}:{} ' |
389 | 753 | '({})'.format(host, port, unit_name)) | 753 | '({})'.format(host, port, unit_name)) |
390 | 754 | return True | 754 | return True |
392 | 755 | elif not port and not conf_ssl: | 755 | elif not conf_ssl: |
393 | 756 | self.log.debug('SSL not enabled @{}:{} ' | 756 | self.log.debug('SSL not enabled @{}:{} ' |
394 | 757 | '({})'.format(host, port, unit_name)) | 757 | '({})'.format(host, port, unit_name)) |
395 | 758 | return False | 758 | return False |
396 | 759 | 759 | ||
397 | === added file 'unit_tests/test_actions_openstack_upgrade.py' | |||
398 | --- unit_tests/test_actions_openstack_upgrade.py 1970-01-01 00:00:00 +0000 | |||
399 | +++ unit_tests/test_actions_openstack_upgrade.py 2015-10-08 15:08:00 +0000 | |||
400 | @@ -0,0 +1,62 @@ | |||
401 | 1 | from mock import patch | ||
402 | 2 | import os | ||
403 | 3 | |||
404 | 4 | os.environ['JUJU_UNIT_NAME'] = 'nova_compute' | ||
405 | 5 | |||
406 | 6 | with patch('charmhelpers.core.hookenv.config') as config: | ||
407 | 7 | config.return_value = 'nova' | ||
408 | 8 | import nova_compute_utils as utils # noqa | ||
409 | 9 | |||
410 | 10 | with patch('nova_compute_utils.restart_map'): | ||
411 | 11 | with patch('nova_compute_utils.register_configs'): | ||
412 | 12 | import openstack_upgrade | ||
413 | 13 | |||
414 | 14 | from test_utils import ( | ||
415 | 15 | CharmTestCase | ||
416 | 16 | ) | ||
417 | 17 | |||
418 | 18 | TO_PATCH = [ | ||
419 | 19 | 'config_changed', | ||
420 | 20 | 'do_openstack_upgrade' | ||
421 | 21 | ] | ||
422 | 22 | |||
423 | 23 | |||
424 | 24 | class TestNovaComputeUpgradeActions(CharmTestCase): | ||
425 | 25 | |||
426 | 26 | def setUp(self): | ||
427 | 27 | super(TestNovaComputeUpgradeActions, self).setUp(openstack_upgrade, | ||
428 | 28 | TO_PATCH) | ||
429 | 29 | |||
430 | 30 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
431 | 31 | @patch('charmhelpers.contrib.openstack.utils.action_set') | ||
432 | 32 | @patch('charmhelpers.contrib.openstack.utils.git_install_requested') | ||
433 | 33 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') | ||
434 | 34 | @patch('charmhelpers.contrib.openstack.utils.juju_log') | ||
435 | 35 | def test_openstack_upgrade_true(self, log, upgrade_avail, git_requested, | ||
436 | 36 | action_set, config): | ||
437 | 37 | |||
438 | 38 | git_requested.return_value = False | ||
439 | 39 | upgrade_avail.return_value = True | ||
440 | 40 | config.return_value = True | ||
441 | 41 | |||
442 | 42 | openstack_upgrade.openstack_upgrade() | ||
443 | 43 | |||
444 | 44 | self.assertTrue(self.do_openstack_upgrade.called) | ||
445 | 45 | self.assertTrue(self.config_changed.called) | ||
446 | 46 | |||
447 | 47 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
448 | 48 | @patch('charmhelpers.contrib.openstack.utils.action_set') | ||
449 | 49 | @patch('charmhelpers.contrib.openstack.utils.git_install_requested') # noqa | ||
450 | 50 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') # noqa | ||
451 | 51 | @patch('charmhelpers.contrib.openstack.utils.juju_log') | ||
452 | 52 | def test_openstack_upgrade_false(self, log, upgrade_avail, git_requested, | ||
453 | 53 | action_set, config): | ||
454 | 54 | |||
455 | 55 | git_requested.return_value = False | ||
456 | 56 | upgrade_avail.return_value = True | ||
457 | 57 | config.return_value = False | ||
458 | 58 | |||
459 | 59 | openstack_upgrade.openstack_upgrade() | ||
460 | 60 | |||
461 | 61 | self.assertFalse(self.do_openstack_upgrade.called) | ||
462 | 62 | self.assertFalse(self.config_changed.called) | ||
463 | 0 | 63 | ||
464 | === modified file 'unit_tests/test_nova_compute_hooks.py' | |||
465 | --- unit_tests/test_nova_compute_hooks.py 2015-10-06 09:04:58 +0000 | |||
466 | +++ unit_tests/test_nova_compute_hooks.py 2015-10-08 15:08:00 +0000 | |||
467 | @@ -115,6 +115,15 @@ | |||
468 | 115 | hooks.config_changed() | 115 | hooks.config_changed() |
469 | 116 | self.assertTrue(self.do_openstack_upgrade.called) | 116 | self.assertTrue(self.do_openstack_upgrade.called) |
470 | 117 | 117 | ||
471 | 118 | @patch.object(hooks, 'git_install_requested') | ||
472 | 119 | def test_config_changed_with_openstack_upgrade_action(self, git_requested): | ||
473 | 120 | git_requested.return_value = False | ||
474 | 121 | self.openstack_upgrade_available.return_value = True | ||
475 | 122 | self.test_config.set('action-managed-upgrade', True) | ||
476 | 123 | |||
477 | 124 | hooks.config_changed() | ||
478 | 125 | self.assertFalse(self.do_openstack_upgrade.called) | ||
479 | 126 | |||
480 | 118 | @patch.object(hooks, 'compute_joined') | 127 | @patch.object(hooks, 'compute_joined') |
481 | 119 | def test_config_changed_with_migration(self, compute_joined): | 128 | def test_config_changed_with_migration(self, compute_joined): |
482 | 120 | self.git_install_requested.return_value = False | 129 | self.git_install_requested.return_value = False |
charm_lint_check #11392 nova-compute-next for ddellav mp273560
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/11392/