Add Zookeeper driver for DB

The devstack only supports ubuntu,
because I don't have other envs.

I'll update devstack scripts when I set
up other envs.

To solve the stateful client bug, lazy
initialization is introduced.

Change-Id: I3e74601f2b867b695578f18be2faf7efc33bc7aa
Implements: blueprint zookeeper-driver
Closes-Bug: #1534504
This commit is contained in:
Li Ma 2015-12-26 13:31:30 +08:00
parent 5ef99e2d6f
commit 4033d3833b
5 changed files with 253 additions and 0 deletions

View File

@ -37,6 +37,10 @@ if is_service_enabled df-ramcloud ; then
source $DEST/dragonflow/devstack/ramcloud_driver
NB_DRIVER_CLASS="dragonflow.db.drivers.ramcloud_db_driver.RamCloudDbDriver"
fi
if is_service_enabled df-zookeeper ; then
source $DEST/dragonflow/devstack/zookeeper_driver
NB_DRIVER_CLASS="dragonflow.db.drivers.zookeeper_db_driver.ZookeeperDbDriver"
fi
# Dragonflow installation uses functions from these files
source $TOP_DIR/lib/neutron_plugins/ovs_base

73
devstack/zookeeper_driver Normal file
View File

@ -0,0 +1,73 @@
#!/bin/bash
#
#
# ``plugin.sh`` calls the following methods in the sourced driver:
#
# - nb_db_driver_install_server
# - nb_db_driver_install_client
# - nb_db_driver_start_server
# - nb_db_driver_stop_server
# - nb_db_driver_clean
HOSTNAME=`hostname -f`
ZOOKEEPER_IP=${REMOTE_DB_IP:-${HOST_IP}}
ZOOKEEPER_PORT=${REMOTE_DB_PORT:-2181}
ZOOKEEPER_DATA_DIR="/var/lib/zookeeper"
ZOOKEEPER_LOG_DIR="/var/log/zookeeper"
ZOOKEEPER_DIR="/etc/zookeeper"
ZOOKEEPER_CONF_DIR="${ZOOKEEPER_DIR}/conf"
ZOOKEEPER_CONF_FILE="${ZOOKEEPER_CONF_DIR}/zoo.cfg"
ZOOKEEPER_TABLE_NAMES=${ZOOKEEPER_TABLE_NAMES:-'secgroup','dragonflow','chassis','lswitch','lport','lrouter','tunnel_key'}
function nb_db_driver_install_server {
if is_service_enabled df-zookeeper ; then
if is_ubuntu; then
echo "Installing Zookeeper server"
sudo mkdir -p $ZOOKEEPER_DATA_DIR
sudo mkdir -p $ZOOKEEPER_LOG_DIR
sudo mkdir -p $ZOOKEEPER_CONF_DIR
install_package "zookeeperd"
sudo service zookeeper stop || true
echo "Configuring Zookeeper"
sudo sed -i "/^dataDir=/c dataDir=${ZOOKEEPER_DATA_DIR}" $ZOOKEEPER_CONF_FILE
sudo sed -i "/^dataLogDir=/c dataLogDir=${ZOOKEEPER_LOG_DIR}" $ZOOKEEPER_CONF_FILE
sudo sed -i "/^server.1=/c server.1=${HOSTNAME}:2888:3888" $ZOOKEEPER_CONF_FILE
echo "1" | sudo tee $ZOOKEEPER_CONF_DIR/myid
else
die $LINENO "Other distributions are not supported"
fi
fi
}
function nb_db_driver_install_client {
echo "Installing Kazoo client"
sudo pip install kazoo
}
function nb_db_driver_status_server
{
if is_service_enabled df-zookeeper ; then
TEMP_PIDS=`pgrep -f "zookeeper"`
if [ -z "$TEMP_PIDS" ]; then
return 1
fi
fi
return 0
}
function nb_db_driver_start_server {
if is_service_enabled df-zookeeper ; then
if is_ubuntu; then
sudo service zookeeper restart
fi
fi
}
function nb_db_driver_stop_server {
if is_service_enabled df-zookeeper ; then
if is_ubuntu; then
sudo service zookeeper stop || true
fi
fi
}

View File

