Merge lp:~lazypower/charms/trusty/mongodb/fixing_st00f into lp:charms/mongodb

Proposed by Charles Butler
Status: Rejected
Rejected by: Charles Butler
Proposed branch: lp:~lazypower/charms/trusty/mongodb/fixing_st00f
Merge into: lp:charms/mongodb
Diff against target: 1403 lines (+597/-274)
11 files modified
.bzrignore (+3/-0)
Makefile (+9/-1)
hooks/charmhelpers/core/fstab.py (+116/-0)
hooks/charmhelpers/core/hookenv.py (+103/-5)
hooks/charmhelpers/core/host.py (+38/-8)
hooks/charmhelpers/fetch/__init__.py (+130/-81)
hooks/charmhelpers/fetch/bzrurl.py (+2/-1)
hooks/hooks.py (+148/-171)
hooks/install (+0/-5)
metadata.yaml (+5/-2)
tests/200_relate_ceilometer.test (+43/-0)
To merge this branch: bzr merge lp:~lazypower/charms/trusty/mongodb/fixing_st00f
Reviewer Review Type Date Requested Status
Charles Butler (community) Needs Fixing
Benjamin Saller (community) Needs Fixing
Review via email: mp+228714@code.launchpad.net

Description of the change

Proposed resolution for MongoDB - refactors to use charmhelpers, adds a basic relation test to validate ceilometer client integration isn't broken, and various fixes relating to clustering.

This is a rework of patch 49 that was reverted.

To post a comment you must log in.
54. By Charles Butler

Backports Storage Subordinate work into the charmhelpers revision

55. By Charles Butler

Fixes typo in raise statement of test

Revision history for this message
Benjamin Saller (bcsaller) wrote :

Unless my read of the inline diff below is wrong you have some merge conflicts that need addressing. Happy to peek at it again after. Thanks!

review: Needs Fixing
56. By Charles Butler

Dirty merge cleanup

Revision history for this message
Charles Butler (lazypower) wrote :

And just noticed this "dirty merge cleanup" leaves a ton of stuff around. WIll revert, and re-try, again.

review: Needs Fixing
Revision history for this message
Charles Butler (lazypower) wrote :

Rejecting - this was an olllld merge that was bubbling up in the queue - and has been merged from a different, cleaner branch.

Revision history for this message
Marco Ceppi (marcoceppi) wrote :

Thanks for removing from the queue.

Unmerged revisions

56. By Charles Butler

Dirty merge cleanup

55. By Charles Butler

Fixes typo in raise statement of test

54. By Charles Butler

