diff --git a/nova/api/opts.py b/nova/api/opts.py index 492b58637a18..8976afcbe2f7 100644 --- a/nova/api/opts.py +++ b/nova/api/opts.py @@ -108,7 +108,6 @@ import nova.scheduler.weights.metrics import nova.scheduler.weights.ram import nova.service import nova.servicegroup.api -import nova.servicegroup.drivers.zk import nova.spice import nova.utils import nova.vnc diff --git a/nova/exception.py b/nova/exception.py index 978747f1162a..23c3c6eb7b43 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -1607,11 +1607,6 @@ class InstanceRecreateNotSupported(Invalid): msg_fmt = _('Instance recreate is not supported.') -class ServiceGroupUnavailable(NovaException): - msg_fmt = _("The service from servicegroup driver %(driver)s is " - "temporarily unavailable.") - - class DBNotAllowed(NovaException): msg_fmt = _('%(binary)s attempted direct database access which is ' 'not allowed by policy') diff --git a/nova/opts.py b/nova/opts.py index 3a9749769757..d72798377d89 100644 --- a/nova/opts.py +++ b/nova/opts.py @@ -47,7 +47,6 @@ import nova.quota import nova.rdp import nova.service import nova.servicegroup.api -import nova.servicegroup.drivers.zk import nova.spice import nova.utils import nova.volume @@ -110,5 +109,4 @@ def list_opts(): [nova.consoleauth.rpcapi.rpcapi_cap_opt], )), ('workarounds', nova.utils.workarounds_opts), - ('zookeeper', nova.servicegroup.drivers.zk.zk_driver_opts) ] diff --git a/nova/servicegroup/api.py b/nova/servicegroup/api.py index 0197b736e17b..2a09baa42f6f 100644 --- a/nova/servicegroup/api.py +++ b/nova/servicegroup/api.py @@ -26,7 +26,6 @@ LOG = logging.getLogger(__name__) _driver_name_class_mapping = { 'db': 'nova.servicegroup.drivers.db.DbDriver', - 'zk': 'nova.servicegroup.drivers.zk.ZooKeeperDriver', 'mc': 'nova.servicegroup.drivers.mc.MemcachedDriver' } _default_driver = 'db' diff --git a/nova/servicegroup/drivers/zk.py b/nova/servicegroup/drivers/zk.py deleted file mode 100644 index 6962831ecbde..000000000000 --- a/nova/servicegroup/drivers/zk.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright (c) AT&T 2012-2013 Yun Mao -# Copyright 2012 IBM Corp. -# -# 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 os - -import eventlet -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import importutils - -from nova import exception -from nova.i18n import _LE, _LW -from nova.servicegroup.drivers import base - -evzookeeper = importutils.try_import('evzookeeper') -membership = importutils.try_import('evzookeeper.membership') -zookeeper = importutils.try_import('zookeeper') - -zk_driver_opts = [ - cfg.StrOpt('address', - help='The ZooKeeper addresses for servicegroup service in the ' - 'format of host1:port,host2:port,host3:port'), - cfg.IntOpt('recv_timeout', - default=4000, - help='The recv_timeout parameter for the zk session'), - cfg.StrOpt('sg_prefix', - default="/servicegroups", - help='The prefix used in ZooKeeper to store ephemeral nodes'), - cfg.IntOpt('sg_retry_interval', - default=5, - help='Number of seconds to wait until retrying to join the ' - 'session'), - ] - -CONF = cfg.CONF -CONF.register_opts(zk_driver_opts, group="zookeeper") - -LOG = logging.getLogger(__name__) - - -class ZooKeeperDriver(base.Driver): - """ZooKeeper driver for the service group API.""" - - def __init__(self, *args, **kwargs): - """Create the zk session object.""" - if not all([evzookeeper, membership, zookeeper]): - raise ImportError('zookeeper module not found') - self._memberships = {} - self._monitors = {} - super(ZooKeeperDriver, self).__init__() - self._cached_session = None - - @property - def _session(self): - """Creates zookeeper session in lazy manner. - - Session is created in lazy manner to mitigate lock problem - in zookeeper. - - Lock happens when many processes try to use the same zk handle. - Lazy creation allows to deffer initialization of session until - is really required by worker (child process). - - :returns: ZKSession -- new or created earlier - """ - if self._cached_session is None: - self._cached_session = self._init_session() - return self._cached_session - - def _init_session(self): - """Initializes new session. - - Optionally creates required servicegroup prefix. - - :returns ZKSession - newly created session - """ - with open(os.devnull, "w") as null: - session = evzookeeper.ZKSession( - CONF.zookeeper.address, - recv_timeout=CONF.zookeeper.recv_timeout, - zklog_fd=null) - # Make sure the prefix exists - try: - session.create(CONF.zookeeper.sg_prefix, "", - acl=[evzookeeper.ZOO_OPEN_ACL_UNSAFE]) - except zookeeper.NodeExistsException: - pass - # Log a warning about quality for this driver. - LOG.warning(_LW('The ZooKeeper service group driver in Nova is not ' - 'tested by the OpenStack project and thus its quality ' - 'can not be ensured. This may change in the future, ' - 'but current deployers should be aware that the use ' - 'of it in production right now may be risky.')) - return session - - def join(self, member, group, service=None): - """Add a new member to a service group. - - :param member: the joined member ID/name - :param group: the group ID/name, of the joined member - :param service: a `nova.service.Service` object - """ - process_id = str(os.getpid()) - LOG.debug('ZooKeeperDriver: join new member %(id)s(%(pid)s) to the ' - '%(gr)s group, service=%(sr)s', - {'id': member, 'pid': process_id, - 'gr': group, 'sr': service}) - member = self._memberships.get((group, member), None) - if member is None: - # the first time to join. Generate a new object - path = "%s/%s/%s" % (CONF.zookeeper.sg_prefix, group, member) - try: - zk_member = membership.Membership(self._session, path, - process_id) - except RuntimeError: - LOG.exception(_LE("Unable to join. It is possible that either" - " another node exists with the same name, or" - " this node just restarted. We will try " - "again in a short while to make sure.")) - eventlet.sleep(CONF.zookeeper.sg_retry_interval) - zk_member = membership.Membership(self._session, path, member) - self._memberships[(group, member)] = zk_member - - def is_up(self, service_ref): - group_id = service_ref['topic'] - member_id = service_ref['host'] - all_members = self._get_all(group_id) - return member_id in all_members - - def _get_all(self, group_id): - """Return all members in a list, or a ServiceGroupUnavailable - exception. - """ - monitor = self._monitors.get(group_id, None) - if monitor is None: - path = "%s/%s" % (CONF.zookeeper.sg_prefix, group_id) - - with open(os.devnull, "w") as null: - local_session = evzookeeper.ZKSession( - CONF.zookeeper.address, - recv_timeout=CONF.zookeeper.recv_timeout, - zklog_fd=null) - - monitor = membership.MembershipMonitor(local_session, path) - self._monitors[group_id] = monitor - # Note(maoy): When initialized for the first time, it takes a - # while to retrieve all members from zookeeper. To prevent - # None to be returned, we sleep 5 sec max to wait for data to - # be ready. - timeout = 5 # seconds - interval = 0.1 - tries = int(timeout / interval) - for _retry in range(tries): - eventlet.sleep(interval) - all_members = monitor.get_all() - if all_members is not None: - # Stop the tries once the cache is populated - LOG.debug('got info about members in %r: %r', - path, ', '.join(all_members)) - break - else: - # if all_members, weren't populated - LOG.warning(_LW('Problem with acquiring the list of ' - 'children of %(path)r within a given ' - 'timeout=%(timeout)d seconds'), - path, timeout) - else: - all_members = monitor.get_all() - - if all_members is None: - raise exception.ServiceGroupUnavailable(driver="ZooKeeperDriver") - - def have_processes(member): - """Predicate that given member has processes (subnode exists).""" - value, stat = monitor.get_member_details(member) - # only check nodes that are created by Membership class - if value == 'ZKMembers': - num_children = stat['numChildren'] - return num_children > 0 - else: - # unknown type of node found - ignoring - return False - - # filter only this members that have processes running - all_members = filter(have_processes, all_members) - - return all_members diff --git a/nova/tests/unit/servicegroup/test_zk_driver.py b/nova/tests/unit/servicegroup/test_zk_driver.py deleted file mode 100644 index 1af71902d9ce..000000000000 --- a/nova/tests/unit/servicegroup/test_zk_driver.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) AT&T 2012-2013 Yun Mao -# Copyright 2012 IBM Corp. -# -# 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. - -"""Test the ZooKeeper driver for servicegroup. - -You need to install ZooKeeper locally and related dependencies -to run the test. It's unclear how to install python-zookeeper lib -in venv so you might have to run the test without it. - -To set up in Ubuntu 12.04: -$ sudo apt-get install zookeeper zookeeperd python-zookeeper -$ sudo pip install evzookeeper -$ nosetests nova.tests.unit.servicegroup.test_zk_driver -""" -import os - -import mock - -from nova import servicegroup -from nova.servicegroup.drivers import zk -from nova import test - - -class ZKServiceGroupTestCase(test.NoDBTestCase): - - def setUp(self): - super(ZKServiceGroupTestCase, self).setUp() - self.flags(servicegroup_driver='zk') - self.flags(address='localhost:2181', group="zookeeper") - try: - __import__('evzookeeper') - __import__('zookeeper') - except ImportError: - self.skipTest("Unable to test due to lack of ZooKeeper") - - # Need to do this here, as opposed to the setUp() method, otherwise - # the decorate will cause an import error... - @mock.patch('evzookeeper.ZKSession') - def _setup_sg_api(self, zk_sess_mock): - self.zk_sess = mock.MagicMock() - zk_sess_mock.return_value = self.zk_sess - self.flags(servicegroup_driver='zk') - self.flags(address='ignored', group="zookeeper") - self.servicegroup_api = servicegroup.API() - - def test_zookeeper_hierarchy_structure(self): - """Test that hierarchy created by join method contains process id.""" - from zookeeper import NoNodeException - self.servicegroup_api = servicegroup.API() - service_id = {'topic': 'unittest', 'host': 'serviceC'} - # use existing session object - session = self.servicegroup_api._driver._session - # prepare a path that contains process id - pid = os.getpid() - path = '/servicegroups/%s/%s/%s' % (service_id['topic'], - service_id['host'], - pid) - # assert that node doesn't exist yet - self.assertRaises(NoNodeException, session.get, path) - # join - self.servicegroup_api.join(service_id['host'], - service_id['topic'], - None) - # expected existing "process id" node - self.assertTrue(session.get(path)) - - def test_lazy_session(self): - """Session object (contains zk handle) should be created in - lazy manner, because handle cannot be shared by forked processes. - """ - # insied import because this test runs conditionaly (look at setUp) - import evzookeeper - driver = zk.ZooKeeperDriver() - # check that internal private attribute session is empty - self.assertIsNone(driver.__dict__['_ZooKeeperDriver__session']) - # after first use of property ... - driver._session - # check that internal private session attribute is ready - self.assertIsInstance(driver.__dict__['_ZooKeeperDriver__session'], - evzookeeper.ZKSession) - - @mock.patch('evzookeeper.membership.Membership') - def test_join(self, mem_mock): - self._setup_sg_api() - mem_mock.return_value = mock.sentinel.zk_mem - self.servicegroup_api.join('fake-host', 'fake-topic') - mem_mock.assert_called_once_with(self.zk_sess, - '/fake-topic', - 'fake-host') diff --git a/releasenotes/notes/zookeeper-servicegroup-driver-removed-c3bcaa6f9fe976ed.yaml b/releasenotes/notes/zookeeper-servicegroup-driver-removed-c3bcaa6f9fe976ed.yaml new file mode 100644 index 000000000000..5f5b6aa89e80 --- /dev/null +++ b/releasenotes/notes/zookeeper-servicegroup-driver-removed-c3bcaa6f9fe976ed.yaml @@ -0,0 +1,15 @@ +--- +upgrade: + - | + The Zookeeper Service Group driver has been removed. + + The driver has no known users and is not actively mantained. A warning log + message about the driver's state was added for the Kilo release. Also, + evzookeeper library that the driver depends on is unmaintained and + `incompatible with recent eventlet releases`_. + + A future release of Nova will `use the Tooz library to track + service liveliness`_, and Tooz supports Zookeeper. + + .. _`incompatible with recent eventlet releases`: https://bugs.launchpad.net/nova/+bug/1443910 + .. _`use the Tooz library to track service liveliness`: http://specs.openstack.org/openstack/nova-specs/specs/liberty/approved/service-group-using-tooz.html