summaryrefslogtreecommitdiff
diff options
authorMark Mims <mark.mims@canonical.com>2012-08-21 16:27:54 -0600
committerMark Mims <mark.mims@canonical.com>2012-08-21 16:27:54 -0600
commit17f55bf9c56bb04199922f1bc37737513fa59e58 (patch)
tree6285d223f2cee962fb693735f0b3fe1c6e482665
parentf0c06e500182eed40e9811562ddc31985f69f7a3 (diff)
parentd8517d01c217848193f4940ba675c0a59685d2be (diff)
merging lp:~negronjl/charms/precise/mongodb/trunk
-rw-r--r--README193
-rw-r--r--config.yaml136
l---------[-rwxr-xr-x]hooks/config-changed26
l---------hooks/configsvr-relation-changed1
l---------hooks/configsvr-relation-joined1
l---------[-rwxr-xr-x]hooks/database-relation-joined14
-rwxr-xr-xhooks/hooks.py1168
l---------[-rwxr-xr-x]hooks/install84
l---------hooks/mongos-relation-broken1
l---------hooks/mongos-relation-changed1
l---------hooks/mongos-relation-joined1
l---------[-rwxr-xr-x]hooks/replica-set-relation-changed94
l---------[-rwxr-xr-x]hooks/replica-set-relation-joined29
l---------[-rwxr-xr-x]hooks/start7
l---------[-rwxr-xr-x]hooks/stop11
-rw-r--r--metadata.yaml21
16 files changed, 1513 insertions, 275 deletions
diff --git a/README b/README
index 9cef823..801e7a2 100644
--- a/README
+++ b/README
@@ -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