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:
parent
5ef99e2d6f
commit
4033d3833b
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
prelude: >
|
||||
Support Zookeeper driver for Dragonflow
|
||||
features:
|
||||
- Support Zookeeper driver for Dragonflow
|
Loading…
Reference in New Issue