@ -99,6 +99,7 @@ class DbApi(object):
:param table: table name
:type table: string
:returns: list of values
:raises: DragonflowException.DBKeyNotFound if key not found
"""
@abc.abstractmethod

View File

@ -0,0 +1,170 @@
# 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 kazoo
from kazoo.client import KazooClient
from kazoo.handlers.eventlet import SequentialEventletHandler
from kazoo.retry import KazooRetry
from oslo_log import log
import six
from dragonflow.common import exceptions as df_exceptions
from dragonflow.db import db_api
LOG = log.getLogger(__name__)
ROOT_NS = '/openstack'
CLIENT_CONNECTION_RETRIES = -1
def _parse_hosts(hosts):
if isinstance(hosts, six.string_types):
return hosts.strip()
if isinstance(hosts, (dict)):
host_ports = []
for (k, v) in six.iteritems(hosts):
host_ports.append("%s:%s" % (k, v))
hosts = host_ports
if isinstance(hosts, (list, set, tuple)):
return ",".join([str(h) for h in hosts])
return hosts
class ZookeeperDbDriver(db_api.DbApi):
def __init__(self):
super(ZookeeperDbDriver, self).__init__()
self.client = None
self.db_ip = None
self.db_port = None
self.config = None
def initialize(self, db_ip, db_port, **args):
self.db_ip = db_ip
self.db_port = db_port
self.config = args['config']
def _lazy_initialize(self):
if not self.client:
hosts = _parse_hosts(self.config.remote_db_hosts)
_handler = SequentialEventletHandler()
_retry = KazooRetry(max_tries=CLIENT_CONNECTION_RETRIES,
delay=0.5,
backoff=2,
sleep_func=_handler.sleep_func)
self.client = KazooClient(hosts=hosts,
handler=_handler,
connection_retry=_retry)
self.client.start()
self.client.ensure_path(ROOT_NS)
def support_publish_subscribe(self):
return False
def _generate_path(self, table, key):
if not key:
return ROOT_NS + '/' + table
else:
return ROOT_NS + '/' + table + '/' + key
def get_key(self, table, key):
path = self._generate_path(table, key)
try:
self._lazy_initialize()
ret = self.client.get(path)[0]
return ret
except kazoo.exceptions.NoNodeError:
raise df_exceptions.DBKeyNotFound(key=key)
def set_key(self, table, key, value):
path = self._generate_path(table, key)
try:
self._lazy_initialize()
self.client.set(path, value)
except kazoo.exceptions.NoNodeError:
raise df_exceptions.DBKeyNotFound(key=key)
except kazoo.exceptions.ZookeeperError as e:
LOG.exception(e)
def create_key(self, table, key, value):
path = self._generate_path(table, key)
try:
self._lazy_initialize()
self.client.create(path, value, makepath=True)
except kazoo.exceptions.ZookeeperError as e:
LOG.exception(e)
def delete_key(self, table, key):
path = self._generate_path(table, key)
try:
self._lazy_initialize()
self.client.delete(path)
except kazoo.exceptions.NoNodeError:
raise df_exceptions.DBKeyNotFound(key=key)
def get_all_entries(self, table):
res = []
path = self._generate_path(table, None)
try:
self._lazy_initialize()
directory = self.client.get_children(path)
for key in directory:
res.append(self.get_key(table, key))
except kazoo.exceptions.NoNodeError:
raise df_exceptions.DBKeyNotFound(key=table)
return res
def get_all_keys(self, table):
path = self._generate_path(table, None)
try:
self._lazy_initialize()
return self.client.get_children(path)
except kazoo.exceptions.NoNodeError:
raise df_exceptions.DBKeyNotFound(key=table)
def _allocate_unique_key(self):
path = self._generate_path('tunnel_key', 'key')
self.client.ensure_path(path)
prev_value = 0
version_exception = True
while version_exception:
try:
prev_value, stat = self.client.get(path)
prev_value = int(prev_value)
prev_version = stat.version
self.client.set(path, str(prev_value + 1), prev_version)
return prev_value + 1
except kazoo.exceptions.BadVersionError:
version_exception = True
except kazoo.exceptions.NoNodeError:
self.client.create(path, "1", makepath=True)
return 1
def allocate_unique_key(self):
self._lazy_initialize()
return self._allocate_unique_key()
def register_notification_callback(self, callback):
#NOTE(nick-ma-z): The pub-sub mechanism is not initially supported.
# The watcher function of Zookeeper only supports
# one-time trigger. You have to continuously register
# watchers for each children. Moreover, the delay
# between trigger and registration causes lose of
# events. The DataWatch of Kazoo is also not that
# stable and easy to use. Thanks to build-in pub-sub
# of dragonflow, we don't need to work hard on zk side.
# Please set 'use_df_pub_sub=True' in the configuration
# to enable the build-in pub-sub function.
return

View File

@ -0,0 +1,5 @@
---
prelude: >
Support Zookeeper driver for Dragonflow
features:
- Support Zookeeper driver for Dragonflow