trove/trove/guestagent/datastore/couchbase/service.py

300 lines
11 KiB
Python

# Copyright (c) 2013 eBay Software Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import pexpect
import os
import subprocess
from trove.common import cfg
from trove.common import exception
from trove.common import instance as rd_instance
from trove.common import utils as utils
from trove.guestagent import pkg
from trove.guestagent.common import operating_system
from trove.guestagent.datastore import service
from trove.guestagent.datastore.couchbase import system
from trove.guestagent.db import models
from trove.openstack.common import log as logging
from trove.openstack.common.gettextutils import _
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
packager = pkg.Package()
class CouchbaseApp(object):
"""
Handles installation and configuration of couchbase
on a trove instance.
"""
def __init__(self, status, state_change_wait_time=None):
"""
Sets default status and state_change_wait_time
"""
if state_change_wait_time:
self.state_change_wait_time = state_change_wait_time
else:
self.state_change_wait_time = CONF.state_change_wait_time
self.status = status
def install_if_needed(self, packages):
"""
Install couchbase if needed, do nothing if it is already installed.
"""
LOG.info(_('Preparing Guest as Couchbase Server.'))
if not packager.pkg_is_installed(packages):
LOG.debug('Installing Couchbase.')
self._install_couchbase(packages)
def initial_setup(self):
self.ip_address = operating_system.get_ip_address()
mount_point = CONF.couchbase.mount_point
try:
LOG.info(_('Couchbase Server change data dir path.'))
operating_system.update_owner('couchbase',
'couchbase',
mount_point)
pwd = CouchbaseRootAccess.get_password()
utils.execute_with_timeout(
(system.cmd_node_init
% {'data_path': mount_point,
'IP': self.ip_address,
'PWD': pwd}), shell=True)
utils.execute_with_timeout(
system.cmd_rm_old_data_dir, shell=True)
LOG.debug('Couchbase Server initialize cluster.')
utils.execute_with_timeout(
(system.cmd_cluster_init
% {'IP': self.ip_address, 'PWD': pwd}),
shell=True)
utils.execute_with_timeout(system.cmd_set_swappiness, shell=True)
utils.execute_with_timeout(system.cmd_update_sysctl_conf,
shell=True)
LOG.info(_('Couchbase Server initial setup finished.'))
except exception.ProcessExecutionError:
LOG.exception(_('Error performing initial Couchbase setup.'))
raise RuntimeError("Couchbase Server initial setup failed")
def complete_install_or_restart(self):
"""
finalize status updates for install or restart.
"""
self.status.end_install_or_restart()
def _install_couchbase(self, packages):
"""
Install the Couchbase Server.
"""
LOG.debug('Installing Couchbase Server. Creating %s' %
system.COUCHBASE_CONF_DIR)
utils.execute_with_timeout('mkdir',
'-p',
system.COUCHBASE_CONF_DIR,
run_as_root=True,
root_helper='sudo')
pkg_opts = {}
packager.pkg_install(packages, pkg_opts, system.TIME_OUT)
self.start_db()
LOG.debug('Finished installing Couchbase Server.')
def _enable_db_on_boot(self):
"""
Enables Couchbase Server on boot.
"""
LOG.info(_('Enabling Couchbase Server on boot.'))
try:
couchbase_service = operating_system.service_discovery(
system.SERVICE_CANDIDATES)
utils.execute_with_timeout(
couchbase_service['cmd_enable'], shell=True)
except KeyError:
raise RuntimeError(_(
"Command to enable Couchbase Server on boot not found."))
def _disable_db_on_boot(self):
LOG.debug("Disabling Couchbase Server on boot.")
try:
couchbase_service = operating_system.service_discovery(
system.SERVICE_CANDIDATES)
utils.execute_with_timeout(
couchbase_service['cmd_disable'], shell=True)
except KeyError:
raise RuntimeError(
"Command to disable Couchbase Server on boot not found.")
def stop_db(self, update_db=False, do_not_start_on_reboot=False):
"""
Stops Couchbase Server on the trove instance.
"""
LOG.debug('Stopping Couchbase Server.')
if do_not_start_on_reboot:
self._disable_db_on_boot()
try:
couchbase_service = operating_system.service_discovery(
system.SERVICE_CANDIDATES)
utils.execute_with_timeout(
couchbase_service['cmd_stop'], shell=True)
except KeyError:
raise RuntimeError("Command to stop Couchbase Server not found.")
if not self.status.wait_for_real_status_to_change_to(
rd_instance.ServiceStatuses.SHUTDOWN,
self.state_change_wait_time, update_db):
LOG.error(_('Could not stop Couchbase Server.'))
self.status.end_install_or_restart()
raise RuntimeError(_("Could not stop Couchbase Server."))
def restart(self):
LOG.info(_("Restarting Couchbase Server."))
try:
self.status.begin_restart()
self.stop_db()
self.start_db()
finally:
self.status.end_install_or_restart()
def start_db(self, update_db=False):
"""
Start the Couchbase Server.
"""
LOG.info(_("Starting Couchbase Server."))
self._enable_db_on_boot()
try:
couchbase_service = operating_system.service_discovery(
system.SERVICE_CANDIDATES)
utils.execute_with_timeout(
couchbase_service['cmd_start'], shell=True)
except exception.ProcessExecutionError:
pass
except KeyError:
raise RuntimeError("Command to start Couchbase Server not found.")
if not self.status.wait_for_real_status_to_change_to(
rd_instance.ServiceStatuses.RUNNING,
self.state_change_wait_time, update_db):
LOG.error(_("Start up of Couchbase Server failed."))
try:
utils.execute_with_timeout(system.cmd_kill)
except exception.ProcessExecutionError:
LOG.exception(_('Error killing Couchbase start command.'))
self.status.end_install_or_restart()
raise RuntimeError("Could not start Couchbase Server")
def enable_root(self, root_password=None):
return CouchbaseRootAccess.enable_root(root_password)
def start_db_with_conf_changes(self, config_contents):
LOG.info(_("Starting Couchbase with configuration changes."))
LOG.info(_("Configuration contents:\n %s.") % config_contents)
if self.status.is_running:
LOG.error(_("Cannot start Couchbase with configuration changes. "
"Couchbase state == %s.") % self.status)
raise RuntimeError("Couchbase is not stopped.")
self._write_config(config_contents)
self.start_db(True)
def reset_configuration(self, configuration):
config_contents = configuration['config_contents']
LOG.debug("Resetting configuration.")
self._write_config(config_contents)
def _write_config(self, config_contents):
"""
Update contents of Couchbase configuration file
"""
return
class CouchbaseAppStatus(service.BaseDbStatus):
"""
Handles all of the status updating for the couchbase guest agent.
"""
def _get_actual_db_status(self):
self.ip_address = operating_system.get_ip_address()
try:
pwd = CouchbaseRootAccess.get_password()
out, err = utils.execute_with_timeout(
(system.cmd_couchbase_status %
{'IP': self.ip_address, 'PWD': pwd}),
shell=True)
server_stats = json.loads(out)
if not err and server_stats["clusterMembership"] == "active":
return rd_instance.ServiceStatuses.RUNNING
else:
return rd_instance.ServiceStatuses.SHUTDOWN
except exception.ProcessExecutionError:
LOG.exception(_("Error getting Couchbase status."))
return rd_instance.ServiceStatuses.SHUTDOWN
class CouchbaseRootAccess(object):
@classmethod
def enable_root(cls, root_password=None):
user = models.RootUser()
user.name = "root"
user.host = "%"
user.password = root_password or utils.generate_random_password()
if root_password:
CouchbaseRootAccess().write_password_to_file(root_password)
else:
CouchbaseRootAccess().set_password(user.password)
return user.serialize()
def set_password(self, root_password):
self.ip_address = operating_system.get_ip_address()
child = pexpect.spawn(system.cmd_reset_pwd % {'IP': self.ip_address})
try:
child.expect('.*password.*')
child.sendline(root_password)
child.expect('.*(yes/no).*')
child.sendline('yes')
child.expect('.*successfully.*')
except pexpect.TIMEOUT:
child.delayafterclose = 1
child.delayafterterminate = 1
try:
child.close(force=True)
except pexpect.ExceptionPexpect:
# Close fails to terminate a sudo process on some OSes.
subprocess.call(['sudo', 'kill', str(child.pid)])
self.write_password_to_file(root_password)
def write_password_to_file(self, root_password):
utils.execute_with_timeout('mkdir',
'-p',
system.COUCHBASE_CONF_DIR,
run_as_root=True,
root_helper='sudo')
utils.execute_with_timeout("sudo sh -c 'echo " +
root_password +
' > ' +
system.pwd_file + "'",
shell=True)
@staticmethod
def get_password():
pwd = "password"
if os.path.exists(system.pwd_file):
with open(system.pwd_file) as file:
pwd = file.readline().strip()
return pwd