Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
289 lines (218 sloc) 7.8 KB
import os
import warnings
import subprocess
import platform
import json
from charmhelpers.fetch import (
apt_install,
apt_purge,
apt_update,
)
from charmhelpers.core.host import (
lsb_release,
)
def _as_text(bytestring):
"""Naive conversion of subprocess output to Python string"""
return bytestring.decode("utf-8", "replace")
# FIXME: Do this as a JSONDecoder, if possible
def clean_json(s):
return _as_text(s).replace('ISODate(',
'').replace(', 1',
'').replace('Timestamp(',
'').replace(')', '')
def apt_key(key_id):
subprocess.check_call(['apt-key', 'adv', '--keyserver',
'hkps://keyserver.ubuntu.com', '--recv', key_id])
class MongoDB(object):
upstream_list = '/etc/apt/sources.list.d/mongodb.list'
config_file = '/etc/mongodb.conf'
# Juju configuration options used for MongoDB
config_options = ['dbpath', 'logpath', 'logappend', 'bind_ip', 'port',
'journal', 'cpu', 'auth', 'verbose', 'objcheck', 'quota',
'oplog', 'nocursors', 'nohints', 'noscripting',
'notablescans', 'noprealloc', 'nssize',
'oplogSize', 'opIdMem', 'replicaset']
config_map = {
# JUJU CFG: MONGO CFG
'replicaset': 'replSet',
}
def __init__(self, source, version=None):
if source not in self.package_map.keys():
raise Exception('{0} is not a valid source'.format(source))
self.source = source
self.version = version
def install(self):
apt_install(self.packages())
def uninstall(self):
apt_purge(self.packages())
subprocess.check_call([
'apt-get',
'autoremove',
'--purge',
'--assume-yes'
])
def configure(self, config):
cfg = {
self.config_map.get(k, k): v
for k, v in iter(config.items()) if v and k in self.config_options
}
self._render_config(cfg)
def packages(self):
return [p.format(self.version) for p in self.package_map[self.source]]
def add_upstream(self):
with open(self.upstream_list, 'w') as f:
distrib = lsb_release()['DISTRIB_CODENAME']
f.write(self.upstream_repo.format(distrib))
def _render_config(self, cfg):
with open(self.config_file, 'w') as f:
f.write('\n'.join(
sorted(['%s = %s' % (k, v) for (k, v) in cfg.items()])))
def run(self, cmd):
"""Run a mongo command returns result of command as obj"""
p = subprocess.Popen(["mongo", "--quiet"], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate(input=cmd.encode('UTF-8'))
if p.returncode:
raise IOError("mongo command failed {!r}:\n"
"{}".format(cmd, _as_text(err)))
return json.loads(clean_json(out))
def init_replicaset(self):
r = self.run('rs.initiate()')
if r['ok']:
return True
if r['errmsg'] == 'already initialized':
return True
return False
class MongoDB20(MongoDB):
package_map = {
'upstream': [
'mongodb-10gen={}',
],
'archive': [
'mongodb-server',
],
}
upstream_repo = ('deb http://downloads-distro.mongodb.org'
'/repo/ubuntu-upstart dist 10gen')
def install(self):
if self.source == 'upstream':
self.add_upstream()
apt_update()
super(MongoDB20, self).install()
def add_upstream(self):
apt_key('7F0CEB10')
super(MongoDB20, self).add_upstream()
def uninstall(self):
super(MongoDB20, self).uninstall()
if os.path.exists(self.upstream_list):
os.unlink(self.upstream_list)
class MongoDB22(MongoDB20):
pass
class MongoDB24(MongoDB20):
pass
class MongoDB26(MongoDB20):
package_map = {
'upstream': [
'mongodb-org-server={}',
'mongodb-org-shell={}',
'mongodb-org-tools={}',
],
'archive': [
'mongodb-server',
],
}
class MongoDB30(MongoDB):
package_map = {
'upstream': [
'mongodb-org-server={}',
'mongodb-org-shell={}',
'mongodb-org-tools={}',
],
}
upstream_repo = ('deb http://repo.mongodb.org/apt/ubuntu '
'{0}/mongodb-org/3.0 multiverse')
def install(self):
self.add_upstream()
apt_update()
super(MongoDB30, self).install()
def uninstall(self):
super(MongoDB30, self).uninstall()
if os.path.exists(self.upstream_list):
os.unlink(self.upstream_list)
class MongoDB31(MongoDB30):
package_map = {
'upstream': [
'mongodb-org-unstable-server={}',
'mongodb-org-unstable-shell={}',
'mongodb-org-unstable-tools={}',
],
}
upstream_repo = ('deb http://repo.mongodb.org/apt/ubuntu '
'{0}/mongodb-org/3.1 multiverse')
class MongoDB32(MongoDB30):
upstream_repo = ('deb http://repo.mongodb.org/apt/ubuntu '
'{0}/mongodb-org/3.2 multiverse')
def add_upstream(self):
apt_key('EA312927')
super(MongoDB32, self).add_upstream()
class MongoDBzSeries(MongoDB32):
package_map = {
'archive': [
'mongodb-server',
],
}
upstream_repo = ('deb http://ppa.launchpad.net/ubuntu-s390x-community'
'/mongodb/ubuntu {0} main')
def __init__(self, source, version=None):
lsb = lsb_release()
year = lsb['DISTRIB_RELEASE'].split('.')[0]
if int(year) < 16:
distrib = lsb['DISTRIB_CODENAME']
raise Exception('{0} is not deployable on zSeries'.format(distrib))
super(MongoDBzSeries, self).__init__(source, version)
def add_upstream(self):
apt_key('3427B191')
super(MongoDBzSeries, self).add_upstream()
def installed():
return os.path.isfile('/usr/bin/mongo')
def version():
if not installed():
return None
return subprocess.check_output(
['/usr/bin/mongo', '--version'],
stderr=subprocess.STDOUT).decode('UTF-8').split(': ')[1]
_distro_map = {
'precise': MongoDB20,
'trusty': MongoDB24,
'xenial': MongoDB26,
}
_arch_map = {
's390x': MongoDBzSeries,
}
def mongodb(ver=None):
if not ver and installed():
ver = version()
if platform.machine() in _arch_map:
return _arch_map[platform.machine()]('archive')
if not ver or ver == 'archive':
distro = lsb_release()['DISTRIB_CODENAME']
if distro not in _distro_map.keys():
_msg = 'Unknown distribution: {0}. Please deploy only on: {1}'
raise Exception(_msg.format(distro, _distro_map.keys()))
return _distro_map[distro]('archive')
def subclasses(cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in subclasses(s)]
def search(version):
# Does a count down search of version until next lowest match. So
# long as it doesn't drop below a major version things should be good.
major, minor = [c for c in version.replace('.', '')[:2]]
minor_range = reversed(range(0, int(minor) + 1))
needles = ['MongoDB{0}{1}'.format(major, v) for v in minor_range]
for needle in needles:
for m in subclasses(MongoDB):
if m.__name__ == needle:
return m('upstream', version)
warnings.warn('No viable major version found')
return None
return search(ver)