Merge "Fix Zookeeper autodetection"
This commit is contained in:
commit
81478dd72a
@ -1,4 +1,18 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
||||
# Copyright 2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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 logging
|
||||
import os
|
||||
@ -10,6 +24,10 @@ import monasca_setup.detection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_ZOOKEEPER_DEFAULT_CONFIG_PATH = '/etc/zookeeper/conf/zoo.cfg'
|
||||
_ZOOKEEPER_DEFAULT_IP = 'localhost'
|
||||
_ZOOKEEPER_DEFAULT_PORT = 2181
|
||||
|
||||
|
||||
class Zookeeper(monasca_setup.detection.Plugin):
|
||||
|
||||
@ -21,27 +39,71 @@ class Zookeeper(monasca_setup.detection.Plugin):
|
||||
"""Run detection, set self.available True if the service is detected.
|
||||
|
||||
"""
|
||||
if monasca_setup.detection.find_process_cmdline('org.apache.zookeeper') is not None:
|
||||
self.available = True
|
||||
process_found = monasca_setup.detection.find_process_cmdline('org.apache.zookeeper')
|
||||
self._cfg_file = self._get_config_file(process_found) if process_found else None
|
||||
has_config_file = self._cfg_file and os.path.isfile(self._cfg_file)
|
||||
|
||||
self.available = process_found and has_config_file
|
||||
|
||||
if not self.available:
|
||||
err_str = 'Plugin for Zookeeper will not be configured.'
|
||||
if not process_found:
|
||||
log.error('Zookeeper process has not been found: {0}'.format(err_str))
|
||||
elif not has_config_file:
|
||||
log.error('Zookeeper plugin cannot find configuration file: {0}. {1}'.format(self._cfg_file, err_str))
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return.
|
||||
|
||||
"""
|
||||
config = monasca_setup.agent_config.Plugins()
|
||||
host, port = self._read_config_file(self._cfg_file)
|
||||
# First watch the process
|
||||
log.info("\tWatching the zookeeper process.")
|
||||
config.merge(monasca_setup.detection.watch_process(['org.apache.zookeeper.server'], 'zookeeper',
|
||||
exact_match=False))
|
||||
|
||||
log.info("\tEnabling the zookeeper plugin")
|
||||
with open(os.path.join(self.template_dir, 'conf.d/zk.yaml.example'), 'r') as zk_template:
|
||||
zk_config = yaml.safe_load(zk_template.read())
|
||||
config['zk'] = zk_config
|
||||
config['zk'] = {
|
||||
'init_config': None, 'instances':
|
||||
[{'name': host, 'host': host, 'port': port, 'timeout': 3}]
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
def dependencies_installed(self):
|
||||
# The current plugin just does a simple socket connection to zookeeper and
|
||||
# parses the stat command
|
||||
return True
|
||||
return True # pragma: no cover
|
||||
|
||||
@staticmethod
|
||||
def _get_config_file(process):
|
||||
# Config file should be on the last place in cmdline
|
||||
cfg = process.as_dict(['cmdline'])['cmdline'][-1]
|
||||
# check if the last value in cmdline is a file
|
||||
# if not return default config file
|
||||
if os.path.isfile(cfg):
|
||||
log.debug('Found zookeeper config file: {0}'.format(cfg))
|
||||
return cfg
|
||||
log.debug('Missing zookeeper config file. Using default file: {0}'.
|
||||
format(_ZOOKEEPER_DEFAULT_CONFIG_PATH))
|
||||
return _ZOOKEEPER_DEFAULT_CONFIG_PATH
|
||||
|
||||
@staticmethod
|
||||
def _read_config_file(cfg_file):
|
||||
ip_address = _ZOOKEEPER_DEFAULT_IP
|
||||
port = _ZOOKEEPER_DEFAULT_PORT
|
||||
try:
|
||||
cfg = open(cfg_file, 'r')
|
||||
for line in cfg:
|
||||
if 'clientPortAddress' in line:
|
||||
log.debug('Found clientPort in config file: {0}'.format(line))
|
||||
ip_address = line.split('=')[1].strip()
|
||||
if 'clientPort' in line and 'clientPortAddress' not in line:
|
||||
log.debug('Found clientPortAddress in config file: {0}'.format(line))
|
||||
port = int(line.split('=')[1].strip())
|
||||
except Exception as ex:
|
||||
log.error('Failed to parse %s', cfg_file)
|
||||
log.exception(ex)
|
||||
return None
|
||||
return ip_address, port
|
||||
|
149
tests/detection/test_zookeeper.py
Normal file
149
tests/detection/test_zookeeper.py
Normal file
@ -0,0 +1,149 @@
|
||||
# Copyright 2016 FUJITSU LIMITED
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from oslotest import base
|
||||
import psutil
|
||||
|
||||
from monasca_setup.detection.plugins import zookeeper as zk
|
||||
|
||||
_DEFAULT_CFG_FILE = '/etc/zookeeper/conf/zoo.cfg'
|
||||
|
||||
|
||||
def _get_cmd(config_file=_DEFAULT_CFG_FILE):
|
||||
return 'org.apache.zookeeper.server.quorum.QuorumPeerMain {0}'.format(config_file)
|
||||
|
||||
|
||||
_ZOOKEEPER_CMD = _get_cmd()
|
||||
|
||||
|
||||
class FakeProcess(object):
|
||||
cmdLine = None
|
||||
|
||||
def as_dict(self, attrs=None):
|
||||
all_attrs = {'name': 'java',
|
||||
'exe': FakeProcess.exe(),
|
||||
'cmdline': FakeProcess.cmdline()}
|
||||
if attrs:
|
||||
for key in attrs:
|
||||
if key not in all_attrs:
|
||||
all_attrs.pop(key, None)
|
||||
return all_attrs
|
||||
|
||||
@staticmethod
|
||||
def exe():
|
||||
line = FakeProcess.cmdLine
|
||||
if not line:
|
||||
return None
|
||||
return line[0]
|
||||
|
||||
@staticmethod
|
||||
def cmdline():
|
||||
return FakeProcess.cmdLine
|
||||
|
||||
|
||||
class TestZookeeperDetection(base.BaseTestCase):
|
||||
|
||||
FAKE_CONFIG_FILE_WITH_OPTIONS = ['tickTime=2000', 'initLimit=10',
|
||||
'syncLimit=5', 'dataDir=/var/lib/zookeeper',
|
||||
'clientPortAddress=192.168.10.6', 'clientPort=2182']
|
||||
|
||||
FAKE_CONFIG_FILE_WITHOUT_OPTIONS = ['tickTime=2000', 'initLimit=10',
|
||||
'syncLimit=5', 'dataDir=/var/lib/zookeeper']
|
||||
|
||||
BUILD_CONFIG = {'init_config': None,
|
||||
'instances': [{'name': '192.168.10.6', 'host': '192.168.10.6',
|
||||
'port': 2181, 'timeout': 3}]}
|
||||
|
||||
def setUp(self):
|
||||
super(TestZookeeperDetection, self).setUp()
|
||||
with mock.patch.object(zk.Zookeeper, '_detect') as mock_detect:
|
||||
self._zk = zk.Zookeeper('zookeeper')
|
||||
self.assertTrue(mock_detect.called)
|
||||
|
||||
def test_should_not_configure_if_no_process(self):
|
||||
FakeProcess.cmdLine = []
|
||||
self._detect(proc=True)
|
||||
self.assertFalse(self._zk.available)
|
||||
|
||||
def test_should_not_configure_has_process_no_config_located(self):
|
||||
FakeProcess.cmdLine = [_ZOOKEEPER_CMD]
|
||||
self._zk._get_config_file = mock.Mock(return_value=None)
|
||||
self._detect()
|
||||
self.assertFalse(self._zk.available)
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.zookeeper.os.path.isfile')
|
||||
def test_should_be_available_if_everything_matches(self, is_f):
|
||||
FakeProcess.cmdLine = [_ZOOKEEPER_CMD]
|
||||
is_f.return_value = True
|
||||
|
||||
self._zk._get_config_file = mock.Mock(return_value=_DEFAULT_CFG_FILE)
|
||||
|
||||
self._detect()
|
||||
self.assertTrue(self._zk.available)
|
||||
|
||||
def test_should_detect_config_file_from_cmdline(self):
|
||||
FakeProcess.cmdLine = [_ZOOKEEPER_CMD]
|
||||
self.assertTrue(_DEFAULT_CFG_FILE, zk.Zookeeper._get_config_file(FakeProcess()))
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.zookeeper.os.path.isfile')
|
||||
def test_should_be_available_use_default_config_file(self, is_f):
|
||||
FakeProcess.cmdLine = [_get_cmd(config_file='')]
|
||||
is_f.return_value = True
|
||||
|
||||
self._detect()
|
||||
self.assertTrue(self._zk.available)
|
||||
|
||||
def test_should_return_default_config_file_if_in_cmdline_is_missing(self):
|
||||
FakeProcess.cmdLine = ['zookeeper']
|
||||
self.assertEqual('/etc/zookeeper/conf/zoo.cfg', zk.Zookeeper._get_config_file(FakeProcess()))
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.zookeeper.open')
|
||||
def test_should_return_options_from_config_file(self, open_file):
|
||||
open_file.return_value = self.FAKE_CONFIG_FILE_WITH_OPTIONS
|
||||
FakeProcess.cmdLine = []
|
||||
ip_address = '192.168.10.6'
|
||||
port = 2182
|
||||
self.assertEqual((ip_address, port), zk.Zookeeper._read_config_file(FakeProcess()))
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.zookeeper.open')
|
||||
def test_should_return_default_options_missing_in_config_file(self, open_file):
|
||||
open_file.return_value = self.FAKE_CONFIG_FILE_WITHOUT_OPTIONS
|
||||
FakeProcess.cmdLine = []
|
||||
ip_address = 'localhost'
|
||||
port = 2181
|
||||
self.assertEqual((ip_address, port), zk.Zookeeper._read_config_file(FakeProcess()))
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.zookeeper.open')
|
||||
def test_exception_while_parrsing_file(self, open_file):
|
||||
open_file.return_value = ['clientPort=aa']
|
||||
self.assertEqual(None, zk.Zookeeper._read_config_file(FakeProcess()))
|
||||
|
||||
def test_should_build_config(self):
|
||||
config = ('192.168.10.6', 2181)
|
||||
FakeProcess.cmdLine = [_ZOOKEEPER_CMD]
|
||||
self._zk._get_config_file = mock.Mock(return_value=None)
|
||||
self._detect()
|
||||
self._zk._read_config_file = mock.Mock(return_value=config)
|
||||
self.assertEqual(self.BUILD_CONFIG, self._zk.build_config()['zk'])
|
||||
|
||||
def _detect(self, proc=False):
|
||||
self._zk.available = False
|
||||
processes = [FakeProcess()] if not proc else []
|
||||
process_iter = mock.patch.object(psutil, 'process_iter',
|
||||
return_value=processes)
|
||||
with process_iter as mock_process_iter:
|
||||
self._zk._detect()
|
||||
self.assertTrue(mock_process_iter.called)
|
Loading…
x
Reference in New Issue
Block a user