Merge lp:~james-page/charms/trusty/neutron-api/lp1531102-trunk into lp:~openstack-charmers-archive/charms/trusty/neutron-api/trunk
- Trusty Tahr (14.04)
- lp1531102-trunk
- Merge into trunk
Proposed by
James Page
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 121 | ||||
Proposed branch: | lp:~james-page/charms/trusty/neutron-api/lp1531102-trunk | ||||
Merge into: | lp:~openstack-charmers-archive/charms/trusty/neutron-api/trunk | ||||
Diff against target: |
606 lines (+275/-39) 7 files modified
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+67/-8) hooks/charmhelpers/contrib/openstack/amulet/utils.py (+25/-3) hooks/charmhelpers/contrib/openstack/utils.py (+21/-17) hooks/charmhelpers/core/hugepage.py (+2/-0) hooks/charmhelpers/core/kernel.py (+68/-0) tests/charmhelpers/contrib/openstack/amulet/deployment.py (+67/-8) tests/charmhelpers/contrib/openstack/amulet/utils.py (+25/-3) |
||||
To merge this branch: | bzr merge lp:~james-page/charms/trusty/neutron-api/lp1531102-trunk | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Approve | ||
Review via email: mp+281862@code.launchpad.net |
Commit message
Resync helpers
Description of the change
Resync helpers to resolve issues with liberty point releases.
To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : | # |
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #15636 neutron-api for james-page mp281862
UNIT OK: passed
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #8560 neutron-api for james-page mp281862
AMULET OK: passed
Build: http://
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
2 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-10-22 13:21:43 +0000 | |||
3 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2016-01-07 14:15:21 +0000 | |||
4 | @@ -14,13 +14,18 @@ | |||
5 | 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 |
6 | 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/>. |
7 | 16 | 16 | ||
8 | 17 | import logging | ||
9 | 17 | import re | 18 | import re |
10 | 19 | import sys | ||
11 | 18 | import six | 20 | import six |
12 | 19 | from collections import OrderedDict | 21 | from collections import OrderedDict |
13 | 20 | from charmhelpers.contrib.amulet.deployment import ( | 22 | from charmhelpers.contrib.amulet.deployment import ( |
14 | 21 | AmuletDeployment | 23 | AmuletDeployment |
15 | 22 | ) | 24 | ) |
16 | 23 | 25 | ||
17 | 26 | DEBUG = logging.DEBUG | ||
18 | 27 | ERROR = logging.ERROR | ||
19 | 28 | |||
20 | 24 | 29 | ||
21 | 25 | class OpenStackAmuletDeployment(AmuletDeployment): | 30 | class OpenStackAmuletDeployment(AmuletDeployment): |
22 | 26 | """OpenStack amulet deployment. | 31 | """OpenStack amulet deployment. |
23 | @@ -29,9 +34,12 @@ | |||
24 | 29 | that is specifically for use by OpenStack charms. | 34 | that is specifically for use by OpenStack charms. |
25 | 30 | """ | 35 | """ |
26 | 31 | 36 | ||
28 | 32 | def __init__(self, series=None, openstack=None, source=None, stable=True): | 37 | def __init__(self, series=None, openstack=None, source=None, |
29 | 38 | stable=True, log_level=DEBUG): | ||
30 | 33 | """Initialize the deployment environment.""" | 39 | """Initialize the deployment environment.""" |
31 | 34 | super(OpenStackAmuletDeployment, self).__init__(series) | 40 | super(OpenStackAmuletDeployment, self).__init__(series) |
32 | 41 | self.log = self.get_logger(level=log_level) | ||
33 | 42 | self.log.info('OpenStackAmuletDeployment: init') | ||
34 | 35 | self.openstack = openstack | 43 | self.openstack = openstack |
35 | 36 | self.source = source | 44 | self.source = source |
36 | 37 | self.stable = stable | 45 | self.stable = stable |
37 | @@ -39,6 +47,22 @@ | |||
38 | 39 | # out. | 47 | # out. |
39 | 40 | self.current_next = "trusty" | 48 | self.current_next = "trusty" |
40 | 41 | 49 | ||
41 | 50 | def get_logger(self, name="deployment-logger", level=logging.DEBUG): | ||
42 | 51 | """Get a logger object that will log to stdout.""" | ||
43 | 52 | log = logging | ||
44 | 53 | logger = log.getLogger(name) | ||
45 | 54 | fmt = log.Formatter("%(asctime)s %(funcName)s " | ||
46 | 55 | "%(levelname)s: %(message)s") | ||
47 | 56 | |||
48 | 57 | handler = log.StreamHandler(stream=sys.stdout) | ||
49 | 58 | handler.setLevel(level) | ||
50 | 59 | handler.setFormatter(fmt) | ||
51 | 60 | |||
52 | 61 | logger.addHandler(handler) | ||
53 | 62 | logger.setLevel(level) | ||
54 | 63 | |||
55 | 64 | return logger | ||
56 | 65 | |||
57 | 42 | def _determine_branch_locations(self, other_services): | 66 | def _determine_branch_locations(self, other_services): |
58 | 43 | """Determine the branch locations for the other services. | 67 | """Determine the branch locations for the other services. |
59 | 44 | 68 | ||
60 | @@ -46,6 +70,8 @@ | |||
61 | 46 | stable or next (dev) branch, and based on this, use the corresonding | 70 | stable or next (dev) branch, and based on this, use the corresonding |
62 | 47 | stable or next branches for the other_services.""" | 71 | stable or next branches for the other_services.""" |
63 | 48 | 72 | ||
64 | 73 | self.log.info('OpenStackAmuletDeployment: determine branch locations') | ||
65 | 74 | |||
66 | 49 | # Charms outside the lp:~openstack-charmers namespace | 75 | # Charms outside the lp:~openstack-charmers namespace |
67 | 50 | base_charms = ['mysql', 'mongodb', 'nrpe'] | 76 | base_charms = ['mysql', 'mongodb', 'nrpe'] |
68 | 51 | 77 | ||
69 | @@ -83,6 +109,8 @@ | |||
70 | 83 | 109 | ||
71 | 84 | def _add_services(self, this_service, other_services): | 110 | def _add_services(self, this_service, other_services): |
72 | 85 | """Add services to the deployment and set openstack-origin/source.""" | 111 | """Add services to the deployment and set openstack-origin/source.""" |
73 | 112 | self.log.info('OpenStackAmuletDeployment: adding services') | ||
74 | 113 | |||
75 | 86 | other_services = self._determine_branch_locations(other_services) | 114 | other_services = self._determine_branch_locations(other_services) |
76 | 87 | 115 | ||
77 | 88 | super(OpenStackAmuletDeployment, self)._add_services(this_service, | 116 | super(OpenStackAmuletDeployment, self)._add_services(this_service, |
78 | @@ -112,11 +140,12 @@ | |||
79 | 112 | 140 | ||
80 | 113 | def _configure_services(self, configs): | 141 | def _configure_services(self, configs): |
81 | 114 | """Configure all of the services.""" | 142 | """Configure all of the services.""" |
82 | 143 | self.log.info('OpenStackAmuletDeployment: configure services') | ||
83 | 115 | for service, config in six.iteritems(configs): | 144 | for service, config in six.iteritems(configs): |
84 | 116 | self.d.configure(service, config) | 145 | self.d.configure(service, config) |
85 | 117 | 146 | ||
86 | 118 | def _auto_wait_for_status(self, message=None, exclude_services=None, | 147 | def _auto_wait_for_status(self, message=None, exclude_services=None, |
88 | 119 | timeout=1800): | 148 | include_only=None, timeout=1800): |
89 | 120 | """Wait for all units to have a specific extended status, except | 149 | """Wait for all units to have a specific extended status, except |
90 | 121 | for any defined as excluded. Unless specified via message, any | 150 | for any defined as excluded. Unless specified via message, any |
91 | 122 | status containing any case of 'ready' will be considered a match. | 151 | status containing any case of 'ready' will be considered a match. |
92 | @@ -127,7 +156,7 @@ | |||
93 | 127 | message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE) | 156 | message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE) |
94 | 128 | 157 | ||
95 | 129 | Wait for all units to reach this status (exact match): | 158 | Wait for all units to reach this status (exact match): |
97 | 130 | message = 'Unit is ready' | 159 | message = re.compile('^Unit is ready and clustered$') |
98 | 131 | 160 | ||
99 | 132 | Wait for all units to reach any one of these (exact match): | 161 | Wait for all units to reach any one of these (exact match): |
100 | 133 | message = re.compile('Unit is ready|OK|Ready') | 162 | message = re.compile('Unit is ready|OK|Ready') |
101 | @@ -139,20 +168,50 @@ | |||
102 | 139 | https://github.com/juju/amulet/blob/master/amulet/sentry.py | 168 | https://github.com/juju/amulet/blob/master/amulet/sentry.py |
103 | 140 | 169 | ||
104 | 141 | :param message: Expected status match | 170 | :param message: Expected status match |
106 | 142 | :param exclude_services: List of juju service names to ignore | 171 | :param exclude_services: List of juju service names to ignore, |
107 | 172 | not to be used in conjuction with include_only. | ||
108 | 173 | :param include_only: List of juju service names to exclusively check, | ||
109 | 174 | not to be used in conjuction with exclude_services. | ||
110 | 143 | :param timeout: Maximum time in seconds to wait for status match | 175 | :param timeout: Maximum time in seconds to wait for status match |
111 | 144 | :returns: None. Raises if timeout is hit. | 176 | :returns: None. Raises if timeout is hit. |
112 | 145 | """ | 177 | """ |
115 | 146 | 178 | self.log.info('Waiting for extended status on units...') | |
116 | 147 | if not message: | 179 | |
117 | 180 | all_services = self.d.services.keys() | ||
118 | 181 | |||
119 | 182 | if exclude_services and include_only: | ||
120 | 183 | raise ValueError('exclude_services can not be used ' | ||
121 | 184 | 'with include_only') | ||
122 | 185 | |||
123 | 186 | if message: | ||
124 | 187 | if isinstance(message, re._pattern_type): | ||
125 | 188 | match = message.pattern | ||
126 | 189 | else: | ||
127 | 190 | match = message | ||
128 | 191 | |||
129 | 192 | self.log.debug('Custom extended status wait match: ' | ||
130 | 193 | '{}'.format(match)) | ||
131 | 194 | else: | ||
132 | 195 | self.log.debug('Default extended status wait match: contains ' | ||
133 | 196 | 'READY (case-insensitive)') | ||
134 | 148 | message = re.compile('.*ready.*', re.IGNORECASE) | 197 | message = re.compile('.*ready.*', re.IGNORECASE) |
135 | 149 | 198 | ||
137 | 150 | if not exclude_services: | 199 | if exclude_services: |
138 | 200 | self.log.debug('Excluding services from extended status match: ' | ||
139 | 201 | '{}'.format(exclude_services)) | ||
140 | 202 | else: | ||
141 | 151 | exclude_services = [] | 203 | exclude_services = [] |
142 | 152 | 204 | ||
144 | 153 | services = list(set(self.d.services.keys()) - set(exclude_services)) | 205 | if include_only: |
145 | 206 | services = include_only | ||
146 | 207 | else: | ||
147 | 208 | services = list(set(all_services) - set(exclude_services)) | ||
148 | 209 | |||
149 | 210 | self.log.debug('Waiting up to {}s for extended status on services: ' | ||
150 | 211 | '{}'.format(timeout, services)) | ||
151 | 154 | service_messages = {service: message for service in services} | 212 | service_messages = {service: message for service in services} |
152 | 155 | self.d.sentry.wait_for_messages(service_messages, timeout=timeout) | 213 | self.d.sentry.wait_for_messages(service_messages, timeout=timeout) |
153 | 214 | self.log.info('OK') | ||
154 | 156 | 215 | ||
155 | 157 | def _get_openstack_release(self): | 216 | def _get_openstack_release(self): |
156 | 158 | """Get openstack release. | 217 | """Get openstack release. |
157 | 159 | 218 | ||
158 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py' | |||
159 | --- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-22 13:21:43 +0000 | |||
160 | +++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2016-01-07 14:15:21 +0000 | |||
161 | @@ -18,6 +18,7 @@ | |||
162 | 18 | import json | 18 | import json |
163 | 19 | import logging | 19 | import logging |
164 | 20 | import os | 20 | import os |
165 | 21 | import re | ||
166 | 21 | import six | 22 | import six |
167 | 22 | import time | 23 | import time |
168 | 23 | import urllib | 24 | import urllib |
169 | @@ -604,7 +605,22 @@ | |||
170 | 604 | '{}'.format(sample_type, samples)) | 605 | '{}'.format(sample_type, samples)) |
171 | 605 | return None | 606 | return None |
172 | 606 | 607 | ||
174 | 607 | # rabbitmq/amqp specific helpers: | 608 | # rabbitmq/amqp specific helpers: |
175 | 609 | |||
176 | 610 | def rmq_wait_for_cluster(self, deployment, init_sleep=15, timeout=1200): | ||
177 | 611 | """Wait for rmq units extended status to show cluster readiness, | ||
178 | 612 | after an optional initial sleep period. Initial sleep is likely | ||
179 | 613 | necessary to be effective following a config change, as status | ||
180 | 614 | message may not instantly update to non-ready.""" | ||
181 | 615 | |||
182 | 616 | if init_sleep: | ||
183 | 617 | time.sleep(init_sleep) | ||
184 | 618 | |||
185 | 619 | message = re.compile('^Unit is ready and clustered$') | ||
186 | 620 | deployment._auto_wait_for_status(message=message, | ||
187 | 621 | timeout=timeout, | ||
188 | 622 | include_only=['rabbitmq-server']) | ||
189 | 623 | |||
190 | 608 | def add_rmq_test_user(self, sentry_units, | 624 | def add_rmq_test_user(self, sentry_units, |
191 | 609 | username="testuser1", password="changeme"): | 625 | username="testuser1", password="changeme"): |
192 | 610 | """Add a test user via the first rmq juju unit, check connection as | 626 | """Add a test user via the first rmq juju unit, check connection as |
193 | @@ -805,7 +821,10 @@ | |||
194 | 805 | if port: | 821 | if port: |
195 | 806 | config['ssl_port'] = port | 822 | config['ssl_port'] = port |
196 | 807 | 823 | ||
198 | 808 | deployment.configure('rabbitmq-server', config) | 824 | deployment.d.configure('rabbitmq-server', config) |
199 | 825 | |||
200 | 826 | # Wait for unit status | ||
201 | 827 | self.rmq_wait_for_cluster(deployment) | ||
202 | 809 | 828 | ||
203 | 810 | # Confirm | 829 | # Confirm |
204 | 811 | tries = 0 | 830 | tries = 0 |
205 | @@ -832,7 +851,10 @@ | |||
206 | 832 | 851 | ||
207 | 833 | # Disable RMQ SSL | 852 | # Disable RMQ SSL |
208 | 834 | config = {'ssl': 'off'} | 853 | config = {'ssl': 'off'} |
210 | 835 | deployment.configure('rabbitmq-server', config) | 854 | deployment.d.configure('rabbitmq-server', config) |
211 | 855 | |||
212 | 856 | # Wait for unit status | ||
213 | 857 | self.rmq_wait_for_cluster(deployment) | ||
214 | 836 | 858 | ||
215 | 837 | # Confirm | 859 | # Confirm |
216 | 838 | tries = 0 | 860 | tries = 0 |
217 | 839 | 861 | ||
218 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
219 | --- hooks/charmhelpers/contrib/openstack/utils.py 2015-10-22 13:21:43 +0000 | |||
220 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2016-01-07 14:15:21 +0000 | |||
221 | @@ -127,31 +127,31 @@ | |||
222 | 127 | # >= Liberty version->codename mapping | 127 | # >= Liberty version->codename mapping |
223 | 128 | PACKAGE_CODENAMES = { | 128 | PACKAGE_CODENAMES = { |
224 | 129 | 'nova-common': OrderedDict([ | 129 | 'nova-common': OrderedDict([ |
226 | 130 | ('12.0.0', 'liberty'), | 130 | ('12.0', 'liberty'), |
227 | 131 | ]), | 131 | ]), |
228 | 132 | 'neutron-common': OrderedDict([ | 132 | 'neutron-common': OrderedDict([ |
230 | 133 | ('7.0.0', 'liberty'), | 133 | ('7.0', 'liberty'), |
231 | 134 | ]), | 134 | ]), |
232 | 135 | 'cinder-common': OrderedDict([ | 135 | 'cinder-common': OrderedDict([ |
234 | 136 | ('7.0.0', 'liberty'), | 136 | ('7.0', 'liberty'), |
235 | 137 | ]), | 137 | ]), |
236 | 138 | 'keystone': OrderedDict([ | 138 | 'keystone': OrderedDict([ |
238 | 139 | ('8.0.0', 'liberty'), | 139 | ('8.0', 'liberty'), |
239 | 140 | ]), | 140 | ]), |
240 | 141 | 'horizon-common': OrderedDict([ | 141 | 'horizon-common': OrderedDict([ |
242 | 142 | ('8.0.0', 'liberty'), | 142 | ('8.0', 'liberty'), |
243 | 143 | ]), | 143 | ]), |
244 | 144 | 'ceilometer-common': OrderedDict([ | 144 | 'ceilometer-common': OrderedDict([ |
246 | 145 | ('5.0.0', 'liberty'), | 145 | ('5.0', 'liberty'), |
247 | 146 | ]), | 146 | ]), |
248 | 147 | 'heat-common': OrderedDict([ | 147 | 'heat-common': OrderedDict([ |
250 | 148 | ('5.0.0', 'liberty'), | 148 | ('5.0', 'liberty'), |
251 | 149 | ]), | 149 | ]), |
252 | 150 | 'glance-common': OrderedDict([ | 150 | 'glance-common': OrderedDict([ |
254 | 151 | ('11.0.0', 'liberty'), | 151 | ('11.0', 'liberty'), |
255 | 152 | ]), | 152 | ]), |
256 | 153 | 'openstack-dashboard': OrderedDict([ | 153 | 'openstack-dashboard': OrderedDict([ |
258 | 154 | ('8.0.0', 'liberty'), | 154 | ('8.0', 'liberty'), |
259 | 155 | ]), | 155 | ]), |
260 | 156 | } | 156 | } |
261 | 157 | 157 | ||
262 | @@ -238,7 +238,14 @@ | |||
263 | 238 | error_out(e) | 238 | error_out(e) |
264 | 239 | 239 | ||
265 | 240 | vers = apt.upstream_version(pkg.current_ver.ver_str) | 240 | vers = apt.upstream_version(pkg.current_ver.ver_str) |
267 | 241 | match = re.match('^(\d+)\.(\d+)\.(\d+)', vers) | 241 | if 'swift' in pkg.name: |
268 | 242 | # Fully x.y.z match for swift versions | ||
269 | 243 | match = re.match('^(\d+)\.(\d+)\.(\d+)', vers) | ||
270 | 244 | else: | ||
271 | 245 | # x.y match only for 20XX.X | ||
272 | 246 | # and ignore patch level for other packages | ||
273 | 247 | match = re.match('^(\d+)\.(\d+)', vers) | ||
274 | 248 | |||
275 | 242 | if match: | 249 | if match: |
276 | 243 | vers = match.group(0) | 250 | vers = match.group(0) |
277 | 244 | 251 | ||
278 | @@ -250,13 +257,8 @@ | |||
279 | 250 | # < Liberty co-ordinated project versions | 257 | # < Liberty co-ordinated project versions |
280 | 251 | try: | 258 | try: |
281 | 252 | if 'swift' in pkg.name: | 259 | if 'swift' in pkg.name: |
287 | 253 | swift_vers = vers[:5] | 260 | return SWIFT_CODENAMES[vers] |
283 | 254 | if swift_vers not in SWIFT_CODENAMES: | ||
284 | 255 | # Deal with 1.10.0 upward | ||
285 | 256 | swift_vers = vers[:6] | ||
286 | 257 | return SWIFT_CODENAMES[swift_vers] | ||
288 | 258 | else: | 261 | else: |
289 | 259 | vers = vers[:6] | ||
290 | 260 | return OPENSTACK_CODENAMES[vers] | 262 | return OPENSTACK_CODENAMES[vers] |
291 | 261 | except KeyError: | 263 | except KeyError: |
292 | 262 | if not fatal: | 264 | if not fatal: |
293 | @@ -859,7 +861,9 @@ | |||
294 | 859 | if charm_state != 'active' and charm_state != 'unknown': | 861 | if charm_state != 'active' and charm_state != 'unknown': |
295 | 860 | state = workload_state_compare(state, charm_state) | 862 | state = workload_state_compare(state, charm_state) |
296 | 861 | if message: | 863 | if message: |
298 | 862 | message = "{} {}".format(message, charm_message) | 864 | charm_message = charm_message.replace("Incomplete relations: ", |
299 | 865 | "") | ||
300 | 866 | message = "{}, {}".format(message, charm_message) | ||
301 | 863 | else: | 867 | else: |
302 | 864 | message = charm_message | 868 | message = charm_message |
303 | 865 | 869 | ||
304 | 866 | 870 | ||
305 | === modified file 'hooks/charmhelpers/core/hugepage.py' | |||
306 | --- hooks/charmhelpers/core/hugepage.py 2015-10-22 13:21:43 +0000 | |||
307 | +++ hooks/charmhelpers/core/hugepage.py 2016-01-07 14:15:21 +0000 | |||
308 | @@ -46,6 +46,8 @@ | |||
309 | 46 | group_info = add_group(group) | 46 | group_info = add_group(group) |
310 | 47 | gid = group_info.gr_gid | 47 | gid = group_info.gr_gid |
311 | 48 | add_user_to_group(user, group) | 48 | add_user_to_group(user, group) |
312 | 49 | if max_map_count < 2 * nr_hugepages: | ||
313 | 50 | max_map_count = 2 * nr_hugepages | ||
314 | 49 | sysctl_settings = { | 51 | sysctl_settings = { |
315 | 50 | 'vm.nr_hugepages': nr_hugepages, | 52 | 'vm.nr_hugepages': nr_hugepages, |
316 | 51 | 'vm.max_map_count': max_map_count, | 53 | 'vm.max_map_count': max_map_count, |
317 | 52 | 54 | ||
318 | === added file 'hooks/charmhelpers/core/kernel.py' | |||
319 | --- hooks/charmhelpers/core/kernel.py 1970-01-01 00:00:00 +0000 | |||
320 | +++ hooks/charmhelpers/core/kernel.py 2016-01-07 14:15:21 +0000 | |||
321 | @@ -0,0 +1,68 @@ | |||
322 | 1 | #!/usr/bin/env python | ||
323 | 2 | # -*- coding: utf-8 -*- | ||
324 | 3 | |||
325 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
326 | 5 | # | ||
327 | 6 | # This file is part of charm-helpers. | ||
328 | 7 | # | ||
329 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
330 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
331 | 10 | # published by the Free Software Foundation. | ||
332 | 11 | # | ||
333 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
334 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
335 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
336 | 15 | # GNU Lesser General Public License for more details. | ||
337 | 16 | # | ||
338 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
339 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
340 | 19 | |||
341 | 20 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
342 | 21 | |||
343 | 22 | from charmhelpers.core.hookenv import ( | ||
344 | 23 | log, | ||
345 | 24 | INFO | ||
346 | 25 | ) | ||
347 | 26 | |||
348 | 27 | from subprocess import check_call, check_output | ||
349 | 28 | import re | ||
350 | 29 | |||
351 | 30 | |||
352 | 31 | def modprobe(module, persist=True): | ||
353 | 32 | """Load a kernel module and configure for auto-load on reboot.""" | ||
354 | 33 | cmd = ['modprobe', module] | ||
355 | 34 | |||
356 | 35 | log('Loading kernel module %s' % module, level=INFO) | ||
357 | 36 | |||
358 | 37 | check_call(cmd) | ||
359 | 38 | if persist: | ||
360 | 39 | with open('/etc/modules', 'r+') as modules: | ||
361 | 40 | if module not in modules.read(): | ||
362 | 41 | modules.write(module) | ||
363 | 42 | |||
364 | 43 | |||
365 | 44 | def rmmod(module, force=False): | ||
366 | 45 | """Remove a module from the linux kernel""" | ||
367 | 46 | cmd = ['rmmod'] | ||
368 | 47 | if force: | ||
369 | 48 | cmd.append('-f') | ||
370 | 49 | cmd.append(module) | ||
371 | 50 | log('Removing kernel module %s' % module, level=INFO) | ||
372 | 51 | return check_call(cmd) | ||
373 | 52 | |||
374 | 53 | |||
375 | 54 | def lsmod(): | ||
376 | 55 | """Shows what kernel modules are currently loaded""" | ||
377 | 56 | return check_output(['lsmod'], | ||
378 | 57 | universal_newlines=True) | ||
379 | 58 | |||
380 | 59 | |||
381 | 60 | def is_module_loaded(module): | ||
382 | 61 | """Checks if a kernel module is already loaded""" | ||
383 | 62 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) | ||
384 | 63 | return len(matches) > 0 | ||
385 | 64 | |||
386 | 65 | |||
387 | 66 | def update_initramfs(version='all'): | ||
388 | 67 | """Updates an initramfs image""" | ||
389 | 68 | return check_call(["update-initramfs", "-k", version, "-u"]) | ||
390 | 0 | 69 | ||
391 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
392 | --- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-10-22 13:21:43 +0000 | |||
393 | +++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2016-01-07 14:15:21 +0000 | |||
394 | @@ -14,13 +14,18 @@ | |||
395 | 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 |
396 | 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/>. |
397 | 16 | 16 | ||
398 | 17 | import logging | ||
399 | 17 | import re | 18 | import re |
400 | 19 | import sys | ||
401 | 18 | import six | 20 | import six |
402 | 19 | from collections import OrderedDict | 21 | from collections import OrderedDict |
403 | 20 | from charmhelpers.contrib.amulet.deployment import ( | 22 | from charmhelpers.contrib.amulet.deployment import ( |
404 | 21 | AmuletDeployment | 23 | AmuletDeployment |
405 | 22 | ) | 24 | ) |
406 | 23 | 25 | ||
407 | 26 | DEBUG = logging.DEBUG | ||
408 | 27 | ERROR = logging.ERROR | ||
409 | 28 | |||
410 | 24 | 29 | ||
411 | 25 | class OpenStackAmuletDeployment(AmuletDeployment): | 30 | class OpenStackAmuletDeployment(AmuletDeployment): |
412 | 26 | """OpenStack amulet deployment. | 31 | """OpenStack amulet deployment. |
413 | @@ -29,9 +34,12 @@ | |||
414 | 29 | that is specifically for use by OpenStack charms. | 34 | that is specifically for use by OpenStack charms. |
415 | 30 | """ | 35 | """ |
416 | 31 | 36 | ||
418 | 32 | def __init__(self, series=None, openstack=None, source=None, stable=True): | 37 | def __init__(self, series=None, openstack=None, source=None, |
419 | 38 | stable=True, log_level=DEBUG): | ||
420 | 33 | """Initialize the deployment environment.""" | 39 | """Initialize the deployment environment.""" |
421 | 34 | super(OpenStackAmuletDeployment, self).__init__(series) | 40 | super(OpenStackAmuletDeployment, self).__init__(series) |
422 | 41 | self.log = self.get_logger(level=log_level) | ||
423 | 42 | self.log.info('OpenStackAmuletDeployment: init') | ||
424 | 35 | self.openstack = openstack | 43 | self.openstack = openstack |
425 | 36 | self.source = source | 44 | self.source = source |
426 | 37 | self.stable = stable | 45 | self.stable = stable |
427 | @@ -39,6 +47,22 @@ | |||
428 | 39 | # out. | 47 | # out. |
429 | 40 | self.current_next = "trusty" | 48 | self.current_next = "trusty" |
430 | 41 | 49 | ||
431 | 50 | def get_logger(self, name="deployment-logger", level=logging.DEBUG): | ||
432 | 51 | """Get a logger object that will log to stdout.""" | ||
433 | 52 | log = logging | ||
434 | 53 | logger = log.getLogger(name) | ||
435 | 54 | fmt = log.Formatter("%(asctime)s %(funcName)s " | ||
436 | 55 | "%(levelname)s: %(message)s") | ||
437 | 56 | |||
438 | 57 | handler = log.StreamHandler(stream=sys.stdout) | ||
439 | 58 | handler.setLevel(level) | ||
440 | 59 | handler.setFormatter(fmt) | ||
441 | 60 | |||
442 | 61 | logger.addHandler(handler) | ||
443 | 62 | logger.setLevel(level) | ||
444 | 63 | |||
445 | 64 | return logger | ||
446 | 65 | |||
447 | 42 | def _determine_branch_locations(self, other_services): | 66 | def _determine_branch_locations(self, other_services): |
448 | 43 | """Determine the branch locations for the other services. | 67 | """Determine the branch locations for the other services. |
449 | 44 | 68 | ||
450 | @@ -46,6 +70,8 @@ | |||
451 | 46 | stable or next (dev) branch, and based on this, use the corresonding | 70 | stable or next (dev) branch, and based on this, use the corresonding |
452 | 47 | stable or next branches for the other_services.""" | 71 | stable or next branches for the other_services.""" |
453 | 48 | 72 | ||
454 | 73 | self.log.info('OpenStackAmuletDeployment: determine branch locations') | ||
455 | 74 | |||
456 | 49 | # Charms outside the lp:~openstack-charmers namespace | 75 | # Charms outside the lp:~openstack-charmers namespace |
457 | 50 | base_charms = ['mysql', 'mongodb', 'nrpe'] | 76 | base_charms = ['mysql', 'mongodb', 'nrpe'] |
458 | 51 | 77 | ||
459 | @@ -83,6 +109,8 @@ | |||
460 | 83 | 109 | ||
461 | 84 | def _add_services(self, this_service, other_services): | 110 | def _add_services(self, this_service, other_services): |
462 | 85 | """Add services to the deployment and set openstack-origin/source.""" | 111 | """Add services to the deployment and set openstack-origin/source.""" |
463 | 112 | self.log.info('OpenStackAmuletDeployment: adding services') | ||
464 | 113 | |||
465 | 86 | other_services = self._determine_branch_locations(other_services) | 114 | other_services = self._determine_branch_locations(other_services) |
466 | 87 | 115 | ||
467 | 88 | super(OpenStackAmuletDeployment, self)._add_services(this_service, | 116 | super(OpenStackAmuletDeployment, self)._add_services(this_service, |
468 | @@ -112,11 +140,12 @@ | |||
469 | 112 | 140 | ||
470 | 113 | def _configure_services(self, configs): | 141 | def _configure_services(self, configs): |
471 | 114 | """Configure all of the services.""" | 142 | """Configure all of the services.""" |
472 | 143 | self.log.info('OpenStackAmuletDeployment: configure services') | ||
473 | 115 | for service, config in six.iteritems(configs): | 144 | for service, config in six.iteritems(configs): |
474 | 116 | self.d.configure(service, config) | 145 | self.d.configure(service, config) |
475 | 117 | 146 | ||
476 | 118 | def _auto_wait_for_status(self, message=None, exclude_services=None, | 147 | def _auto_wait_for_status(self, message=None, exclude_services=None, |
478 | 119 | timeout=1800): | 148 | include_only=None, timeout=1800): |
479 | 120 | """Wait for all units to have a specific extended status, except | 149 | """Wait for all units to have a specific extended status, except |
480 | 121 | for any defined as excluded. Unless specified via message, any | 150 | for any defined as excluded. Unless specified via message, any |
481 | 122 | status containing any case of 'ready' will be considered a match. | 151 | status containing any case of 'ready' will be considered a match. |
482 | @@ -127,7 +156,7 @@ | |||
483 | 127 | message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE) | 156 | message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE) |
484 | 128 | 157 | ||
485 | 129 | Wait for all units to reach this status (exact match): | 158 | Wait for all units to reach this status (exact match): |
487 | 130 | message = 'Unit is ready' | 159 | message = re.compile('^Unit is ready and clustered$') |
488 | 131 | 160 | ||
489 | 132 | Wait for all units to reach any one of these (exact match): | 161 | Wait for all units to reach any one of these (exact match): |
490 | 133 | message = re.compile('Unit is ready|OK|Ready') | 162 | message = re.compile('Unit is ready|OK|Ready') |
491 | @@ -139,20 +168,50 @@ | |||
492 | 139 | https://github.com/juju/amulet/blob/master/amulet/sentry.py | 168 | https://github.com/juju/amulet/blob/master/amulet/sentry.py |
493 | 140 | 169 | ||
494 | 141 | :param message: Expected status match | 170 | :param message: Expected status match |
496 | 142 | :param exclude_services: List of juju service names to ignore | 171 | :param exclude_services: List of juju service names to ignore, |
497 | 172 | not to be used in conjuction with include_only. | ||
498 | 173 | :param include_only: List of juju service names to exclusively check, | ||
499 | 174 | not to be used in conjuction with exclude_services. | ||
500 | 143 | :param timeout: Maximum time in seconds to wait for status match | 175 | :param timeout: Maximum time in seconds to wait for status match |
501 | 144 | :returns: None. Raises if timeout is hit. | 176 | :returns: None. Raises if timeout is hit. |
502 | 145 | """ | 177 | """ |
505 | 146 | 178 | self.log.info('Waiting for extended status on units...') | |
506 | 147 | if not message: | 179 | |
507 | 180 | all_services = self.d.services.keys() | ||
508 | 181 | |||
509 | 182 | if exclude_services and include_only: | ||
510 | 183 | raise ValueError('exclude_services can not be used ' | ||
511 | 184 | 'with include_only') | ||
512 | 185 | |||
513 | 186 | if message: | ||
514 | 187 | if isinstance(message, re._pattern_type): | ||
515 | 188 | match = message.pattern | ||
516 | 189 | else: | ||
517 | 190 | match = message | ||
518 | 191 | |||
519 | 192 | self.log.debug('Custom extended status wait match: ' | ||
520 | 193 | '{}'.format(match)) | ||
521 | 194 | else: | ||
522 | 195 | self.log.debug('Default extended status wait match: contains ' | ||
523 | 196 | 'READY (case-insensitive)') | ||
524 | 148 | message = re.compile('.*ready.*', re.IGNORECASE) | 197 | message = re.compile('.*ready.*', re.IGNORECASE) |
525 | 149 | 198 | ||
527 | 150 | if not exclude_services: | 199 | if exclude_services: |
528 | 200 | self.log.debug('Excluding services from extended status match: ' | ||
529 | 201 | '{}'.format(exclude_services)) | ||
530 | 202 | else: | ||
531 | 151 | exclude_services = [] | 203 | exclude_services = [] |
532 | 152 | 204 | ||
534 | 153 | services = list(set(self.d.services.keys()) - set(exclude_services)) | 205 | if include_only: |
535 | 206 | services = include_only | ||
536 | 207 | else: | ||
537 | 208 | services = list(set(all_services) - set(exclude_services)) | ||
538 | 209 | |||
539 | 210 | self.log.debug('Waiting up to {}s for extended status on services: ' | ||
540 | 211 | '{}'.format(timeout, services)) | ||
541 | 154 | service_messages = {service: message for service in services} | 212 | service_messages = {service: message for service in services} |
542 | 155 | self.d.sentry.wait_for_messages(service_messages, timeout=timeout) | 213 | self.d.sentry.wait_for_messages(service_messages, timeout=timeout) |
543 | 214 | self.log.info('OK') | ||
544 | 156 | 215 | ||
545 | 157 | def _get_openstack_release(self): | 216 | def _get_openstack_release(self): |
546 | 158 | """Get openstack release. | 217 | """Get openstack release. |
547 | 159 | 218 | ||
548 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py' | |||
549 | --- tests/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-22 13:21:43 +0000 | |||
550 | +++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2016-01-07 14:15:21 +0000 | |||
551 | @@ -18,6 +18,7 @@ | |||
552 | 18 | import json | 18 | import json |
553 | 19 | import logging | 19 | import logging |
554 | 20 | import os | 20 | import os |
555 | 21 | import re | ||
556 | 21 | import six | 22 | import six |
557 | 22 | import time | 23 | import time |
558 | 23 | import urllib | 24 | import urllib |
559 | @@ -604,7 +605,22 @@ | |||
560 | 604 | '{}'.format(sample_type, samples)) | 605 | '{}'.format(sample_type, samples)) |
561 | 605 | return None | 606 | return None |
562 | 606 | 607 | ||
564 | 607 | # rabbitmq/amqp specific helpers: | 608 | # rabbitmq/amqp specific helpers: |
565 | 609 | |||
566 | 610 | def rmq_wait_for_cluster(self, deployment, init_sleep=15, timeout=1200): | ||
567 | 611 | """Wait for rmq units extended status to show cluster readiness, | ||
568 | 612 | after an optional initial sleep period. Initial sleep is likely | ||
569 | 613 | necessary to be effective following a config change, as status | ||
570 | 614 | message may not instantly update to non-ready.""" | ||
571 | 615 | |||
572 | 616 | if init_sleep: | ||
573 | 617 | time.sleep(init_sleep) | ||
574 | 618 | |||
575 | 619 | message = re.compile('^Unit is ready and clustered$') | ||
576 | 620 | deployment._auto_wait_for_status(message=message, | ||
577 | 621 | timeout=timeout, | ||
578 | 622 | include_only=['rabbitmq-server']) | ||
579 | 623 | |||
580 | 608 | def add_rmq_test_user(self, sentry_units, | 624 | def add_rmq_test_user(self, sentry_units, |
581 | 609 | username="testuser1", password="changeme"): | 625 | username="testuser1", password="changeme"): |
582 | 610 | """Add a test user via the first rmq juju unit, check connection as | 626 | """Add a test user via the first rmq juju unit, check connection as |
583 | @@ -805,7 +821,10 @@ | |||
584 | 805 | if port: | 821 | if port: |
585 | 806 | config['ssl_port'] = port | 822 | config['ssl_port'] = port |
586 | 807 | 823 | ||
588 | 808 | deployment.configure('rabbitmq-server', config) | 824 | deployment.d.configure('rabbitmq-server', config) |
589 | 825 | |||
590 | 826 | # Wait for unit status | ||
591 | 827 | self.rmq_wait_for_cluster(deployment) | ||
592 | 809 | 828 | ||
593 | 810 | # Confirm | 829 | # Confirm |
594 | 811 | tries = 0 | 830 | tries = 0 |
595 | @@ -832,7 +851,10 @@ | |||
596 | 832 | 851 | ||
597 | 833 | # Disable RMQ SSL | 852 | # Disable RMQ SSL |
598 | 834 | config = {'ssl': 'off'} | 853 | config = {'ssl': 'off'} |
600 | 835 | deployment.configure('rabbitmq-server', config) | 854 | deployment.d.configure('rabbitmq-server', config) |
601 | 855 | |||
602 | 856 | # Wait for unit status | ||
603 | 857 | self.rmq_wait_for_cluster(deployment) | ||
604 | 836 | 858 | ||
605 | 837 | # Confirm | 859 | # Confirm |
606 | 838 | tries = 0 | 860 | tries = 0 |
charm_lint_check #16740 neutron-api for james-page mp281862
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/16740/