Backports Storage Subordinate work into the charmhelpers revision

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2014-07-29 16:34:12 +0000
4@@ -0,0 +1,3 @@
5+.git
6+bin/*
7+scripts/charm-helpers-sync.py
8
9=== modified file 'Makefile'
10--- Makefile 2014-04-11 20:55:42 +0000
11+++ Makefile 2014-07-29 16:34:12 +0000
12@@ -13,9 +13,17 @@
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16+PYTHON := /usr/bin/env python
17
18 unittest:
19 tests/10-unit.test
20
21 sync:
22- @charm-helper-sync -c charm-helpers-sync.yaml
23+ @mkdir -p bin
24+ @bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py > bin/charm_helpers_sync.py
25+ @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-sync.yaml
26+
27+clean:
28+ @find . -name \*.pyc -delete
29+ @find . -name '*.bak' -delete
30+
31
32=== added file '__init__.py'
33=== added directory 'bin'
34=== added file 'hooks/charmhelpers/core/fstab.py'
35--- hooks/charmhelpers/core/fstab.py 1970-01-01 00:00:00 +0000
36+++ hooks/charmhelpers/core/fstab.py 2014-07-29 16:34:12 +0000
37@@ -0,0 +1,116 @@
38+#!/usr/bin/env python
39+# -*- coding: utf-8 -*-
40+
41+__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
42+
43+import os
44+
45+
46+class Fstab(file):
47+ """This class extends file in order to implement a file reader/writer
48+ for file `/etc/fstab`
49+ """
50+
51+ class Entry(object):
52+ """Entry class represents a non-comment line on the `/etc/fstab` file
53+ """
54+ def __init__(self, device, mountpoint, filesystem,
55+ options, d=0, p=0):
56+ self.device = device
57+ self.mountpoint = mountpoint
58+ self.filesystem = filesystem
59+
60+ if not options:
61+ options = "defaults"
62+
63+ self.options = options
64+ self.d = d
65+ self.p = p
66+
67+ def __eq__(self, o):
68+ return str(self) == str(o)
69+
70+ def __str__(self):
71+ return "{} {} {} {} {} {}".format(self.device,
72+ self.mountpoint,
73+ self.filesystem,
74+ self.options,
75+ self.d,
76+ self.p)
77+
78+ DEFAULT_PATH = os.path.join(os.path.sep, 'etc', 'fstab')
79+
80+ def __init__(self, path=None):
81+ if path:
82+ self._path = path
83+ else:
84+ self._path = self.DEFAULT_PATH
85+ file.__init__(self, self._path, 'r+')
86+
87+ def _hydrate_entry(self, line):
88+ # NOTE: use split with no arguments to split on any
89+ # whitespace including tabs
90+ return Fstab.Entry(*filter(
91+ lambda x: x not in ('', None),
92+ line.strip("\n").split()))
93+
94+ @property
95+ def entries(self):
96+ self.seek(0)
97+ for line in self.readlines():
98+ try:
99+ if not line.startswith("#"):
100+ yield self._hydrate_entry(line)
101+ except ValueError:
102+ pass
103+
104+ def get_entry_by_attr(self, attr, value):
105+ for entry in self.entries:
106+ e_attr = getattr(entry, attr)
107+ if e_attr == value:
108+ return entry
109+ return None
110+
111+ def add_entry(self, entry):
112+ if self.get_entry_by_attr('device', entry.device):
113+ return False
114+
115+ self.write(str(entry) + '\n')
116+ self.truncate()
117+ return entry
118+
119+ def remove_entry(self, entry):
120+ self.seek(0)
121+
122+ lines = self.readlines()
123+
124+ found = False
125+ for index, line in enumerate(lines):
126+ if not line.startswith("#"):
127+ if self._hydrate_entry(line) == entry:
128+ found = True
129+ break
130+
131+ if not found:
132+ return False
133+
134+ lines.remove(line)
135+
136+ self.seek(0)
137+ self.write(''.join(lines))
138+ self.truncate()
139+ return True
140+
141+ @classmethod
142+ def remove_by_mountpoint(cls, mountpoint, path=None):
143+ fstab = cls(path=path)
144+ entry = fstab.get_entry_by_attr('mountpoint', mountpoint)
145+ if entry:
146+ return fstab.remove_entry(entry)
147+ return False
148+
149+ @classmethod
150+ def add(cls, device, mountpoint, filesystem, options=None, path=None):
151+ return cls(path=path).add_entry(Fstab.Entry(device,
152+ mountpoint, filesystem,
153+ options=options))
154
155=== modified file 'hooks/charmhelpers/core/hookenv.py'
156--- hooks/charmhelpers/core/hookenv.py 2014-04-11 20:55:42 +0000
157+++ hooks/charmhelpers/core/hookenv.py 2014-07-29 16:34:12 +0000
158@@ -25,7 +25,7 @@
159 def cached(func):
160 """Cache return values for multiple executions of func + args
161
162- For example:
163+ For example::
164
165 @cached
166 def unit_get(attribute):
167@@ -155,6 +155,100 @@
168 return os.path.basename(sys.argv[0])
169
170
171+class Config(dict):
172+ """A Juju charm config dictionary that can write itself to
173+ disk (as json) and track which values have changed since
174+ the previous hook invocation.
175+
176+ Do not instantiate this object directly - instead call
177+ ``hookenv.config()``
178+
179+ Example usage::
180+
181+ >>> # inside a hook
182+ >>> from charmhelpers.core import hookenv
183+ >>> config = hookenv.config()
184+ >>> config['foo']
185+ 'bar'
186+ >>> config['mykey'] = 'myval'
187+ >>> config.save()
188+
189+
190+ >>> # user runs `juju set mycharm foo=baz`
191+ >>> # now we're inside subsequent config-changed hook
192+ >>> config = hookenv.config()
193+ >>> config['foo']
194+ 'baz'
195+ >>> # test to see if this val has changed since last hook
196+ >>> config.changed('foo')
197+ True
198+ >>> # what was the previous value?
199+ >>> config.previous('foo')
200+ 'bar'
201+ >>> # keys/values that we add are preserved across hooks
202+ >>> config['mykey']
203+ 'myval'
204+ >>> # don't forget to save at the end of hook!
205+ >>> config.save()
206+
207+ """
208+ CONFIG_FILE_NAME = '.juju-persistent-config'
209+
210+ def __init__(self, *args, **kw):
211+ super(Config, self).__init__(*args, **kw)
212+ self._prev_dict = None
213+ self.path = os.path.join(charm_dir(), Config.CONFIG_FILE_NAME)
214+ if os.path.exists(self.path):
215+ self.load_previous()
216+
217+ def load_previous(self, path=None):
218+ """Load previous copy of config from disk so that current values
219+ can be compared to previous values.
220+
221+ :param path:
222+
223+ File path from which to load the previous config. If `None`,
224+ config is loaded from the default location. If `path` is
225+ specified, subsequent `save()` calls will write to the same
226+ path.
227+
228+ """
229+ self.path = path or self.path
230+ with open(self.path) as f:
231+ self._prev_dict = json.load(f)
232+
233+ def changed(self, key):
234+ """Return true if the value for this key has changed since
235+ the last save.
236+
237+ """
238+ if self._prev_dict is None:
239+ return True
240+ return self.previous(key) != self.get(key)
241+
242+ def previous(self, key):
243+ """Return previous value for this key, or None if there
244+ is no "previous" value.
245+
246+ """
247+ if self._prev_dict:
248+ return self._prev_dict.get(key)
249+ return None
250+
251+ def save(self):
252+ """Save this config to disk.
253+
254+ Preserves items in _prev_dict that do not exist in self.
255+
256+ """
257+ if self._prev_dict:
258+ for k, v in self._prev_dict.iteritems():
259+ if k not in self:
260+ self[k] = v
261+ with open(self.path, 'w') as f:
262+ json.dump(self, f)
263+
264+
265 @cached
266 def config(scope=None):
267 """Juju charm configuration"""
268@@ -163,7 +257,10 @@
269 config_cmd_line.append(scope)
270 config_cmd_line.append('--format=json')
271 try:
272- return json.loads(subprocess.check_output(config_cmd_line))
273+ config_data = json.loads(subprocess.check_output(config_cmd_line))
274+ if scope is not None:
275+ return config_data
276+ return Config(config_data)
277 except ValueError:
278 return None
279
280@@ -348,18 +445,19 @@
281 class Hooks(object):
282 """A convenient handler for hook functions.
283
284- Example:
285+ Example::
286+
287 hooks = Hooks()
288
289 # register a hook, taking its name from the function name
290 @hooks.hook()
291 def install():
292- ...
293+ pass # your code here
294
295 # register a hook, providing a custom hook name
296 @hooks.hook("config-changed")
297 def config_changed():
298- ...
299+ pass # your code here
300
301 if __name__ == "__main__":
302 # execute a hook based on the name the program is called by
303
304=== modified file 'hooks/charmhelpers/core/host.py'
305--- hooks/charmhelpers/core/host.py 2014-04-11 20:55:42 +0000
306+++ hooks/charmhelpers/core/host.py 2014-07-29 16:34:12 +0000
307@@ -16,6 +16,7 @@
308 from collections import OrderedDict
309
310 from hookenv import log
311+from fstab import Fstab
312
313
314 def service_start(service_name):
315@@ -34,7 +35,8 @@
316
317
318 def service_reload(service_name, restart_on_failure=False):
319- """Reload a system service, optionally falling back to restart if reload fails"""
320+ """Reload a system service, optionally falling back to restart if
321+ reload fails"""
322 service_result = service('reload', service_name)
323 if not service_result and restart_on_failure:
324 service_result = service('restart', service_name)
325@@ -143,7 +145,19 @@
326 target.write(content)
327
328
329-def mount(device, mountpoint, options=None, persist=False):
330+def fstab_remove(mp):
331+ """Remove the given mountpoint entry from /etc/fstab
332+ """
333+ return Fstab.remove_by_mountpoint(mp)
334+
335+
336+def fstab_add(dev, mp, fs, options=None):
337+ """Adds the given device entry to the /etc/fstab file
338+ """
339+ return Fstab.add(dev, mp, fs, options=options)
340+
341+
342+def mount(device, mountpoint, options=None, persist=False, filesystem="ext3"):
343 """Mount a filesystem at a particular mountpoint"""
344 cmd_args = ['mount']
345 if options is not None:
346@@ -154,9 +168,9 @@
347 except subprocess.CalledProcessError, e:
348 log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
349 return False
350+
351 if persist:
352- # TODO: update fstab
353- pass
354+ return fstab_add(device, mountpoint, filesystem, options=options)
355 return True
356
357
358@@ -168,9 +182,9 @@
359 except subprocess.CalledProcessError, e:
360 log('Error unmounting {}\n{}'.format(mountpoint, e.output))
361 return False
362+
363 if persist:
364- # TODO: update fstab
365- pass
366+ return fstab_remove(mountpoint)
367 return True
368
369
370@@ -197,13 +211,13 @@
371 def restart_on_change(restart_map, stopstart=False):
372 """Restart services based on configuration files changing
373
374- This function is used a decorator, for example
375+ This function is used a decorator, for example::
376
377 @restart_on_change({
378 '/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
379 })
380 def ceph_client_changed():
381- ...
382+ pass # your code here
383
384 In this example, the cinder-api and cinder-volume services
385 would be restarted if /etc/ceph/ceph.conf is changed by the
386@@ -295,3 +309,19 @@
387 if 'link/ether' in words:
388 hwaddr = words[words.index('link/ether') + 1]
389 return hwaddr
390+
391+
392+def cmp_pkgrevno(package, revno, pkgcache=None):
393+ '''Compare supplied revno with the revno of the installed package
394+
395+ * 1 => Installed revno is greater than supplied arg
396+ * 0 => Installed revno is the same as supplied arg
397+ * -1 => Installed revno is less than supplied arg
398+
399+ '''
400+ import apt_pkg
401+ if not pkgcache:
402+ apt_pkg.init()
403+ pkgcache = apt_pkg.Cache()
404+ pkg = pkgcache[package]
405+ return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
406
407=== modified file 'hooks/charmhelpers/fetch/__init__.py'
408--- hooks/charmhelpers/fetch/__init__.py 2014-04-11 20:55:42 +0000
409+++ hooks/charmhelpers/fetch/__init__.py 2014-07-29 16:34:12 +0000
410@@ -1,4 +1,5 @@
411 import importlib
412+import time
413 from yaml import safe_load
414 from charmhelpers.core.host import (
415 lsb_release
416@@ -12,9 +13,9 @@
417 config,
418 log,
419 )
420-import apt_pkg
421 import os
422
423+
424 CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
425 deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
426 """
427@@ -54,12 +55,74 @@
428 'icehouse/proposed': 'precise-proposed/icehouse',
429 'precise-icehouse/proposed': 'precise-proposed/icehouse',
430 'precise-proposed/icehouse': 'precise-proposed/icehouse',
431+ # Juno
432+ 'juno': 'trusty-updates/juno',
433+ 'trusty-juno': 'trusty-updates/juno',
434+ 'trusty-juno/updates': 'trusty-updates/juno',
435+ 'trusty-updates/juno': 'trusty-updates/juno',
436+ 'juno/proposed': 'trusty-proposed/juno',
437+ 'juno/proposed': 'trusty-proposed/juno',
438+ 'trusty-juno/proposed': 'trusty-proposed/juno',
439+ 'trusty-proposed/juno': 'trusty-proposed/juno',
440 }
441
442+# The order of this list is very important. Handlers should be listed in from
443+# least- to most-specific URL matching.
444+FETCH_HANDLERS = (
445+ 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
446+ 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
447+)
448+
449+APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT.
450+APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks.
451+APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times.
452+
453+
454+class SourceConfigError(Exception):
455+ pass
456+
457+
458+class UnhandledSource(Exception):
459+ pass
460+
461+
462+class AptLockError(Exception):
463+ pass
464+
465+
466+class BaseFetchHandler(object):
467+
468+ """Base class for FetchHandler implementations in fetch plugins"""
469+
470+ def can_handle(self, source):
471+ """Returns True if the source can be handled. Otherwise returns
472+ a string explaining why it cannot"""
473+ return "Wrong source type"
474+
475+ def install(self, source):
476+ """Try to download and unpack the source. Return the path to the
477+ unpacked files or raise UnhandledSource."""
478+ raise UnhandledSource("Wrong source type {}".format(source))
479+
480+ def parse_url(self, url):
481+ return urlparse(url)
482+
483+ def base_url(self, url):
484+ """Return url without querystring or fragment"""
485+ parts = list(self.parse_url(url))
486+ parts[4:] = ['' for i in parts[4:]]
487+ return urlunparse(parts)
488+
489
490 def filter_installed_packages(packages):
491 """Returns a list of packages that require installation"""
492+ import apt_pkg
493 apt_pkg.init()
494+
495+ # Tell apt to build an in-memory cache to prevent race conditions (if
496+ # another process is already building the cache).
497+ apt_pkg.config.set("Dir::Cache::pkgcache", "")
498+
499 cache = apt_pkg.Cache()
500 _pkgs = []
501 for package in packages:
502@@ -87,14 +150,7 @@
503 cmd.extend(packages)
504 log("Installing {} with options: {}".format(packages,
505 options))
506- env = os.environ.copy()
507- if 'DEBIAN_FRONTEND' not in env:
508- env['DEBIAN_FRONTEND'] = 'noninteractive'
509-
510- if fatal:
511- subprocess.check_call(cmd, env=env)
512- else:
513- subprocess.call(cmd, env=env)
514+ _run_apt_command(cmd, fatal)
515
516
517 def apt_upgrade(options=None, fatal=False, dist=False):
518@@ -109,24 +165,13 @@
519 else:
520 cmd.append('upgrade')
521 log("Upgrading with options: {}".format(options))
522-
523- env = os.environ.copy()
524- if 'DEBIAN_FRONTEND' not in env:
525- env['DEBIAN_FRONTEND'] = 'noninteractive'
526-
527- if fatal:
528- subprocess.check_call(cmd, env=env)
529- else:
530- subprocess.call(cmd, env=env)
531+ _run_apt_command(cmd, fatal)
532
533
534 def apt_update(fatal=False):
535 """Update local apt cache"""
536 cmd = ['apt-get', 'update']
537- if fatal:
538- subprocess.check_call(cmd)
539- else:
540- subprocess.call(cmd)
541+ _run_apt_command(cmd, fatal)
542
543
544 def apt_purge(packages, fatal=False):
545@@ -137,10 +182,7 @@
546 else:
547 cmd.extend(packages)
548 log("Purging {}".format(packages))
549- if fatal:
550- subprocess.check_call(cmd)
551- else:
552- subprocess.call(cmd)
553+ _run_apt_command(cmd, fatal)
554
555
556 def apt_hold(packages, fatal=False):
557@@ -151,6 +193,7 @@
558 else:
559 cmd.extend(packages)
560 log("Holding {}".format(packages))
561+
562 if fatal:
563 subprocess.check_call(cmd)
564 else:
565@@ -184,57 +227,50 @@
566 apt.write(PROPOSED_POCKET.format(release))
567 if key:
568 subprocess.check_call(['apt-key', 'adv', '--keyserver',
569- 'keyserver.ubuntu.com', '--recv',
570+ 'hkp://keyserver.ubuntu.com:80', '--recv',
571 key])
572
573
574-class SourceConfigError(Exception):
575- pass
576-
577-
578 def configure_sources(update=False,
579 sources_var='install_sources',
580 keys_var='install_keys'):
581 """
582- Configure multiple sources from charm configuration
583+ Configure multiple sources from charm configuration.
584+
585+ The lists are encoded as yaml fragments in the configuration.
586+ The frament needs to be included as a string.
587
588 Example config:
589- install_sources:
590+ install_sources: |
591 - "ppa:foo"
592 - "http://example.com/repo precise main"
593- install_keys:
594+ install_keys: |
595 - null
596 - "a1b2c3d4"
597
598 Note that 'null' (a.k.a. None) should not be quoted.
599 """
600- sources = safe_load(config(sources_var))
601- keys = config(keys_var)
602- if keys is not None:
603- keys = safe_load(keys)
604- if isinstance(sources, basestring) and (
605- keys is None or isinstance(keys, basestring)):
606- add_source(sources, keys)
607+ sources = safe_load((config(sources_var) or '').strip()) or []
608+ keys = safe_load((config(keys_var) or '').strip()) or None
609+
610+ if isinstance(sources, basestring):
611+ sources = [sources]
612+
613+ if keys is None:
614+ for source in sources:
615+ add_source(source, None)
616 else:
617- if not len(sources) == len(keys):
618- msg = 'Install sources and keys lists are different lengths'
619- raise SourceConfigError(msg)
620- for src_num in range(len(sources)):
621- add_source(sources[src_num], keys[src_num])
622+ if isinstance(keys, basestring):
623+ keys = [keys]
624+
625+ if len(sources) != len(keys):
626+ raise SourceConfigError(
627+ 'Install sources and keys lists are different lengths')
628+ for source, key in zip(sources, keys):
629+ add_source(source, key)
630 if update:
631 apt_update(fatal=True)
632
633-# The order of this list is very important. Handlers should be listed in from
634-# least- to most-specific URL matching.
635-FETCH_HANDLERS = (
636- 'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
637- 'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
638-)
639-
640-
641-class UnhandledSource(Exception):
642- pass
643-
644
645 def install_remote(source):
646 """
647@@ -265,30 +301,6 @@
648 return install_remote(source)
649
650
651-class BaseFetchHandler(object):
652-
653- """Base class for FetchHandler implementations in fetch plugins"""
654-
655- def can_handle(self, source):
656- """Returns True if the source can be handled. Otherwise returns
657- a string explaining why it cannot"""
658- return "Wrong source type"
659-
660- def install(self, source):
661- """Try to download and unpack the source. Return the path to the
662- unpacked files or raise UnhandledSource."""
663- raise UnhandledSource("Wrong source type {}".format(source))
664-
665- def parse_url(self, url):
666- return urlparse(url)
667-
668- def base_url(self, url):
669- """Return url without querystring or fragment"""
670- parts = list(self.parse_url(url))
671- parts[4:] = ['' for i in parts[4:]]
672- return urlunparse(parts)
673-
674-
675 def plugins(fetch_handlers=None):
676 if not fetch_handlers:
677 fetch_handlers = FETCH_HANDLERS
678@@ -306,3 +318,40 @@
679 log("FetchHandler {} not found, skipping plugin".format(
680 handler_name))
681 return plugin_list
682+
683+
684+def _run_apt_command(cmd, fatal=False):
685+ """
686+ Run an APT command, checking output and retrying if the fatal flag is set
687+ to True.
688+
689+ :param: cmd: str: The apt command to run.
690+ :param: fatal: bool: Whether the command's output should be checked and
691+ retried.
692+ """
693+ env = os.environ.copy()
694+
695+ if 'DEBIAN_FRONTEND' not in env:
696+ env['DEBIAN_FRONTEND'] = 'noninteractive'
697+
698+ if fatal:
699+ retry_count = 0
700+ result = None
701+
702+ # If the command is considered "fatal", we need to retry if the apt
703+ # lock was not acquired.
704+
705+ while result is None or result == APT_NO_LOCK:
706+ try:
707+ result = subprocess.check_call(cmd, env=env)
708+ except subprocess.CalledProcessError, e:
709+ retry_count = retry_count + 1
710+ if retry_count > APT_NO_LOCK_RETRY_COUNT:
711+ raise
712+ result = e.returncode
713+ log("Couldn't acquire DPKG lock. Will retry in {} seconds."
714+ "".format(APT_NO_LOCK_RETRY_DELAY))
715+ time.sleep(APT_NO_LOCK_RETRY_DELAY)
716+
717+ else:
718+ subprocess.call(cmd, env=env)
719
720=== modified file 'hooks/charmhelpers/fetch/bzrurl.py'
721--- hooks/charmhelpers/fetch/bzrurl.py 2014-04-11 20:55:42 +0000
722+++ hooks/charmhelpers/fetch/bzrurl.py 2014-07-29 16:34:12 +0000
723@@ -39,7 +39,8 @@
724 def install(self, source):
725 url_parts = self.parse_url(source)
726 branch_name = url_parts.path.strip("/").split("/")[-1]
727- dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", branch_name)
728+ dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",
729+ branch_name)
730 if not os.path.exists(dest_dir):
731 mkdir(dest_dir, perms=0755)
732 try:
733
734=== modified file 'hooks/hooks.py'
735--- hooks/hooks.py 2014-07-29 10:22:55 +0000
736+++ hooks/hooks.py 2014-07-29 16:34:12 +0000
737@@ -6,7 +6,6 @@
738 '''
739
740 import commands
741-import json
742 import os
743 import re
744 import signal
745@@ -15,7 +14,6 @@
746 import sys
747 import time
748 import yaml
749-import argparse
750
751 from os import chmod
752 from os import remove
753@@ -29,10 +27,26 @@
754 apt_update,
755 apt_install
756 )
757+
758 from charmhelpers.core.hookenv import (
759- config
760-)
761-
762+ config,
763+ unit_get,
764+ relation_get,
765+ relation_set,
766+ relations_of_type,
767+ relation_id,
768+ open_port,
769+ close_port,
770+ Hooks,
771+)
772+
773+from charmhelpers.core.hookenv import log as juju_log
774+
775+from charmhelpers.core.host import (
776+ service,
777+)
778+
779+hooks = Hooks()
780
781 ###############################################################################
782 # Global variables
783@@ -40,8 +54,8 @@
784 default_mongodb_config = "/etc/mongodb.conf"
785 default_mongodb_init_config = "/etc/init/mongodb.conf"
786 default_mongos_list = "/etc/mongos.list"
787-default_wait_for = 20
788-default_max_tries = 20
789+default_wait_for = 10
790+default_max_tries = 5
791
792 ###############################################################################
793 # Supporting functions
794@@ -494,7 +508,7 @@
795 config.append("")
796
797 # arbiter
798- if config_data['arbiter'] != "disabled" and\
799+ if config_data['arbiter'] != "disabled" and \
800 config_data['arbiter'] != "enabled":
801 config.append("arbiter = %s" % config_data['arbiter'])
802 config.append("")
803@@ -657,7 +671,7 @@
804
805
806 def configsvr_status(wait_for=default_wait_for, max_tries=default_max_tries):
807- config_data = config_get()
808+ config_data = config()
809 current_try = 0
810 while (process_check_pidfile('/var/run/mongodb/configsvr.pid') !=
811 (None, None)) and not port_check(
812@@ -685,7 +699,7 @@
813 juju_log("disable_configsvr: port not defined.")
814 return(False)
815 try:
816- config_server_port = config_get('config_server_port')
817+ config_server_port = config('config_server_port')
818 pid = open('/var/run/mongodb/configsvr.pid').read()
819 os.kill(int(pid), signal.SIGTERM)
820 os.unlink('/var/run/mongodb/configsvr.pid')
821@@ -747,7 +761,7 @@
822
823
824 def mongos_status(wait_for=default_wait_for, max_tries=default_max_tries):
825- config_data = config_get()
826+ config_data = config()
827 current_try = 0
828 while (process_check_pidfile('/var/run/mongodb/mongos.pid') !=
829 (None, None)) and not port_check(
830@@ -839,17 +853,17 @@
831
832 def restart_mongod(wait_for=default_wait_for, max_tries=default_max_tries):
833 my_hostname = unit_get('public-address')
834- my_port = config_get('port')
835+ my_port = config('port')
836 current_try = 0
837
838- service('mongodb', 'stop')
839+ service('stop', 'mongodb')
840 if os.path.exists('/var/lib/mongodb/mongod.lock'):
841 os.remove('/var/lib/mongodb/mongod.lock')
842
843- if not service('mongodb', 'start'):
844+ if not service('start', 'mongodb'):
845 return False
846
847- while (service('mongodb', 'status') and
848+ while (service('status', 'mongodb') and
849 not port_check(my_hostname, my_port) and
850 current_try < max_tries):
851 juju_log(
852@@ -859,14 +873,14 @@
853 current_try += 1
854
855 return(
856- (service('mongodb', 'status') == port_check(my_hostname, my_port))
857+ (service('status', 'mongodb') == port_check(my_hostname, my_port))
858 is True)
859
860
861 def backup_cronjob(disable=False):
862 """Generate the cronjob to backup with mongodbump."""
863 juju_log('Setting up cronjob')
864- config_data = config_get()
865+ config_data = config()
866 backupdir = config_data['backup_directory']
867 bind_ip = config_data['bind_ip']
868 cron_file = '/etc/cron.d/mongodb'
869@@ -915,18 +929,19 @@
870 ###############################################################################
871 # Hook functions
872 ###############################################################################
873+@hooks.hook('install')
874 def install_hook():
875 juju_log("Installing mongodb")
876 add_source(config('source'), config('key'))
877 apt_update(fatal=True)
878 apt_install(packages=['mongodb', 'python-yaml'], fatal=True)
879- return True
880-
881-
882+
883+
884+@hooks.hook('config-changed')
885 def config_changed():
886 juju_log("Entering config_changed")
887 print "Entering config_changed"
888- config_data = config_get()
889+ config_data = config()
890 print "config_data: ", config_data
891 mongodb_config = open(default_mongodb_config).read()
892
893@@ -1048,7 +1063,7 @@
894 juju_log("config_changed: Exceptions: %s" % str(e))
895
896 if mongos_pid is not None:
897- mongos_port = re.search('--port (\w+)', mongos_cmd_line).group(2)
898+ mongos_port = re.search('--port (\w+)', mongos_cmd_line).group(1)
899 disable_mongos(mongos_port)
900 enable_mongos(config_data['mongos_port'])
901 else:
902@@ -1058,6 +1073,7 @@
903 return(True)
904
905
906+@hooks.hook('start')
907 def start_hook():
908 juju_log("start_hook")
909 retVal = restart_mongod()
910@@ -1065,10 +1081,11 @@
911 return(retVal)
912
913
914+@hooks.hook('stop')
915 def stop_hook():
916 juju_log("stop_hook")
917 try:
918- retVal = service('mongodb', 'stop')
919+ retVal = service('stop', 'mongodb')
920 os.remove('/var/lib/mongodb/mongod.lock')
921 #FIXME Need to check if this is still needed
922 except Exception, e:
923@@ -1079,15 +1096,16 @@
924 return(retVal)
925
926
927+@hooks.hook('database-relation-joined')
928 def database_relation_joined():
929 juju_log("database_relation_joined")
930 my_hostname = unit_get('public-address')
931- my_port = config_get('port')
932- my_replset = config_get('replicaset')
933+ my_port = config('port')
934+ my_replset = config('replicaset')
935 juju_log("my_hostname: %s" % my_hostname)
936 juju_log("my_port: %s" % my_port)
937 juju_log("my_replset: %s" % my_replset)
938- return(relation_set(
939+ return(relation_set(relation_id(),
940 {
941 'hostname': my_hostname,
942 'port': my_port,
943@@ -1096,34 +1114,36 @@
944 }))
945
946
947+@hooks.hook('replicaset-relation-joined')
948 def replica_set_relation_joined():
949 juju_log("replica_set_relation_joined")
950 my_hostname = unit_get('public-address')
951- my_port = config_get('port')
952- my_replset = config_get('replicaset')
953+ my_port = config('port')
954+ my_replset = config('replicaset')
955 my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1]
956 juju_log("my_hostname: %s" % my_hostname)
957 juju_log("my_port: %s" % my_port)
958 juju_log("my_replset: %s" % my_replset)
959 juju_log("my_install_order: %s" % my_install_order)
960- return(enable_replset(my_replset) ==
961- restart_mongod() ==
962- relation_set(
963- {
964- 'hostname': my_hostname,
965- 'port': my_port,
966- 'replset': my_replset,
967- 'install-order': my_install_order,
968- 'type': 'replset',
969- }))
970-
971-
972+ enable_replset(my_replset)
973+ restart_mongod()
974+
975+ relation_set(relation_id(), {
976+ 'hostname': my_hostname,
977+ 'port': my_port,
978+ 'replset': my_replset,
979+ 'install-order': my_install_order,
980+ 'type': 'replset',
981+ })
982+
983+
984+@hooks.hook('replicaset-relation-changed')
985 def replica_set_relation_changed():
986 juju_log("replica_set_relation_changed")
987 my_hostname = unit_get('public-address')
988- my_port = config_get('port')
989+ my_port = config('port')
990 my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1]
991- my_replicaset_master = config_get('replicaset_master')
992+ my_replicaset_master = config('replicaset_master')
993
994 # If we are joining an existing replicaset cluster, just join and leave.
995 if my_replicaset_master != "auto":
996@@ -1135,44 +1155,46 @@
997 master_install_order = my_install_order
998
999 # Check the nodes in the relation to find the master
1000- for member in relation_list():
1001+ for member in relations_of_type('replica-set'):
1002+ member = member['__unit__']
1003 juju_log("replica_set_relation_changed: member: %s" % member)
1004 hostname = relation_get('hostname', member)
1005 port = relation_get('port', member)
1006- install_order = relation_get('install-order', member)
1007- juju_log("replica_set_relation_changed: install_order: %s" % install_order)
1008- if install_order is None:
1009- juju_log("replica_set_relation_changed: install_order is None. relation is not ready")
1010+ inst_ordr = relation_get('install-order', member)
1011+ juju_log("replica_set_relation_changed: install_order: %s" % inst_ordr)
1012+ if inst_ordr is None:
1013+ juju_log("replica_set_relation_changed: install_order is None."
1014+ " relation is not ready")
1015 break
1016- if int(install_order) < int(master_install_order):
1017+ if int(inst_ordr) < int(master_install_order):
1018 master_hostname = hostname
1019 master_port = port
1020- master_install_order = install_order
1021+ master_install_order = inst_ordr
1022
1023 # Initiate the replset
1024 init_replset("%s:%s" % (master_hostname, master_port))
1025
1026 # Add the rest of the nodes to the replset
1027- for member in relation_list():
1028- hostname = relation_get('hostname', member)
1029- port = relation_get('port', member)
1030+ for member in relations_of_type('replica-set'):
1031+ hostname = relation_get('hostname', member['__unit__'])
1032+ port = relation_get('port', member['__unit__'])
1033 if master_hostname != hostname:
1034 if hostname == my_hostname:
1035- subprocess.call(['mongo',
1036- '--eval',
1037- "rs.add(\"%s\")" % hostname])
1038+ subprocess.call(['mongo', '--eval',
1039+ "rs.add(\"%s\")" % hostname])
1040 else:
1041 join_replset("%s:%s" % (master_hostname, master_port),
1042- "%s:%s" % (hostname, port))
1043+ "%s:%s" % (hostname, port))
1044
1045 # Add this node to the replset ( if needed )
1046 if master_hostname != my_hostname:
1047 join_replset("%s:%s" % (master_hostname, master_port),
1048- "%s:%s" % (my_hostname, my_port))
1049-
1050- return(True)
1051-
1052-
1053+ "%s:%s" % (my_hostname, my_port))
1054+
1055+
1056+
1057+
1058+@hooks.hook('data-relation-joined')
1059 def data_relation_joined():
1060 juju_log("data_relation_joined")
1061
1062@@ -1182,6 +1204,7 @@
1063 }))
1064
1065
1066+@hooks.hook('data-relation-changed')
1067 def data_relation_changed():
1068 juju_log("data_relation_changed")
1069
1070@@ -1192,57 +1215,60 @@
1071 return(config_changed())
1072
1073
1074+@hooks.hook('data-relation-departed')
1075 def data_relation_departed():
1076 juju_log("data_relation_departed")
1077 return(config_changed())
1078
1079-
1080+@hooks.hook('configsvr-relation-joined')
1081 def configsvr_relation_joined():
1082 juju_log("configsvr_relation_joined")
1083 my_hostname = unit_get('public-address')
1084- my_port = config_get('config_server_port')
1085+ my_port = config('config_server_port')
1086 my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1]
1087- return(relation_set(
1088- {
1089- 'hostname': my_hostname,
1090- 'port': my_port,
1091- 'install-order': my_install_order,
1092- 'type': 'configsvr',
1093- }))
1094-
1095-
1096+ return(relation_set(relation_id(),
1097+ {
1098+ 'hostname': my_hostname,
1099+ 'port': my_port,
1100+ 'install-order': my_install_order,
1101+ 'type': 'configsvr',
1102+ }))
1103+
1104+
1105+@hooks.hook('configsvr-relation-changed')
1106 def configsvr_relation_changed():
1107 juju_log("configsvr_relation_changed")
1108- config_data = config_get()
1109+ config_data = config()
1110 my_port = config_data['config_server_port']
1111 disable_configsvr(my_port)
1112- retVal = enable_configsvr(config_data)
1113- juju_log("configsvr_relation_changed returns: %s" % retVal)
1114- return(retVal)
1115-
1116-
1117+
1118+
1119+@hooks.hook('mongos-cfg-relation-joined')
1120+@hooks.hook('mongos-relation-joined')
1121 def mongos_relation_joined():
1122 juju_log("mongos_relation_joined")
1123 my_hostname = unit_get('public-address')
1124- my_port = config_get('mongos_port')
1125+ my_port = config('mongos_port')
1126 my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1]
1127- return(relation_set(
1128- {
1129- 'hostname': my_hostname,
1130- 'port': my_port,
1131- 'install-order': my_install_order,
1132- 'type': 'mongos'
1133- }))
1134-
1135-
1136+ relation_set(relation_id(),
1137+ {
1138+ 'hostname': my_hostname,
1139+ 'port': my_port,
1140+ 'install-order': my_install_order,
1141+ 'type': 'mongos'
1142+ })
1143+
1144+
1145+@hooks.hook('mongos-cfg-relation-changed')
1146+@hooks.hook('mongos-relation-changed')
1147 def mongos_relation_changed():
1148 juju_log("mongos_relation_changed")
1149- config_data = config_get()
1150+ config_data = config()
1151 retVal = False
1152- for member in relation_list():
1153- hostname = relation_get('hostname', member)
1154- port = relation_get('port', member)
1155- rel_type = relation_get('type', member)
1156+ for member in relations_of_type('mongos-cfg'):
1157+ hostname = relation_get('hostname', member['__unit__'])
1158+ port = relation_get('port', member['__unit__'])
1159+ rel_type = relation_get('type', member['__unit__'])
1160 if hostname is None or port is None or rel_type is None:
1161 juju_log("mongos_relation_changed: relation data not ready.")
1162 break
1163@@ -1264,34 +1290,33 @@
1164 if mongos_ready():
1165 mongos_host = "%s:%s" % (
1166 unit_get('public-address'),
1167- config_get('mongos_port'))
1168+ config('mongos_port'))
1169 shard_command1 = "sh.addShard(\"%s:%s\")" % (hostname, port)
1170- retVal1 = mongo_client(mongos_host, shard_command1)
1171+ mongo_client(mongos_host, shard_command1)
1172 replicaset = relation_get('replset', member)
1173- shard_command2 = "sh.addShard(\"%s/%s:%s\")" % \
1174+ shard_command2 = "sh.addShard(\"%s/%s:%s\")" % \
1175 (replicaset, hostname, port)
1176- retVal2 = mongo_client(mongos_host, shard_command2)
1177- retVal = retVal1 is True and retVal2 is True
1178- else:
1179- juju_log("Not enough config server for mongos yet.")
1180- retVal = True
1181+ mongo_client(mongos_host, shard_command2)
1182+
1183+
1184 else:
1185 juju_log("mongos_relation_change: undefined rel_type: %s" %
1186- rel_type)
1187+ rel_type)
1188 return(False)
1189 juju_log("mongos_relation_changed returns: %s" % retVal)
1190- return(retVal)
1191-
1192-
1193+
1194+
1195+
1196+@hooks.hook('mongos-relation-broken')
1197 def mongos_relation_broken():
1198-# config_servers = load_config_servers(default_mongos_list)
1199-# for member in relation_list():
1200-# hostname = relation_get('hostname', member)
1201-# port = relation_get('port', member)
1202-# if '%s:%s' % (hostname, port) in config_servers:
1203-# config_servers.remove('%s:%s' % (hostname, port))
1204-# return(update_file(default_mongos_list, '\n'.join(config_servers)))
1205- return(True)
1206+ config_servers = load_config_servers(default_mongos_list)
1207+ for member in relations_of_type('mongos'):
1208+ hostname = relation_get('hostname', member)
1209+ port = relation_get('port', member)
1210+ if '%s:%s' % (hostname, port) in config_servers:
1211+ config_servers.remove('%s:%s' % (hostname, port))
1212+
1213+ update_file(default_mongos_list, '\n'.join(config_servers))
1214
1215
1216 def run(command, exit_on_error=True):
1217@@ -1318,7 +1343,7 @@
1218 #
1219 #------------------------------
1220 def volume_get_volid_from_volume_map():
1221- config_data = config_get()
1222+ config_data = config()
1223 volume_map = {}
1224 try:
1225 volume_map = yaml.load(config_data['volume-map'].strip())
1226@@ -1379,19 +1404,21 @@
1227 # None config state is invalid - we should not serve
1228 def volume_get_volume_id():
1229
1230+ config_data = config()
1231+
1232+
1233
1234 volid = volume_get_id_for_storage_subordinate()
1235 if volid:
1236 return volid
1237
1238- config_data = config_get()
1239 ephemeral_storage = config_data['volume-ephemeral-storage']
1240 volid = volume_get_volid_from_volume_map()
1241 juju_unit_name = os.environ['JUJU_UNIT_NAME']
1242 if ephemeral_storage in [True, 'yes', 'Yes', 'true', 'True']:
1243 if volid:
1244 juju_log(
1245- "volume-ephemeral-storage is True, but " +
1246+ "volume-ephemeral-storage is True, but
1247 "volume-map[{!r}] -> {}".format(juju_unit_name, volid))
1248 return None
1249 else:
1250@@ -1424,6 +1451,7 @@
1251 return None
1252 return output
1253
1254+
1255 #------------------------------------------------------------------------------
1256 # Core logic for permanent storage changes:
1257 # NOTE the only 2 "True" return points:
1258@@ -1435,7 +1463,7 @@
1259 # - manipulate /var/lib/mongodb/VERSION/CLUSTER symlink
1260 #------------------------------------------------------------------------------
1261 def config_changed_volume_apply():
1262- config_data = config_get()
1263+ config_data = config()
1264 data_directory_path = config_data["dbpath"]
1265 assert(data_directory_path)
1266 volid = volume_get_volume_id()
1267@@ -1548,57 +1576,6 @@
1268 ###############################################################################
1269 # Main section
1270 ###############################################################################
1271-if __name__ == '__main__':
1272- parser = argparse.ArgumentParser()
1273- parser.add_argument('-H', '--hook_name', dest='hook_name',
1274- help='hook to call')
1275- args = parser.parse_args()
1276- if args.hook_name is not None:
1277- hook_name = args.hook_name
1278- else:
1279- hook_name = os.path.basename(sys.argv[0])
1280-
1281- if hook_name == "install":
1282- retVal = install_hook()
1283- elif hook_name == "config-changed":
1284- retVal = config_changed()
1285- elif hook_name == "start":
1286- retVal = start_hook()
1287- elif hook_name == "stop":
1288- retVal = stop_hook()
1289- elif hook_name == "database-relation-joined":
1290- retVal = database_relation_joined()
1291- elif hook_name == "replica-set-relation-joined":
1292- retVal = replica_set_relation_joined()
1293- elif hook_name == "replica-set-relation-changed":
1294- retVal = replica_set_relation_changed()
1295- elif hook_name == "configsvr-relation-joined":
1296- retVal = configsvr_relation_joined()
1297- elif hook_name == "configsvr-relation-changed":
1298- retVal = configsvr_relation_changed()
1299- elif hook_name == "mongos-cfg-relation-joined":
1300- retVal = mongos_relation_joined()
1301- elif hook_name == "mongos-cfg-relation-changed":
1302- retVal = mongos_relation_changed()
1303- elif hook_name == "mongos-cfg-relation-broken":
1304- retVal = mongos_relation_broken()
1305- elif hook_name == "mongos-relation-joined":
1306- retVal = mongos_relation_joined()
1307- elif hook_name == "mongos-relation-changed":
1308- retVal = mongos_relation_changed()
1309- elif hook_name == "mongos-relation-broken":
1310- retVal = mongos_relation_broken()
1311- elif hook_name == "data-relation-joined":
1312- retVal = data_relation_joined()
1313- elif hook_name == "data-relation-changed":
1314- retVal = data_relation_changed()
1315- elif hook_name == "data-relation-departed":
1316- retVal = data_relation_departed()
1317- else:
1318- print "Unknown hook"
1319- retVal = False
1320-
1321- if retVal is True:
1322- sys.exit(0)
1323- else:
1324- sys.exit(1)
1325+if __name__ == "__main__":
1326+ # execute a hook based on the name the program is called by
1327+ hooks.execute(sys.argv)
1328\ No newline at end of file
1329
1330=== modified file 'hooks/install'
1331--- hooks/install 2013-11-25 19:48:00 +0000
1332+++ hooks/install 1970-01-01 00:00:00 +0000
1333@@ -1,5 +0,0 @@
1334-#!/bin/bash
1335-
1336-sudo apt-get install "python-yaml"
1337-
1338-hooks/hooks.py -H install
1339
1340=== target is u'hooks.py'
1341=== modified file 'metadata.yaml'
1342--- metadata.yaml 2014-06-18 11:13:54 +0000
1343+++ metadata.yaml 2014-07-29 16:34:12 +0000
1344@@ -1,6 +1,9 @@
1345 name: mongodb
1346-maintainer: Juan Negron <juan.negron@canonical.com>
1347-summary: MongoDB (from humongous) is an open-source document database
1348+summary: An open-source document database, and the leading NoSQL database
1349+maintainers:
1350+ - Juan Negron <juan.negron@canonical.com>
1351+ - Marco Ceppi <marco@ceppi.net>
1352+ - Charles Butler <chuck@dasroot.net>
1353 description: |
1354 MongoDB is a high-performance, open source, schema-free document-
1355 oriented data store that's easy to deploy, manage and use. It's
1356
1357=== added file 'tests/200_relate_ceilometer.test'
1358--- tests/200_relate_ceilometer.test 1970-01-01 00:00:00 +0000
1359+++ tests/200_relate_ceilometer.test 2014-07-29 16:34:12 +0000
1360@@ -0,0 +1,43 @@
1361+#!/usr/bin/env python3
1362+
1363+import amulet
1364+import pdb
1365+
1366+class TestDeploy(object):
1367+
1368+ def __init__(self, time=2500):
1369+ # Attempt to load the deployment topology from a bundle.
1370+ self.deploy = amulet.Deployment(series="trusty")
1371+
1372+ # If something errored out, attempt to continue by
1373+ # manually specifying a standalone deployment
1374+ self.deploy.add('mongodb')
1375+ self.deploy.add('ceilometer', 'cs:trusty/ceilometer')
1376+ # send blank configs to finalize the objects in the deployment map
1377+ self.deploy.configure('mongodb', {})
1378+ self.deploy.configure('ceilometer', {})
1379+
1380+ self.deploy.relate('mongodb:database', 'ceilometer:shared-db')
1381+
1382+ try:
1383+ self.deploy.setup(time)
1384+ self.deploy.sentry.wait(time)
1385+ except:
1386+ amulet.raise_status(amulet.FAIL, msg="Environment standup timeout")
1387+ # sentry = self.deploy.sentry
1388+
1389+ def run(self):
1390+ for test in dir(self):
1391+ if test.startswith('test_'):
1392+ getattr(self, test)()
1393+
1394+ def test_mongo_relation(self):
1395+ unit = self.deploy.sentry.unit['ceilometer/0']
1396+ mongo = self.deploy.sentry.unit['mongodb/0'].info['public-address']
1397+ cont = unit.file_contents('/etc/ceilometer/ceilometer.conf')
1398+ if mongo not in cont:
1399+ amulet.raise_status(amulet.FAIL, "Unable to verify ceilometer cfg")
1400+
1401+if __name__ == '__main__':
1402+ runner = TestDeploy()
1403+ runner.run()

Subscribers

People subscribed via source and target branches