Merge lp:~tribaal/charms/trusty/nova-compute/sync-charm-helpers into lp:~openstack-charmers-archive/charms/trusty/nova-compute/trunk

Proposed by Chris Glass
Status: Merged
Merged at revision: 61
Proposed branch: lp:~tribaal/charms/trusty/nova-compute/sync-charm-helpers
Merge into: lp:~openstack-charmers-archive/charms/trusty/nova-compute/trunk
Diff against target: 541 lines (+263/-74)
8 files modified
hooks/charmhelpers/contrib/openstack/context.py (+1/-1)
hooks/charmhelpers/contrib/openstack/neutron.py (+17/-1)
hooks/charmhelpers/contrib/openstack/utils.py (+8/-1)
hooks/charmhelpers/contrib/storage/linux/lvm.py (+1/-1)
hooks/charmhelpers/contrib/storage/linux/utils.py (+28/-5)
hooks/charmhelpers/core/hookenv.py (+98/-1)
hooks/charmhelpers/core/host.py (+14/-0)
hooks/charmhelpers/fetch/__init__.py (+96/-64)
To merge this branch: bzr merge lp:~tribaal/charms/trusty/nova-compute/sync-charm-helpers
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+220028@code.launchpad.net

Description of the change

This branch syncs back the changes from charm-helpers as of http://bazaar.launchpad.net/~charm-helpers/charm-helpers/devel/revision/153?start_revid=153 into the charm.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
--- hooks/charmhelpers/contrib/openstack/context.py 2014-04-04 16:45:38 +0000
+++ hooks/charmhelpers/contrib/openstack/context.py 2014-05-19 12:48:55 +0000
@@ -570,7 +570,7 @@
570570
571 if self.plugin == 'ovs':571 if self.plugin == 'ovs':
572 ctxt.update(self.ovs_ctxt())572 ctxt.update(self.ovs_ctxt())
573 elif self.plugin == 'nvp':573 elif self.plugin in ['nvp', 'nsx']:
574 ctxt.update(self.nvp_ctxt())574 ctxt.update(self.nvp_ctxt())
575575
576 alchemy_flags = config('neutron-alchemy-flags')576 alchemy_flags = config('neutron-alchemy-flags')
577577
=== modified file 'hooks/charmhelpers/contrib/openstack/neutron.py'
--- hooks/charmhelpers/contrib/openstack/neutron.py 2014-04-04 16:45:38 +0000
+++ hooks/charmhelpers/contrib/openstack/neutron.py 2014-05-19 12:48:55 +0000
@@ -114,14 +114,30 @@
114 'server_packages': ['neutron-server',114 'server_packages': ['neutron-server',
115 'neutron-plugin-nicira'],115 'neutron-plugin-nicira'],
116 'server_services': ['neutron-server']116 'server_services': ['neutron-server']
117 },
118 'nsx': {
119 'config': '/etc/neutron/plugins/vmware/nsx.ini',
120 'driver': 'vmware',
121 'contexts': [
122 context.SharedDBContext(user=config('neutron-database-user'),
123 database=config('neutron-database'),
124 relation_prefix='neutron',
125 ssl_dir=NEUTRON_CONF_DIR)],
126 'services': [],
127 'packages': [],
128 'server_packages': ['neutron-server',
129 'neutron-plugin-vmware'],
130 'server_services': ['neutron-server']
117 }131 }
118 }132 }
119 # NOTE: patch in ml2 plugin for icehouse onwards
120 if release >= 'icehouse':133 if release >= 'icehouse':
134 # NOTE: patch in ml2 plugin for icehouse onwards
121 plugins['ovs']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini'135 plugins['ovs']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini'
122 plugins['ovs']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin'136 plugins['ovs']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin'
123 plugins['ovs']['server_packages'] = ['neutron-server',137 plugins['ovs']['server_packages'] = ['neutron-server',
124 'neutron-plugin-ml2']138 'neutron-plugin-ml2']
139 # NOTE: patch in vmware renames nvp->nsx for icehouse onwards
140 plugins['nvp'] = plugins['nsx']
125 return plugins141 return plugins
126142
127143
128144
=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
--- hooks/charmhelpers/contrib/openstack/utils.py 2014-04-10 16:09:53 +0000
+++ hooks/charmhelpers/contrib/openstack/utils.py 2014-05-19 12:48:55 +0000
@@ -131,6 +131,11 @@
131def get_os_codename_package(package, fatal=True):131def get_os_codename_package(package, fatal=True):
132 '''Derive OpenStack release codename from an installed package.'''132 '''Derive OpenStack release codename from an installed package.'''
133 apt.init()133 apt.init()
134
135 # Tell apt to build an in-memory cache to prevent race conditions (if
136 # another process is already building the cache).
137 apt.config.set("Dir::Cache::pkgcache", "")
138
134 cache = apt.Cache()139 cache = apt.Cache()
135140
136 try:141 try:
@@ -183,7 +188,7 @@
183 if cname == codename:188 if cname == codename:
184 return version189 return version
185 #e = "Could not determine OpenStack version for package: %s" % pkg190 #e = "Could not determine OpenStack version for package: %s" % pkg
186 #error_out(e)191 # error_out(e)
187192
188193
189os_rel = None194os_rel = None
@@ -401,6 +406,8 @@
401 rtype = 'PTR'406 rtype = 'PTR'
402 elif isinstance(address, basestring):407 elif isinstance(address, basestring):
403 rtype = 'A'408 rtype = 'A'
409 else:
410 return None
404411
405 answers = dns.resolver.query(address, rtype)412 answers = dns.resolver.query(address, rtype)
406 if answers:413 if answers:
407414
=== modified file 'hooks/charmhelpers/contrib/storage/linux/lvm.py'
--- hooks/charmhelpers/contrib/storage/linux/lvm.py 2013-07-19 02:37:30 +0000
+++ hooks/charmhelpers/contrib/storage/linux/lvm.py 2014-05-19 12:48:55 +0000
@@ -62,7 +62,7 @@
62 pvd = check_output(['pvdisplay', block_device]).splitlines()62 pvd = check_output(['pvdisplay', block_device]).splitlines()
63 for l in pvd:63 for l in pvd:
64 if l.strip().startswith('VG Name'):64 if l.strip().startswith('VG Name'):
65 vg = ' '.join(l.split()).split(' ').pop()65 vg = ' '.join(l.strip().split()[2:])
66 return vg66 return vg
6767
6868
6969
=== modified file 'hooks/charmhelpers/contrib/storage/linux/utils.py'
--- hooks/charmhelpers/contrib/storage/linux/utils.py 2014-04-04 16:45:38 +0000
+++ hooks/charmhelpers/contrib/storage/linux/utils.py 2014-05-19 12:48:55 +0000
@@ -1,8 +1,11 @@
1from os import stat1import os
2import re
2from stat import S_ISBLK3from stat import S_ISBLK
34
4from subprocess import (5from subprocess import (
5 check_call6 check_call,
7 check_output,
8 call
6)9)
710
811
@@ -12,7 +15,9 @@
1215
13 :returns: boolean: True if path is a block device, False if not.16 :returns: boolean: True if path is a block device, False if not.
14 '''17 '''
15 return S_ISBLK(stat(path).st_mode)18 if not os.path.exists(path):
19 return False
20 return S_ISBLK(os.stat(path).st_mode)
1621
1722
18def zap_disk(block_device):23def zap_disk(block_device):
@@ -22,5 +27,23 @@
2227
23 :param block_device: str: Full path of block device to clean.28 :param block_device: str: Full path of block device to clean.
24 '''29 '''
25 check_call(['sgdisk', '--zap-all', '--clear',30 # sometimes sgdisk exits non-zero; this is OK, dd will clean up
26 '--mbrtogpt', block_device])31 call(['sgdisk', '--zap-all', '--mbrtogpt',
32 '--clear', block_device])
33 dev_end = check_output(['blockdev', '--getsz', block_device])
34 gpt_end = int(dev_end.split()[0]) - 100
35 check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device),
36 'bs=1M', 'count=1'])
37 check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device),
38 'bs=512', 'count=100', 'seek=%s' % (gpt_end)])
39
40def is_device_mounted(device):
41 '''Given a device path, return True if that device is mounted, and False
42 if it isn't.
43
44 :param device: str: Full path of the device to check.
45 :returns: boolean: True if the path represents a mounted device, False if
46 it doesn't.
47 '''
48 out = check_output(['mount'])
49 return bool(re.search(device + r"[0-9]+\b", out))
2750
=== modified file 'hooks/charmhelpers/core/hookenv.py'
--- hooks/charmhelpers/core/hookenv.py 2014-03-27 11:08:20 +0000
+++ hooks/charmhelpers/core/hookenv.py 2014-05-19 12:48:55 +0000
@@ -155,6 +155,100 @@
155 return os.path.basename(sys.argv[0])155 return os.path.basename(sys.argv[0])
156156
157157
158class Config(dict):
159 """A Juju charm config dictionary that can write itself to
160 disk (as json) and track which values have changed since
161 the previous hook invocation.
162
163 Do not instantiate this object directly - instead call
164 ``hookenv.config()``
165
166 Example usage::
167
168 >>> # inside a hook
169 >>> from charmhelpers.core import hookenv
170 >>> config = hookenv.config()
171 >>> config['foo']
172 'bar'
173 >>> config['mykey'] = 'myval'
174 >>> config.save()
175
176
177 >>> # user runs `juju set mycharm foo=baz`
178 >>> # now we're inside subsequent config-changed hook
179 >>> config = hookenv.config()
180 >>> config['foo']
181 'baz'
182 >>> # test to see if this val has changed since last hook
183 >>> config.changed('foo')
184 True
185 >>> # what was the previous value?
186 >>> config.previous('foo')
187 'bar'
188 >>> # keys/values that we add are preserved across hooks
189 >>> config['mykey']
190 'myval'
191 >>> # don't forget to save at the end of hook!
192 >>> config.save()
193
194 """
195 CONFIG_FILE_NAME = '.juju-persistent-config'
196
197 def __init__(self, *args, **kw):
198 super(Config, self).__init__(*args, **kw)
199 self._prev_dict = None
200 self.path = os.path.join(charm_dir(), Config.CONFIG_FILE_NAME)
201 if os.path.exists(self.path):
202 self.load_previous()
203
204 def load_previous(self, path=None):
205 """Load previous copy of config from disk so that current values
206 can be compared to previous values.
207
208 :param path:
209
210 File path from which to load the previous config. If `None`,
211 config is loaded from the default location. If `path` is
212 specified, subsequent `save()` calls will write to the same
213 path.
214
215 """
216 self.path = path or self.path
217 with open(self.path) as f:
218 self._prev_dict = json.load(f)
219
220 def changed(self, key):
221 """Return true if the value for this key has changed since
222 the last save.
223
224 """
225 if self._prev_dict is None:
226 return True
227 return self.previous(key) != self.get(key)
228
229 def previous(self, key):
230 """Return previous value for this key, or None if there
231 is no "previous" value.
232
233 """
234 if self._prev_dict:
235 return self._prev_dict.get(key)
236 return None
237
238 def save(self):
239 """Save this config to disk.
240
241 Preserves items in _prev_dict that do not exist in self.
242
243 """
244 if self._prev_dict:
245 for k, v in self._prev_dict.iteritems():
246 if k not in self:
247 self[k] = v
248 with open(self.path, 'w') as f:
249 json.dump(self, f)
250
251
158@cached252@cached
159def config(scope=None):253def config(scope=None):
160 """Juju charm configuration"""254 """Juju charm configuration"""
@@ -163,7 +257,10 @@
163 config_cmd_line.append(scope)257 config_cmd_line.append(scope)
164 config_cmd_line.append('--format=json')258 config_cmd_line.append('--format=json')
165 try:259 try:
166 return json.loads(subprocess.check_output(config_cmd_line))260 config_data = json.loads(subprocess.check_output(config_cmd_line))
261 if scope is not None:
262 return config_data
263 return Config(config_data)
167 except ValueError:264 except ValueError:
168 return None265 return None
169266
170267
=== modified file 'hooks/charmhelpers/core/host.py'
--- hooks/charmhelpers/core/host.py 2014-04-04 16:45:38 +0000
+++ hooks/charmhelpers/core/host.py 2014-05-19 12:48:55 +0000
@@ -12,6 +12,7 @@
12import string12import string
13import subprocess13import subprocess
14import hashlib14import hashlib
15import apt_pkg
1516
16from collections import OrderedDict17from collections import OrderedDict
1718
@@ -295,3 +296,16 @@
295 if 'link/ether' in words:296 if 'link/ether' in words:
296 hwaddr = words[words.index('link/ether') + 1]297 hwaddr = words[words.index('link/ether') + 1]
297 return hwaddr298 return hwaddr
299
300
301def cmp_pkgrevno(package, revno, pkgcache=None):
302 '''Compare supplied revno with the revno of the installed package
303 1 => Installed revno is greater than supplied arg
304 0 => Installed revno is the same as supplied arg
305 -1 => Installed revno is less than supplied arg
306 '''
307 if not pkgcache:
308 apt_pkg.init()
309 pkgcache = apt_pkg.Cache()
310 pkg = pkgcache[package]
311 return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
298312
=== modified file 'hooks/charmhelpers/fetch/__init__.py'
--- hooks/charmhelpers/fetch/__init__.py 2014-04-24 17:34:11 +0000
+++ hooks/charmhelpers/fetch/__init__.py 2014-05-19 12:48:55 +0000
@@ -1,4 +1,5 @@
1import importlib1import importlib
2import time
2from yaml import safe_load3from yaml import safe_load
3from charmhelpers.core.host import (4from charmhelpers.core.host import (
4 lsb_release5 lsb_release
@@ -15,6 +16,7 @@
15import apt_pkg16import apt_pkg
16import os17import os
1718
19
18CLOUD_ARCHIVE = """# Ubuntu Cloud Archive20CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
19deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main21deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
20"""22"""
@@ -56,10 +58,62 @@
56 'precise-proposed/icehouse': 'precise-proposed/icehouse',58 'precise-proposed/icehouse': 'precise-proposed/icehouse',
57}59}
5860
61# The order of this list is very important. Handlers should be listed in from
62# least- to most-specific URL matching.
63FETCH_HANDLERS = (
64 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
65 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
66)
67
68APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT.
69APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks.
70APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times.
71
72
73class SourceConfigError(Exception):
74 pass
75
76
77class UnhandledSource(Exception):
78 pass
79
80
81class AptLockError(Exception):
82 pass
83
84
85class BaseFetchHandler(object):
86
87 """Base class for FetchHandler implementations in fetch plugins"""
88
89 def can_handle(self, source):
90 """Returns True if the source can be handled. Otherwise returns
91 a string explaining why it cannot"""
92 return "Wrong source type"
93
94 def install(self, source):
95 """Try to download and unpack the source. Return the path to the
96 unpacked files or raise UnhandledSource."""
97 raise UnhandledSource("Wrong source type {}".format(source))
98
99 def parse_url(self, url):
100 return urlparse(url)
101
102 def base_url(self, url):
103 """Return url without querystring or fragment"""
104 parts = list(self.parse_url(url))
105 parts[4:] = ['' for i in parts[4:]]
106 return urlunparse(parts)
107
59108
60def filter_installed_packages(packages):109def filter_installed_packages(packages):
61 """Returns a list of packages that require installation"""110 """Returns a list of packages that require installation"""
62 apt_pkg.init()111 apt_pkg.init()
112
113 # Tell apt to build an in-memory cache to prevent race conditions (if
114 # another process is already building the cache).
115 apt_pkg.config.set("Dir::Cache::pkgcache", "")
116
63 cache = apt_pkg.Cache()117 cache = apt_pkg.Cache()
64 _pkgs = []118 _pkgs = []
65 for package in packages:119 for package in packages:
@@ -87,14 +141,7 @@
87 cmd.extend(packages)141 cmd.extend(packages)
88 log("Installing {} with options: {}".format(packages,142 log("Installing {} with options: {}".format(packages,
89 options))143 options))
90 env = os.environ.copy()144 _run_apt_command(cmd, fatal)
91 if 'DEBIAN_FRONTEND' not in env:
92 env['DEBIAN_FRONTEND'] = 'noninteractive'
93
94 if fatal:
95 subprocess.check_call(cmd, env=env)
96 else:
97 subprocess.call(cmd, env=env)
98145
99146
100def apt_upgrade(options=None, fatal=False, dist=False):147def apt_upgrade(options=None, fatal=False, dist=False):
@@ -109,24 +156,13 @@
109 else:156 else:
110 cmd.append('upgrade')157 cmd.append('upgrade')
111 log("Upgrading with options: {}".format(options))158 log("Upgrading with options: {}".format(options))
112159 _run_apt_command(cmd, fatal)
113 env = os.environ.copy()
114 if 'DEBIAN_FRONTEND' not in env:
115 env['DEBIAN_FRONTEND'] = 'noninteractive'
116
117 if fatal:
118 subprocess.check_call(cmd, env=env)
119 else:
120 subprocess.call(cmd, env=env)
121160
122161
123def apt_update(fatal=False):162def apt_update(fatal=False):
124 """Update local apt cache"""163 """Update local apt cache"""
125 cmd = ['apt-get', 'update']164 cmd = ['apt-get', 'update']
126 if fatal:165 _run_apt_command(cmd, fatal)
127 subprocess.check_call(cmd)
128 else:
129 subprocess.call(cmd)
130166
131167
132def apt_purge(packages, fatal=False):168def apt_purge(packages, fatal=False):
@@ -137,10 +173,7 @@
137 else:173 else:
138 cmd.extend(packages)174 cmd.extend(packages)
139 log("Purging {}".format(packages))175 log("Purging {}".format(packages))
140 if fatal:176 _run_apt_command(cmd, fatal)
141 subprocess.check_call(cmd)
142 else:
143 subprocess.call(cmd)
144177
145178
146def apt_hold(packages, fatal=False):179def apt_hold(packages, fatal=False):
@@ -151,6 +184,7 @@
151 else:184 else:
152 cmd.extend(packages)185 cmd.extend(packages)
153 log("Holding {}".format(packages))186 log("Holding {}".format(packages))
187
154 if fatal:188 if fatal:
155 subprocess.check_call(cmd)189 subprocess.check_call(cmd)
156 else:190 else:
@@ -188,10 +222,6 @@
188 key])222 key])
189223
190224
191class SourceConfigError(Exception):
192 pass
193
194
195def configure_sources(update=False,225def configure_sources(update=False,
196 sources_var='install_sources',226 sources_var='install_sources',
197 keys_var='install_keys'):227 keys_var='install_keys'):
@@ -224,17 +254,6 @@
224 if update:254 if update:
225 apt_update(fatal=True)255 apt_update(fatal=True)
226256
227# The order of this list is very important. Handlers should be listed in from
228# least- to most-specific URL matching.
229FETCH_HANDLERS = (
230 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
231 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
232)
233
234
235class UnhandledSource(Exception):
236 pass
237
238257
239def install_remote(source):258def install_remote(source):
240 """259 """
@@ -265,30 +284,6 @@
265 return install_remote(source)284 return install_remote(source)
266285
267286
268class BaseFetchHandler(object):
269
270 """Base class for FetchHandler implementations in fetch plugins"""
271
272 def can_handle(self, source):
273 """Returns True if the source can be handled. Otherwise returns
274 a string explaining why it cannot"""
275 return "Wrong source type"
276
277 def install(self, source):
278 """Try to download and unpack the source. Return the path to the
279 unpacked files or raise UnhandledSource."""
280 raise UnhandledSource("Wrong source type {}".format(source))
281
282 def parse_url(self, url):
283 return urlparse(url)
284
285 def base_url(self, url):
286 """Return url without querystring or fragment"""
287 parts = list(self.parse_url(url))
288 parts[4:] = ['' for i in parts[4:]]
289 return urlunparse(parts)
290
291
292def plugins(fetch_handlers=None):287def plugins(fetch_handlers=None):
293 if not fetch_handlers:288 if not fetch_handlers:
294 fetch_handlers = FETCH_HANDLERS289 fetch_handlers = FETCH_HANDLERS
@@ -306,3 +301,40 @@
306 log("FetchHandler {} not found, skipping plugin".format(301 log("FetchHandler {} not found, skipping plugin".format(
307 handler_name))302 handler_name))
308 return plugin_list303 return plugin_list
304
305
306def _run_apt_command(cmd, fatal=False):
307 """
308 Run an APT command, checking output and retrying if the fatal flag is set
309 to True.
310
311 :param: cmd: str: The apt command to run.
312 :param: fatal: bool: Whether the command's output should be checked and
313 retried.
314 """
315 env = os.environ.copy()
316
317 if 'DEBIAN_FRONTEND' not in env:
318 env['DEBIAN_FRONTEND'] = 'noninteractive'
319
320 if fatal:
321 retry_count = 0
322 result = None
323
324 # If the command is considered "fatal", we need to retry if the apt
325 # lock was not acquired.
326
327 while result is None or result == APT_NO_LOCK:
328 try:
329 result = subprocess.check_call(cmd, env=env)
330 except subprocess.CalledProcessError, e:
331 retry_count = retry_count + 1
332 if retry_count > APT_NO_LOCK_RETRY_COUNT:
333 raise
334 result = e.returncode
335 log("Couldn't acquire DPKG lock. Will retry in {} seconds."
336 "".format(APT_NO_LOCK_RETRY_DELAY))
337 time.sleep(APT_NO_LOCK_RETRY_DELAY)
338
339 else:
340 subprocess.call(cmd, env=env)

Subscribers

People subscribed via source and target branches