diff options
| author | Mark Mims <mark.mims@canonical.com> | 2012-08-21 16:27:54 -0600 |
|---|---|---|
| committer | Mark Mims <mark.mims@canonical.com> | 2012-08-21 16:27:54 -0600 |
| commit | 17f55bf9c56bb04199922f1bc37737513fa59e58 (patch) | |
| tree | 6285d223f2cee962fb693735f0b3fe1c6e482665 | |
| parent | f0c06e500182eed40e9811562ddc31985f69f7a3 (diff) | |
| parent | d8517d01c217848193f4940ba675c0a59685d2be (diff) | |
merging lp:~negronjl/charms/precise/mongodb/trunk
| -rw-r--r-- | README | 193 | ||||
| -rw-r--r-- | config.yaml | 136 | ||||
| l---------[-rwxr-xr-x] | hooks/config-changed | 26 | ||||
| l--------- | hooks/configsvr-relation-changed | 1 | ||||
| l--------- | hooks/configsvr-relation-joined | 1 | ||||
| l---------[-rwxr-xr-x] | hooks/database-relation-joined | 14 | ||||
| -rwxr-xr-x | hooks/hooks.py | 1168 | ||||
| l---------[-rwxr-xr-x] | hooks/install | 84 | ||||
| l--------- | hooks/mongos-relation-broken | 1 | ||||
| l--------- | hooks/mongos-relation-changed | 1 | ||||
| l--------- | hooks/mongos-relation-joined | 1 | ||||
| l---------[-rwxr-xr-x] | hooks/replica-set-relation-changed | 94 | ||||
| l---------[-rwxr-xr-x] | hooks/replica-set-relation-joined | 29 | ||||
| l---------[-rwxr-xr-x] | hooks/start | 7 | ||||
| l---------[-rwxr-xr-x] | hooks/stop | 11 | ||||
| -rw-r--r-- | metadata.yaml | 21 |
16 files changed, 1513 insertions, 275 deletions
@@ -40,20 +40,150 @@ The MongoDB charm allows for certain values to be configurable via the config.ya A sample of the default settings of the config.yaml file at the time of writing are as follows: options: + dbpath: + default: "/var/lib/mongodb" + type: string + description: The path where the data files will be kept. + logpath: + default: "/var/log/mongodb/mongodb.log" + type: string + description: The path where to send log data. + logappend: + default: True + type: boolean + description: Append log entries to existing log file + bind_ip: + default: "all" + type: string + description: IP address that mongodb should listen for connections. + port: + default: 27017 + type: int + description: Default MongoDB port + journal: + default: True + type: boolean + description: Enable journaling, http://www.mongodb.org/display/DOCS/Journaling + cpu: + default: False + type: boolean + description: Enables periodic logging of CPU utilization and I/O wait + auth: + default: False + type: boolean + description: Turn on/off security + verbose: + default: False + type: boolean + description: Verbose logging output + objcheck: + default: False + type: boolean + description: Inspect all client data for validity on receipt (useful for developing drivers) + quota: + default: False + type: boolean + description: Enable db quota management + diaglog: + default: 0 + type: int + description: Set oplogging level where n is 0=off (default), 1=W, 2=R, 3=both, 7=W+some reads + nocursors: + default: False + type: boolean + description: Diagnostic/debugging option + nohints: + default: False + type: boolean + description: Ignore query hints + noscripting: + default: False + type: boolean + description: Turns off server-side scripting. This will result in greatly limited functionality + notablescan: + default: False + type: boolean + description: Turns off table scans. Any query that would do a table scan fails + noprealloc: + default: False + type: boolean + description: Disable data file preallocation + nssize: + default: "default" + type: string + description: Specify .ns file size for new databases + mms-token: + default: "disabled" + type: string + description: Accout token for Mongo monitoring server + mms-name: + default: "disabled" + type: string + description: Server name for Mongo monitoring server + mms-interval: + default: "disabled" + type: string + description: Ping interval for Mongo monitoring server ( in number of seconds ) + autoresync: + default: False + type: boolean + description: Automatically resync if slave data is stale + oplogSize: + default: "default" + type: string + description: Custom size for replication operation log + opIdMem: + default: "default" + type: string + description: Size limit for in-memory storage of op ids replicaset: default: myset type: string description: Name of the replica set web_admin_ui: - default: yes - type: string - description: Replica Set Admin UI ( yes / no ) + default: True + type: boolean + description: Replica Set Admin UI ( accessible via default_port + 1000 ) replicaset_master: default: auto type: string - description: Replica Set master ( optional ). Possible values are 'auto' for automatic - detection based on install time or 'host:port' to connect to 'host' on - 'port' and register as a member. + description: Replica Set master ( optional ). Possible values are 'auto' for automatic detection based on install time or 'host:port' to connect to 'host' on 'port' and register as a member. + master: + default: "self" + type: string + description: Who is the master DB. If not "self", put the Master DB here as "host:port" + config_server_port: + default: 27019 + type: int + description: Port number to use for the config-server + config_server_dbpath: + default: "/mnt/var/lib/mongodb/configsvr" + type: string + description: The path where the config server data files will be kept. + config_server_logpath: + default: "/mnt/var/log/mongodb/configsvr.log" + type: string + description: The path where to send config server log data. + arbiter: + default: "disabled" + type: string + description: Enable arbiter mode. Possible values are 'disabled' for no arbiter, 'enable' to become an arbiter or 'host:port' to declare another host as an arbiter. replicaset_master must be set for this option to work. + mongos_logpath: + default: "/mnt/var/log/mongodb/mongos.log" + type: string + description: The path where to send log data from the mongo router. + mongos_port: + default: 27021 + type: int + description: Port number to use for the mongo router + extra_config_options: + default: "none" + type: string + description: Extra options ( comma separated ) to be included ( at the end ) in the mongodb.conf file. + extra_daemon_options: + default: "none" + type: string + description: Extra options ( exactly as you would type them in the command line ) to be added via the command line to the mongodb daemon Where: @@ -74,6 +204,8 @@ Where: - ie: hostname ( will connect to hostname on the default port of 27017 ) - ie: hostname:port ( will connect to hostname on port number <port> ) +Most of the options in config.yaml have been modeled after the default configuration file for mongodb (normally in /etc/mongodb.conf) and should be familiar to most mongodb admins. Each option in this charm have a brief description of what it does. + Deployment ========== @@ -149,6 +281,55 @@ Add multiple nodes to your replicaset We now have a working MongoDB replica-set. +Sharding +-------- + +According the the mongodb documentation found on their website (http://docs.mongodb.org/manual/tutorial/deploy-shard-cluster/), one way of deploying a Shard Cluster is as follows: + +- deploy config servers +- deploy a mongo shell (mongos) +- deploy shards +- connect the config servers to the mongo shell +- add the shards to the mongo shell + + +Using Juju we can deploy a sharded cluster using the following commands: + +- Bootstrap the environment +--- juju bootstrap + +- Config Servers ( we'll deploy 3 of them ) +--- juju deploy mongodb configsvr + +- Mongo Shell ( We just deploy one for now ) +--- juju deploy mongodb mongos + +- Shards ( We'll deploy three replica-sets ) +--- juju deploy mongodb shard1 -n3 +--- juju deploy mongodb shard2 -n3 +--- juju deploy mongodb shard3 -n3 + +- Connect the Config Servers to the Mongo shell (mongos) +--- juju add-relation mongos:mongos configsvr:configsvr + +- Connect each Shard to the Mongo shell (mongos) +--- juju add-realtion mongos:mongos shard1:database +--- juju add-realtion mongos:mongos shard2:database +--- juju add-realtion mongos:mongos shard3:database + +With the above commands, we should now have a three replica-set sharded cluster running. +Using the default configuration, here are some details of our sharded cluster: +- mongos is running on port 27021 +- configsvr is running on port 27019 +- the shards are running on the default mongodb port of 27017 +- The web admin is turned on by default and accessible with your browser on port 28017 on each of the shards. + +To verify that your sharded cluster is running, connect to the mongo shell and run sh.status(): +- mongo --host <mongos_host>:<mongos_port> +- run sh.status() +You should see your the hosts for your shards in the status output. + + Troubleshooting =============== diff --git a/config.yaml b/config.yaml index 56fd97c..969d45e 100644 --- a/config.yaml +++ b/config.yaml @@ -1,17 +1,145 @@ options: - default_port: + dbpath: + default: "/var/lib/mongodb" + type: string + description: The path where the data files will be kept. + logpath: + default: "/var/log/mongodb/mongodb.log" + type: string + description: The path where to send log data. + logappend: + default: True + type: boolean + description: Append log entries to existing log file + bind_ip: + default: "all" + type: string + description: IP address that mongodb should listen for connections. + port: default: 27017 type: int description: Default MongoDB port + journal: + default: True + type: boolean + description: Enable journaling, http://www.mongodb.org/display/DOCS/Journaling + cpu: + default: False + type: boolean + description: Enables periodic logging of CPU utilization and I/O wait + auth: + default: False + type: boolean + description: Turn on/off security + verbose: + default: False + type: boolean + description: Verbose logging output + objcheck: + default: False + type: boolean + description: Inspect all client data for validity on receipt (useful for developing drivers) + quota: + default: False + type: boolean + description: Enable db quota management + diaglog: + default: 0 + type: int + description: Set oplogging level where n is 0=off (default), 1=W, 2=R, 3=both, 7=W+some reads + nocursors: + default: False + type: boolean + description: Diagnostic/debugging option + nohints: + default: False + type: boolean + description: Ignore query hints + noscripting: + default: False + type: boolean + description: Turns off server-side scripting. This will result in greatly limited functionality + notablescan: + default: False + type: boolean + description: Turns off table scans. Any query that would do a table scan fails + noprealloc: + default: False + type: boolean + description: Disable data file preallocation + nssize: + default: "default" + type: string + description: Specify .ns file size for new databases + mms-token: + default: "disabled" + type: string + description: Accout token for Mongo monitoring server + mms-name: + default: "disabled" + type: string + description: Server name for Mongo monitoring server + mms-interval: + default: "disabled" + type: string + description: Ping interval for Mongo monitoring server ( in number of seconds ) + autoresync: + default: False + type: boolean + description: Automatically resync if slave data is stale + oplogSize: + default: "default" + type: string + description: Custom size for replication operation log + opIdMem: + default: "default" + type: string + description: Size limit for in-memory storage of op ids replicaset: default: myset type: string description: Name of the replica set web_admin_ui: - default: "yes" - type: string - description: Replica Set Admin UI ( yes / no ) + default: True + type: boolean + description: Replica Set Admin UI ( accessible via default_port + 1000 ) replicaset_master: default: auto type: string description: Replica Set master ( optional ). Possible values are 'auto' for automatic detection based on install time or 'host:port' to connect to 'host' on 'port' and register as a member. + master: + default: "self" + type: string + description: Who is the master DB. If not "self", put the Master DB here as "host:port" + config_server_port: + default: 27019 + type: int + description: Port number to use for the config-server + config_server_dbpath: + default: "/mnt/var/lib/mongodb/configsvr" + type: string + description: The path where the config server data files will be kept. + config_server_logpath: + default: "/mnt/var/log/mongodb/configsvr.log" + type: string + description: The path where to send config server log data. + arbiter: + default: "disabled" + type: string + description: Enable arbiter mode. Possible values are 'disabled' for no arbiter, 'enable' to become an arbiter or 'host:port' to declare another host as an arbiter. replicaset_master must be set for this option to work. + mongos_logpath: + default: "/mnt/var/log/mongodb/mongos.log" + type: string + description: The path where to send log data from the mongo router. + mongos_port: + default: 27021 + type: int + description: Port number to use for the mongo router + extra_config_options: + default: "none" + type: string + description: Extra options ( comma separated ) to be included ( at the end ) in the mongodb.conf file. + extra_daemon_options: + default: "none" + type: string + description: Extra options ( exactly as you would type them in the command line ) to be added via the command line to the mongodb daemon diff --git a/hooks/config-changed b/hooks/config-changed index df92ffb..f94593a 100755..120000 --- a/hooks/config-changed +++ b/hooks/config-changed @@ -1,25 +1 @@ -#!/bin/bash -# This must be renamed to the name of the relation. The goal here is to -# affect any change needed by relationships being formed -# This script should be idempotent. - -set -ux - -DEFAULT_REPLSET_NAME=`config-get replicaset` -REPLICASET_MASTER=`config-get replicaset_master` -HOSTNAME=`unit-get public-address` - -############################################################################################################ -# Are we connecting to an existing replica set? -############################################################################################################ -if [ "${REPLICASET_MASTER}" != "auto" ]; then - grep "${DEFAULT_REPLSET_NAME}" /etc/init/mongodb.conf - if [ $? -ne 0 ];then - sed -i -e "s/ -- / -- --replSet ${DEFAULT_REPLSET_NAME} /" /etc/init/mongodb.conf - service mongodb stop - rm -f /var/lib/mongodb/mongod.lock - service mongodb start - fi - mongo --host ${REPLICASET_MASTER} --eval "rs.add(\""${HOSTNAME}"\")" -fi - +./hooks.py \ No newline at end of file diff --git a/hooks/configsvr-relation-changed b/hooks/configsvr-relation-changed new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/configsvr-relation-changed @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/configsvr-relation-joined b/hooks/configsvr-relation-joined new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/configsvr-relation-joined @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/database-relation-joined b/hooks/database-relation-joined index 23ead04..f94593a 100755..120000 --- a/hooks/database-relation-joined +++ b/hooks/database-relation-joined @@ -1,13 +1 @@ -#!/bin/bash -# This must be renamed to the name of the relation. The goal here is to -# affect any change needed by relationships being formed -# This script should be idempotent. - -set -ux - -HOSTNAME=`unit-get public-address` -REPLICASET=`config-get replicaset` - -relation-set hostname=${HOSTNAME} replset=${REPLICASET} - -echo $JUJU_REMOTE_UNIT joined +./hooks.py \ No newline at end of file diff --git a/hooks/hooks.py b/hooks/hooks.py new file mode 100755 index 0000000..f472197 --- /dev/null +++ b/hooks/hooks.py @@ -0,0 +1,1168 @@ +#!/usr/bin/env python +''' +Created on Aug 1, 2012 + +@author: negronjl +''' + +import os +import sys +import json +import subprocess +import re +import signal +import socket +import time + + +############################################################################### +# Supporting functions +############################################################################### + + +#------------------------------------------------------------------------------ +# juju_log: calls juju-log and records the message defined by the message +# variable +#------------------------------------------------------------------------------ +def juju_log(message=None): + return (subprocess.call(['juju-log', str(message)]) == 0) + + +#------------------------------------------------------------------------------ +# service: Analogous to calling service on the command line to start/stop +# and get status of a service/daemon. +# Parameters: +# service_name: The name of the service to act on. +# service_action: The action (start, stop, status, etc.) +# Returns: True if the command was successfully executed or False on +# error. +#------------------------------------------------------------------------------ +def service(service_name=None, service_action=None): + juju_log("service: %s, action: %s" % (service_name, service_action)) + if service_name is not None and service_action is not None: + retVal = subprocess.call( + ["service", service_name, service_action]) == 0 + else: + retVal = False + juju_log("service %s %s returns: %s" % \ + (service_name, service_action, retVal)) + return(retVal) + + +#------------------------------------------------------------------------------ +# unit_get: Convenience function wrapping the juju command unit-get +# Parameter: +# setting_name: The setting to get out of unit_get +# Returns: The requested information or None on error +#------------------------------------------------------------------------------ +def unit_get(setting_name=None): + juju_log("unit_get: %s" % setting_name) + try: + cmd_line = ['unit-get', '--format=json'] + if setting_name is not None: + cmd_line.append(setting_name) + unit_data = json.loads(subprocess.check_output(cmd_line)) + except Exception, e: + subprocess.call(['juju-log', str(e)]) + unit_data = None + finally: + juju_log("unit_get %s returns: %s" % (setting_name, unit_data)) + return(unit_data) + + +#------------------------------------------------------------------------------ +# config_get: Returns a dictionary containing all of the config information +# Optional parameter: scope +# scope: limits the scope of the returned configuration to the +# desired config item. +#------------------------------------------------------------------------------ +def config_get(scope=None): + juju_log("config_get: %s" % scope) + try: + config_cmd_line = ['config-get'] + if scope is not None: + config_cmd_line.append(scope) + config_cmd_line.append('--format=json') + config_data = json.loads(subprocess.check_output(config_cmd_line)) + except Exception, e: + juju_log(str(e)) + config_data = None + finally: + juju_log("config_get: %s returns: %s" % (scope, config_data)) + return(config_data) + + +#------------------------------------------------------------------------------ +# relation_get: Returns a dictionary containing the relation information +# Optional parameters: scope, relation_id +# scope: limits the scope of the returned data to the +# desired item. +# unit_name: limits the data ( and optionally the scope ) +# to the specified unit +# relation_id: specify relation id for out of context usage. +#------------------------------------------------------------------------------ +def relation_get(scope=None, unit_name=None, relation_id=None): + juju_log("relation_get: scope: %s, unit_name: %s, relation_id: %s" % + (scope, unit_name, relation_id)) + try: + relation_cmd_line = ['relation-get', '--format=json'] + if relation_id is not None: + relation_cmd_line.extend(('-r', relation_id)) + if scope is not None: + relation_cmd_line.append(scope) + else: + relation_cmd_line.append('') + if unit_name is not None: + relation_cmd_line.append(unit_name) + relation_data = json.loads(subprocess.check_output(relation_cmd_line)) + except Exception, e: + juju_log(str(e)) + relation_data = None + finally: + juju_log("relation_get returns: %s" % relation_data) + return(relation_data) + + +#------------------------------------------------------------------------------ +# relation_set: Convenience function wrapping the juju command relation-set +# Parameters: +# key_value_pairs: A dictionary containing the key/value pairs +# to be set. +# Optional Parameter: +# relation_id: The relation id to use +# Returns: True on success or False on failure +#------------------------------------------------------------------------------ +def relation_set(key_value_pairs=None, relation_id=None): + juju_log("relation_set: kv: %s, relation_id: %s" % + (key_value_pairs, relation_id)) + if key_value_pairs is None or type(key_value_pairs) != type({}): + juju_log("relation_set: Invalid key_value_pais.") + return(False) + try: + relation_cmd_line = ['relation-set', '--format=json'] + if relation_id is not None: + relation_cmd_line.append('-r %s' % relation_id) + for (key, value) in key_value_pairs.items(): + relation_cmd_line.append('%s=%s' % (key, value)) + retVal = (subprocess.call(relation_cmd_line) == 0) + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + juju_log("relation_set returns: %s" % retVal) + return(retVal) + + +def relation_list(relation_id=None): + juju_log("relation_list: relation_id: %s" % relation_id) + try: + relation_cmd_line = ['relation-list', '--format=json'] + if relation_id is not None: + relation_cmd_line.append('-r %s' % relation_id) + relation_data = json.loads(subprocess.check_output(relation_cmd_line)) + except Exception, e: + juju_log(str(e)) + relation_data = None + finally: + juju_log("relation_id %s returns: %s" % (relation_id, relation_data)) + return(relation_data) + + +#------------------------------------------------------------------------------ +# apt_get_install( package ): Installs a package +#------------------------------------------------------------------------------ +def apt_get_install(packages=None): + juju_log("apt_get_install: %s" % packages) + if packages is None: + return(False) + if type(packages) == type(' '): + packages = [packages] + cmd_line = ['apt-get', '-y', 'install', '-qq'] + cmd_line.extend(packages) + retVal = subprocess.call(cmd_line) == 0 + juju_log("apt_get_install %s returns: %s" % (packages, retVal)) + return(retVal) + + +#------------------------------------------------------------------------------ +# open_port: Convenience function to open a port in juju to +# expose a service +#------------------------------------------------------------------------------ +def open_port(port=None, protocol="TCP"): + juju_log("open_port: port: %d protocol: %s" % (int(port), protocol)) + if port is None: + retVal = False + else: + retVal = subprocess.call(['/usr/bin/open-port', "%d/%s" % \ + (int(port), protocol)]) == 0 + juju_log("open_port %d/%s returns: %s" % (int(port), protocol, retVal)) + return(retVal) + + +#------------------------------------------------------------------------------ +# close_port: Convenience function to close a port in juju to +# unexpose a service +#------------------------------------------------------------------------------ +def close_port(port=None, protocol="TCP"): + juju_log("close_port: port: %d protocol: %s" % (int(port), protocol)) + if port is None: + retVal = False + else: + retVal = subprocess.call(['/usr/bin/close-port', "%d/%s" % \ + (int(port), protocol)]) == 0 + juju_log("close_port %d/%s returns: %s" % (int(port), protocol, retVal)) + return(retVal) + + +def port_check(host=None, port=None, protocol='TCP'): + if host is None or port is None: + juju_log("port_check: host and port must be defined.") + return(False) + if protocol.upper() == 'TCP': + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + elif protocol.upper() == 'UDP': + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + else: + juju_log("port_check: Unrecognized protocol %s" % protocol) + return(False) + try: + s.connect((host, int(port))) + s.shutdown(socket.SHUT_RDWR) + juju_log("port_check: %s:%s/%s is open" % (host, port, protocol)) + return(True) + except Exception, e: + juju_log("port_check: Unable to connect to %s:%s/%s." % + (host, port, protocol)) + juju_log("port_check: Exception: %s" % str(e)) + return(False) + + +#------------------------------------------------------------------------------ +# update_service_ports: Convenience function that evaluate the old and new +# service ports to decide which ports need to be +# opened and which to close +#------------------------------------------------------------------------------ +def update_service_ports(old_service_ports=None, new_service_ports=None): + juju_log("update_service_ports") + if old_service_ports is None or new_service_ports is None: + return(None) + for port in old_service_ports: + if port not in new_service_ports: + juju_log("closing port: %d" % int(port)) + close_port(port) + for port in new_service_ports: + if port not in old_service_ports: + juju_log("opening port: %d" % int(port)) + open_port(port) + + +def regex_sub(pat_replace=None, data=None): + juju_log("regex_sub") + if not pat_replace or not data: + raise Exception("pat_replace or data not defined") + if type(pat_replace) != type([]): + raise Exception("pat_replace must be a list of pat, replace tuples") + new_data = data + for (pattern, replace) in pat_replace: + new_data = re.sub(pattern, replace, data, 0, re.MULTILINE) + return(new_data) + + +def update_file(filename=None, new_data=None, old_data=None): + juju_log("update_file: %s" % filename) + if filename is None or new_data is None: + retVal = False + try: + if old_data != new_data: + with open(filename, 'w') as f: + f.write(new_data) + retVal = True + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + juju_log("update_file %s returns: %s" % (filename, retVal)) + return(retVal) + + +def process_check(pid=None): + try: + if pid is not None: + cmd_line = subprocess.check_output('ps -p %d -o cmd h' % + int(pid), shell=True) + retVal = (pid, cmd_line) + else: + juju_log("process_check: pid not defined.") + retVal = (None, None) + except Exception, e: + juju_log("process_check exception: %s" % str(e)) + retVal = (None, None) + finally: + juju_log("process_check returs pid: %s and cmd_line: %s" % + retVal) + return(retVal) + + +def process_check_pidfile(pidfile=None): + if pidfile is not None and os.path.exists(pidfile): + return(process_check(open(pidfile).read())) + else: + juju_log("process_check_pidfile: undefined or non-existant pidfile.") + return((None, None)) + + +############################################################################### +# Global variables +############################################################################### +hook_name = os.path.basename(sys.argv[0]) +default_mongodb_config = "/etc/mongodb.conf" +default_mongodb_init_config = "/etc/init/mongodb.conf" +default_mongos_list = "/etc/mongos.list" +default_wait_for = 20 +default_max_tries = 20 + + +############################################################################### +# Charm support functions +############################################################################### +def mongodb_conf(config_data=None): + if config_data is None: + return(None) + config = [] + + # header + config.append("# mongodb.conf") + config.append("") + + # dbpath + # Create the directory if not there already + subprocess.call(['mkdir', '-p', '%s' % config_data['dbpath']]) + # Make sure the mongodb user has access to it + subprocess.call(['chown', '-R', 'mongodb:mongodb', config_data['dbpath']]) + config.append("dbpath=%s" % config_data['dbpath']) + config.append("") + + # logpath + # Create the directory if not there already + subprocess.call(['mkdir', + '-p', + '%s' % os.path.dirname(config_data['logpath'])]) + subprocess.call(['chown', + '-R', + 'mongodb:mongodb', os.path.dirname(config_data['logpath'])]) + config.append("logpath=%s" % config_data['logpath']) + config.append("") + + # log_append + if config_data['logappend'] == True: + config.append("logappend=true") + config.append("") + + # bind_ip + if config_data['bind_ip'] != "all": + config.append("bind_ip = %s" % config_data['bind_ip']) + config.append("") + + # port + config.append("port = %d" % config_data['port']) + config.append("") + + # journal + if config_data['journal'] == True: + config.append("journal=true") + config.append("") + + # cpu + if config_data['cpu'] == True: + config.append("cpu = true") + config.append("") + + # auth + if config_data['auth'] == True: + config.append("auth = true") + config.append("") + + # verbose + if config_data['verbose'] == True: + config.append("verbose = true") + config.append("") + + # objcheck + if config_data['objcheck'] == True: + config.append("objcheck = true") + config.append("") + + # quota + if config_data['quota'] == True: + config.append("quota = true") + config.append("") + + # diaglog + config.append("diaglog = %d" % config_data['diaglog']) + config.append("") + + # nocursors + if config_data['nocursors'] == True: + config.append("nocursors = true") + config.append("") + + # nohints + if config_data['nohints'] == True: + config.append("nohints = true") + config.append("") + + # nohttpinterface + if config_data['web_admin_ui'] == False: + config.append("nohttpinterface = true") + config.append("") + + # noscripting + if config_data['noscripting'] == True: + config.append("noscripting = true") + config.append("") + + # notablescan + if config_data['notablescan'] == True: + config.append("notablescan = true") + config.append("") + + # noprealloc + if config_data['noprealloc'] == True: + config.append("noprealloc = true") + config.append("") + + # nssize + if config_data['nssize'] != "default": + config.append("nssize = %s" % config_data['nssize']) + config.append("") + + # mms-token + if config_data['mms-token'] != "disabled": + config.append("mms-token = %s" % config_data['mms-token']) + config.append("") + + # mms-name + if config_data['mms-name'] != "disabled": + config.append("mms-name = %s" % config_data['mms-name']) + config.append("") + + # mms-interval + if config_data['mms-interval'] != "disabled": + config.append("mms-interval = %s" % config_data['mms-interval']) + config.append("") + + # master/slave + if config_data['master'] == "self": + config.append("master = true") + config.append("") + else: + config.append("slave = true") + config.append("source = %s" % config_data['master']) + config.append("") + + # arbiter + if config_data['arbiter'] != "disabled" and\ + config_data['arbiter'] != "enabled": + config.append("arbiter = %s" % config_data['arbiter']) + config.append("") + + # autoresync + if config_data['autoresync'] == True: + config.append("autoresync") + config.append("") + + # oplogSize + if config_data['oplogSize'] != "default": + config.append("oplogSize = %s" % config_data['optlogSize']) + config.append("") + + # opIdMem + if config_data['opIdMem'] != "default": + config.append("opIdMem = %s" % config_data['opIdMem']) + config.append("") + + # extra config options + if config_data['extra_config_options'] != "none": + for config_option in config_data['extra_config_options'].split(','): + config.append(config_option) + + return('\n'.join(config)) + + +def mongo_client(host=None, command=None): + if host is None or command is None: + return(False) + else: + cmd_line = '/usr/bin/mongo' + cmd_line += ' --host %s' % host + cmd_line += ' --eval \'%s\'' % command + juju_log("Executing: %s" % cmd_line) + return(subprocess.call(cmd_line, shell=True) == 0) + + +def init_replset(master_node=None): + if master_node is None: + juju_log("init_replset: master_node must be defined.") + retVal = False + else: + retVal = mongo_client(master_node, 'rs.initiate()') + juju_log("init_replset returns: %s" % retVal) + return(retVal) + + +def join_replset(master_node=None, host=None): + juju_log("join_replset: master_node: %s, host: %s" % + (master_node, host)) + if master_node is None or host is None: + retVal = False + else: + retVal = mongo_client(master_node, "rs.add(\"%s\")" % host) + juju_log("join_replset returns: %s" % retVal) + return(retVal) + + +def enable_replset(replicaset_name=None): + if replicaset_name is None: + retVal = False + try: + mongodb_init_config = open(default_mongodb_init_config).read() + if re.search(' --replSet %s ' % replicaset_name, \ + mongodb_init_config, re.MULTILINE) is None: + mongodb_init_config = regex_sub([(' -- ', \ + ' -- --replSet %s ' % replicaset_name)], \ + mongodb_init_config) + retVal = update_file(default_mongodb_init_config, mongodb_init_config) + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + return(retVal) + + +def update_daemon_options(daemon_options=None): + mongodb_init_config = open(default_mongodb_init_config).read() + pat_replace = [] + if daemon_options is None or daemon_options == "none": + pat_replace.append( + (' --config /etc/mongodb.conf.*', ' --config /etc/mongodb.conf; fi')) + else: + pat_replace.append( + (' --config /etc/mongodb.conf.*', + ' --config /etc/mongodb.conf %s; fi' % daemon_options)) + regex_sub(pat_replace, mongodb_init_config) + return(update_file(default_mongodb_init_config, mongodb_init_config)) + + +def disable_replset(replicaset_name=None): + if replicaset_name is None: + retVal = False + try: + mongodb_init_config = open(default_mongodb_init_config).read() + if re.search(' --replSet %s ' % replicaset_name, \ + mongodb_init_config, re.MULTILINE) is not None: + mongodb_init_config = regex_sub([ + (' --replSet %s ' % replicaset_name, ' ') + ], \ + mongodb_init_config) + retVal = update_file(default_mongodb_init_config, mongodb_init_config) + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + return(retVal) + + +def enable_web_admin_ui(port=None): + if port is None: + juju_log("enable_web_admin_ui: port not defined.") + return(False) + try: + mongodb_init_config = open(default_mongodb_init_config).read() + if re.search(' --rest ', mongodb_init_config, re.MULTILINE) is None: + mongodb_init_config = regex_sub([(' -- ', ' -- --rest ')], \ + mongodb_init_config) + retVal = update_file(default_mongodb_init_config, mongodb_init_config) + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + if retVal == True: + open_port(port) + return(retVal) + + +def disable_web_admin_ui(port=None): + if port is None: + juju_log("disable_web_admin_ui: port not defined.") + return(False) + try: + mongodb_init_config = open(default_mongodb_init_config).read() + if re.search(' --rest ', + mongodb_init_config, + re.MULTILINE) is not None: + mongodb_init_config = regex_sub([(' --rest ', ' ')], \ + mongodb_init_config) + retVal = update_file(default_mongodb_init_config, mongodb_init_config) + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + if retVal == True: + close_port(port) + return(retVal) + + +def enable_arbiter(master_node=None, host=None): + juju_log("enable_arbiter: master_node: %s, host: %s" % + (master_node, host)) + if master_node is None or host is None: + retVal = False + else: + retVal = mongo_client(master_node, "rs.addArb(\"%s\")" % host) + juju_log("enable_arbiter returns: %s" % retVal) + return(retVal) + + +def configsvr_status(wait_for=default_wait_for, max_tries=default_max_tries): + config_data = config_get() + current_try = 0 + while (process_check_pidfile('/var/run/mongodb/configsvr.pid') != \ + (None, None)) and port_check( + unit_get('public-address'), + config_data['config_server_port']) == False and \ + current_try < max_tries: + juju_log("configsvr_status: Waiting for Config Server to be ready ...") + time.sleep(wait_for) + current_try += 1 + retVal = ( + process_check_pidfile('/var/run/mongodb/configsvr.pid') != (None, None) + ) == \ + port_check(unit_get('public-address'), + config_data['config_server_port']) == True + if retVal == True: + return(process_check_pidfile('/var/run/mongodb/configsvr.pid')) + else: + return((None, None)) + + +def configsvr_ready(wait_for=default_wait_for, max_tries=default_max_tries): + return(configsvr_status(wait_for, max_tries) != (None, None)) + + +def disable_configsvr(port=None): + if port is None: + juju_log("disable_configsvr: port not defined.") + return(False) + try: + config_server_port = config_get('config_server_port') + pid = open('/var/run/mongodb/configsvr.pid').read() + os.kill(int(pid), signal.SIGTERM) + os.unlink('/var/run/mongodb/configsvr.pid') + retVal = True + except Exception, e: + juju_log('no config server running ...') + juju_log("Exception: %s" % str(e)) + retVal = False + finally: + juju_log("disable_configsvr returns %s" % retVal) + close_port(config_server_port) + return(retVal) + + +def enable_configsvr(config_data, wait_for=default_wait_for, +max_tries=default_max_tries): + if config_data is None: + juju_log("enable_configsvr: config_data not defined.") + return(False) + + # Stop any running config servers + disable_configsvr() + + # Make sure dbpath and logpath exist + subprocess.call( + [ + 'mkdir', + '-p', + '%s' % config_data['config_server_dbpath'] + ] + ) + subprocess.call( + [ + 'mkdir', + '-p', + '%s' % os.path.dirname(config_data['config_server_logpath']) + ] + ) + + # Start the config server + juju_log("enable_configsvr: Starting the config server") + cmd_line = "/usr/bin/mongod" + cmd_line += " --configsvr" + cmd_line += " --port %d" % config_data['config_server_port'] + cmd_line += " --dbpath %s" % config_data['config_server_dbpath'] + cmd_line += " --logpath %s" % config_data['config_server_logpath'] + cmd_line += " --pidfilepath /var/run/mongodb/configsvr.pid" + cmd_line += " --fork" + subprocess.call(cmd_line, shell=True) + + retVal = configsvr_ready(wait_for, max_tries) + if retVal == True: + open_port(config_data['config_server_port']) + if config_data['web_admin_ui'] == True: + port = int(config_data['config_server_port']) + 1000 + open_port(port) + juju_log("enable_configsvr returns: %s" % retVal) + return(retVal) + + +def mongos_status(wait_for=default_wait_for, max_tries=default_max_tries): + config_data = config_get() + current_try = 0 + while (process_check_pidfile('/var/run/mongodb/mongos.pid') != \ + (None, None)) and port_check( + unit_get('public-address'), + config_data['mongos_port']) == False and \ + current_try < max_tries: + juju_log("mongos_status: Waiting for Mongo shell to be ready ...") + time.sleep(wait_for) + current_try += 1 + retVal = \ + (process_check_pidfile('/var/run/mongodb/mongos.pid') != \ + (None, None)) == \ + port_check(unit_get('public-address'), + config_data['mongos_port']) == True + if retVal == True: + return(process_check_pidfile('/var/run/mongodb/mongos.pid')) + else: + return((None, None)) + + +def mongos_ready(wait_for=default_wait_for, max_tries=default_max_tries): + return(mongos_status(wait_for, max_tries) != (None, None)) + + +def disable_mongos(port=None): + if port is None: + juju_log("disable_mongos: port not defined") + return(False) + try: + pid = open('/var/run/mongodb/mongos.pid').read() + os.kill(int(pid), signal.SIGTERM) + os.unlink('/var/run/mongodb/mongos.pid') + retVal = True + except Exception, e: + juju_log('no mongo router running ...') + juju_log("Exception: %s" % str(e)) + retVal = False + finally: + juju_log("disable_mongos returns %s" % retVal) + close_port(port) + return(retVal) + + +def enable_mongos(config_data=None, config_servers=None, + wait_for=default_wait_for, max_tries=default_max_tries): + juju_log("enable_mongos") + if config_data is None or config_servers is None: + juju_log("enable_mongos: config_data and config_servers are mandatory") + return(False) + if type(config_servers) != type([]): + juju_log("enable_mongos: config_servers must be a list") + return(False) + disable_mongos() + # Make sure logpath exist + subprocess.call( + [ + 'mkdir', + '-p', + '%s' % os.path.dirname(config_data['mongos_logpath']) + ] + ) + cmd_line = "/usr/bin/mongos" + cmd_line += " --logpath %s" % config_data['mongos_logpath'] + cmd_line += " --pidfilepath /var/run/mongodb/mongos.pid" + cmd_line += " --port %d" % config_data['mongos_port'] + cmd_line += " --fork" + if len(config_servers) > 0: + cmd_line += ' --configdb %s' % ','.join(config_servers) + subprocess.call(cmd_line, shell=True) + retVal = mongos_ready(wait_for, max_tries) + if retVal == True: + open_port(config_data['mongos_port']) + juju_log("enable_mongos returns: %s" % retVal) + return(retVal) + + +def load_config_servers(mongos_list=None): + if os.path.exists(mongos_list): + retVal = [line.strip() for line in open(mongos_list).readlines()] + else: + retVal = [] + return(retVal) + + +def restart_mongod(wait_for=default_wait_for, max_tries=default_max_tries): + my_hostname = unit_get('public-address') + my_port = config_get('port') + current_try = 0 + + service('mongodb', 'stop') + if os.path.exists('/var/lib/mongodb/mongod.lock'): + os.remove('/var/lib/mongodb/mongod.lock') + + service('mongodb', 'start') + + while service('mongodb', 'status') == True and \ + port_check(my_hostname, my_port) == False and \ + current_try < max_tries: + juju_log("restart_mongod: Waiting for MongoDB to be ready ...") + time.sleep(wait_for) + current_try += 1 + + return( + service('mongodb', 'status') == \ + port_check(my_hostname, my_port) == True + ) + + +############################################################################### +# Hook functions +############################################################################### +def install_hook(): + juju_log("Installing mongodb") + if not apt_get_install('mongodb'): + juju_log("Installation of mongodb failed.") + return(False) + else: + return(True) + + +def config_changed(): + juju_log("Entering config_changed") + print "Entering config_changed" + config_data = config_get() + print "config_data: ", config_data + mongodb_config = open(default_mongodb_config).read() + + # current ports + current_mongodb_port = re.search('^#*port\s+=\s+(\w+)', + mongodb_config, + re.MULTILINE).group(1) + current_web_admin_ui_port = int(current_mongodb_port) + 1000 + new_web_admin_ui_port = int(config_data['port']) + 1000 + + print "current_mongodb_port: ", current_mongodb_port + public_address = unit_get('public-address') + print "public_address: ", public_address + + # Update mongodb configuration file + mongodb_config = mongodb_conf(config_data) + update_file(default_mongodb_config, mongodb_config) + + # web_admin_ui + if config_data['web_admin_ui'] == True: + enable_web_admin_ui(new_web_admin_ui_port) + else: + disable_web_admin_ui(current_web_admin_ui_port) + + # replicaset_master + if config_data['replicaset_master'] != "auto": + enable_replset(config_data['replicaset']) + join_replset(config_data['replicaset_master']) + + # extra demon options + update_daemon_options(config_data['extra_daemon_options']) + + # restart mongodb + restart_mongod() + + # attach to replSet ( if needed ) + if config_data['replicaset_master'] != "auto": + join_replset(config_data['replicaset_master'], public_address) + + # arbiter + if config_data['replicaset_master'] != 'auto': + if config_data['arbiter'] != "disabled" and\ + config_data['replicaset_master'] != "auto": + if config_data['arbiter'] == 'enable': + enable_arbiter(config_data['replicaset_master'],\ + "%s:%s" % (public_address, config_data['port'])) + else: + enable_arbiter(config_data['replicaset_master'],\ + config_data['arbiter']) + + # expose necessary ports + update_service_ports([current_mongodb_port], [config_data['port']]) + + if config_data['web_admin_ui'] == True: + current_web_admin_ui_port = int(current_mongodb_port) + 1000 + new_web_admin_ui_port = int(config_data['port']) + 1000 + close_port(current_web_admin_ui_port) + open_port(new_web_admin_ui_port) + + # update config-server information and port + try: + (configsvr_pid, configsvr_cmd_line) = configsvr_status() + except Exception, e: + configsvr_pid = None + configsvr_cmd_line = None + juju_log("config_changed: configsvr_status failed.") + juju_log("config_changed: Exception: %s" % str(e)) + + if configsvr_pid is not None: + configsvr_port = re.search('--port (\w+)', configsvr_cmd_line).group(2) + disable_configsvr(configsvr_port) + enable_configsvr(config_data['config_server_port']) + else: + open_port(config_data['config_server_port']) + + # update mongos information and port + try: + (mongos_pid, mongos_cmd_line) = mongos_status() + except Exception, e: + mongos_pid = None + mongos_cmd_line = None + juju_log("config_changed: mongos_status failed.") + juju_log("config_changed: Exceptions: %s" % str(e)) + + if mongos_pid is not None: + mongos_port = re.search('--port (\w+)', mongos_cmd_line).group(2) + disable_mongos(mongos_port) + enable_mongos(config_data['mongos_port']) + else: + open_port(config_data['mongos_port']) + + print "About to leave config_changed" + return(True) + + +def start_hook(): + juju_log("start_hook") + retVal = restart_mongod() + juju_log("start_hook returns: %s" % retVal) + return(retVal) + + +def stop_hook(): + juju_log("stop_hook") + try: + retVal = service('mongodb', 'stop') + os.remove('/var/lib/mongodb/mongod.lock') + except Exception, e: + juju_log(str(e)) + retVal = False + finally: + juju_log("stop_hook returns: %s" % retVal) + return(retVal) + + +def database_relation_joined(): + juju_log("database_relation_joined") + my_hostname = unit_get('public-address') + my_port = config_get('port') + my_replset = config_get('replicaset') + juju_log("my_hostname: %s" % my_hostname) + juju_log("my_port: %s" % my_port) + juju_log("my_replset: %s" % my_replset) + return(relation_set( + { + 'hostname': my_hostname, + 'port': my_port, + 'replset': my_replset, + 'type': 'database', + })) + + +def replica_set_relation_joined(): + juju_log("replica_set_relation_joined") + my_hostname = unit_get('public-address') + my_port = config_get('port') + my_replset = config_get('replicaset') + my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1] + juju_log("my_hostname: %s" % my_hostname) + juju_log("my_port: %s" % my_port) + juju_log("my_replset: %s" % my_replset) + juju_log("my_install_order: %s" % my_install_order) + return(enable_replset(my_replset) == \ + restart_mongod() == \ + relation_set( + { + 'hostname': my_hostname, + 'port': my_port, + 'replset': my_replset, + 'install-order': my_install_order, + 'type': 'replset', + })) + + +def replica_set_relation_changed(): + juju_log("replica_set_relation_changed") + my_hostname = unit_get('public-address') + my_port = config_get('port') + my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1] + my_replicaset_master = config_get('replicaset_master') + + # If we are joining an existing replicaset cluster, just join and leave. + if my_replicaset_master != "auto": + return(join_replset(my_replicaset_master, my_hostname)) + + # Default to this node being the master + master_hostname = my_hostname + master_port = my_port + master_install_order = my_install_order + + # Check the nodes in the relation to find the master + for member in relation_list(): + hostname = relation_get('hostname', member) + port = relation_get('port', member) + install_order = relation_get('install-order', member) + if int(install_order) < int(master_install_order): + master_hostname = hostname + master_port = port + master_install_order = install_order + + # Initiate the replset + init_replset("%s:%s" % (master_hostname, master_port)) + + # Add the rest of the nodes to the replset + for member in relation_list(): + hostname = relation_get('hostname', member) + port = relation_get('port', member) + if master_hostname != hostname: + if hostname == my_hostname: + subprocess.call(['mongo', + '--eval', + "rs.add(\"%s\")" % hostname]) + else: + join_replset("%s:%s" % (master_hostname, master_port), + "%s:%s" % (hostname, port)) + + # Add this node to the replset ( if needed ) + if master_hostname != my_hostname: + join_replset("%s:%s" % (master_hostname, master_port), + "%s:%s" % (my_hostname, my_port)) + + return(True) + + +def configsvr_relation_joined(): + juju_log("configsvr_relation_joined") + my_hostname = unit_get('public-address') + my_port = config_get('config_server_port') + my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1] + return(relation_set( + { + 'hostname': my_hostname, + 'port': my_port, + 'install-order': my_install_order, + 'type': 'configsvr', + })) + + +def configsvr_relation_changed(): + juju_log("configsvr_relation_changed") + config_data = config_get() + my_port = config_data['config_server_port'] + disable_configsvr(my_port) + retVal = enable_configsvr(config_data) + juju_log("configsvr_relation_changed returns: %s" % retVal) + return(retVal) + + +def mongos_relation_joined(): + juju_log("mongos_relation_joined") + config_data = config_get() + my_hostname = unit_get('public-address') + my_port = config_data['mongos_port'] + my_install_order = os.environ['JUJU_UNIT_NAME'].split('/')[1] + return(relation_set( + { + 'hostname': my_hostname, + 'port': my_port, + 'install-order': my_install_order, + 'type': 'mongos' + })) + + +def mongos_relation_changed(): + juju_log("mongos_relation_changed") + config_servers = load_config_servers(default_mongos_list) + print "Loaded config_servers: ", config_servers + config_data = config_get() + for member in relation_list(): + hostname = relation_get('hostname', member) + port = relation_get('port', member) + rel_type = relation_get('type', member) + if rel_type == 'configsvr': + print "Adding config server: %s:%s" % (hostname, port) + juju_log("Adding config server: %s:%s" % (hostname, port)) + if hostname is not None and \ + port is not None and \ + hostname != '' and \ + port != '' and \ + "%s:%s" % (hostname, port) not in config_servers: + config_servers.append("%s:%s" % (hostname, port)) + disable_mongos(config_data['mongos_port']) + retVal = enable_mongos(config_data, config_servers) + if retVal == True: + update_file(default_mongos_list, '\n'.join(config_servers)) + elif rel_type == 'database': + if mongos_ready() == True: + mongos_host = "%s:%s" % ( + unit_get('public-address'), + config_get('mongos_port')) + shard_command = "sh.addShard(\"%s:%s\")" % (hostname, port) + retVal = mongo_client(mongos_host, shard_command) + else: + juju_log("mongos_relation_change: undefined rel_type: %s" % + rel_type) + return(False) + juju_log("mongos_relation_changed returns: %s" % retVal) + return(retVal) + + +def mongos_relation_broken(): +# config_servers = load_config_servers(default_mongos_list) +# for member in relation_list(): +# hostname = relation_get('hostname', member) +# port = relation_get('port', member) +# if '%s:%s' % (hostname, port) in config_servers: +# config_servers.remove('%s:%s' % (hostname, port)) +# return(update_file(default_mongos_list, '\n'.join(config_servers))) + return(True) + +############################################################################### +# Main section +############################################################################### +if hook_name == "install": + retVal = install_hook() +elif hook_name == "config-changed": + retVal = config_changed() +elif hook_name == "start": + retVal = start_hook() +elif hook_name == "stop": + retVal = stop_hook() +elif hook_name == "database-relation-joined": + retVal = database_relation_joined() +elif hook_name == "replica-set-relation-joined": + retVal = replica_set_relation_joined() +elif hook_name == "replica-set-relation-changed": + retVal = replica_set_relation_changed() +elif hook_name == "configsvr-relation-joined": + retVal = configsvr_relation_joined() +elif hook_name == "configsvr-relation-changed": + retVal = configsvr_relation_changed() +elif hook_name == "mongos-relation-joined": + retVal = mongos_relation_joined() +elif hook_name == "mongos-relation-changed": + retVal = mongos_relation_changed() +elif hook_name == "mongos-relation-broken": + retVal = mongos_relation_broken() +else: + print "Unknown hook" + retVal = False + +if retVal == True: + sys.exit(0) +else: + sys.exit(1) diff --git a/hooks/install b/hooks/install index b0bf7c2..f94593a 100755..120000 --- a/hooks/install +++ b/hooks/install @@ -1,83 +1 @@ -#!/bin/bash -# Here do anything needed to install the service -# i.e. apt-get install -y foo or bzr branch http://myserver/mycode /srv/webroot - -set -ux - -############################################################################################################ -# Set some variables that we'll need for later -############################################################################################################ -DEFAULT_REPLSET_NAME=`config-get replicaset` -#HOSTNAME=`hostname -f` -HOSTNAME=`unit-get public-address` -EPOCH=`date +%s` -INSTALL_ORDER=$(echo ${JUJU_UNIT_NAME} | awk -F/ '{ print $2 }') -WEB_ADMIN_UI=`config-get web_admin_ui` -REPLICASET_MASTER=`config-get replicaset_master` -DEFAULT_PORT=`config-get default_port` - - -############################################################################################################ -# Install mongodb -############################################################################################################ -DEBIAN_FRONTEND=noninteractive apt-get install -y mongodb - - -############################################################################################################ -# Change the default mongodb configuration -############################################################################################################ -sed -e "s/#master = true/master = true/" \ - -e "s/bind_ip/#bind_ip/" \ - -e "s/#port = 27017/port = ${DEFAULT_PORT}/" \ - -i /etc/mongodb.conf - - -############################################################################################################ -# Reconfigure the upstart script to include the replica-set option. -# We'll need this so, when we add nodes, they can all talk to each other. -# Replica sets can only talk to each other if they all belong to the same -# set. In our case, we have defaulted to "myset". -############################################################################################################ -# Web Admin UI -if [ "$WEB_ADMIN_UI" == "yes" ]; then - sed -i -e "s/ -- / -- --rest /" /etc/init/mongodb.conf -fi - -if [ "${REPLICASET_MASTER}" != "auto" ]; then - sed -i -e "s/ -- / -- --replSet ${DEFAULT_REPLSET_NAME} /" /etc/init/mongodb.conf -fi - - -############################################################################################################ -# stop then start ( *** not restart **** ) mongodb so we can finish the configuration -############################################################################################################ -service mongodb stop -# There is a bug in the upstart script that leaves a lock file orphaned.... Let's wipe that file out -rm -f /var/lib/mongodb/mongod.lock -service mongodb start - - -############################################################################################################ -# Are we connecting to an existing replica set? -############################################################################################################ -if [ "${REPLICASET_MASTER}" != "auto" ]; then - grep "${DEFAULT_REPLSET_NAME}" /etc/init/mongodb.conf - if [ $? -ne 0 ];then - sed -i -e "s/ -- / -- --replSet ${DEFAULT_REPLSET_NAME} /" /etc/init/mongodb.conf - service mongodb stop - rm -f /var/lib/mongodb/mongod.lock - service mongodb start - fi - mongo --host ${REPLICASET_MASTER} --eval "rs.add(\""${HOSTNAME}"\")" -fi - - -############################################################################################################ -# Register the port -############################################################################################################ -[ -x /usr/bin/open-port ] && open-port ${DEFAULT_PORT}/TCP -if [ "$WEB_ADMIN_UI" == "yes" ]; then - [ -x /usr/bin/open-port ] && open-port $((${DEFAULT_PORT} + 1000))/TCP -fi - -exit 0 +./hooks.py \ No newline at end of file diff --git a/hooks/mongos-relation-broken b/hooks/mongos-relation-broken new file mode 120000 index 0000000..f94593a --- /dev/null +++ b/hooks/mongos-relation-broken @@ -0,0 +1 @@ +./hooks.py \ No newline at end of file diff --git a/hooks/mongos-relation-changed b/hooks/mongos-relation-changed new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/mongos-relation-changed @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/mongos-relation-joined b/hooks/mongos-relation-joined new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/mongos-relation-joined @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/replica-set-relation-changed b/hooks/replica-set-relation-changed index d0b41bc..f94593a 100755..120000 --- a/hooks/replica-set-relation-changed +++ b/hooks/replica-set-relation-changed @@ -1,93 +1 @@ -#!/bin/bash -# This must be renamed to the name of the relation. The goal here is to -# affect any change needed by relationships being formed, modified, or broken -# This script should be idempotent. - -############################################################################################################ -# Set debugging information -############################################################################################################ -set -ux - -############################################################################################################ -# Set some global variables -############################################################################################################ -MY_HOSTNAME=`unit-get public-address` -MY_REPLSET=`config-get replicaset` -MY_INSTALL_ORDER=$(echo ${JUJU_UNIT_NAME} | awk -F/ '{ print $2 }') -MY_REPLICASET_MASTER=`config-get replicaset_master` - - -############################################################################################################ -# If we are joining an existing replicaset cluster, just join and leave. -############################################################################################################ -if [ "${MY_REPLICASET_MASTER}" != "auto" ]; then - mongo --host ${MY_REPLICASET_MASTER} --eval "rs.add(\""${MY_HOSTNAME}"\")" - exit $? -fi - -MASTER_HOSTNAME=${MY_HOSTNAME} -MASTER_REPLSET=${MY_REPLSET} -MASTER_INSTALL_ORDER=${MY_INSTALL_ORDER} - -echo "My hosntmae: ${MY_HOSTNAME}" -echo "My ReplSet: ${MY_REPLSET}" -echo "My install order: ${MY_INSTALL_ORDER}" - -############################################################################################################ -# Here we need to find out which is the first node ( we record the install order ). -# The one with the lowest order time is the master. -# Initialize the master node. -# Add the other nodes to the master's replica set. -############################################################################################################ -# Find the master ( lowest install order ) -for MEMBER in `relation-list` -do - HOSTNAME=`relation-get hostname ${MEMBER}` - REPLSET=`relation-get replset ${MEMBER}` - INSTALL_ORDER=`relation-get install-order ${MEMBER}` - if [ ${INSTALL_ORDER} -lt ${MASTER_INSTALL_ORDER} ];then - MASTER_HOSTNAME=${HOSTNAME} - MASTER_REPLSET=${REPLSET} - MASTER_INSTALL_ORDER=${INSTALL_ORDER} - fi -done - -echo "Master Hostname: ${MASTER_HOSTNAME}" -echo "Master ReplSet: ${MASTER_REPLSET}" -echo "Master install order: ${MASTER_INSTALL_ORDER}" - -# We should now have all the information about the master node. -# If the node has already been initialized, it will just inform you -# about it with no other consequence. -if [ "${MASTER_HOSTNAME}" == "${MY_HOSTNAME}" ]; then - mongo --eval "rs.initiate()" -else - mongo --host ${MASTER_HOSTNAME} --eval "rs.initiate()" -fi - -# Now we need to add the rest of nodes to the replica set -for MEMBER in `relation-list` -do - HOSTNAME=`relation-get hostname ${MEMBER}` - REPLSET=`relation-get replset ${MEMBER}` - INSTALL_ORDER=`relation-get install-order ${MEMBER}` - if [ "${MASTER_HOSTNAME}" != "${HOSTNAME}" ]; then - if [ "${HOSTNAME}" == "${MY_HOSTNAME}" ]; then - mongo --eval "rs.add(\""${HOSTNAME}"\")" - else - mongo --host ${MASTER_HOSTNAME} --eval "rs.add(\""${HOSTNAME}"\")" - fi - fi -done - -# Add myself to the replicaset if needed -if [ "${MASTER_HOSTNAME}" != "${MY_HOSTNAME}" ]; then - mongo --host ${MASTER_HOSTNAME} --eval "rs.add(\""${MY_HOSTNAME}"\")" -fi - - -echo $JUJU_REMOTE_UNIT modified its settings -echo Relation settings: -relation-get -echo Relation members: -relation-list +./hooks.py \ No newline at end of file diff --git a/hooks/replica-set-relation-joined b/hooks/replica-set-relation-joined index ecf1e71..f94593a 100755..120000 --- a/hooks/replica-set-relation-joined +++ b/hooks/replica-set-relation-joined @@ -1,28 +1 @@ -#!/bin/bash -# This must be renamed to the name of the relation. The goal here is to -# affect any change needed by relationships being formed -# This script should be idempotent. - -set -ux - -DEFAULT_REPLSET_NAME=`config-get replicaset` -INSTALL_ORDER=$(echo ${JUJU_UNIT_NAME} | awk -F/ '{ print $2 }') -HOSTNAME=`unit-get public-address` - -############################################################################################################ -# Reconfigure the upstart script to include the replica-set option. -# We'll need this so, when we add nodes, they can all talk to each other. -# Replica sets can only talk to each other if they all belong to the same -# set. In our case, we have defaulted to "myset". -############################################################################################################ -grep "${DEFAULT_REPLSET_NAME}" /etc/init/mongodb.conf -if [ $? -ne 0 ];then - sed -i -e "s/ -- / -- --replSet ${DEFAULT_REPLSET_NAME} /" /etc/init/mongodb.conf - service mongodb stop - rm -f /var/lib/mongodb/mongod.lock - service mongodb start -fi - -relation-set hostname=${HOSTNAME} replset=${DEFAULT_REPLSET_NAME} install-order=${INSTALL_ORDER} - -echo $JUJU_REMOTE_UNIT joined +./hooks.py \ No newline at end of file diff --git a/hooks/start b/hooks/start index cd918d6..f94593a 100755..120000 --- a/hooks/start +++ b/hooks/start @@ -1,6 +1 @@ -#!/bin/bash -# Here put anything that is needed to start the service. -# Note that currently this is run directly after install -# i.e. 'service apache2 start' - -service mongodb status && service mongodb restart || service mongodb start +./hooks.py \ No newline at end of file diff --git a/hooks/stop b/hooks/stop index 5175b72..f94593a 100755..120000 --- a/hooks/stop +++ b/hooks/stop @@ -1,10 +1 @@ -#!/bin/bash -# This will be run when the service is being torn down, allowing you to disable -# it in various ways.. -# For example, if your web app uses a text file to signal to the load balancer -# that it is live... you could remove it and sleep for a bit to allow the load -# balancer to stop sending traffic. -# rm /srv/webroot/server-live.txt && sleep 30 - -service mongodb stop -rm -f /var/lib/mongodb/mongod.lock +./hooks.py \ No newline at end of file diff --git a/metadata.yaml b/metadata.yaml index eea0a59..6891335 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -5,16 +5,23 @@ description: | MongoDB is a high-performance, open source, schema-free document- oriented data store that's easy to deploy, manage and use. It's network accessible, written in C++ and offers the following features : - * Collection oriented storage - easy storage of object- style data - * Full index support, including on inner objects * Query profiling - * Replication and fail-over support * Efficient storage of binary - data including large objects (e.g. videos) * Auto-sharding for - cloud-level scalability (Q209) High performance, scalability, and - reasonable depth of functionality are the goals for the project. This - is a metapackage that depends on all the mongodb parts. + * Collection oriented storage - easy storage of object-style data + * Full index support, including on inner objects + * Query profiling + * Replication and fail-over support + * Efficient storage of binary data including large + objects (e.g. videos) + * Auto-sharding for cloud-level scalability (Q209) + High performance, scalability, and reasonable depth of functionality + are the goals for the project. provides: database: interface: mongodb + configsvr: + interface: mongodb +requires: + mongos: + interface: mongodb peers: replica-set: interface: mongodb-replica-set |
