Updates Octavia to support octavia-lib

This is the base patch that updates octavia to use the new octavia-lib.
It is backwards compatible by using debtcollector moves.

It adds a new controller process called the "driver-agent".

This patch also adds unit test coverage for a few additional modules.

Depends-On: https://review.openstack.org/#/c/641180/

Change-Id: I438e1548ec0fb6111d1ab85b05015007d9d0a006
This commit is contained in:
Michael Johnson 2018-10-26 17:05:22 -07:00 committed by Carlos Goncalves
parent c00faa7edd
commit 8997def2b5
42 changed files with 1594 additions and 2032 deletions

View File

@ -23,7 +23,7 @@ For example
For example For example
ENABLED_SERVICES+=octavia,o-api,o-cw,o-hk,o-hm ENABLED_SERVICES+=octavia,o-api,o-cw,o-hk,o-hm,o-da
For more information, see the "Externally Hosted Plugins" section of For more information, see the "Externally Hosted Plugins" section of
https://docs.openstack.org/devstack/latest/plugins.html https://docs.openstack.org/devstack/latest/plugins.html

View File

@ -45,7 +45,7 @@ ENABLED_SERVICES+=,neutron-metadata-agent,neutron-qos
# Tempest (optional) # Tempest (optional)
#ENABLED_SERVICES+=,tempest #ENABLED_SERVICES+=,tempest
# Octavia # Octavia
ENABLED_SERVICES+=,octavia,o-api,o-cw,o-hm,o-hk ENABLED_SERVICES+=,octavia,o-api,o-cw,o-hm,o-hk,o-da
EOF EOF
# Create the stack user # Create the stack user

View File

@ -29,6 +29,15 @@ function octaviaclient_install {
fi fi
} }
function octavia_lib_install {
if use_library_from_git "octavia-lib"; then
git_clone_by_name "octavia-lib"
setup_dev_lib "octavia-lib"
else
pip_install_gr octavia-lib
fi
}
function install_diskimage_builder { function install_diskimage_builder {
if use_library_from_git "diskimage-builder"; then if use_library_from_git "diskimage-builder"; then
GITREPO["diskimage-builder"]=$DISKIMAGE_BUILDER_REPO_URL GITREPO["diskimage-builder"]=$DISKIMAGE_BUILDER_REPO_URL
@ -209,6 +218,9 @@ function octavia_configure {
sudo mkdir -m 755 -p $OCTAVIA_CONF_DIR sudo mkdir -m 755 -p $OCTAVIA_CONF_DIR
safe_chown $STACK_USER $OCTAVIA_CONF_DIR safe_chown $STACK_USER $OCTAVIA_CONF_DIR
sudo mkdir -m 700 -p $OCTAVIA_RUN_DIR
safe_chown $STACK_USER $OCTAVIA_RUN_DIR
if ! [ -e $OCTAVIA_CONF ] ; then if ! [ -e $OCTAVIA_CONF ] ; then
cp $OCTAVIA_DIR/etc/octavia.conf $OCTAVIA_CONF cp $OCTAVIA_DIR/etc/octavia.conf $OCTAVIA_CONF
fi fi
@ -477,6 +489,7 @@ function octavia_start {
run_process $OCTAVIA_API "$OCTAVIA_API_BINARY $OCTAVIA_API_ARGS" run_process $OCTAVIA_API "$OCTAVIA_API_BINARY $OCTAVIA_API_ARGS"
fi fi
run_process $OCTAVIA_DRIVER_AGENT "$OCTAVIA_DRIVER_AGENT_BINARY $OCTAVIA_DRIVER_AGENT_ARGS"
run_process $OCTAVIA_CONSUMER "$OCTAVIA_CONSUMER_BINARY $OCTAVIA_CONSUMER_ARGS" run_process $OCTAVIA_CONSUMER "$OCTAVIA_CONSUMER_BINARY $OCTAVIA_CONSUMER_ARGS"
run_process $OCTAVIA_HOUSEKEEPER "$OCTAVIA_HOUSEKEEPER_BINARY $OCTAVIA_HOUSEKEEPER_ARGS" run_process $OCTAVIA_HOUSEKEEPER "$OCTAVIA_HOUSEKEEPER_BINARY $OCTAVIA_HOUSEKEEPER_ARGS"
run_process $OCTAVIA_HEALTHMANAGER "$OCTAVIA_HEALTHMANAGER_BINARY $OCTAVIA_HEALTHMANAGER_ARGS" run_process $OCTAVIA_HEALTHMANAGER "$OCTAVIA_HEALTHMANAGER_BINARY $OCTAVIA_HEALTHMANAGER_ARGS"
@ -489,6 +502,7 @@ function octavia_stop {
else else
stop_process $OCTAVIA_API stop_process $OCTAVIA_API
fi fi
stop_process $OCTAVIA_DRIVER_AGENT
stop_process $OCTAVIA_CONSUMER stop_process $OCTAVIA_CONSUMER
stop_process $OCTAVIA_HOUSEKEEPER stop_process $OCTAVIA_HOUSEKEEPER
stop_process $OCTAVIA_HEALTHMANAGER stop_process $OCTAVIA_HEALTHMANAGER
@ -523,6 +537,9 @@ function octavia_cleanup {
if [ ${OCTAVIA_CONF_DIR}x != x ] ; then if [ ${OCTAVIA_CONF_DIR}x != x ] ; then
sudo rm -rf ${OCTAVIA_CONF_DIR} sudo rm -rf ${OCTAVIA_CONF_DIR}
fi fi
if [ ${OCTAVIA_RUN_DIR}x != x ] ; then
sudo rm -rf ${OCTAVIA_RUN_DIR}
fi
if [ ${OCTAVIA_AMP_SSH_KEY_PATH}x != x ] ; then if [ ${OCTAVIA_AMP_SSH_KEY_PATH}x != x ] ; then
rm -f ${OCTAVIA_AMP_SSH_KEY_PATH} ${OCTAVIA_AMP_SSH_KEY_PATH}.pub rm -f ${OCTAVIA_AMP_SSH_KEY_PATH} ${OCTAVIA_AMP_SSH_KEY_PATH}.pub
fi fi
@ -635,6 +652,7 @@ if is_service_enabled $OCTAVIA; then
if [[ "$1" == "stack" && "$2" == "install" ]]; then if [[ "$1" == "stack" && "$2" == "install" ]]; then
# Perform installation of service source # Perform installation of service source
echo_summary "Installing octavia" echo_summary "Installing octavia"
octavia_lib_install
octavia_install octavia_install
octaviaclient_install octaviaclient_install

View File

@ -61,6 +61,7 @@ enable_service o-hm
enable_service o-hk enable_service o-hk
enable_service o-api enable_service o-api
enable_service o-api-ha enable_service o-api-ha
enable_service o-da
OCTAVIA_USE_PREGENERATED_CERTS=True OCTAVIA_USE_PREGENERATED_CERTS=True
OCTAVIA_USE_PREGENERATED_SSH_KEY=True OCTAVIA_USE_PREGENERATED_SSH_KEY=True

View File

@ -73,6 +73,7 @@ enable_service o-cw
enable_service o-hm enable_service o-hm
enable_service o-hk enable_service o-hk
enable_service o-api enable_service o-api
enable_service o-da
# enable DVR # enable DVR

View File

@ -16,6 +16,7 @@ OCTAVIA_DHCLIENT_CONF=${OCTAVIA_DHCLIENT_CONF:-${OCTAVIA_DHCLIENT_DIR}/dhclient.
OCTAVIA_CONF=${OCTAVIA_CONF:-${OCTAVIA_CONF_DIR}/octavia.conf} OCTAVIA_CONF=${OCTAVIA_CONF:-${OCTAVIA_CONF_DIR}/octavia.conf}
OCTAVIA_AUDIT_MAP=${OCTAVIA_AUDIT_MAP:-${OCTAVIA_CONF_DIR}/octavia_api_audit_map.conf} OCTAVIA_AUDIT_MAP=${OCTAVIA_AUDIT_MAP:-${OCTAVIA_CONF_DIR}/octavia_api_audit_map.conf}
OCTAVIA_TEMPEST_DIR=${OCTAVIA_TEMPEST_DIR:-${OCTAVIA_DIR}/octavia/tests/tempest} OCTAVIA_TEMPEST_DIR=${OCTAVIA_TEMPEST_DIR:-${OCTAVIA_DIR}/octavia/tests/tempest}
OCTAVIA_RUN_DIR=${OCTAVIA_RUN_DIR:-"/var/run/octavia"}
OCTAVIA_AMPHORA_DRIVER=${OCTAVIA_AMPHORA_DRIVER:-"amphora_haproxy_rest_driver"} OCTAVIA_AMPHORA_DRIVER=${OCTAVIA_AMPHORA_DRIVER:-"amphora_haproxy_rest_driver"}
OCTAVIA_NETWORK_DRIVER=${OCTAVIA_NETWORK_DRIVER:-"allowed_address_pairs_driver"} OCTAVIA_NETWORK_DRIVER=${OCTAVIA_NETWORK_DRIVER:-"allowed_address_pairs_driver"}
@ -61,11 +62,13 @@ OCTAVIA_API_BINARY=${OCTAVIA_API_BINARY:-${OCTAVIA_BIN_DIR}/octavia-api}
OCTAVIA_CONSUMER_BINARY=${OCTAVIA_CONSUMER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-worker} OCTAVIA_CONSUMER_BINARY=${OCTAVIA_CONSUMER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-worker}
OCTAVIA_HOUSEKEEPER_BINARY=${OCTAVIA_HOUSEKEEPER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-housekeeping} OCTAVIA_HOUSEKEEPER_BINARY=${OCTAVIA_HOUSEKEEPER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-housekeeping}
OCTAVIA_HEALTHMANAGER_BINARY=${OCTAVIA_HEALTHMANAGER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-health-manager} OCTAVIA_HEALTHMANAGER_BINARY=${OCTAVIA_HEALTHMANAGER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-health-manager}
OCTAVIA_DRIVER_AGENT_BINARY=${OCTAVIA_DRIVER_AGENT_BINARY:-${OCTAVIA_BIN_DIR}/octavia-driver-agent}
OCTAVIA_API_ARGS=${OCTAVIA_API_ARGS:-" --config-file $OCTAVIA_CONF"} OCTAVIA_API_ARGS=${OCTAVIA_API_ARGS:-" --config-file $OCTAVIA_CONF"}
OCTAVIA_CONSUMER_ARGS=${OCTAVIA_CONSUMER_ARGS:-" --config-file $OCTAVIA_CONF"} OCTAVIA_CONSUMER_ARGS=${OCTAVIA_CONSUMER_ARGS:-" --config-file $OCTAVIA_CONF"}
OCTAVIA_HOUSEKEEPER_ARGS=${OCTAVIA_HOUSEKEEPER_ARGS:-" --config-file $OCTAVIA_CONF"} OCTAVIA_HOUSEKEEPER_ARGS=${OCTAVIA_HOUSEKEEPER_ARGS:-" --config-file $OCTAVIA_CONF"}
OCTAVIA_HEALTHMANAGER_ARGS=${OCTAVIA_HEALTHMANAGER_ARGS:-" --config-file $OCTAVIA_CONF"} OCTAVIA_HEALTHMANAGER_ARGS=${OCTAVIA_HEALTHMANAGER_ARGS:-" --config-file $OCTAVIA_CONF"}
OCTAVIA_DRIVER_AGENT_ARGS=${OCTAVIA_DRIVER_AGENT_ARGS:-" --config-file $OCTAVIA_CONF"}
OCTAVIA_TEMPEST=${OCTAVIA_TEMPEST:-"disabled"} OCTAVIA_TEMPEST=${OCTAVIA_TEMPEST:-"disabled"}
@ -75,12 +78,18 @@ OCTAVIA_HOUSEKEEPER="o-hk"
OCTAVIA_HEALTHMANAGER="o-hm" OCTAVIA_HEALTHMANAGER="o-hm"
OCTAVIA_SERVICE="octavia" OCTAVIA_SERVICE="octavia"
OCTAVIA_API_HAPROXY="o-api-ha" OCTAVIA_API_HAPROXY="o-api-ha"
OCTAVIA_DRIVER_AGENT="o-da"
# Client settings # Client settings
GITREPO["python-octaviaclient"]=${OCTAVIACLIENT_REPO:-${GIT_BASE}/openstack/python-octaviaclient.git} GITREPO["python-octaviaclient"]=${OCTAVIACLIENT_REPO:-${GIT_BASE}/openstack/python-octaviaclient.git}
GITBRANCH["python-octaviaclient"]=${OCTAVIACLIENT_BRANCH:-master} GITBRANCH["python-octaviaclient"]=${OCTAVIACLIENT_BRANCH:-master}
GITDIR["python-octaviaclient"]=$DEST/python-octaviaclient GITDIR["python-octaviaclient"]=$DEST/python-octaviaclient
# Library settings
GITREPO["octavia-lib"]=${OCTAVIA_LIB_REPO:-${GIT_BASE}/openstack/octavia-lib.git}
GITBRANCH["octavia-lib"]=${OCTAVIA_LIB_BRANCH:-master}
GITDIR["octavia-lib"]=$DEST/octavia-lib
NEUTRON_LBAAS_DIR=$DEST/neutron-lbaas NEUTRON_LBAAS_DIR=$DEST/neutron-lbaas
NEUTRON_LBAAS_CONF=$NEUTRON_CONF_DIR/neutron_lbaas.conf NEUTRON_LBAAS_CONF=$NEUTRON_CONF_DIR/neutron_lbaas.conf
OCTAVIA_SERVICE_PROVIDER=${OCTAVIA_SERVICE_PROVIDER:-"LOADBALANCERV2:Octavia:neutron_lbaas.drivers.octavia.driver.OctaviaDriver:default"} OCTAVIA_SERVICE_PROVIDER=${OCTAVIA_SERVICE_PROVIDER:-"LOADBALANCERV2:Octavia:neutron_lbaas.drivers.octavia.driver.OctaviaDriver:default"}

View File

@ -55,10 +55,11 @@ Provider drivers should only access the following Octavia APIs. All other
Octavia APIs are not considered stable or safe for provider driver use and Octavia APIs are not considered stable or safe for provider driver use and
may change at any time. may change at any time.
* octavia.api.drivers.data_models * octavia_lib.api.drivers.data_models
* octavia.api.drivers.driver_lib * octavia_lib.api.drivers.driver_lib
* octavia.api.drivers.exceptions * octavia_lib.api.drivers.exceptions
* octavia.api.drivers.provider_base * octavia_lib.api.drivers.provider_base
* octavia_lib.common.constants
Octavia Provider Driver API Octavia Provider Driver API
=========================== ===========================
@ -1695,7 +1696,7 @@ Driver Support Library
Provider drivers need support for updating provisioning status, operating Provider drivers need support for updating provisioning status, operating
status, and statistics. Drivers will not directly use database operations, status, and statistics. Drivers will not directly use database operations,
and instead will callback to Octavia using a new API. and instead will callback to octavia-lib using a new API.
.. warning:: .. warning::
@ -1708,7 +1709,7 @@ and instead will callback to Octavia using a new API.
This library is interim and will be removed when the driver support endpoint This library is interim and will be removed when the driver support endpoint
is made available. At which point drivers will not import any code from is made available. At which point drivers will not import any code from
Octavia. octavia-lib.
Update Provisioning and Operating Status API Update Provisioning and Operating Status API
-------------------------------------------- --------------------------------------------
@ -1723,6 +1724,13 @@ and operating status parameters are as defined by Octavia status codes. If an
existing object is not included in the input parameter, the status remains existing object is not included in the input parameter, the status remains
unchanged. unchanged.
.. note::
If the driver-agent exceeds its configured `status_max_processes` this call
may block while it waits for a status process slot to become available.
The operator will be notified if the driver-agent approaches or reaches
the configured limit.
provisioning_status: status associated with lifecycle of the provisioning_status: status associated with lifecycle of the
resource. See `Octavia Provisioning Status Codes <https://developer.openstack.org/api-ref/load-balancer/v2/index.html#provisioning-status-codes>`_. resource. See `Octavia Provisioning Status Codes <https://developer.openstack.org/api-ref/load-balancer/v2/index.html#provisioning-status-codes>`_.
@ -1765,6 +1773,13 @@ with multiple listener statistics is used to update statistics in a single
call. If an existing listener is not included, the statistics that object call. If an existing listener is not included, the statistics that object
remain unchanged. remain unchanged.
.. note::
If the driver-agent exceeds its configured `stats_max_processes` this call
may block while it waits for a stats process slot to become available.
The operator will be notified if the driver-agent approaches or reaches
the configured limit.
The general form of the input dictionary is a list of listener statistics: The general form of the input dictionary is a list of listener statistics:
.. code-block:: python .. code-block:: python

View File

@ -83,10 +83,9 @@ It is also possible to use Octavia as a Neutron LBaaS plugin, in the same way
as any other vendor. You can think of Octavia as an "open source vendor" for as any other vendor. You can think of Octavia as an "open source vendor" for
Neutron LBaaS. Neutron LBaaS.
Soon, Octavia will support third-party vendor drivers just like Neutron LBaaS, Octavia supports third-party vendor drivers just like Neutron LBaaS,
and will then fully replace Neutron LBaaS as the load balancing solution for and fully replaces Neutron LBaaS as the load balancing solution for
OpenStack. At that time, third-party vendor drivers that presently "plug in" to OpenStack.
Neutron LBaaS will plug in to Octavia instead.
For further information on OpenStack Neutron LBaaS deprecation, please refer to For further information on OpenStack Neutron LBaaS deprecation, please refer to
https://wiki.openstack.org/wiki/Neutron/LBaaS/Deprecation. https://wiki.openstack.org/wiki/Neutron/LBaaS/Deprecation.
@ -119,7 +118,7 @@ A 10,000-foot overview of Octavia components
:width: 660px :width: 660px
:alt: Octavia Component Overview :alt: Octavia Component Overview
Octavia version 0.9 consists of the following major components: Octavia version 4.0 consists of the following major components:
* **amphorae** - Amphorae are the individual virtual machines, containers, or * **amphorae** - Amphorae are the individual virtual machines, containers, or
bare metal servers that accomplish the delivery of load balancing services to bare metal servers that accomplish the delivery of load balancing services to
@ -128,7 +127,7 @@ Octavia version 0.9 consists of the following major components:
HAProxy. HAProxy.
* **controller** - The Controller is the "brains" of Octavia. It consists of * **controller** - The Controller is the "brains" of Octavia. It consists of
four sub-components, which are individual daemons. They can be run on five sub-components, which are individual daemons. They can be run on
separate back-end infrastructure if desired: separate back-end infrastructure if desired:
* **API Controller** - As the name implies, this subcomponent runs Octavia's * **API Controller** - As the name implies, this subcomponent runs Octavia's
@ -147,6 +146,9 @@ Octavia version 0.9 consists of the following major components:
database records, manages the spares pool, and manages amphora certificate database records, manages the spares pool, and manages amphora certificate
rotation. rotation.
* **Driver Agent** - The driver agent receives status and statistics updates
from provider drivers.
* **network** - Octavia cannot accomplish what it does without manipulating * **network** - Octavia cannot accomplish what it does without manipulating
the network environment. Amphorae are spun up with a network interface on the the network environment. Amphorae are spun up with a network interface on the
"load balancer network," and they may also plug directly into tenant networks "load balancer network," and they may also plug directly into tenant networks

View File

@ -1,16 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by Microsoft Visio, SVG Export OctaviaComp.svg Page-1 --> <!-- Generated by Microsoft Visio, SVG Export Octaviav2APIDriverAgent.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="11.6929in" height="8.26772in" width="11.6929in" height="8.26772in" viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB"
viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB" class="st15"> class="st17">
<v:documentProperties v:langID="1033" v:metric="true" v:viewMarkup="false">
<v:userDefs>
<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
</v:userDefs>
</v:documentProperties>
<style type="text/css"> <style type="text/css">
<![CDATA[ <![CDATA[
.st1 {fill:#339933;fill-opacity:0.64;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st1 {fill:#339933;fill-opacity:0.64;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
@ -18,16 +11,18 @@
.st3 {fill:#ffffff;font-family:Segoe UI;font-size:1.16666em} .st3 {fill:#ffffff;font-family:Segoe UI;font-size:1.16666em}
.st4 {font-size:1em} .st4 {font-size:1em}
.st5 {fill:#d80073;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st5 {fill:#d80073;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st6 {marker-end:url(#mrkr4-43);marker-start:url(#mrkr4-41);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2} .st6 {marker-end:url(#mrkr4-44);marker-start:url(#mrkr4-42);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st7 {fill:#1897d5;fill-opacity:1;stroke:#1897d5;stroke-opacity:1;stroke-width:0.44247787610619} .st7 {fill:#1897d5;fill-opacity:1;stroke:#1897d5;stroke-opacity:1;stroke-width:0.44247787610619}
.st8 {marker-end:url(#mrkr4-43);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2} .st8 {marker-end:url(#mrkr4-44);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st9 {fill:#f09609;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st9 {fill:#f09609;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st10 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st10 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st11 {fill:#339933;fill-opacity:0.79;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st11 {fill:#339933;fill-opacity:0.79;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st12 {fill:#339933;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st12 {fill:#339933;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st13 {marker-start:url(#mrkr4-41);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2} .st13 {marker-start:url(#mrkr4-42);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}
.st14 {fill:#a200ff;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5} .st14 {fill:#a200ff;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st15 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3} .st15 {marker-end:url(#mrkr4-161);stroke:#1897d5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.25}
.st16 {fill:#1897d5;fill-opacity:1;stroke:#1897d5;stroke-opacity:1;stroke-width:0.47169811320755}
.st17 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]> ]]>
</style> </style>
@ -35,162 +30,97 @@
<g id="lend4"> <g id="lend4">
<path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/> <path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/>
</g> </g>
<marker id="mrkr4-41" class="st7" v:arrowType="4" v:arrowSize="2" v:setback="4.34" refX="4.34" orient="auto" <marker id="mrkr4-42" class="st7" refX="4.34" orient="auto" markerUnits="strokeWidth" overflow="visible">
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(2.26) "/> <use xlink:href="#lend4" transform="scale(2.26) "/>
</marker> </marker>
<marker id="mrkr4-43" class="st7" v:arrowType="4" v:arrowSize="2" v:setback="4.52" refX="-4.52" orient="auto" <marker id="mrkr4-44" class="st7" refX="-4.52" orient="auto" markerUnits="strokeWidth" overflow="visible">
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(-2.26,-2.26) "/> <use xlink:href="#lend4" transform="scale(-2.26,-2.26) "/>
</marker> </marker>
<marker id="mrkr4-161" class="st16" refX="-4.24" orient="auto" markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(-2.12,-2.12) "/>
</marker>
</defs> </defs>
<g v:mID="0" v:index="1" v:groupContext="foregroundPage"> <g>
<v:userDefs>
<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
</v:userDefs>
<title>Page-1</title> <title>Page-1</title>
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394" <g id="shape1023-1" transform="translate(305.179,-99.2126)">
v:shadowOffsetY="-8.50394"/>
<v:layer v:name="Connector" v:index="0"/>
<v:layer v:name="Flowchart" v:index="1"/>
<g id="shape1023-1" v:mID="1023" v:groupContext="shape" transform="translate(307.343,-28.3465)">
<title>Parallelogram.1023</title> <title>Parallelogram.1023</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st1"/> <path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st1"/>
</g> </g>
<g id="shape1-3" v:mID="1" v:groupContext="shape" transform="translate(331.214,-253.382)"> <g id="shape1-3" transform="translate(329.049,-324.248)">
<title>Rectangle</title> <title>Rectangle</title>
<desc>Controller Worker Driver</desc> <desc>Controller Worker Driver</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="223.228" cy="569.54" width="446.46" height="51.4712"/>
<rect x="0" y="543.804" width="446.457" height="51.4712" class="st2"/> <rect x="0" y="543.804" width="446.457" height="51.4712" class="st2"/>
<text x="147.47" y="573.74" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Controller Worker Driver</text> </g> <text x="147.47" y="573.74" class="st3">Controller Worker Driver</text> </g>
<g id="shape2-6" v:mID="2" v:groupContext="shape" transform="translate(451.214,-166.925)"> <g id="shape2-6" transform="translate(449.049,-237.791)">
<title>Square</title> <title>Square</title>
<desc>Certificate Driver</desc> <desc>Certificate Driver</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="43.2283" cy="552.047" width="86.46" height="86.4567"/>
<rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/> <rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/>
<text x="12.02" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Certificate<v:newlineChar/><tspan <text x="12.02" y="547.85" class="st3">Certificate <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g> <g id="shape3-10" transform="translate(569.049,-237.791)">
<g id="shape3-10" v:mID="3" v:groupContext="shape" transform="translate(571.214,-166.925)">
<title>Square.3</title> <title>Square.3</title>
<desc>Compute Driver</desc> <desc>Compute Driver</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="43.2283" cy="552.047" width="86.46" height="86.4567"/>
<rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/> <rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/>
<text x="14.65" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Compute<v:newlineChar/><tspan <text x="14.65" y="547.85" class="st3">Compute <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g> <g id="shape4-14" transform="translate(689.049,-237.791)">
<g id="shape4-14" v:mID="4" v:groupContext="shape" transform="translate(691.214,-166.925)">
<title>Square.4</title> <title>Square.4</title>
<desc>Network Driver</desc> <desc>Network Driver</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="43.2283" cy="552.047" width="86.46" height="86.4567"/>
<rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/> <rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/>
<text x="16.89" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Network<v:newlineChar/><tspan <text x="16.89" y="547.85" class="st3">Network <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g> <g id="shape5-18" transform="translate(329.049,-237.791)">
<g id="shape5-18" v:mID="5" v:groupContext="shape" transform="translate(331.214,-166.925)">
<title>Square.5</title> <title>Square.5</title>
<desc>Amphora Driver</desc> <desc>Amphora Driver</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="43.2283" cy="552.047" width="86.46" height="86.4567"/>
<rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/> <rect x="0" y="508.819" width="86.4567" height="86.4567" class="st2"/>
<text x="14.51" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Amphora<v:newlineChar/><tspan <text x="14.51" y="547.85" class="st3">Amphora <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g> <g id="shape1001-22" transform="translate(685.028,-124.406)">
<g id="shape1001-22" v:mID="1001" v:groupContext="shape" transform="translate(687.192,-53.5394)">
<title>Ellipse.6</title> <title>Ellipse.6</title>
<desc>Neutron</desc> <desc>Neutron</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="47.25" cy="563.776" width="82.69" height="55.125"/>
<path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/> <path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/>
<text x="21.52" y="567.98" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Neutron</text> </g> <text x="21.52" y="567.98" class="st3">Neutron</text> </g>
<g id="shape1002-25" v:mID="1002" v:groupContext="shape" transform="translate(567.192,-53.5394)"> <g id="shape1002-25" transform="translate(565.028,-124.406)">
<title>Ellipse.5</title> <title>Ellipse.5</title>
<desc>Nova</desc> <desc>Nova</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="47.25" cy="563.776" width="82.69" height="55.125"/>
<path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/> <path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/>
<text x="31" y="567.98" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Nova</text> </g> <text x="31" y="567.98" class="st3">Nova</text> </g>
<g id="shape1003-28" v:mID="1003" v:groupContext="shape" transform="translate(447.192,-53.5394)"> <g id="shape1003-28" transform="translate(445.028,-124.406)">
<title>Ellipse.69</title> <title>Ellipse.69</title>
<desc>Barbican</desc> <desc>Barbican / Castellan</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="47.25" cy="563.776" width="82.69" height="55.125"/>
<path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/> <path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/>
<text x="20.68" y="567.98" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Barbican</text> </g> <text x="16.03" y="559.58" class="st3">Barbican / <tspan x="19.44" dy="1.2em" class="st4">Castellan</tspan></text> </g>
<g id="shape1004-31" v:mID="1004" v:groupContext="shape" transform="translate(210.906,-330.27)"> <g id="shape1004-32" transform="translate(208.742,-401.136)">
<title>Ellipse.3</title> <title>Ellipse.3</title>
<desc>Oslo Messaging</desc> <desc>Oslo Messaging</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="47.25" cy="563.776" width="82.69" height="55.125"/>
<path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/> <path d="M0 563.78 A47.25 31.5 0 0 1 94.5 563.78 A47.25 31.5 0 1 1 0 563.78 Z" class="st5"/>
<text x="33.21" y="559.58" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Oslo<v:newlineChar/><tspan <text x="33.21" y="559.58" class="st3">Oslo <tspan x="13.9" dy="1.2em" class="st4">Messaging</tspan></text> </g>
x="13.9" dy="1.2em" class="st4">Messaging</tspan></text> </g> <g id="shape1006-36" transform="translate(329.049,-342.897)">
<g id="shape1006-35" v:mID="1006" v:groupContext="shape" v:layerMember="0" transform="translate(331.214,-272.031)">
<title>Dynamic connector.1006</title> <title>Dynamic connector.1006</title>
<path d="M-8.68 588.19 L-9.04 588.19 L-29.47 588.19" class="st6"/> <path d="M-8.68 588.19 L-9.04 588.19 L-29.47 588.19" class="st6"/>
</g> </g>
<g id="shape1007-44" v:mID="1007" v:groupContext="shape" v:layerMember="0" transform="translate(727.356,-166.925)"> <g id="shape1007-45" transform="translate(739.364,-237.791)">
<title>Dynamic connector.1007</title> <title>Dynamic connector.1007</title>
<path d="M7.09 595.28 L7.09 636.62" class="st8"/> <path d="M-7.09 595.28 L-7.09 636.62" class="st8"/>
</g> </g>
<g id="shape1008-49" v:mID="1008" v:groupContext="shape" v:layerMember="0" transform="translate(607.356,-166.925)"> <g id="shape1008-50" transform="translate(605.191,-237.791)">
<title>Dynamic connector.1008</title> <title>Dynamic connector.1008</title>
<path d="M7.09 595.28 L7.09 636.62" class="st8"/> <path d="M7.09 595.28 L7.09 636.62" class="st8"/>
</g> </g>
<g id="shape1009-54" v:mID="1009" v:groupContext="shape" v:layerMember="0" transform="translate(487.356,-166.925)"> <g id="shape1009-55" transform="translate(485.191,-237.791)">
<title>Dynamic connector.1009</title> <title>Dynamic connector.1009</title>
<path d="M7.09 595.28 L7.09 636.62" class="st8"/> <path d="M7.09 595.28 L7.09 636.62" class="st8"/>
</g> </g>
<g id="shape1010-59" v:mID="1010" v:groupContext="shape" v:layerMember="0" transform="translate(417.671,-203.067)"> <g id="shape1010-60" transform="translate(415.506,-288.106)">
<title>Dynamic connector.1010</title> <title>Dynamic connector.1010</title>
<path d="M0 588.19 L24.5 588.19" class="st8"/> <path d="M0 602.36 L24.5 602.36" class="st8"/>
</g> </g>
<g id="group1011-64" transform="translate(71.7133,-304.853)" v:mID="1011" v:groupContext="group"> <g id="group1011-65" transform="translate(69.5486,-375.719)">
<title>Sheet.1011</title> <title>Sheet.1011</title>
<g id="shape9-65" v:mID="9" v:groupContext="shape" transform="translate(0,-0.447576)"> <g id="shape9-66" transform="translate(0,-0.447576)">
<title>Square.9</title> <title>Square.9</title>
<desc>Octavia API</desc> <desc>Octavia API</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.6929" cy="538.583" width="113.39" height="113.386"/>
<rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/> <rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
<text x="33.64" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Octavia<v:newlineChar/><tspan <text x="21.42" y="504.09" class="st3">Octavia API</text> </g>
x="46.39" dy="1.2em" class="st4">API</tspan></text> </g> <g id="shape15-69" transform="translate(80.3899,-5.68434E-13)">
<g id="shape15-69" v:mID="15" v:groupContext="shape" transform="translate(80.3899,0)">
<title>Sheet.15</title> <title>Sheet.15</title>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64, <image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
@ -202,25 +132,19 @@
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY 5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
RK5CYII="/> RK5CYII="/>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
</g> </g>
</g> </g>
<g id="group1012-73" transform="translate(331.214,-304.853)" v:mID="1012" v:groupContext="group"> <g id="group1012-73" transform="translate(329.049,-375.719)">
<title>Sheet.1012</title> <title>Sheet.1012</title>
<g id="shape6-74" v:mID="6" v:groupContext="shape" transform="translate(-1.59872E-14,-0.447576)"> <g id="shape6-74" transform="translate(0,-0.447576)">
<title>Square.6</title> <title>Square.6</title>
<desc>Octavia Worker</desc> <desc>Octavia Worker</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.6929" cy="538.583" width="113.39" height="113.386"/>
<rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/> <rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
<text x="33.64" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Octavia<v:newlineChar/><tspan <text x="33.64" y="534.38" class="st3">Octavia <tspan x="34.05" dy="1.2em" class="st4">Worker</tspan></text> </g>
x="34.05" dy="1.2em" class="st4">Worker</tspan></text> </g> <g id="shape16-78" transform="translate(80.3899,0)">
<g id="shape16-78" v:mID="16" v:groupContext="shape" transform="translate(80.3899,0)">
<title>Sheet.16</title> <title>Sheet.16</title>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64, <image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
@ -232,25 +156,19 @@
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY 5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
RK5CYII="/> RK5CYII="/>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
</g> </g>
</g> </g>
<g id="group1013-82" transform="translate(497.749,-305.301)" v:mID="1013" v:groupContext="group"> <g id="group1013-82" transform="translate(495.585,-376.167)">
<title>Sheet.1013</title> <title>Sheet.1013</title>
<g id="shape7-83" v:mID="7" v:groupContext="shape"> <g id="shape7-83">
<title>Square.7</title> <title>Square.7</title>
<desc>Health Manager</desc> <desc>Health Manager</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.6929" cy="538.583" width="113.39" height="113.386"/>
<rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/> <rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
<text x="36.47" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Health<v:newlineChar/><tspan <text x="36.47" y="534.38" class="st3">Health <tspan x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g> <g id="shape17-87" transform="translate(80.3899,0)">
<g id="shape17-87" v:mID="17" v:groupContext="shape" transform="translate(80.3899,0)">
<title>Sheet.17</title> <title>Sheet.17</title>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64, <image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
@ -262,25 +180,19 @@
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY 5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
RK5CYII="/> RK5CYII="/>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
</g> </g>
</g> </g>
<g id="group1014-91" transform="translate(664.285,-304.853)" v:mID="1014" v:groupContext="group"> <g id="group1014-91" transform="translate(662.12,-375.719)">
<title>Sheet.1014</title> <title>Sheet.1014</title>
<g id="shape8-92" v:mID="8" v:groupContext="shape" transform="translate(3.19744E-14,-0.447576)"> <g id="shape8-92" transform="translate(3.19744E-14,-0.447576)">
<title>Square.8</title> <title>Square.8</title>
<desc>Housekeeping Manager</desc> <desc>Housekeeping Manager</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.6929" cy="538.583" width="113.39" height="113.386"/>
<rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/> <rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
<text x="12.33" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Housekeeping<v:newlineChar/><tspan <text x="12.33" y="534.38" class="st3">Housekeeping <tspan x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g> <g id="shape18-96" transform="translate(80.3899,0)">
<g id="shape18-96" v:mID="18" v:groupContext="shape" transform="translate(80.3899,0)">
<title>Sheet.18</title> <title>Sheet.18</title>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64, <image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
@ -292,109 +204,88 @@
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY 5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
RK5CYII="/> RK5CYII="/>
<rect v:rectContext="foreign" x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
</g> </g>
</g> </g>
<g id="shape1016-100" v:mID="1016" v:groupContext="shape" v:layerMember="0" transform="translate(185.099,-368.968)"> <g id="shape1016-100" transform="translate(168.761,-425.464)">
<title>Dynamic connector.1016</title> <title>Dynamic connector.1016</title>
<path d="M0 602.25 L16.77 602.4" class="st8"/> <path d="M0 588.27 L30.94 588.14" class="st8"/>
</g> </g>
<g id="shape1017-105" v:mID="1017" v:groupContext="shape" v:layerMember="0" transform="translate(305.406,-354.795)"> <g id="shape1017-105" transform="translate(303.242,-425.661)">
<title>Dynamic connector.1017</title> <title>Dynamic connector.1017</title>
<path d="M0 588.3 L16.77 588.16" class="st8"/> <path d="M0 588.3 L16.77 588.16" class="st8"/>
</g> </g>
<g id="shape1018-110" v:mID="1018" v:groupContext="shape" v:layerMember="0" transform="translate(128.406,-305.301)"> <g id="shape1018-110" transform="translate(126.242,-376.167)">
<title>Dynamic connector.1018</title> <title>Dynamic connector.1018</title>
<path d="M0 603.96 L0 604.32 L0 621.46 L86.16 621.46" class="st6"/> <path d="M0 603.96 L0 604.32 L0 621.46 L86.16 621.46" class="st6"/>
</g> </g>
<g id="group1019-117" transform="translate(223.609,-244.57)" v:mID="1019" v:groupContext="group"> <g id="group1019-117" transform="translate(221.445,-315.436)">
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<title>Can.1019</title> <title>Can.1019</title>
<desc>Database</desc> <desc>Database</desc>
<g id="shape1020-118" v:mID="1020" v:groupContext="shape"> <g id="shape1020-118">
<title>Sheet.1020</title> <title>Sheet.1020</title>
<v:userDefs>
<v:ud v:nameU="ControlHalfHeight" v:prompt="" v:val="VT0(0.11811023622047):24"/>
<v:ud v:nameU="FillForegnd" v:prompt="" v:val="VT0(1):26"/>
<v:ud v:nameU="ControlHalfHeight" v:prompt="" v:val="VT0(0.095964566929134):1"/>
<v:ud v:nameU="FillForegnd" v:prompt="" v:val="VT5(#d80073)"/>
</v:userDefs>
<path d="M0 588.37 A34.5472 6.90945 -180 0 0 69.09 588.37 L69.09 533.09 L0 533.09 L0 588.37 Z" class="st5"/> <path d="M0 588.37 A34.5472 6.90945 -180 0 0 69.09 588.37 L69.09 533.09 L0 533.09 L0 588.37 Z" class="st5"/>
</g> </g>
<g id="shape1019-120" v:mID="1019" v:groupContext="groupContent"> <g id="shape1019-120">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="34.5472" cy="560.728" width="69.1" height="69.0945"/>
<ellipse cx="34.5472" cy="533.091" rx="34.5472" ry="6.90945" class="st5"/> <ellipse cx="34.5472" cy="533.091" rx="34.5472" ry="6.90945" class="st5"/>
<text x="5.84" y="564.93" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Database</text> </g> <text x="5.84" y="564.93" class="st3">Database</text> </g>
</g> </g>
<g id="shape1021-123" v:mID="1021" v:groupContext="shape" transform="translate(293.17,-39.3661)"> <g id="shape1021-123" transform="translate(291.005,-110.232)">
<title>Parallelogram.1021</title> <title>Parallelogram.1021</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st11"/> <path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st11"/>
</g> </g>
<g id="shape1022-125" v:mID="1022" v:groupContext="shape" transform="translate(278.531,-50.3858)"> <g id="shape1022-125" transform="translate(276.366,-121.252)">
<title>Parallelogram.1022</title> <title>Parallelogram.1022</title>
<desc>Amphora</desc> <desc>Amphora</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.6929" cy="552.756" width="113.39" height="85.0394"/>
<path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st12"/> <path d="M0 595.28 L102.05 595.28 L113.39 510.24 L11.34 510.24 L0 595.28 Z" class="st12"/>
<text x="27.98" y="556.96" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Amphora</text> </g> <text x="27.98" y="556.96" class="st3">Amphora</text> </g>
<g id="group1054-128" transform="translate(359.038,-138.579)" v:mID="1054" v:groupContext="group"> <g id="group1054-128" transform="translate(356.874,-209.445)">
<title>Sheet.1054</title> <title>Sheet.1054</title>
<g id="shape1050-129" v:mID="1050" v:groupContext="shape" v:layerMember="0" transform="translate(30.8081,-28.3465)"> <g id="shape1050-129" transform="translate(30.8081,-28.3465)">
<title>Dynamic connector.1050</title> <title>Dynamic connector.1050</title>
<path d="M-6.99 603.95 L-7 604.31 L-7.33 623.62" class="st13"/> <path d="M-6.99 603.95 L-7 604.31 L-7.33 623.62" class="st13"/>
</g> </g>
<g id="shape1051-134" v:mID="1051" v:groupContext="shape" v:layerMember="0" transform="translate(14.1732,-28.3465)"> <g id="shape1051-134" transform="translate(14.1732,-28.3465)">
<title>Dynamic connector.1051</title> <title>Dynamic connector.1051</title>
<path d="M-6.84 595.28 L-7.17 614.58" class="st8"/> <path d="M-6.84 595.28 L-7.17 614.58" class="st8"/>
</g> </g>
</g> </g>
<g id="group1064-139" transform="translate(60.1975,-453.543)" v:mID="1064" v:groupContext="group"> <g id="shape1056-139" transform="translate(83.7218,-411.024)">
<title>Sheet.1064</title> <title>Sheet.1056</title>
<g id="shape1056-140" v:mID="1056" v:groupContext="shape"> <desc>Amphora Driver</desc>
<title>Rectangle.1056</title> <rect x="0" y="552.756" width="85.0394" height="42.5197" class="st14"/>
<desc>Neutron</desc> <text x="13.8" y="569.82" class="st3">Amphora <tspan x="24.04" dy="1.2em" class="st4">Driver</tspan></text> </g>
<v:userDefs> <g id="group1057-143" transform="translate(69.5486,-212.598)">
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/> <title>Sheet.1057</title>
</v:userDefs> <g id="shape1058-144">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> <title>Square.7</title>
<v:textRect cx="68.2087" cy="491.339" width="136.42" height="18.8976"/> <desc>Driver Agent</desc>
<rect x="0" y="481.89" width="136.417" height="113.386" class="st5"/> <rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
<text x="42.48" y="495.54" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Neutron</text> </g> <text x="17.66" y="542.78" class="st3">Driver Agent</text> </g>
<g id="shape1057-143" v:mID="1057" v:groupContext="shape" transform="translate(11.5157,-11.3386)"> <g id="shape19-147" transform="translate(80.3899,0)">
<title>Rectangle.1057</title> <title>Sheet.19</title>
<desc>LBaaS v2 User API Handler</desc> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<v:userDefs> <image x="0" y="564.279" width="32.9959" height="30.9961" preserveAspectRatio="none" xlink:href="data:image/png;base64,
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/> iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
</v:userDefs> AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> BAJxvC9sX/UNb2/3iqpNV311M29mumd3evq2Go1G/x0r9o0V+8aKfWPFLsjmiv587s+CFach+yxOxHmhfxfX4qtYyGNtWNEhOxQ0
<v:textRect cx="56.6929" cy="537.101" width="113.39" height="42.3913"/> 4Lb+XanH5sVfETptNtXp7VjRIfstngUd+CMeBYEfRB5Du3Z+HFZ0yDbqAF1gQwfOj8OKgexjam+LCNIGm7gQ/45DtgDhq8SKIFsX
<rect x="0" y="515.906" width="113.386" height="79.3701" class="st14"/> T+KnIB/y6+7KpTgW+DlyccCKIPsl7gXnnR3PAsHJjyuBn4lrPY5lxapaFDjIDh1lMuYxx5aNZ8XJ69gEt+NAcGw7gqeddmSMndp4
<text x="29.5" y="532.9" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>LBaaS v2<v:newlineChar/><tspan VqyqTYFTOlzDcJTZKNZ8ECTjQJRzgbG9vGa81omBjCfNjoJhw/wvaU5mas2wYiDbTY4yEyU7zW+qJbNvQkZi7osbkZ0FlOVxDUnr
x="4.28" dy="1.2em" class="st4">User API Handler</tspan></text> </g> juqxcj5Ju1bOD7z4ej1pnNa/JSQlZ7yU1lDMmhIzbo5/g1Z8zfhw0AQJS1AKUlstuRPMtQXrjRDI+Fs+E9nZe2ATyzaWE0EWtYIz
<g id="shape1058-147" v:mID="1058" v:groupContext="shape" transform="translate(20.9587,-17.0079)"> 5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
<title>Rectangle.1058</title> tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
<desc>Octavia Driver</desc> RK5CYII="/>
<v:userDefs> <rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
<v:ud v:nameU="visVersion" v:val="VT0(15):26"/> </g>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="47.25" cy="577.01" width="94.5" height="36.5315"/>
<rect x="0" y="558.744" width="94.5" height="36.5315" class="st9"/>
<text x="24.2" y="572.81" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Octavia <tspan
x="28.77" dy="1.2em" class="st4">Driver</tspan></text> </g>
</g> </g>
<g id="shape1065-151" v:mID="1065" v:groupContext="shape" v:layerMember="0" transform="translate(121.32,-470.551)"> <g id="shape1059-151" transform="translate(90.2835,-411.024)">
<title>Dynamic connector.1065</title> <title>Dynamic connector.1059</title>
<path d="M7.09 595.28 L7.09 638.1" class="st8"/> <path d="M7.09 595.28 L7.09 671.27" class="st8"/>
</g>
<g id="shape1061-156" transform="translate(182.934,-269.291)">
<title>Dynamic connector</title>
<path d="M0 595.28 L73.06 595.28 L73.06 558.67" class="st15"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -460,3 +460,23 @@
# A URL representing messaging driver to use for notification. If not # A URL representing messaging driver to use for notification. If not
# specified, we fall back to the same configuration used for RPC. # specified, we fall back to the same configuration used for RPC.
# transport_url = # transport_url =
[driver_agent]
# status_socket_path = /var/run/octavia/status.sock
# stats_socket_path = /var/run/octavia/stats.sock
# Maximum time to wait for a status message before checking for shutdown
# status_request_timeout = 5
# Maximum number of status processes per driver-agent
# status_max_processes = 50
# Maximum time to wait for a stats message before checking for shutdown
# stats_request_timeout = 5
# Maximum number of stats processes per driver-agent
# stats_max_processes = 50
# Percentage of max_processes (both status and stats) in use to start
# logging warning messages about an overloaded driver-agent.
# max_process_warning_percent = .75

View File

@ -72,6 +72,7 @@ munch==2.2.0
netaddr==0.7.19 netaddr==0.7.19
netifaces==0.10.4 netifaces==0.10.4
networkx==1.11 networkx==1.11
octavia-lib==1.1.1
openstacksdk==0.12.0 openstacksdk==0.12.0
os-client-config==1.29.0 os-client-config==1.29.0
os-service-types==1.2.0 os-service-types==1.2.0

View File

@ -14,15 +14,17 @@
from jsonschema import exceptions as js_exceptions from jsonschema import exceptions as js_exceptions
from jsonschema import validate from jsonschema import validate
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
import oslo_messaging as messaging import oslo_messaging as messaging
from stevedore import driver as stevedore_driver from stevedore import driver as stevedore_driver
from octavia_lib.api.drivers import data_models as driver_dm
from octavia_lib.api.drivers import exceptions
from octavia_lib.api.drivers import provider_base as driver_base
from octavia.api.drivers.amphora_driver import flavor_schema from octavia.api.drivers.amphora_driver import flavor_schema
from octavia.api.drivers import data_models as driver_dm
from octavia.api.drivers import exceptions
from octavia.api.drivers import provider_base as driver_base
from octavia.api.drivers import utils as driver_utils from octavia.api.drivers import utils as driver_utils
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.common import data_models from octavia.common import data_models
@ -93,7 +95,7 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
# expects # expects
vip_qos_policy_id = lb_dict.pop('vip_qos_policy_id', None) vip_qos_policy_id = lb_dict.pop('vip_qos_policy_id', None)
if vip_qos_policy_id: if vip_qos_policy_id:
vip_dict = {"qos_policy_id": vip_qos_policy_id} vip_dict = {"vip_qos_policy_id": vip_qos_policy_id}
lb_dict["vip"] = vip_dict lb_dict["vip"] = vip_dict
payload = {consts.LOAD_BALANCER_ID: lb_id, payload = {consts.LOAD_BALANCER_ID: lb_id,

View File

@ -15,270 +15,44 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import six import warnings
from oslo_log import log as logging from debtcollector import moves
LOG = logging.getLogger(__name__) from octavia_lib.api.drivers import data_models as lib_data_models
class BaseDataModel(object): warnings.simplefilter('default', DeprecationWarning)
def to_dict(self, calling_classes=None, recurse=False,
render_unsets=False, **kwargs):
"""Converts a data model to a dictionary."""
calling_classes = calling_classes or []
ret = {}
for attr in self.__dict__:
if attr.startswith('_') or not kwargs.get(attr, True):
continue
value = self.__dict__[attr]
if recurse: BaseDataModel = moves.moved_class(lib_data_models.BaseDataModel,
if isinstance(getattr(self, attr), list): 'BaseDataModel', __name__,
ret[attr] = [] version='Stein', removal_version='U')
for item in value:
if isinstance(item, BaseDataModel):
if type(self) not in calling_classes:
ret[attr].append(
item.to_dict(calling_classes=(
calling_classes + [type(self)]),
recurse=True,
render_unsets=render_unsets))
else:
ret[attr].append(None)
else:
ret[attr].append(item)
elif isinstance(getattr(self, attr), BaseDataModel):
if type(self) not in calling_classes:
ret[attr] = value.to_dict(
render_unsets=render_unsets,
calling_classes=calling_classes + [type(self)])
else:
ret[attr] = None
elif six.PY2 and isinstance(value, six.text_type):
ret[attr.encode('utf8')] = value.encode('utf8')
elif isinstance(value, UnsetType):
if render_unsets:
ret[attr] = None
else:
continue
else:
ret[attr] = value
else:
if (isinstance(getattr(self, attr), (BaseDataModel, list)) or
isinstance(value, UnsetType)):
if render_unsets:
ret[attr] = None
else:
continue
else:
ret[attr] = value
return ret UnsetType = moves.moved_class(lib_data_models.UnsetType, 'UnsetType', __name__,
version='Stein', removal_version='U')
def __eq__(self, other): LoadBalancer = moves.moved_class(lib_data_models.LoadBalancer, 'LoadBalancer',
if isinstance(other, self.__class__): __name__, version='Stein',
return self.to_dict() == other.to_dict() removal_version='U')
return False
def __ne__(self, other): Listener = moves.moved_class(lib_data_models.Listener, 'Listener', __name__,
return not self.__eq__(other) version='Stein', removal_version='U')
@classmethod Pool = moves.moved_class(lib_data_models.Pool, 'Pool', __name__,
def from_dict(cls, dict): version='Stein', removal_version='U')
return cls(**dict)
Member = moves.moved_class(lib_data_models.Member, 'Member', __name__,
version='Stein', removal_version='U')
class UnsetType(object): HealthMonitor = moves.moved_class(lib_data_models.HealthMonitor,
def __bool__(self): 'HealthMonitor', __name__,
return False version='Stein', removal_version='U')
__nonzero__ = __bool__
def __repr__(self): L7Policy = moves.moved_class(lib_data_models.L7Policy, 'L7Policy', __name__,
return 'Unset' version='Stein', removal_version='U')
L7Rule = moves.moved_class(lib_data_models.L7Rule, 'L7Rule', __name__,
version='Stein', removal_version='U')
Unset = UnsetType() VIP = moves.moved_class(lib_data_models.VIP, 'VIP', __name__,
version='Stein', removal_version='U')
class LoadBalancer(BaseDataModel):
def __init__(self, admin_state_up=Unset, description=Unset, flavor=Unset,
listeners=Unset, loadbalancer_id=Unset, name=Unset,
pools=Unset, project_id=Unset, vip_address=Unset,
vip_network_id=Unset, vip_port_id=Unset, vip_subnet_id=Unset,
vip_qos_policy_id=Unset):
self.admin_state_up = admin_state_up
self.description = description
self.flavor = flavor
self.listeners = listeners
self.loadbalancer_id = loadbalancer_id
self.name = name
self.pools = pools
self.project_id = project_id
self.vip_address = vip_address
self.vip_network_id = vip_network_id
self.vip_port_id = vip_port_id
self.vip_subnet_id = vip_subnet_id
self.vip_qos_policy_id = vip_qos_policy_id
class Listener(BaseDataModel):
def __init__(self, admin_state_up=Unset, connection_limit=Unset,
default_pool=Unset, default_pool_id=Unset,
default_tls_container_ref=Unset,
default_tls_container_data=Unset, description=Unset,
insert_headers=Unset, l7policies=Unset, listener_id=Unset,
loadbalancer_id=Unset, name=Unset, protocol=Unset,
protocol_port=Unset, sni_container_refs=Unset,
sni_container_data=Unset, timeout_client_data=Unset,
timeout_member_connect=Unset, timeout_member_data=Unset,
timeout_tcp_inspect=Unset, client_ca_tls_container_ref=Unset,
client_ca_tls_container_data=Unset,
client_authentication=Unset, client_crl_container_ref=Unset,
client_crl_container_data=Unset):
self.admin_state_up = admin_state_up
self.connection_limit = connection_limit
self.default_pool = default_pool
self.default_pool_id = default_pool_id
self.default_tls_container_data = default_tls_container_data
self.default_tls_container_ref = default_tls_container_ref
self.description = description
self.insert_headers = insert_headers
self.l7policies = l7policies
self.listener_id = listener_id
self.loadbalancer_id = loadbalancer_id
self.name = name
self.protocol = protocol
self.protocol_port = protocol_port
self.sni_container_data = sni_container_data
self.sni_container_refs = sni_container_refs
self.timeout_client_data = timeout_client_data
self.timeout_member_connect = timeout_member_connect
self.timeout_member_data = timeout_member_data
self.timeout_tcp_inspect = timeout_tcp_inspect
self.client_ca_tls_container_ref = client_ca_tls_container_ref
self.client_ca_tls_container_data = client_ca_tls_container_data
self.client_authentication = client_authentication
self.client_crl_container_ref = client_crl_container_ref
self.client_crl_container_data = client_crl_container_data
class Pool(BaseDataModel):
def __init__(self, admin_state_up=Unset, description=Unset,
healthmonitor=Unset, lb_algorithm=Unset,
loadbalancer_id=Unset, members=Unset, name=Unset,
pool_id=Unset, listener_id=Unset, protocol=Unset,
session_persistence=Unset, tls_container_ref=Unset,
tls_container_data=Unset, ca_tls_container_ref=Unset,
ca_tls_container_data=Unset, crl_container_ref=Unset,
crl_container_data=Unset, tls_enabled=Unset):
self.admin_state_up = admin_state_up
self.description = description
self.healthmonitor = healthmonitor
self.lb_algorithm = lb_algorithm
self.loadbalancer_id = loadbalancer_id
self.members = members
self.name = name
self.pool_id = pool_id
self.listener_id = listener_id
self.protocol = protocol
self.session_persistence = session_persistence
self.tls_container_ref = tls_container_ref
self.tls_container_data = tls_container_data
self.ca_tls_container_ref = ca_tls_container_ref
self.ca_tls_container_data = ca_tls_container_data
self.crl_container_ref = crl_container_ref
self.crl_container_data = crl_container_data
self.tls_enabled = tls_enabled
class Member(BaseDataModel):
def __init__(self, address=Unset, admin_state_up=Unset, member_id=Unset,
monitor_address=Unset, monitor_port=Unset, name=Unset,
pool_id=Unset, protocol_port=Unset, subnet_id=Unset,
weight=Unset, backup=Unset):
self.address = address
self.admin_state_up = admin_state_up
self.member_id = member_id
self.monitor_address = monitor_address
self.monitor_port = monitor_port
self.name = name
self.pool_id = pool_id
self.protocol_port = protocol_port
self.subnet_id = subnet_id
self.weight = weight
self.backup = backup
class HealthMonitor(BaseDataModel):
def __init__(self, admin_state_up=Unset, delay=Unset, expected_codes=Unset,
healthmonitor_id=Unset, http_method=Unset, max_retries=Unset,
max_retries_down=Unset, name=Unset, pool_id=Unset,
timeout=Unset, type=Unset, url_path=Unset, http_version=Unset,
domain_name=Unset):
self.admin_state_up = admin_state_up
self.delay = delay
self.expected_codes = expected_codes
self.healthmonitor_id = healthmonitor_id
self.http_method = http_method
self.max_retries = max_retries
self.max_retries_down = max_retries_down
self.name = name
self.pool_id = pool_id
self.timeout = timeout
self.type = type
self.url_path = url_path
self.http_version = http_version
self.domain_name = domain_name
class L7Policy(BaseDataModel):
def __init__(self, action=Unset, admin_state_up=Unset, description=Unset,
l7policy_id=Unset, listener_id=Unset, name=Unset,
position=Unset, redirect_pool_id=Unset, redirect_url=Unset,
rules=Unset, redirect_prefix=Unset, redirect_http_code=Unset):
self.action = action
self.admin_state_up = admin_state_up
self.description = description
self.l7policy_id = l7policy_id
self.listener_id = listener_id
self.name = name
self.position = position
self.redirect_pool_id = redirect_pool_id
self.redirect_url = redirect_url
self.rules = rules
self.redirect_prefix = redirect_prefix
self.redirect_http_code = redirect_http_code
class L7Rule(BaseDataModel):
def __init__(self, admin_state_up=Unset, compare_type=Unset, invert=Unset,
key=Unset, l7policy_id=Unset, l7rule_id=Unset, type=Unset,
value=Unset):
self.admin_state_up = admin_state_up
self.compare_type = compare_type
self.invert = invert
self.key = key
self.l7policy_id = l7policy_id
self.l7rule_id = l7rule_id
self.type = type
self.value = value
class VIP(BaseDataModel):
def __init__(self, vip_address=Unset, vip_network_id=Unset,
vip_port_id=Unset, vip_subnet_id=Unset,
vip_qos_policy_id=Unset):
self.vip_address = vip_address
self.vip_network_id = vip_network_id
self.vip_port_id = vip_port_id
self.vip_subnet_id = vip_subnet_id
self.vip_qos_policy_id = vip_qos_policy_id

View File

@ -0,0 +1,11 @@
# 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.

View File

@ -0,0 +1,144 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 errno
import os
import signal
import threading
import six.moves.socketserver as socketserver
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from octavia.api.drivers.driver_agent import driver_updater
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def _recv(recv_socket):
size_str = b''
char = recv_socket.recv(1)
while char != b'\n':
size_str += char
char = recv_socket.recv(1)
payload_size = int(size_str)
mv_buffer = memoryview(bytearray(payload_size))
next_offset = 0
while payload_size - next_offset > 0:
recv_size = recv_socket.recv_into(mv_buffer[next_offset:],
payload_size - next_offset)
next_offset += recv_size
return jsonutils.loads(mv_buffer.tobytes())
class StatusRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
# Get the update data
status = _recv(self.request)
# Process the update
updater = driver_updater.DriverUpdater()
response = updater.update_loadbalancer_status(status)
# Send the response
json_data = jsonutils.dump_as_bytes(response)
len_str = '{}\n'.format(len(json_data)).encode('utf-8')
self.request.send(len_str)
self.request.sendall(json_data)
class StatsRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
# Get the update data
stats = _recv(self.request)
# Process the update
updater = driver_updater.DriverUpdater()
response = updater.update_listener_statistics(stats)
# Send the response
json_data = jsonutils.dump_as_bytes(response)
len_str = '{}\n'.format(len(json_data)).encode('utf-8')
self.request.send(len_str)
self.request.sendall(json_data)
class ForkingUDSServer(socketserver.ForkingMixIn,
socketserver.UnixStreamServer):
pass
def _mutate_config(*args, **kwargs):
CONF.mutate_config_files()
def _cleanup_socket_file(filename):
# Remove the socket file if it already exists
try:
os.remove(filename)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def status_listener(exit_event):
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGHUP, _mutate_config)
_cleanup_socket_file(CONF.driver_agent.status_socket_path)
server = ForkingUDSServer(CONF.driver_agent.status_socket_path,
StatusRequestHandler)
server.timeout = CONF.driver_agent.status_request_timeout
server.max_children = CONF.driver_agent.status_max_processes
while not exit_event.is_set():
server.handle_request()
LOG.info('Waiting for driver status listener to shutdown...')
# Can't shut ourselves down as we would deadlock, spawn a thread
threading.Thread(target=server.shutdown).start()
LOG.info('Driver status listener shutdown finished.')
server.server_close()
_cleanup_socket_file(CONF.driver_agent.status_socket_path)
def stats_listener(exit_event):
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGHUP, _mutate_config)
_cleanup_socket_file(CONF.driver_agent.stats_socket_path)
server = ForkingUDSServer(CONF.driver_agent.stats_socket_path,
StatsRequestHandler)
server.timeout = CONF.driver_agent.stats_request_timeout
server.max_children = CONF.driver_agent.stats_max_processes
while not exit_event.is_set():
server.handle_request()
LOG.info('Waiting for driver statistics listener to shutdown...')
# Can't shut ourselves down as we would deadlock, spawn a thread
threading.Thread(target=server.shutdown).start()
LOG.info('Driver statistics listener shutdown finished.')
server.server_close()
_cleanup_socket_file(CONF.driver_agent.stats_socket_path)

View File

@ -0,0 +1,174 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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.
from octavia_lib.api.drivers import exceptions as driver_exceptions
from octavia_lib.common import constants as lib_consts
from octavia.common import constants as consts
from octavia.common import utils
from octavia.db import api as db_apis
from octavia.db import repositories as repo
class DriverUpdater(object):
def __init__(self, **kwargs):
self.loadbalancer_repo = repo.LoadBalancerRepository()
self.listener_repo = repo.ListenerRepository()
self.pool_repo = repo.PoolRepository()
self.health_mon_repo = repo.HealthMonitorRepository()
self.member_repo = repo.MemberRepository()
self.l7policy_repo = repo.L7PolicyRepository()
self.l7rule_repo = repo.L7RuleRepository()
self.listener_stats_repo = repo.ListenerStatisticsRepository()
self.db_session = db_apis.get_session()
super(DriverUpdater, self).__init__(**kwargs)
def _check_for_lb_vip_deallocate(self, repo, lb_id):
lb = repo.get(self.db_session, id=lb_id)
if lb.vip.octavia_owned:
vip = lb.vip
# We need a backreference
vip.load_balancer = lb
# Only lookup the network driver if we have a VIP to deallocate
network_driver = utils.get_network_driver()
network_driver.deallocate_vip(vip)
def _process_status_update(self, repo, object_name, record,
delete_record=False):
# Zero it out so that if the ID is missing from a record we do not
# report the last LB as the failed record in the exception
record_id = None
try:
record_id = record['id']
record_kwargs = {}
prov_status = record.get(consts.PROVISIONING_STATUS, None)
if prov_status:
if (prov_status == consts.DELETED and
object_name == consts.LOADBALANCERS):
self._check_for_lb_vip_deallocate(repo, record_id)
elif prov_status == consts.DELETED and delete_record:
repo.delete(self.db_session, id=record_id)
return
record_kwargs[consts.PROVISIONING_STATUS] = prov_status
op_status = record.get(consts.OPERATING_STATUS, None)
if op_status:
record_kwargs[consts.OPERATING_STATUS] = op_status
if prov_status or op_status:
repo.update(self.db_session, record_id, **record_kwargs)
except Exception as e:
# We need to raise a failure here to notify the driver it is
# sending bad status data.
raise driver_exceptions.UpdateStatusError(
fault_string=str(e), status_object_id=record_id,
status_object=object_name)
def update_loadbalancer_status(self, status):
"""Update load balancer status.
:param status: dictionary defining the provisioning status and
operating status for load balancer objects, including pools,
members, listeners, L7 policies, and L7 rules.
iod (string): ID for the object.
provisioning_status (string): Provisioning status for the object.
operating_status (string): Operating status for the object.
:type status: dict
:raises: UpdateStatusError
:returns: None
"""
try:
members = status.pop(consts.MEMBERS, [])
for member in members:
self._process_status_update(self.member_repo, consts.MEMBERS,
member, delete_record=True)
health_mons = status.pop(consts.HEALTHMONITORS, [])
for health_mon in health_mons:
self._process_status_update(
self.health_mon_repo, consts.HEALTHMONITORS, health_mon,
delete_record=True)
pools = status.pop(consts.POOLS, [])
for pool in pools:
self._process_status_update(self.pool_repo, consts.POOLS,
pool, delete_record=True)
l7rules = status.pop(consts.L7RULES, [])
for l7rule in l7rules:
self._process_status_update(self.l7rule_repo, consts.L7RULES,
l7rule, delete_record=True)
l7policies = status.pop(consts.L7POLICIES, [])
for l7policy in l7policies:
self._process_status_update(
self.l7policy_repo, consts.L7POLICIES, l7policy,
delete_record=True)
listeners = status.pop(lib_consts.LISTENERS, [])
for listener in listeners:
self._process_status_update(
self.listener_repo, lib_consts.LISTENERS, listener,
delete_record=True)
lbs = status.pop(consts.LOADBALANCERS, [])
for lb in lbs:
self._process_status_update(self.loadbalancer_repo,
consts.LOADBALANCERS, lb)
except driver_exceptions.UpdateStatusError as e:
return {lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.FAULT_STRING: e.fault_string,
lib_consts.STATUS_OBJECT: e.status_object,
lib_consts.STATUS_OBJECT_ID: e.status_object_id}
except Exception as e:
return {lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.FAULT_STRING: str(e)}
return {lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_OK}
def update_listener_statistics(self, statistics):
"""Update listener statistics.
:param statistics: Statistics for listeners:
id (string): ID for listener.
active_connections (int): Number of currently active connections.
bytes_in (int): Total bytes received.
bytes_out (int): Total bytes sent.
request_errors (int): Total requests not fulfilled.
total_connections (int): The total connections handled.
:type statistics: dict
:raises: UpdateStatisticsError
:returns: None
"""
listener_stats = statistics.get(lib_consts.LISTENERS, [])
for stat in listener_stats:
try:
listener_id = stat.pop('id')
except Exception as e:
return {
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.FAULT_STRING: str(e),
lib_consts.STATS_OBJECT: lib_consts.LISTENERS}
# Provider drivers other than the amphora driver do not have
# an amphora ID, use the listener ID again here to meet the
# constraint requirement.
try:
self.listener_stats_repo.replace(self.db_session, listener_id,
listener_id, **stat)
except Exception as e:
return {
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.FAULT_STRING: str(e),
lib_consts.STATS_OBJECT: lib_consts.LISTENERS,
lib_consts.STATS_OBJECT_ID: listener_id}
return {lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_OK}

View File

@ -12,144 +12,15 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from octavia.api.drivers import exceptions as driver_exceptions import warnings
from octavia.common import constants as consts
from octavia.common import utils from debtcollector import moves
from octavia.db import api as db_apis
from octavia.db import repositories as repo from octavia_lib.api.drivers import driver_lib as lib_driver_lib
class DriverLibrary(object): warnings.simplefilter('default', DeprecationWarning)
def __init__(self, **kwargs): DriverLibrary = moves.moved_class(
self.loadbalancer_repo = repo.LoadBalancerRepository() lib_driver_lib.DriverLibrary, 'DriverLibrary', __name__,
self.listener_repo = repo.ListenerRepository() version='Stein', removal_version='U')
self.pool_repo = repo.PoolRepository()
self.health_mon_repo = repo.HealthMonitorRepository()
self.member_repo = repo.MemberRepository()
self.l7policy_repo = repo.L7PolicyRepository()
self.l7rule_repo = repo.L7RuleRepository()
self.listener_stats_repo = repo.ListenerStatisticsRepository()
self.db_session = db_apis.get_session()
super(DriverLibrary, self).__init__(**kwargs)
def _check_for_lb_vip_deallocate(self, repo, lb_id):
lb = repo.get(self.db_session, id=lb_id)
if lb.vip.octavia_owned:
vip = lb.vip
# We need a backreference
vip.load_balancer = lb
# Only lookup the network driver if we have a VIP to deallocate
network_driver = utils.get_network_driver()
network_driver.deallocate_vip(vip)
def _process_status_update(self, repo, object_name, record,
delete_record=False):
# Zero it out so that if the ID is missing from a record we do not
# report the last LB as the failed record in the exception
record_id = None
try:
record_id = record['id']
record_kwargs = {}
prov_status = record.get(consts.PROVISIONING_STATUS, None)
if prov_status:
if (prov_status == consts.DELETED and
object_name == consts.LOADBALANCERS):
self._check_for_lb_vip_deallocate(repo, record_id)
elif prov_status == consts.DELETED and delete_record:
repo.delete(self.db_session, id=record_id)
return
record_kwargs[consts.PROVISIONING_STATUS] = prov_status
op_status = record.get(consts.OPERATING_STATUS, None)
if op_status:
record_kwargs[consts.OPERATING_STATUS] = op_status
if prov_status or op_status:
repo.update(self.db_session, record_id, **record_kwargs)
except Exception as e:
# We need to raise a failure here to notify the driver it is
# sending bad status data.
raise driver_exceptions.UpdateStatusError(
fault_string=str(e), status_object_id=record_id,
status_object=object_name)
def update_loadbalancer_status(self, status):
"""Update load balancer status.
:param status: dictionary defining the provisioning status and
operating status for load balancer objects, including pools,
members, listeners, L7 policies, and L7 rules.
iod (string): ID for the object.
provisioning_status (string): Provisioning status for the object.
operating_status (string): Operating status for the object.
:type status: dict
:raises: UpdateStatusError
:returns: None
"""
members = status.pop(consts.MEMBERS, [])
for member in members:
self._process_status_update(self.member_repo, consts.MEMBERS,
member, delete_record=True)
health_mons = status.pop(consts.HEALTHMONITORS, [])
for health_mon in health_mons:
self._process_status_update(
self.health_mon_repo, consts.HEALTHMONITORS, health_mon,
delete_record=True)
pools = status.pop(consts.POOLS, [])
for pool in pools:
self._process_status_update(self.pool_repo, consts.POOLS,
pool, delete_record=True)
l7rules = status.pop(consts.L7RULES, [])
for l7rule in l7rules:
self._process_status_update(self.l7rule_repo, consts.L7RULES,
l7rule, delete_record=True)
l7policies = status.pop(consts.L7POLICIES, [])
for l7policy in l7policies:
self._process_status_update(self.l7policy_repo, consts.L7POLICIES,
l7policy, delete_record=True)
listeners = status.pop(consts.LISTENERS, [])
for listener in listeners:
self._process_status_update(self.listener_repo, consts.LISTENERS,
listener, delete_record=True)
lbs = status.pop(consts.LOADBALANCERS, [])
for lb in lbs:
self._process_status_update(self.loadbalancer_repo,
consts.LOADBALANCERS, lb)
def update_listener_statistics(self, statistics):
"""Update listener statistics.
:param statistics: Statistics for listeners:
id (string): ID for listener.
active_connections (int): Number of currently active connections.
bytes_in (int): Total bytes received.
bytes_out (int): Total bytes sent.
request_errors (int): Total requests not fulfilled.
total_connections (int): The total connections handled.
:type statistics: dict
:raises: UpdateStatisticsError
:returns: None
"""
listener_stats = statistics.get('listeners', [])
for stat in listener_stats:
try:
listener_id = stat.pop('id')
except Exception as e:
raise driver_exceptions.UpdateStatisticsError(
fault_string=str(e), stats_object='listeners')
# Provider drivers other than the amphora driver do not have
# an amphora ID, use the listener ID again here to meet the
# constraint requirement.
try:
self.listener_stats_repo.replace(self.db_session, listener_id,
listener_id, **stat)
except Exception as e:
raise driver_exceptions.UpdateStatisticsError(
fault_string=str(e), stats_object='listeners',
stats_object_id=listener_id)

View File

@ -12,137 +12,30 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from octavia.i18n import _ import warnings
from debtcollector import moves
from octavia_lib.api.drivers import exceptions as lib_exceptions
class DriverError(Exception): warnings.simplefilter('default', DeprecationWarning)
"""Catch all exception that drivers can raise.
This exception includes two strings: The user fault string and the DriverError = moves.moved_class(lib_exceptions.DriverError, 'DriverError',
optional operator fault string. The user fault string, __name__, version='Stein', removal_version='U')
"user_fault_string", will be provided to the API requester. The operator
fault string, "operator_fault_string", will be logged in the Octavia API
log file for the operator to use when debugging.
:param user_fault_string: String provided to the API requester. NotImplementedError = moves.moved_class(
:type user_fault_string: string lib_exceptions.NotImplementedError, 'NotImplementedError', __name__,
:param operator_fault_string: Optional string logged by the Octavia API version='Stein', removal_version='U')
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("An unknown driver error occurred.")
operator_fault_string = _("An unknown driver error occurred.")
def __init__(self, *args, **kwargs): UnsupportedOptionError = moves.moved_class(
self.user_fault_string = kwargs.pop('user_fault_string', lib_exceptions.UnsupportedOptionError, 'UnsupportedOptionError', __name__,
self.user_fault_string) version='Stein', removal_version='U')
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(DriverError, self).__init__(*args, **kwargs)
UpdateStatusError = moves.moved_class(
lib_exceptions.UpdateStatusError, 'UpdateStatusError', __name__,
version='Stein', removal_version='U')
class NotImplementedError(Exception): UpdateStatisticsError = moves.moved_class(
"""Exception raised when a driver does not implement an API function. lib_exceptions.UpdateStatisticsError, 'UpdateStatisticsError', __name__,
version='Stein', removal_version='U')
:param user_fault_string: String provided to the API requester.
:type user_fault_string: string
:param operator_fault_string: Optional string logged by the Octavia API
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("This feature is not implemented by the provider.")
operator_fault_string = _("This feature is not implemented by this "
"provider.")
def __init__(self, *args, **kwargs):
self.user_fault_string = kwargs.pop('user_fault_string',
self.user_fault_string)
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(NotImplementedError, self).__init__(*args, **kwargs)
class UnsupportedOptionError(Exception):
"""Exception raised when a driver does not support an option.
Provider drivers will validate that they can complete the request -- that
all options are supported by the driver. If the request fails validation,
drivers will raise an UnsupportedOptionError exception. For example, if a
driver does not support a flavor passed as an option to load balancer
create(), the driver will raise an UnsupportedOptionError and include a
message parameter providing an explanation of the failure.
:param user_fault_string: String provided to the API requester.
:type user_fault_string: string
:param operator_fault_string: Optional string logged by the Octavia API
for the operator to use when debugging.
:type operator_fault_string: string
"""
user_fault_string = _("A specified option is not supported by this "
"provider.")
operator_fault_string = _("A specified option is not supported by this "
"provider.")
def __init__(self, *args, **kwargs):
self.user_fault_string = kwargs.pop('user_fault_string',
self.user_fault_string)
self.operator_fault_string = kwargs.pop('operator_fault_string',
self.operator_fault_string)
super(UnsupportedOptionError, self).__init__(*args, **kwargs)
class UpdateStatusError(Exception):
"""Exception raised when a status update fails.
Each exception will include a message field that describes the
error and references to the failed record if available.
:param fault_string: String describing the fault.
:type fault_string: string
:param status_object: The object the fault occurred on.
:type status_object: string
:param status_object_id: The ID of the object that failed status update.
:type status_object_id: string
:param status_record: The status update record that caused the fault.
:type status_record: string
"""
fault_string = _("The status update had an unknown error.")
status_object = None
status_object_id = None
status_record = None
def __init__(self, *args, **kwargs):
self.fault_string = kwargs.pop('fault_string', self.fault_string)
self.status_object = kwargs.pop('status_object', None)
self.status_object_id = kwargs.pop('status_object_id', None)
self.status_record = kwargs.pop('status_record', None)
super(UpdateStatusError, self).__init__(*args, **kwargs)
class UpdateStatisticsError(Exception):
"""Exception raised when a statistics update fails.
Each exception will include a message field that describes the
error and references to the failed record if available.
:param fault_string: String describing the fault.
:type fault_string: string
:param status_object: The object the fault occurred on.
:type status_object: string
:param status_object_id: The ID of the object that failed stats update.
:type status_object_id: string
:param status_record: The stats update record that caused the fault.
:type status_record: string
"""
fault_string = _("The statistics update had an unknown error.")
stats_object = None
stats_object_id = None
stats_record = None
def __init__(self, *args, **kwargs):
self.fault_string = kwargs.pop('fault_string',
self.fault_string)
self.stats_object = kwargs.pop('stats_object', None)
self.stats_object_id = kwargs.pop('stats_object_id', None)
self.stats_record = kwargs.pop('stats_record', None)
super(UpdateStatisticsError, self).__init__(*args, **kwargs)

View File

@ -15,8 +15,8 @@
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import uuidutils from oslo_utils import uuidutils
from octavia.api.drivers import data_models from octavia_lib.api.drivers import data_models
from octavia.api.drivers import provider_base as driver_base from octavia_lib.api.drivers import provider_base as driver_base
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@ -12,470 +12,15 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from octavia.api.drivers import exceptions import warnings
# This class describes the abstraction of a provider driver interface. from debtcollector import moves
# Load balancing provider drivers will implement this interface.
from octavia_lib.api.drivers import provider_base as lib_provider_base
class ProviderDriver(object): warnings.simplefilter('default', DeprecationWarning)
# name is for internal Octavia use and should not be used by drivers
name = None
# Load Balancer ProviderDriver = moves.moved_class(
def create_vip_port(self, loadbalancer_id, project_id, vip_dictionary): lib_provider_base.ProviderDriver, 'ProviderDriver', __name__,
"""Creates a port for a load balancer VIP. version='Stein', removal_version='U')
If the driver supports creating VIP ports, the driver will create a
VIP port and return the vip_dictionary populated with the vip_port_id.
If the driver does not support port creation, the driver will raise
a NotImplementedError.
:param loadbalancer_id: ID of loadbalancer.
:type loadbalancer_id: string
:param project_id: The project ID to create the VIP under.
:type project_id: string
:param: vip_dictionary: The VIP dictionary.
:type vip_dictionary: dict
:returns: VIP dictionary with vip_port_id.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support creating
VIP ports.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating VIP '
'ports.',
operator_fault_string='This provider does not support creating '
'VIP ports. Octavia will create it.')
def loadbalancer_create(self, loadbalancer):
"""Creates a new load balancer.
:param loadbalancer: The load balancer object.
:type loadbalancer: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support create.
:raises UnsupportedOptionError: The driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'load balancers.',
operator_fault_string='This provider does not support creating '
'load balancers. What?')
def loadbalancer_delete(self, loadbalancer, cascade=False):
"""Deletes a load balancer.
:param loadbalancer: The load balancer to delete.
:type loadbalancer: object
:param cascade: If True, deletes all child objects (listeners,
pools, etc.) in addition to the load balancer.
:type cascade: bool
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'load balancers.',
operator_fault_string='This provider does not support deleting '
'load balancers.')
def loadbalancer_failover(self, loadbalancer_id):
"""Performs a fail over of a load balancer.
:param loadbalancer_id: ID of the load balancer to failover.
:type loadbalancer_id: string
:return: Nothing if the failover request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises: NotImplementedError if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support failing over '
'load balancers.',
operator_fault_string='This provider does not support failing '
'over load balancers.')
def loadbalancer_update(self, old_loadbalancer, new_loadbalncer):
"""Updates a load balancer.
:param old_loadbalancer: The baseline load balancer object.
:type old_loadbalancer: object
:param new_loadbalancer: The updated load balancer object.
:type new_loadbalancer: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support request.
:raises UnsupportedOptionError: The driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'load balancers.',
operator_fault_string='This provider does not support updating '
'load balancers.')
# Listener
def listener_create(self, listener):
"""Creates a new listener.
:param listener: The listener object.
:type listener: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'listeners.',
operator_fault_string='This provider does not support creating '
'listeners.')
def listener_delete(self, listener):
"""Deletes a listener.
:param listener: The listener to delete.
:type listener: object
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'listeners.',
operator_fault_string='This provider does not support deleting '
'listeners.')
def listener_update(self, old_listener, new_listener):
"""Updates a listener.
:param old_listener: The baseline listener object.
:type old_listener: object
:param new_listener: The updated listener object.
:type new_listener: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'listeners.',
operator_fault_string='This provider does not support updating '
'listeners.')
# Pool
def pool_create(self, pool):
"""Creates a new pool.
:param pool: The pool object.
:type pool: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'pools.',
operator_fault_string='This provider does not support creating '
'pools.')
def pool_delete(self, pool):
"""Deletes a pool and its members.
:param pool: The pool to delete.
:type pool: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'pools.',
operator_fault_string='This provider does not support deleting '
'pools.')
def pool_update(self, old_pool, new_pool):
"""Updates a pool.
:param pool: The baseline pool object.
:type pool: object
:param pool: The updated pool object.
:type pool: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'pools.',
operator_fault_string='This provider does not support updating '
'pools.')
# Member
def member_create(self, member):
"""Creates a new member for a pool.
:param member: The member object.
:type member: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'members.',
operator_fault_string='This provider does not support creating '
'members.')
def member_delete(self, member):
"""Deletes a pool member.
:param member: The member to delete.
:type member: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'members.',
operator_fault_string='This provider does not support deleting '
'members.')
def member_update(self, old_member, new_member):
"""Updates a pool member.
:param old_member: The baseline member object.
:type old_member: object
:param new_member: The updated member object.
:type new_member: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'members.',
operator_fault_string='This provider does not support updating '
'members.')
def member_batch_update(self, members):
"""Creates, updates, or deletes a set of pool members.
:param members: List of member objects.
:type members: list
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support batch '
'updating members.',
operator_fault_string='This provider does not support batch '
'updating members.')
# Health Monitor
def health_monitor_create(self, healthmonitor):
"""Creates a new health monitor.
:param healthmonitor: The health monitor object.
:type healthmonitor: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'health monitors.',
operator_fault_string='This provider does not support creating '
'health monitors.')
def health_monitor_delete(self, healthmonitor):
"""Deletes a healthmonitor_id.
:param healthmonitor: The monitor to delete.
:type healthmonitor: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'health monitors.',
operator_fault_string='This provider does not support deleting '
'health monitors.')
def health_monitor_update(self, old_healthmonitor, new_healthmonitor):
"""Updates a health monitor.
:param old_healthmonitor: The baseline health monitor object.
:type old_healthmonitor: object
:param new_healthmonitor: The updated health monitor object.
:type new_healthmonitor: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'health monitors.',
operator_fault_string='This provider does not support updating '
'health monitors.')
# L7 Policy
def l7policy_create(self, l7policy):
"""Creates a new L7 policy.
:param l7policy: The L7 policy object.
:type l7policy: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'l7policies.',
operator_fault_string='This provider does not support creating '
'l7policies.')
def l7policy_delete(self, l7policy):
"""Deletes an L7 policy.
:param l7policy: The L7 policy to delete.
:type l7policy: object
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'l7policies.',
operator_fault_string='This provider does not support deleting '
'l7policies.')
def l7policy_update(self, old_l7policy, new_l7policy):
"""Updates an L7 policy.
:param old_l7policy: The baseline L7 policy object.
:type old_l7policy: object
:param new_l7policy: The updated L7 policy object.
:type new_l7policy: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'l7policies.',
operator_fault_string='This provider does not support updating '
'l7policies.')
# L7 Rule
def l7rule_create(self, l7rule):
"""Creates a new L7 rule.
:param l7rule: The L7 rule object.
:type l7rule: object
:return: Nothing if the create request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support creating '
'l7rules.',
operator_fault_string='This provider does not support creating '
'l7rules.')
def l7rule_delete(self, l7rule):
"""Deletes an L7 rule.
:param l7rule: The L7 rule to delete.
:type l7rule: object
:return: Nothing if the delete request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support deleting '
'l7rules.',
operator_fault_string='This provider does not support deleting '
'l7rules.')
def l7rule_update(self, old_l7rule, new_l7rule):
"""Updates an L7 rule.
:param old_l7rule: The baseline L7 rule object.
:type old_l7rule: object
:param new_l7rule: The updated L7 rule object.
:type new_l7rule: object
:return: Nothing if the update request was accepted.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: if driver does not support request.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support updating '
'l7rules.',
operator_fault_string='This provider does not support updating '
'l7rules.')
# Flavor
def get_supported_flavor_metadata(self):
"""Returns a dict of flavor metadata keys supported by this driver.
The returned dictionary will include key/value pairs, 'name' and
'description.'
:returns: The flavor metadata dictionary
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support flavors.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support getting the '
'supported flavor metadata.',
operator_fault_string='This provider does not support getting '
'the supported flavor metadata.')
def validate_flavor(self, flavor_metadata):
"""Validates if driver can support the flavor.
:param flavor_metadata: Dictionary with flavor metadata.
:type flavor_metadata: dict
:return: Nothing if the flavor is valid and supported.
:raises DriverError: An unexpected error occurred in the driver.
:raises NotImplementedError: The driver does not support flavors.
:raises UnsupportedOptionError: if driver does not
support one of the configuration options.
"""
raise exceptions.NotImplementedError(
user_fault_string='This provider does not support validating '
'flavors.',
operator_fault_string='This provider does not support validating '
'the supported flavor metadata.')

View File

@ -0,0 +1,84 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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.
from functools import partial
import multiprocessing
import os
import signal
import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from octavia.api.drivers.driver_agent import driver_listener
from octavia.common import service
from octavia import version
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def _mutate_config(*args, **kwargs):
CONF.mutate_config_files()
def _handle_mutate_config(status_proc_pid, stats_proc_pid, *args, **kwargs):
LOG.info("Driver agent received HUP signal, mutating config.")
_mutate_config()
os.kill(status_proc_pid, signal.SIGHUP)
os.kill(stats_proc_pid, signal.SIGHUP)
def main():
service.prepare_service(sys.argv)
gmr.TextGuruMeditation.setup_autorun(version)
processes = []
exit_event = multiprocessing.Event()
status_listener_proc = multiprocessing.Process(
name='status_listener', target=driver_listener.status_listener,
args=(exit_event,))
processes.append(status_listener_proc)
LOG.info("Driver agent status listener process starts:")
status_listener_proc.start()
stats_listener_proc = multiprocessing.Process(
name='stats_listener', target=driver_listener.stats_listener,
args=(exit_event,))
processes.append(stats_listener_proc)
LOG.info("Driver agent statistics listener process starts:")
stats_listener_proc.start()
def process_cleanup(*args, **kwargs):
LOG.info("Driver agent exiting due to signal")
exit_event.set()
status_listener_proc.join()
stats_listener_proc.join()
signal.signal(signal.SIGTERM, process_cleanup)
signal.signal(signal.SIGHUP, partial(
_handle_mutate_config, status_listener_proc.pid,
stats_listener_proc.pid))
try:
for process in processes:
process.join()
except KeyboardInterrupt:
process_cleanup()

View File

@ -601,6 +601,37 @@ audit_opts = [
'enabled.')), 'enabled.')),
] ]
driver_agent_opts = [
cfg.StrOpt('status_socket_path',
default='/var/run/octavia/status.sock',
help=_('Path to the driver status unix domain socket file.')),
cfg.StrOpt('stats_socket_path',
default='/var/run/octavia/stats.sock',
help=_('Path to the driver statistics unix domain socket '
'file.')),
cfg.IntOpt('status_request_timeout',
default=5,
help=_('Time, in seconds, to wait for a status update '
'request.')),
cfg.IntOpt('status_max_processes',
default=50,
help=_('Maximum number of concurrent processes to use '
'servicing status updates.')),
cfg.IntOpt('stats_request_timeout',
default=5,
help=_('Time, in seconds, to wait for a statistics update '
'request.')),
cfg.IntOpt('stats_max_processes',
default=50,
help=_('Maximum number of concurrent processes to use '
'servicing statistics updates.')),
cfg.FloatOpt('max_process_warning_percent',
default=0.75, min=0.01, max=0.99,
help=_('Percentage of max_processes (both status and stats) '
'in use to start logging warning messages about an '
'overloaded driver-agent.')),
]
# Register the configuration options # Register the configuration options
cfg.CONF.register_opts(core_opts) cfg.CONF.register_opts(core_opts)
cfg.CONF.register_opts(api_opts, group='api_settings') cfg.CONF.register_opts(api_opts, group='api_settings')
@ -621,6 +652,7 @@ cfg.CONF.register_opts(glance_opts, group='glance')
cfg.CONF.register_opts(neutron_opts, group='neutron') cfg.CONF.register_opts(neutron_opts, group='neutron')
cfg.CONF.register_opts(quota_opts, group='quotas') cfg.CONF.register_opts(quota_opts, group='quotas')
cfg.CONF.register_opts(audit_opts, group='audit') cfg.CONF.register_opts(audit_opts, group='audit')
cfg.CONF.register_opts(driver_agent_opts, group='driver_agent')
cfg.CONF.register_opts(local.certgen_opts, group='certificates') cfg.CONF.register_opts(local.certgen_opts, group='certificates')
cfg.CONF.register_opts(local.certmgr_opts, group='certificates') cfg.CONF.register_opts(local.certmgr_opts, group='certificates')

View File

@ -12,48 +12,183 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
LB_ALGORITHM_ROUND_ROBIN = 'ROUND_ROBIN' from octavia_lib.common import constants as lib_consts
LB_ALGORITHM_LEAST_CONNECTIONS = 'LEAST_CONNECTIONS'
LB_ALGORITHM_SOURCE_IP = 'SOURCE_IP'
SUPPORTED_LB_ALGORITHMS = (LB_ALGORITHM_LEAST_CONNECTIONS,
LB_ALGORITHM_ROUND_ROBIN,
LB_ALGORITHM_SOURCE_IP)
SESSION_PERSISTENCE_SOURCE_IP = 'SOURCE_IP' ##############################################################################
SESSION_PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE' # Constants common to the provider drivers moved to
SESSION_PERSISTENCE_APP_COOKIE = 'APP_COOKIE' # octavia_lib.common.constants
SUPPORTED_SP_TYPES = (SESSION_PERSISTENCE_SOURCE_IP, # These are deprecated, to be removed in the 'U' release
SESSION_PERSISTENCE_HTTP_COOKIE, ##############################################################################
SESSION_PERSISTENCE_APP_COOKIE) # 'loadbalancers'
LOADBALANCERS = lib_consts.LOADBALANCERS
# 'listeners'
LISTENERS = lib_consts.LISTENERS
# 'pools'
POOLS = lib_consts.POOLS
# HEALTHMONITORS = 'healthmonitors'
HEALTHMONITORS = lib_consts.HEALTHMONITORS
# 'members'
MEMBERS = lib_consts.MEMBERS
# 'l7policies'
L7POLICIES = lib_consts.L7POLICIES
# 'l7rules'
L7RULES = lib_consts.L7RULES
HEALTH_MONITOR_PING = 'PING' # 'PING'
HEALTH_MONITOR_TCP = 'TCP' HEALTH_MONITOR_PING = lib_consts.HEALTH_MONITOR_PING
HEALTH_MONITOR_HTTP = 'HTTP' # 'TCP'
HEALTH_MONITOR_HTTPS = 'HTTPS' HEALTH_MONITOR_TCP = lib_consts.HEALTH_MONITOR_TCP
HEALTH_MONITOR_TLS_HELLO = 'TLS-HELLO' # 'HTTP'
HEALTH_MONITOR_UDP_CONNECT = 'UDP-CONNECT' HEALTH_MONITOR_HTTP = lib_consts.HEALTH_MONITOR_HTTP
SUPPORTED_HEALTH_MONITOR_TYPES = (HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS, # 'HTTPS'
HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP, HEALTH_MONITOR_HTTPS = lib_consts.HEALTH_MONITOR_HTTPS
HEALTH_MONITOR_TLS_HELLO, # 'TLS-HELLO'
HEALTH_MONITOR_UDP_CONNECT) HEALTH_MONITOR_TLS_HELLO = lib_consts.HEALTH_MONITOR_TLS_HELLO
HEALTH_MONITOR_HTTP_METHOD_GET = 'GET' # 'UDP-CONNECT'
HEALTH_MONITOR_HTTP_METHOD_HEAD = 'HEAD' HEALTH_MONITOR_UDP_CONNECT = lib_consts.HEALTH_MONITOR_UDP_CONNECT
HEALTH_MONITOR_HTTP_METHOD_POST = 'POST' SUPPORTED_HEALTH_MONITOR_TYPES = lib_consts.SUPPORTED_HEALTH_MONITOR_TYPES
HEALTH_MONITOR_HTTP_METHOD_PUT = 'PUT'
HEALTH_MONITOR_HTTP_METHOD_DELETE = 'DELETE' # 'GET'
HEALTH_MONITOR_HTTP_METHOD_TRACE = 'TRACE' HEALTH_MONITOR_HTTP_METHOD_GET = lib_consts.HEALTH_MONITOR_HTTP_METHOD_GET
HEALTH_MONITOR_HTTP_METHOD_OPTIONS = 'OPTIONS' # 'HEAD'
HEALTH_MONITOR_HTTP_METHOD_CONNECT = 'CONNECT' HEALTH_MONITOR_HTTP_METHOD_HEAD = lib_consts.HEALTH_MONITOR_HTTP_METHOD_HEAD
HEALTH_MONITOR_HTTP_METHOD_PATCH = 'PATCH' # 'POST'
HEALTH_MONITOR_HTTP_DEFAULT_METHOD = HEALTH_MONITOR_HTTP_METHOD_GET HEALTH_MONITOR_HTTP_METHOD_POST = lib_consts.HEALTH_MONITOR_HTTP_METHOD_POST
# 'PUT'
HEALTH_MONITOR_HTTP_METHOD_PUT = lib_consts.HEALTH_MONITOR_HTTP_METHOD_PUT
# 'DELETE'
HEALTH_MONITOR_HTTP_METHOD_DELETE = (
lib_consts.HEALTH_MONITOR_HTTP_METHOD_DELETE)
# 'TRACE'
HEALTH_MONITOR_HTTP_METHOD_TRACE = lib_consts.HEALTH_MONITOR_HTTP_METHOD_TRACE
# 'OPTIONS'
HEALTH_MONITOR_HTTP_METHOD_OPTIONS = (
lib_consts.HEALTH_MONITOR_HTTP_METHOD_OPTIONS)
# 'CONNECT'
HEALTH_MONITOR_HTTP_METHOD_CONNECT = (
lib_consts.HEALTH_MONITOR_HTTP_METHOD_CONNECT)
# 'PATCH'
HEALTH_MONITOR_HTTP_METHOD_PATCH = lib_consts.HEALTH_MONITOR_HTTP_METHOD_PATCH
SUPPORTED_HEALTH_MONITOR_HTTP_METHODS = ( SUPPORTED_HEALTH_MONITOR_HTTP_METHODS = (
HEALTH_MONITOR_HTTP_METHOD_GET, HEALTH_MONITOR_HTTP_METHOD_HEAD, lib_consts.SUPPORTED_HEALTH_MONITOR_HTTP_METHODS)
HEALTH_MONITOR_HTTP_METHOD_POST, HEALTH_MONITOR_HTTP_METHOD_PUT,
HEALTH_MONITOR_HTTP_METHOD_DELETE, HEALTH_MONITOR_HTTP_METHOD_TRACE, # 'REJECT'
HEALTH_MONITOR_HTTP_METHOD_OPTIONS, HEALTH_MONITOR_HTTP_METHOD_CONNECT, L7POLICY_ACTION_REJECT = lib_consts.L7POLICY_ACTION_REJECT
HEALTH_MONITOR_HTTP_METHOD_PATCH) # 'REDIRECT_TO_URL'
SUPPORTED_HTTP_VERSIONS = [1.0, 1.1] L7POLICY_ACTION_REDIRECT_TO_URL = lib_consts.L7POLICY_ACTION_REDIRECT_TO_URL
# 'REDIRECT_TO_POOL'
L7POLICY_ACTION_REDIRECT_TO_POOL = lib_consts.L7POLICY_ACTION_REDIRECT_TO_POOL
# 'REDIRECT_PREFIX'
L7POLICY_ACTION_REDIRECT_PREFIX = lib_consts.L7POLICY_ACTION_REDIRECT_PREFIX
SUPPORTED_L7POLICY_ACTIONS = lib_consts.SUPPORTED_L7POLICY_ACTIONS
# 'REGEX'
L7RULE_COMPARE_TYPE_REGEX = lib_consts.L7RULE_COMPARE_TYPE_REGEX
# 'STARTS_WITH'
L7RULE_COMPARE_TYPE_STARTS_WITH = lib_consts.L7RULE_COMPARE_TYPE_STARTS_WITH
# 'ENDS_WITH'
L7RULE_COMPARE_TYPE_ENDS_WITH = lib_consts.L7RULE_COMPARE_TYPE_ENDS_WITH
# 'CONTAINS'
L7RULE_COMPARE_TYPE_CONTAINS = lib_consts.L7RULE_COMPARE_TYPE_CONTAINS
# 'EQUAL_TO'
L7RULE_COMPARE_TYPE_EQUAL_TO = lib_consts.L7RULE_COMPARE_TYPE_EQUAL_TO
SUPPORTED_L7RULE_COMPARE_TYPES = lib_consts.SUPPORTED_L7RULE_COMPARE_TYPES
# 'HOST_NAME'
L7RULE_TYPE_HOST_NAME = lib_consts.L7RULE_TYPE_HOST_NAME
# 'PATH'
L7RULE_TYPE_PATH = lib_consts.L7RULE_TYPE_PATH
# 'FILE_TYPE'
L7RULE_TYPE_FILE_TYPE = lib_consts.L7RULE_TYPE_FILE_TYPE
# 'HEADER'
L7RULE_TYPE_HEADER = lib_consts.L7RULE_TYPE_HEADER
# 'COOKIE'
L7RULE_TYPE_COOKIE = lib_consts.L7RULE_TYPE_COOKIE
# 'SSL_CONN_HAS_CERT'
L7RULE_TYPE_SSL_CONN_HAS_CERT = lib_consts.L7RULE_TYPE_SSL_CONN_HAS_CERT
# 'SSL_VERIFY_RESULT'
L7RULE_TYPE_SSL_VERIFY_RESULT = lib_consts.L7RULE_TYPE_SSL_VERIFY_RESULT
# 'SSL_DN_FIELD'
L7RULE_TYPE_SSL_DN_FIELD = lib_consts.L7RULE_TYPE_SSL_DN_FIELD
SUPPORTED_L7RULE_TYPES = lib_consts.SUPPORTED_L7RULE_TYPES
# 'ROUND_ROBIN'
LB_ALGORITHM_ROUND_ROBIN = lib_consts.LB_ALGORITHM_ROUND_ROBIN
# 'LEAST_CONNECTIONS'
LB_ALGORITHM_LEAST_CONNECTIONS = lib_consts.LB_ALGORITHM_LEAST_CONNECTIONS
# 'SOURCE_IP'
LB_ALGORITHM_SOURCE_IP = lib_consts.LB_ALGORITHM_SOURCE_IP
SUPPORTED_LB_ALGORITHMS = lib_consts.SUPPORTED_LB_ALGORITHMS
# 'operating_status'
OPERATING_STATUS = lib_consts.OPERATING_STATUS
# 'ONLINE'
ONLINE = lib_consts.ONLINE
# 'OFFLINE'
OFFLINE = lib_consts.OFFLINE
# 'DEGRADED'
DEGRADED = lib_consts.DEGRADED
# 'ERROR'
ERROR = lib_consts.ERROR
# 'DRAINING'
DRAINING = lib_consts.DRAINING
# 'NO_MONITOR'
NO_MONITOR = lib_consts.NO_MONITOR
# 'operating_status'
SUPPORTED_OPERATING_STATUSES = lib_consts.SUPPORTED_OPERATING_STATUSES
# 'TCP'
PROTOCOL_TCP = lib_consts.PROTOCOL_TCP
# 'UDP'
PROTOCOL_UDP = lib_consts.PROTOCOL_UDP
# 'HTTP'
PROTOCOL_HTTP = lib_consts.PROTOCOL_HTTP
# 'HTTPS'
PROTOCOL_HTTPS = lib_consts.PROTOCOL_HTTPS
# 'TERMINATED_HTTPS'
PROTOCOL_TERMINATED_HTTPS = lib_consts.PROTOCOL_TERMINATED_HTTPS
# 'PROXY'
PROTOCOL_PROXY = lib_consts.PROTOCOL_PROXY
SUPPORTED_PROTOCOLS = lib_consts.SUPPORTED_PROTOCOLS
# 'provisioning_status'
PROVISIONING_STATUS = lib_consts.PROVISIONING_STATUS
# Amphora has been allocated to a load balancer 'ALLOCATED'
AMPHORA_ALLOCATED = lib_consts.AMPHORA_ALLOCATED
# Amphora is being built 'BOOTING'
AMPHORA_BOOTING = lib_consts.AMPHORA_BOOTING
# Amphora is ready to be allocated to a load balancer 'READY'
AMPHORA_READY = lib_consts.AMPHORA_READY
# 'ACTIVE'
ACTIVE = lib_consts.ACTIVE
# 'PENDING_DELETE'
PENDING_DELETE = lib_consts.PENDING_DELETE
# 'PENDING_UPDATE'
PENDING_UPDATE = lib_consts.PENDING_UPDATE
# 'PENDING_CREATE'
PENDING_CREATE = lib_consts.PENDING_CREATE
# 'DELETED'
DELETED = lib_consts.DELETED
SUPPORTED_PROVISIONING_STATUSES = lib_consts.SUPPORTED_PROVISIONING_STATUSES
# 'SOURCE_IP'
SESSION_PERSISTENCE_SOURCE_IP = lib_consts.SESSION_PERSISTENCE_SOURCE_IP
# 'HTTP_COOKIE'
SESSION_PERSISTENCE_HTTP_COOKIE = lib_consts.SESSION_PERSISTENCE_HTTP_COOKIE
# 'APP_COOKIE'
SESSION_PERSISTENCE_APP_COOKIE = lib_consts.SESSION_PERSISTENCE_APP_COOKIE
SUPPORTED_SP_TYPES = lib_consts.SUPPORTED_SP_TYPES
# List of HTTP headers which are supported for insertion
SUPPORTED_HTTP_HEADERS = lib_consts.SUPPORTED_HTTP_HEADERS
# List of SSL headers for client certificate
SUPPORTED_SSL_HEADERS = lib_consts.SUPPORTED_SSL_HEADERS
###############################################################################
HEALTH_MONITOR_DEFAULT_EXPECTED_CODES = '200' HEALTH_MONITOR_DEFAULT_EXPECTED_CODES = '200'
HEALTH_MONITOR_HTTP_DEFAULT_METHOD = lib_consts.HEALTH_MONITOR_HTTP_METHOD_GET
HEALTH_MONITOR_DEFAULT_URL_PATH = '/' HEALTH_MONITOR_DEFAULT_URL_PATH = '/'
TYPE = 'type' TYPE = 'type'
URL_PATH = 'url_path' URL_PATH = 'url_path'
@ -67,15 +202,6 @@ RISE_THRESHOLD = 'rise_threshold'
UPDATE_STATS = 'UPDATE_STATS' UPDATE_STATS = 'UPDATE_STATS'
UPDATE_HEALTH = 'UPDATE_HEALTH' UPDATE_HEALTH = 'UPDATE_HEALTH'
PROTOCOL_TCP = 'TCP'
PROTOCOL_UDP = 'UDP'
PROTOCOL_HTTP = 'HTTP'
PROTOCOL_HTTPS = 'HTTPS'
PROTOCOL_TERMINATED_HTTPS = 'TERMINATED_HTTPS'
PROTOCOL_PROXY = 'PROXY'
SUPPORTED_PROTOCOLS = (PROTOCOL_TCP, PROTOCOL_HTTPS, PROTOCOL_HTTP,
PROTOCOL_TERMINATED_HTTPS, PROTOCOL_PROXY, PROTOCOL_UDP)
# API Integer Ranges # API Integer Ranges
MIN_PORT_NUMBER = 1 MIN_PORT_NUMBER = 1
MAX_PORT_NUMBER = 65535 MAX_PORT_NUMBER = 65535
@ -97,86 +223,27 @@ DEFAULT_TIMEOUT_MEMBER_CONNECT = 5000
DEFAULT_TIMEOUT_MEMBER_DATA = 50000 DEFAULT_TIMEOUT_MEMBER_DATA = 50000
DEFAULT_TIMEOUT_TCP_INSPECT = 0 DEFAULT_TIMEOUT_TCP_INSPECT = 0
MUTABLE_STATUSES = (lib_consts.ACTIVE,)
DELETABLE_STATUSES = (lib_consts.ACTIVE, lib_consts.ERROR)
FAILOVERABLE_STATUSES = (lib_consts.ACTIVE, lib_consts.ERROR)
# Note: The database Amphora table has a foreign key constraint against # Note: The database Amphora table has a foreign key constraint against
# the provisioning_status table # the provisioning_status table
# Amphora has been allocated to a load balancer SUPPORTED_AMPHORA_STATUSES = (
AMPHORA_ALLOCATED = 'ALLOCATED' lib_consts.AMPHORA_ALLOCATED, lib_consts.AMPHORA_BOOTING, lib_consts.ERROR,
# Amphora is being built lib_consts.AMPHORA_READY, lib_consts.DELETED, lib_consts.PENDING_CREATE,
AMPHORA_BOOTING = 'BOOTING' lib_consts.PENDING_DELETE)
# Amphora is ready to be allocated to a load balancer
AMPHORA_READY = 'READY'
ACTIVE = 'ACTIVE'
PENDING_DELETE = 'PENDING_DELETE'
PENDING_UPDATE = 'PENDING_UPDATE'
PENDING_CREATE = 'PENDING_CREATE'
DELETED = 'DELETED'
ERROR = 'ERROR'
SUPPORTED_PROVISIONING_STATUSES = (ACTIVE, AMPHORA_ALLOCATED,
AMPHORA_BOOTING, AMPHORA_READY,
PENDING_DELETE, PENDING_CREATE,
PENDING_UPDATE, DELETED, ERROR)
MUTABLE_STATUSES = (ACTIVE,)
DELETABLE_STATUSES = (ACTIVE, ERROR)
FAILOVERABLE_STATUSES = (ACTIVE, ERROR)
SUPPORTED_AMPHORA_STATUSES = (AMPHORA_ALLOCATED, AMPHORA_BOOTING, ERROR,
AMPHORA_READY, DELETED,
PENDING_CREATE, PENDING_DELETE)
ONLINE = 'ONLINE'
OFFLINE = 'OFFLINE'
DEGRADED = 'DEGRADED'
ERROR = 'ERROR'
DRAINING = 'DRAINING'
NO_MONITOR = 'NO_MONITOR'
OPERATING_STATUS = 'operating_status'
PROVISIONING_STATUS = 'provisioning_status'
SUPPORTED_OPERATING_STATUSES = (ONLINE, OFFLINE, DEGRADED, ERROR, DRAINING,
NO_MONITOR)
AMPHORA_VM = 'VM' AMPHORA_VM = 'VM'
SUPPORTED_AMPHORA_TYPES = (AMPHORA_VM,) SUPPORTED_AMPHORA_TYPES = (AMPHORA_VM,)
# L7 constants DISTINGUISHED_NAME_FIELD_REGEX = lib_consts.DISTINGUISHED_NAME_FIELD_REGEX
L7RULE_TYPE_HOST_NAME = 'HOST_NAME'
L7RULE_TYPE_PATH = 'PATH'
L7RULE_TYPE_FILE_TYPE = 'FILE_TYPE'
L7RULE_TYPE_HEADER = 'HEADER'
L7RULE_TYPE_COOKIE = 'COOKIE'
L7RULE_TYPE_SSL_CONN_HAS_CERT = 'SSL_CONN_HAS_CERT'
L7RULE_TYPE_SSL_VERIFY_RESULT = 'SSL_VERIFY_RESULT'
L7RULE_TYPE_SSL_DN_FIELD = 'SSL_DN_FIELD'
SUPPORTED_L7RULE_TYPES = (L7RULE_TYPE_HOST_NAME, L7RULE_TYPE_PATH,
L7RULE_TYPE_FILE_TYPE, L7RULE_TYPE_HEADER,
L7RULE_TYPE_COOKIE, L7RULE_TYPE_SSL_CONN_HAS_CERT,
L7RULE_TYPE_SSL_VERIFY_RESULT,
L7RULE_TYPE_SSL_DN_FIELD)
DISTINGUISHED_NAME_FIELD_REGEX = '^([a-zA-Z][A-Za-z0-9-]*)$'
L7RULE_COMPARE_TYPE_REGEX = 'REGEX'
L7RULE_COMPARE_TYPE_STARTS_WITH = 'STARTS_WITH'
L7RULE_COMPARE_TYPE_ENDS_WITH = 'ENDS_WITH'
L7RULE_COMPARE_TYPE_CONTAINS = 'CONTAINS'
L7RULE_COMPARE_TYPE_EQUAL_TO = 'EQUAL_TO'
SUPPORTED_L7RULE_COMPARE_TYPES = (L7RULE_COMPARE_TYPE_REGEX,
L7RULE_COMPARE_TYPE_STARTS_WITH,
L7RULE_COMPARE_TYPE_ENDS_WITH,
L7RULE_COMPARE_TYPE_CONTAINS,
L7RULE_COMPARE_TYPE_EQUAL_TO)
L7POLICY_ACTION_REJECT = 'REJECT'
L7POLICY_ACTION_REDIRECT_TO_URL = 'REDIRECT_TO_URL'
L7POLICY_ACTION_REDIRECT_TO_POOL = 'REDIRECT_TO_POOL'
L7POLICY_ACTION_REDIRECT_PREFIX = 'REDIRECT_PREFIX'
SUPPORTED_L7POLICY_ACTIONS = (L7POLICY_ACTION_REJECT,
L7POLICY_ACTION_REDIRECT_TO_URL,
L7POLICY_ACTION_REDIRECT_TO_POOL,
L7POLICY_ACTION_REDIRECT_PREFIX)
# For redirect, only codes 301, 302, 303, 307 and 308 are # supported. # For redirect, only codes 301, 302, 303, 307 and 308 are # supported.
SUPPORTED_L7POLICY_REDIRECT_HTTP_CODES = [301, 302, 303, 307, 308] SUPPORTED_L7POLICY_REDIRECT_HTTP_CODES = [301, 302, 303, 307, 308]
SUPPORTED_HTTP_VERSIONS = [1.0, 1.1]
MIN_POLICY_POSITION = 1 MIN_POLICY_POSITION = 1
# Largest a 32-bit integer can be, which is a limitation # Largest a 32-bit integer can be, which is a limitation
# here if you're using MySQL, as most probably are. This just needs # here if you're using MySQL, as most probably are. This just needs
@ -220,17 +287,14 @@ DELTAS = 'deltas'
HEALTH_MON = 'health_mon' HEALTH_MON = 'health_mon'
HEALTH_MONITOR = 'health_monitor' HEALTH_MONITOR = 'health_monitor'
LISTENER = 'listener' LISTENER = 'listener'
LISTENERS = 'listeners'
LISTENER_ID = 'listener_id' LISTENER_ID = 'listener_id'
LOADBALANCER = 'loadbalancer' LOADBALANCER = 'loadbalancer'
LOADBALANCERS = 'loadbalancers'
LOADBALANCER_ID = 'loadbalancer_id' LOADBALANCER_ID = 'loadbalancer_id'
LOAD_BALANCER_ID = 'load_balancer_id' LOAD_BALANCER_ID = 'load_balancer_id'
SERVER_GROUP_ID = 'server_group_id' SERVER_GROUP_ID = 'server_group_id'
ANTI_AFFINITY = 'anti-affinity' ANTI_AFFINITY = 'anti-affinity'
SOFT_ANTI_AFFINITY = 'soft-anti-affinity' SOFT_ANTI_AFFINITY = 'soft-anti-affinity'
MEMBER = 'member' MEMBER = 'member'
MEMBERS = 'members'
MEMBER_ID = 'member_id' MEMBER_ID = 'member_id'
COMPUTE_ID = 'compute_id' COMPUTE_ID = 'compute_id'
COMPUTE_OBJ = 'compute_obj' COMPUTE_OBJ = 'compute_obj'
@ -239,7 +303,6 @@ AMPS_DATA = 'amps_data'
NICS = 'nics' NICS = 'nics'
VIP = 'vip' VIP = 'vip'
POOL = 'pool' POOL = 'pool'
POOLS = 'pools'
POOL_CHILD_COUNT = 'pool_child_count' POOL_CHILD_COUNT = 'pool_child_count'
POOL_ID = 'pool_id' POOL_ID = 'pool_id'
L7POLICY = 'l7policy' L7POLICY = 'l7policy'
@ -253,11 +316,8 @@ ADDED_PORTS = 'added_ports'
PORTS = 'ports' PORTS = 'ports'
MEMBER_PORTS = 'member_ports' MEMBER_PORTS = 'member_ports'
TOPOLOGY = 'topology' TOPOLOGY = 'topology'
HEALTHMONITORS = 'healthmonitors'
HEALTH_MONITOR_ID = 'health_monitor_id' HEALTH_MONITOR_ID = 'health_monitor_id'
L7POLICIES = 'l7policies'
L7POLICY_ID = 'l7policy_id' L7POLICY_ID = 'l7policy_id'
L7RULES = 'l7rules'
L7RULE_ID = 'l7rule_id' L7RULE_ID = 'l7rule_id'
LOAD_BALANCER_UPDATES = 'load_balancer_updates' LOAD_BALANCER_UPDATES = 'load_balancer_updates'
LISTENER_UPDATES = 'listener_updates' LISTENER_UPDATES = 'listener_updates'
@ -418,7 +478,6 @@ DEFAULT_VRRP_ID = 1
VRRP_PROTOCOL_NUM = 112 VRRP_PROTOCOL_NUM = 112
AUTH_HEADER_PROTOCOL_NUMBER = 51 AUTH_HEADER_PROTOCOL_NUMBER = 51
TEMPLATES = '/templates' TEMPLATES = '/templates'
AGENT_API_TEMPLATES = '/templates' AGENT_API_TEMPLATES = '/templates'
@ -474,17 +533,6 @@ PLUGGED_INTERFACES = '/var/lib/octavia/plugged_interfaces'
HAPROXY_USER_GROUP_CFG = '/var/lib/octavia/haproxy-default-user-group.conf' HAPROXY_USER_GROUP_CFG = '/var/lib/octavia/haproxy-default-user-group.conf'
AMPHORA_NAMESPACE = 'amphora-haproxy' AMPHORA_NAMESPACE = 'amphora-haproxy'
# List of HTTP headers which are supported for insertion
SUPPORTED_HTTP_HEADERS = ['X-Forwarded-For',
'X-Forwarded-Port',
'X-Forwarded-Proto']
# List of SSL headers for client certificate
SUPPORTED_SSL_HEADERS = ['X-SSL-Client-Verify', 'X-SSL-Client-Has-Cert',
'X-SSL-Client-DN', 'X-SSL-Client-CN',
'X-SSL-Issuer', 'X-SSL-Client-SHA1',
'X-SSL-Client-Not-Before', 'X-SSL-Client-Not-After']
FLOW_DOC_TITLES = {'AmphoraFlows': 'Amphora Flows', FLOW_DOC_TITLES = {'AmphoraFlows': 'Amphora Flows',
'LoadBalancerFlows': 'Load Balancer Flows', 'LoadBalancerFlows': 'Load Balancer Flows',
'ListenerFlows': 'Listener Flows', 'ListenerFlows': 'Listener Flows',

View File

@ -45,6 +45,7 @@ def list_opts():
('glance', octavia.common.config.glance_opts), ('glance', octavia.common.config.glance_opts),
('quotas', octavia.common.config.quota_opts), ('quotas', octavia.common.config.quota_opts),
('audit', octavia.common.config.audit_opts), ('audit', octavia.common.config.audit_opts),
('driver_agent', octavia.common.config.driver_agent_opts),
add_auth_opts(), add_auth_opts(),
] ]

View File

@ -13,9 +13,12 @@
# under the License. # under the License.
import mock import mock
from oslo_utils import uuidutils
from octavia_lib.api.drivers import data_models as driver_dm
from octavia_lib.api.drivers import exceptions
from octavia.api.drivers.amphora_driver import driver from octavia.api.drivers.amphora_driver import driver
from octavia.api.drivers import data_models as driver_dm
from octavia.api.drivers import exceptions
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.network import base as network_base from octavia.network import base as network_base
from octavia.tests.unit.api.drivers import sample_data_models from octavia.tests.unit.api.drivers import sample_data_models
@ -101,6 +104,20 @@ class TestAmphoraDriver(base.TestRpc):
consts.LOAD_BALANCER_UPDATES: lb_dict} consts.LOAD_BALANCER_UPDATES: lb_dict}
mock_cast.assert_called_with({}, 'update_load_balancer', **payload) mock_cast.assert_called_with({}, 'update_load_balancer', **payload)
@mock.patch('oslo_messaging.RPCClient.cast')
def test_loadbalancer_update_qos(self, mock_cast):
qos_policy_id = uuidutils.generate_uuid()
old_provider_lb = driver_dm.LoadBalancer(
loadbalancer_id=self.sample_data.lb_id)
provider_lb = driver_dm.LoadBalancer(
loadbalancer_id=self.sample_data.lb_id,
vip_qos_policy_id=qos_policy_id)
lb_dict = {'vip': {'vip_qos_policy_id': qos_policy_id}}
self.amp_driver.loadbalancer_update(old_provider_lb, provider_lb)
payload = {consts.LOAD_BALANCER_ID: self.sample_data.lb_id,
consts.LOAD_BALANCER_UPDATES: lb_dict}
mock_cast.assert_called_with({}, 'update_load_balancer', **payload)
# Listener # Listener
@mock.patch('oslo_messaging.RPCClient.cast') @mock.patch('oslo_messaging.RPCClient.cast')
def test_listener_create(self, mock_cast): def test_listener_create(self, mock_cast):

View File

@ -0,0 +1,11 @@
# 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.

View File

@ -0,0 +1,171 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 errno
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
from octavia.api.drivers.driver_agent import driver_listener
import octavia.tests.unit.base as base
CONF = cfg.CONF
class TestDriverListener(base.TestCase):
def setUp(self):
super(TestDriverListener, self).setUp()
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.memoryview')
def test_recv(self, mock_memoryview):
# TEST_STRING len() is 15
TEST_STRING = '{"test": "msg"}'
ref_object = jsonutils.loads(TEST_STRING)
mock_recv_socket = mock.MagicMock()
mock_recv = mock.MagicMock()
mock_recv.side_effect = [b'1', b'5', b'\n']
mock_recv_socket.recv = mock_recv
mock_recv_socket.recv_into.return_value = 15
mock_mv_buffer = mock.MagicMock()
mock_tobytes = mock.MagicMock()
mock_tobytes.return_value = TEST_STRING
mock_mv_buffer.tobytes = mock_tobytes
mock_memoryview.return_value = mock_mv_buffer
result = driver_listener._recv(mock_recv_socket)
self.assertEqual(ref_object, result)
calls = [mock.call(1), mock.call(1), mock.call(1)]
mock_recv.assert_has_calls(calls)
mock_memoryview.assert_called_once_with(bytearray(15))
mock_recv_socket.recv_into.assert_called_once_with(mock_mv_buffer[0:],
15)
@mock.patch('octavia.api.drivers.driver_agent.driver_updater.'
'DriverUpdater')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener._recv')
def test_StatusRequestHandler_handle(self, mock_recv, mock_driverupdater):
TEST_OBJECT = {"test": "msg"}
mock_recv.return_value = 'bogus'
mock_updater = mock.MagicMock()
mock_update_loadbalancer_status = mock.MagicMock()
mock_update_loadbalancer_status.return_value = TEST_OBJECT
mock_updater.update_loadbalancer_status = (
mock_update_loadbalancer_status)
mock_driverupdater.return_value = mock_updater
mock_request = mock.MagicMock()
mock_send = mock.MagicMock()
mock_sendall = mock.MagicMock()
mock_request.send = mock_send
mock_request.sendall = mock_sendall
StatusRequestHandler = driver_listener.StatusRequestHandler(
mock_request, 'bogus', 'bogus')
StatusRequestHandler.handle()
mock_recv.assert_called_with(mock_request)
mock_update_loadbalancer_status.assert_called_with('bogus')
mock_send.assert_called_with(b'15\n')
mock_sendall.assert_called_with(
jsonutils.dumps(TEST_OBJECT).encode('utf-8'))
@mock.patch('octavia.api.drivers.driver_agent.driver_updater.'
'DriverUpdater')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener._recv')
def test_StatsRequestHandler_handle(self, mock_recv, mock_driverupdater):
TEST_OBJECT = {"test": "msg"}
mock_recv.return_value = 'bogus'
mock_updater = mock.MagicMock()
mock_update_listener_stats = mock.MagicMock()
mock_update_listener_stats.return_value = TEST_OBJECT
mock_updater.update_listener_statistics = (mock_update_listener_stats)
mock_driverupdater.return_value = mock_updater
mock_request = mock.MagicMock()
mock_send = mock.MagicMock()
mock_sendall = mock.MagicMock()
mock_request.send = mock_send
mock_request.sendall = mock_sendall
StatsRequestHandler = driver_listener.StatsRequestHandler(
mock_request, 'bogus', 'bogus')
StatsRequestHandler.handle()
mock_recv.assert_called_with(mock_request)
mock_update_listener_stats.assert_called_with('bogus')
mock_send.assert_called_with(b'15\n')
mock_sendall.assert_called_with(jsonutils.dump_as_bytes(TEST_OBJECT))
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.CONF')
def test_mutate_config(self, mock_conf):
driver_listener._mutate_config()
mock_conf.mutate_config_files.assert_called_once()
@mock.patch('os.remove')
def test_cleanup_socket_file(self, mock_remove):
mock_remove.side_effect = [mock.DEFAULT, OSError,
OSError(errno.ENOENT, 'no_file')]
driver_listener._cleanup_socket_file('fake_filename')
mock_remove.assert_called_once_with('fake_filename')
self.assertRaises(OSError, driver_listener._cleanup_socket_file,
'fake_filename')
# Make sure we just pass if the file was not found
driver_listener._cleanup_socket_file('fake_filename')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
'_cleanup_socket_file')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.signal')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
'ForkingUDSServer')
def test_status_listener(self, mock_forking_server,
mock_signal, mock_cleanup):
mock_server = mock.MagicMock()
mock_active_children = mock.PropertyMock(
side_effect=['a', 'a', 'a',
'a' * CONF.driver_agent.status_max_processes, 'a',
'a' * 1000, ''])
type(mock_server).active_children = mock_active_children
mock_forking_server.return_value = mock_server
mock_exit_event = mock.MagicMock()
mock_exit_event.is_set.side_effect = [False, False, False, False, True]
driver_listener.status_listener(mock_exit_event)
mock_server.handle_request.assert_called()
mock_server.server_close.assert_called_once()
self.assertEqual(2, mock_cleanup.call_count)
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
'_cleanup_socket_file')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.signal')
@mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
'ForkingUDSServer')
def test_stats_listener(self, mock_forking_server,
mock_signal, mock_cleanup):
mock_server = mock.MagicMock()
mock_active_children = mock.PropertyMock(
side_effect=['a', 'a', 'a',
'a' * CONF.driver_agent.status_max_processes, 'a',
'a' * 1000, ''])
type(mock_server).active_children = mock_active_children
mock_forking_server.return_value = mock_server
mock_exit_event = mock.MagicMock()
mock_exit_event.is_set.side_effect = [False, False, False, False, True]
driver_listener.stats_listener(mock_exit_event)
mock_server.handle_request.assert_called()
mock_server.server_close.assert_called_once()

View File

@ -0,0 +1,295 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 copy
import mock
from mock import call
from octavia.api.drivers.driver_agent import driver_updater
import octavia.tests.unit.base as base
from octavia_lib.api.drivers import exceptions as driver_exceptions
from octavia_lib.common import constants as lib_consts
class TestDriverUpdater(base.TestCase):
@mock.patch('octavia.db.repositories.LoadBalancerRepository')
@mock.patch('octavia.db.repositories.ListenerRepository')
@mock.patch('octavia.db.repositories.L7PolicyRepository')
@mock.patch('octavia.db.repositories.L7RuleRepository')
@mock.patch('octavia.db.repositories.PoolRepository')
@mock.patch('octavia.db.repositories.HealthMonitorRepository')
@mock.patch('octavia.db.repositories.MemberRepository')
@mock.patch('octavia.db.api.get_session')
def setUp(self, mock_get_session, mock_member_repo, mock_health_repo,
mock_pool_repo, mock_l7r_repo, mock_l7p_repo, mock_list_repo,
mock_lb_repo):
super(TestDriverUpdater, self).setUp()
self.mock_session = "FAKE_DB_SESSION"
mock_get_session.return_value = self.mock_session
member_mock = mock.MagicMock()
mock_member_repo.return_value = member_mock
self.mock_member_repo = member_mock
health_mock = mock.MagicMock()
mock_health_repo.return_value = health_mock
self.mock_health_repo = health_mock
pool_mock = mock.MagicMock()
mock_pool_repo.return_value = pool_mock
self.mock_pool_repo = pool_mock
l7r_mock = mock.MagicMock()
mock_l7r_repo.return_value = l7r_mock
self.mock_l7r_repo = l7r_mock
l7p_mock = mock.MagicMock()
mock_l7p_repo.return_value = l7p_mock
self.mock_l7p_repo = l7p_mock
list_mock = mock.MagicMock()
mock_list_repo.return_value = list_mock
self.mock_list_repo = list_mock
lb_mock = mock.MagicMock()
mock_lb_repo.return_value = lb_mock
self.mock_lb_repo = lb_mock
self.driver_updater = driver_updater.DriverUpdater()
self.ref_ok_response = {lib_consts.STATUS_CODE:
lib_consts.DRVR_STATUS_CODE_OK}
@mock.patch('octavia.common.utils.get_network_driver')
def test_check_for_lb_vip_deallocate(self, mock_get_net_drvr):
mock_repo = mock.MagicMock()
mock_lb = mock.MagicMock()
mock_vip = mock.MagicMock()
mock_octavia_owned = mock.PropertyMock(side_effect=[True, False])
type(mock_vip).octavia_owned = mock_octavia_owned
mock_lb.vip = mock_vip
mock_repo.get.return_value = mock_lb
mock_net_drvr = mock.MagicMock()
mock_get_net_drvr.return_value = mock_net_drvr
self.driver_updater._check_for_lb_vip_deallocate(mock_repo, 'bogus_id')
mock_net_drvr.deallocate_vip.assert_called_once_with(mock_vip)
mock_net_drvr.reset_mock()
self.driver_updater._check_for_lb_vip_deallocate(mock_repo, 'bogus_id')
mock_net_drvr.deallocate_vip.assert_not_called()
@mock.patch('octavia.api.drivers.driver_agent.driver_updater.'
'DriverUpdater._check_for_lb_vip_deallocate')
def test_process_status_update(self, mock_deallocate):
mock_repo = mock.MagicMock()
list_dict = {"id": 2,
lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
list_prov_dict = {"id": 2,
lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE}
list_oper_dict = {"id": 2,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
list_deleted_dict = {
"id": 2, lib_consts.PROVISIONING_STATUS: lib_consts.DELETED,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
# Test with full record
self.driver_updater._process_status_update(mock_repo, 'FakeName',
list_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=lib_consts.ACTIVE,
operating_status=lib_consts.ONLINE)
mock_repo.delete.assert_not_called()
# Test with only provisioning status record
mock_repo.reset_mock()
self.driver_updater._process_status_update(mock_repo, 'FakeName',
list_prov_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=lib_consts.ACTIVE)
mock_repo.delete.assert_not_called()
# Test with only operating status record
mock_repo.reset_mock()
self.driver_updater._process_status_update(mock_repo, 'FakeName',
list_oper_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, operating_status=lib_consts.ONLINE)
mock_repo.delete.assert_not_called()
# Test with deleted but delete_record False
mock_repo.reset_mock()
self.driver_updater._process_status_update(mock_repo, 'FakeName',
list_deleted_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=lib_consts.DELETED,
operating_status=lib_consts.ONLINE)
mock_repo.delete.assert_not_called()
# Test with an empty update
mock_repo.reset_mock()
self.driver_updater._process_status_update(mock_repo, 'FakeName',
{"id": 2})
mock_repo.update.assert_not_called()
mock_repo.delete.assert_not_called()
# Test with deleted and delete_record True
mock_repo.reset_mock()
self.driver_updater._process_status_update(
mock_repo, 'FakeName', list_deleted_dict, delete_record=True)
mock_repo.delete.assert_called_once_with(self.mock_session, id=2)
mock_repo.update.assert_not_called()
# Test with LB Delete
mock_repo.reset_mock()
self.driver_updater._process_status_update(
mock_repo, lib_consts.LOADBALANCERS, list_deleted_dict)
mock_deallocate.assert_called_once_with(mock_repo, 2)
# Test with an exception
mock_repo.reset_mock()
mock_repo.update.side_effect = Exception('boom')
self.assertRaises(driver_exceptions.UpdateStatusError,
self.driver_updater._process_status_update,
mock_repo, 'FakeName', list_dict)
# Test with no ID record
mock_repo.reset_mock()
self.assertRaises(driver_exceptions.UpdateStatusError,
self.driver_updater._process_status_update,
mock_repo, 'FakeName', {"fake": "data"})
@mock.patch('octavia.api.drivers.driver_agent.driver_updater.'
'DriverUpdater._process_status_update')
def test_update_loadbalancer_status(self, mock_status_update):
mock_status_update.side_effect = [
mock.DEFAULT, mock.DEFAULT, mock.DEFAULT, mock.DEFAULT,
mock.DEFAULT, mock.DEFAULT, mock.DEFAULT,
driver_exceptions.UpdateStatusError(
fault_string='boom', status_object='fruit',
status_object_id='1', status_record='grape'),
Exception('boom')]
lb_dict = {"id": 1, lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
list_dict = {"id": 2,
lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
pool_dict = {"id": 3,
lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
member_dict = {"id": 4,
lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
hm_dict = {"id": 5, lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
l7p_dict = {"id": 6, lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
l7r_dict = {"id": 7, lib_consts.PROVISIONING_STATUS: lib_consts.ACTIVE,
lib_consts.OPERATING_STATUS: lib_consts.ONLINE}
status_dict = {lib_consts.LOADBALANCERS: [lb_dict],
lib_consts.LISTENERS: [list_dict],
lib_consts.POOLS: [pool_dict],
lib_consts.MEMBERS: [member_dict],
lib_consts.HEALTHMONITORS: [hm_dict],
lib_consts.L7POLICIES: [l7p_dict],
lib_consts.L7RULES: [l7r_dict]}
result = self.driver_updater.update_loadbalancer_status(
copy.deepcopy(status_dict))
calls = [call(self.mock_member_repo, lib_consts.MEMBERS, member_dict,
delete_record=True),
call(self.mock_health_repo, lib_consts.HEALTHMONITORS,
hm_dict, delete_record=True),
call(self.mock_pool_repo, lib_consts.POOLS, pool_dict,
delete_record=True),
call(self.mock_l7r_repo, lib_consts.L7RULES, l7r_dict,
delete_record=True),
call(self.mock_l7p_repo, lib_consts.L7POLICIES, l7p_dict,
delete_record=True),
call(self.mock_list_repo, lib_consts.LISTENERS, list_dict,
delete_record=True),
call(self.mock_lb_repo, lib_consts.LOADBALANCERS,
lb_dict)]
mock_status_update.assert_has_calls(calls)
self.assertEqual(self.ref_ok_response, result)
# Test empty status updates
mock_status_update.reset_mock()
result = self.driver_updater.update_loadbalancer_status({})
mock_status_update.assert_not_called()
self.assertEqual(self.ref_ok_response, result)
# Test UpdateStatusError case
ref_update_status_error = {
lib_consts.FAULT_STRING: 'boom',
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.STATUS_OBJECT: 'fruit',
lib_consts.STATUS_OBJECT_ID: '1'}
result = self.driver_updater.update_loadbalancer_status(
copy.deepcopy(status_dict))
self.assertEqual(ref_update_status_error, result)
# Test general exceptions
result = self.driver_updater.update_loadbalancer_status(
copy.deepcopy(status_dict))
self.assertEqual({
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.FAULT_STRING: 'boom'}, result)
@mock.patch('octavia.db.repositories.ListenerStatisticsRepository.replace')
def test_update_listener_statistics(self, mock_replace):
listener_stats_list = [{"id": 1, "active_connections": 10,
"bytes_in": 20,
"bytes_out": 30,
"request_errors": 40,
"total_connections": 50},
{"id": 2, "active_connections": 60,
"bytes_in": 70,
"bytes_out": 80,
"request_errors": 90,
"total_connections": 100}]
listener_stats_dict = {"listeners": listener_stats_list}
mock_replace.side_effect = [mock.DEFAULT, mock.DEFAULT,
Exception('boom')]
result = self.driver_updater.update_listener_statistics(
copy.deepcopy(listener_stats_dict))
calls = [call(self.mock_session, 1, 1, active_connections=10,
bytes_in=20, bytes_out=30, request_errors=40,
total_connections=50),
call(self.mock_session, 2, 2, active_connections=60,
bytes_in=70, bytes_out=80, request_errors=90,
total_connections=100)]
mock_replace.assert_has_calls(calls)
self.assertEqual(self.ref_ok_response, result)
# Test empty stats updates
mock_replace.reset_mock()
result = self.driver_updater.update_listener_statistics({})
mock_replace.assert_not_called()
self.assertEqual(self.ref_ok_response, result)
# Test missing ID
bad_id_dict = {"listeners": [{"notID": "one"}]}
result = self.driver_updater.update_listener_statistics(bad_id_dict)
ref_update_listener_stats_error = {
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.STATS_OBJECT: lib_consts.LISTENERS,
lib_consts.FAULT_STRING: "'id'"}
self.assertEqual(ref_update_listener_stats_error, result)
# Test for replace exception
result = self.driver_updater.update_listener_statistics(
copy.deepcopy(listener_stats_dict))
ref_update_listener_stats_error = {
lib_consts.STATUS_CODE: lib_consts.DRVR_STATUS_CODE_FAILED,
lib_consts.STATS_OBJECT: lib_consts.LISTENERS,
lib_consts.FAULT_STRING: 'boom', lib_consts.STATS_OBJECT_ID: 1}
self.assertEqual(ref_update_listener_stats_error, result)

View File

@ -1,217 +0,0 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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.
from copy import deepcopy
from oslo_utils import uuidutils
from octavia.api.drivers import data_models
import octavia.tests.unit.base as base
class TestProviderDataModels(base.TestCase):
def setUp(self):
super(TestProviderDataModels, self).setUp()
self.loadbalancer_id = uuidutils.generate_uuid()
self.project_id = uuidutils.generate_uuid()
self.vip_address = '192.0.2.83'
self.vip_network_id = uuidutils.generate_uuid()
self.vip_port_id = uuidutils.generate_uuid()
self.vip_subnet_id = uuidutils.generate_uuid()
self.listener_id = uuidutils.generate_uuid()
self.vip_qos_policy_id = uuidutils.generate_uuid()
self.default_tls_container_ref = uuidutils.generate_uuid()
self.sni_container_ref_1 = uuidutils.generate_uuid()
self.sni_container_ref_2 = uuidutils.generate_uuid()
self.ref_listener = data_models.Listener(
admin_state_up=True,
connection_limit=5000,
default_pool_id=None,
default_tls_container_data='default_cert_data',
default_tls_container_ref=self.default_tls_container_ref,
description=data_models.Unset,
insert_headers={'X-Forwarded-For': 'true'},
l7policies=[],
listener_id=self.listener_id,
loadbalancer_id=self.loadbalancer_id,
name='super_listener',
protocol='avian',
protocol_port=42,
sni_container_data=['sni_cert_data_1', 'sni_cert_data_2'],
sni_container_refs=[self.sni_container_ref_1,
self.sni_container_ref_2])
self.ref_lb = data_models.LoadBalancer(
admin_state_up=False,
description='One great load balancer',
flavor={'cake': 'chocolate'},
listeners=[self.ref_listener],
loadbalancer_id=self.loadbalancer_id,
name='favorite_lb',
project_id=self.project_id,
vip_address=self.vip_address,
vip_network_id=self.vip_network_id,
vip_port_id=self.vip_port_id,
vip_subnet_id=self.vip_subnet_id,
vip_qos_policy_id=self.vip_qos_policy_id)
self.ref_lb_dict = {'project_id': self.project_id,
'flavor': {'cake': 'chocolate'},
'vip_network_id': self.vip_network_id,
'admin_state_up': False,
'loadbalancer_id': self.loadbalancer_id,
'vip_port_id': self.vip_port_id,
'vip_address': self.vip_address,
'description': 'One great load balancer',
'vip_subnet_id': self.vip_subnet_id,
'name': 'favorite_lb',
'vip_qos_policy_id': self.vip_qos_policy_id}
self.ref_listener = {
'admin_state_up': True,
'connection_limit': 5000,
'default_pool_id': None,
'default_tls_container_data': 'default_cert_data',
'default_tls_container_ref': self.default_tls_container_ref,
'insert_headers': {'X-Forwarded-For': 'true'},
'listener_id': self.listener_id,
'l7policies': [],
'loadbalancer_id': self.loadbalancer_id,
'name': 'super_listener',
'protocol': 'avian',
'protocol_port': 42,
'sni_container_data': ['sni_cert_data_1', 'sni_cert_data_2'],
'sni_container_refs': [self.sni_container_ref_1,
self.sni_container_ref_2]}
self.ref_lb_dict_with_listener = {
'admin_state_up': False,
'description': 'One great load balancer',
'flavor': {'cake': 'chocolate'},
'listeners': [self.ref_listener],
'loadbalancer_id': self.loadbalancer_id,
'name': 'favorite_lb',
'project_id': self.project_id,
'vip_address': self.vip_address,
'vip_network_id': self.vip_network_id,
'vip_port_id': self.vip_port_id,
'vip_subnet_id': self.vip_subnet_id,
'vip_qos_policy_id': self.vip_qos_policy_id}
def test_equality(self):
second_ref_lb = deepcopy(self.ref_lb)
self.assertTrue(self.ref_lb == second_ref_lb)
second_ref_lb.admin_state_up = True
self.assertFalse(self.ref_lb == second_ref_lb)
self.assertFalse(self.ref_lb == self.loadbalancer_id)
def test_inequality(self):
second_ref_lb = deepcopy(self.ref_lb)
self.assertFalse(self.ref_lb != second_ref_lb)
second_ref_lb.admin_state_up = True
self.assertTrue(self.ref_lb != second_ref_lb)
self.assertTrue(self.ref_lb != self.loadbalancer_id)
def test_to_dict(self):
ref_lb_converted_to_dict = self.ref_lb.to_dict()
self.assertEqual(self.ref_lb_dict, ref_lb_converted_to_dict)
def test_to_dict_private_attrs(self):
private_dict = {'_test': 'foo'}
ref_lb_converted_to_dict = self.ref_lb.to_dict(**private_dict)
self.assertEqual(self.ref_lb_dict, ref_lb_converted_to_dict)
def test_to_dict_partial(self):
ref_lb = data_models.LoadBalancer(loadbalancer_id=self.loadbalancer_id)
ref_lb_dict = {'loadbalancer_id': self.loadbalancer_id}
ref_lb_converted_to_dict = ref_lb.to_dict()
self.assertEqual(ref_lb_dict, ref_lb_converted_to_dict)
def test_to_dict_render_unsets(self):
ref_lb_converted_to_dict = self.ref_lb.to_dict(render_unsets=True)
new_ref_lib_dict = deepcopy(self.ref_lb_dict)
new_ref_lib_dict['pools'] = None
new_ref_lib_dict['listeners'] = None
self.assertEqual(new_ref_lib_dict, ref_lb_converted_to_dict)
def test_to_dict_recursive(self):
ref_lb_converted_to_dict = self.ref_lb.to_dict(recurse=True)
self.assertEqual(self.ref_lb_dict_with_listener,
ref_lb_converted_to_dict)
def test_to_dict_recursive_partial(self):
ref_lb = data_models.LoadBalancer(
loadbalancer_id=self.loadbalancer_id,
listeners=[self.ref_listener])
ref_lb_dict_with_listener = {
'loadbalancer_id': self.loadbalancer_id,
'listeners': [self.ref_listener]}
ref_lb_converted_to_dict = ref_lb.to_dict(recurse=True)
self.assertEqual(ref_lb_dict_with_listener, ref_lb_converted_to_dict)
def test_to_dict_recursive_render_unset(self):
ref_lb = data_models.LoadBalancer(
admin_state_up=False,
description='One great load balancer',
flavor={'cake': 'chocolate'},
listeners=[self.ref_listener],
loadbalancer_id=self.loadbalancer_id,
project_id=self.project_id,
vip_address=self.vip_address,
vip_network_id=self.vip_network_id,
vip_port_id=self.vip_port_id,
vip_subnet_id=self.vip_subnet_id,
vip_qos_policy_id=self.vip_qos_policy_id)
ref_lb_dict_with_listener = deepcopy(self.ref_lb_dict_with_listener)
ref_lb_dict_with_listener['pools'] = None
ref_lb_dict_with_listener['name'] = None
ref_lb_converted_to_dict = ref_lb.to_dict(recurse=True,
render_unsets=True)
self.assertEqual(ref_lb_dict_with_listener,
ref_lb_converted_to_dict)
def test_from_dict(self):
lb_object = data_models.LoadBalancer.from_dict(self.ref_lb_dict)
self.assertEqual(self.ref_lb, lb_object)
def test_unset_bool(self):
self.assertFalse(data_models.Unset)
def test_unset_repr(self):
self.assertEqual('Unset', repr(data_models.Unset))

View File

@ -12,239 +12,32 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock # Copyright 2018 Rackspace, US Inc.
from mock import call #
# 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 octavia_lib.api.drivers.driver_lib as lib_driver_lib
from octavia.api.drivers import driver_lib from octavia.api.drivers import driver_lib
from octavia.api.drivers import exceptions as driver_exceptions import octavia.tests.unit.base as base
from octavia.common import constants
from octavia.tests.unit import base
class TestDriverLib(base.TestCase): class TestDriverLib(base.TestCase):
@mock.patch('octavia.db.repositories.L7RuleRepository')
@mock.patch('octavia.db.repositories.L7PolicyRepository') def setUp(self):
@mock.patch('octavia.db.repositories.HealthMonitorRepository')
@mock.patch('octavia.db.repositories.MemberRepository')
@mock.patch('octavia.db.repositories.PoolRepository')
@mock.patch('octavia.db.repositories.ListenerRepository')
@mock.patch('octavia.db.repositories.LoadBalancerRepository')
@mock.patch('octavia.db.api.get_session')
def setUp(self, mock_get_session, mock_lb_repo, mock_list_repo,
mock_pool_repo, mock_member_repo, mock_health_repo,
mock_l7p_repo, mock_l7r_repo):
super(TestDriverLib, self).setUp() super(TestDriverLib, self).setUp()
self.mock_session = "FAKE_DB_SESSION"
mock_get_session.return_value = self.mock_session
lb_mock = mock.MagicMock()
mock_lb_repo.return_value = lb_mock
self.mock_lb_repo = lb_mock
list_mock = mock.MagicMock()
mock_list_repo.return_value = list_mock
self.mock_list_repo = list_mock
pool_mock = mock.MagicMock()
mock_pool_repo.return_value = pool_mock
self.mock_pool_repo = pool_mock
member_mock = mock.MagicMock()
mock_member_repo.return_value = member_mock
self.mock_member_repo = member_mock
health_mock = mock.MagicMock()
mock_health_repo.return_value = health_mock
self.mock_health_repo = health_mock
l7p_mock = mock.MagicMock()
mock_l7p_repo.return_value = l7p_mock
self.mock_l7p_repo = l7p_mock
l7r_mock = mock.MagicMock()
mock_l7r_repo.return_value = l7r_mock
self.mock_l7r_repo = l7r_mock
self.driver_lib = driver_lib.DriverLibrary()
listener_stats_list = [{"id": 1, "active_connections": 10,
"bytes_in": 20,
"bytes_out": 30,
"request_errors": 40,
"total_connections": 50},
{"id": 2, "active_connections": 60,
"bytes_in": 70,
"bytes_out": 80,
"request_errors": 90,
"total_connections": 100}]
self.listener_stats_dict = {"listeners": listener_stats_list}
@mock.patch('octavia.common.utils.get_network_driver') # Silly test to check that debtcollector moves is working
def test_check_for_lb_vip_deallocate(self, mock_get_driver): def test_driver_lib_exists(self):
mock_repo = mock.MagicMock() driver_lib_class = driver_lib.DriverLibrary()
mock_lb = mock.MagicMock() self.assertIsInstance(driver_lib_class, lib_driver_lib.DriverLibrary)
# Test VIP not owned by Octavia
mock_lb.vip.octavia_owned = False
mock_repo.get.return_value = mock_lb
self.driver_lib._check_for_lb_vip_deallocate(mock_repo, 4)
mock_get_driver.assert_not_called()
# Test VIP is owned by Octavia
mock_lb.vip.octavia_owned = True
mock_repo.get.return_value = mock_lb
mock_net_driver = mock.MagicMock()
mock_get_driver.return_value = mock_net_driver
self.driver_lib._check_for_lb_vip_deallocate(mock_repo, 4)
mock_net_driver.deallocate_vip.assert_called_once_with(mock_lb.vip)
@mock.patch('octavia.api.drivers.driver_lib.DriverLibrary.'
'_check_for_lb_vip_deallocate')
def test_process_status_update(self, mock_deallocate):
mock_repo = mock.MagicMock()
list_dict = {"id": 2, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
list_prov_dict = {"id": 2,
constants.PROVISIONING_STATUS: constants.ACTIVE}
list_oper_dict = {"id": 2,
constants.OPERATING_STATUS: constants.ONLINE}
list_deleted_dict = {
"id": 2, constants.PROVISIONING_STATUS: constants.DELETED,
constants.OPERATING_STATUS: constants.ONLINE}
# Test with full record
self.driver_lib._process_status_update(mock_repo, 'FakeName',
list_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=constants.ACTIVE,
operating_status=constants.ONLINE)
mock_repo.delete.assert_not_called()
# Test with only provisioning status record
mock_repo.reset_mock()
self.driver_lib._process_status_update(mock_repo, 'FakeName',
list_prov_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=constants.ACTIVE)
mock_repo.delete.assert_not_called()
# Test with only operating status record
mock_repo.reset_mock()
self.driver_lib._process_status_update(mock_repo, 'FakeName',
list_oper_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, operating_status=constants.ONLINE)
mock_repo.delete.assert_not_called()
# Test with deleted but delete_record False
mock_repo.reset_mock()
self.driver_lib._process_status_update(mock_repo, 'FakeName',
list_deleted_dict)
mock_repo.update.assert_called_once_with(
self.mock_session, 2, provisioning_status=constants.DELETED,
operating_status=constants.ONLINE)
mock_repo.delete.assert_not_called()
# Test with an empty update
mock_repo.reset_mock()
self.driver_lib._process_status_update(mock_repo, 'FakeName',
{"id": 2})
mock_repo.update.assert_not_called()
mock_repo.delete.assert_not_called()
# Test with deleted and delete_record True
mock_repo.reset_mock()
self.driver_lib._process_status_update(
mock_repo, 'FakeName', list_deleted_dict, delete_record=True)
mock_repo.delete.assert_called_once_with(self.mock_session, id=2)
mock_repo.update.assert_not_called()
# Test with LB Delete
mock_repo.reset_mock()
self.driver_lib._process_status_update(
mock_repo, constants.LOADBALANCERS, list_deleted_dict)
mock_deallocate.assert_called_once_with(mock_repo, 2)
# Test with an exception
mock_repo.reset_mock()
mock_repo.update.side_effect = Exception('boom')
self.assertRaises(driver_exceptions.UpdateStatusError,
self.driver_lib._process_status_update,
mock_repo, 'FakeName', list_dict)
# Test with no ID record
mock_repo.reset_mock()
self.assertRaises(driver_exceptions.UpdateStatusError,
self.driver_lib._process_status_update,
mock_repo, 'FakeName', {"fake": "data"})
@mock.patch(
'octavia.api.drivers.driver_lib.DriverLibrary._process_status_update')
def test_update_loadbalancer_status(self, mock_status_update):
lb_dict = {"id": 1, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
list_dict = {"id": 2, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
pool_dict = {"id": 3, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
member_dict = {"id": 4,
constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
hm_dict = {"id": 5, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
l7p_dict = {"id": 6, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
l7r_dict = {"id": 7, constants.PROVISIONING_STATUS: constants.ACTIVE,
constants.OPERATING_STATUS: constants.ONLINE}
status_dict = {constants.LOADBALANCERS: [lb_dict],
constants.LISTENERS: [list_dict],
constants.POOLS: [pool_dict],
constants.MEMBERS: [member_dict],
constants.HEALTHMONITORS: [hm_dict],
constants.L7POLICIES: [l7p_dict],
constants.L7RULES: [l7r_dict]}
self.driver_lib.update_loadbalancer_status(status_dict)
calls = [call(self.mock_member_repo, constants.MEMBERS, member_dict,
delete_record=True),
call(self.mock_health_repo, constants.HEALTHMONITORS,
hm_dict, delete_record=True),
call(self.mock_pool_repo, constants.POOLS, pool_dict,
delete_record=True),
call(self.mock_l7r_repo, constants.L7RULES, l7r_dict,
delete_record=True),
call(self.mock_l7p_repo, constants.L7POLICIES, l7p_dict,
delete_record=True),
call(self.mock_list_repo, constants.LISTENERS, list_dict,
delete_record=True),
call(self.mock_lb_repo, constants.LOADBALANCERS,
lb_dict)]
mock_status_update.assert_has_calls(calls)
mock_status_update.reset_mock()
self.driver_lib.update_loadbalancer_status({})
mock_status_update.assert_not_called()
@mock.patch('octavia.db.repositories.ListenerStatisticsRepository.replace')
def test_update_listener_statistics(self, mock_replace):
self.driver_lib.update_listener_statistics(self.listener_stats_dict)
calls = [call(self.mock_session, 1, 1, active_connections=10,
bytes_in=20, bytes_out=30, request_errors=40,
total_connections=50),
call(self.mock_session, 2, 2, active_connections=60,
bytes_in=70, bytes_out=80, request_errors=90,
total_connections=100)]
mock_replace.assert_has_calls(calls)
mock_replace.reset_mock()
self.driver_lib.update_listener_statistics({})
mock_replace.assert_not_called()
# Test missing ID
bad_id_dict = {"listeners": [{"notID": "one"}]}
self.assertRaises(driver_exceptions.UpdateStatisticsError,
self.driver_lib.update_listener_statistics,
bad_id_dict)
# Coverage doesn't like this test as part of the above test
# So, broke it out in it's own test
@mock.patch('octavia.db.repositories.ListenerStatisticsRepository.replace')
def test_update_listener_statistics_exception(self, mock_replace):
# Test stats exception
mock_replace.side_effect = Exception('boom')
self.assertRaises(driver_exceptions.UpdateStatisticsError,
self.driver_lib.update_listener_statistics,
self.listener_stats_dict)

View File

@ -1,88 +0,0 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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.
from octavia.api.drivers import exceptions
import octavia.tests.unit.base as base
class TestProviderExceptions(base.TestCase):
def setUp(self):
super(TestProviderExceptions, self).setUp()
self.user_fault_string = 'Bad driver'
self.operator_fault_string = 'Fix bad driver.'
self.fault_object = 'MCP'
self.fault_object_id = '-1'
self.fault_record = 'skip'
def test_DriverError(self):
driver_error = exceptions.DriverError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
driver_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
driver_error.operator_fault_string)
self.assertIsInstance(driver_error, Exception)
def test_NotImplementedError(self):
not_implemented_error = exceptions.NotImplementedError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
not_implemented_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
not_implemented_error.operator_fault_string)
self.assertIsInstance(not_implemented_error, Exception)
def test_UnsupportedOptionError(self):
unsupported_option_error = exceptions.UnsupportedOptionError(
user_fault_string=self.user_fault_string,
operator_fault_string=self.operator_fault_string)
self.assertEqual(self.user_fault_string,
unsupported_option_error.user_fault_string)
self.assertEqual(self.operator_fault_string,
unsupported_option_error.operator_fault_string)
self.assertIsInstance(unsupported_option_error, Exception)
def test_UpdateStatusError(self):
update_status_error = exceptions.UpdateStatusError(
fault_string=self.user_fault_string,
status_object=self.fault_object,
status_object_id=self.fault_object_id,
status_record=self.fault_record)
self.assertEqual(self.user_fault_string,
update_status_error.fault_string)
self.assertEqual(self.fault_object, update_status_error.status_object)
self.assertEqual(self.fault_object_id,
update_status_error.status_object_id)
self.assertEqual(self.fault_record, update_status_error.status_record)
def test_UpdateStatisticsError(self):
update_stats_error = exceptions.UpdateStatisticsError(
fault_string=self.user_fault_string,
stats_object=self.fault_object,
stats_object_id=self.fault_object_id,
stats_record=self.fault_record)
self.assertEqual(self.user_fault_string,
update_stats_error.fault_string)
self.assertEqual(self.fault_object, update_stats_error.stats_object)
self.assertEqual(self.fault_object_id,
update_stats_error.stats_object_id)
self.assertEqual(self.fault_record, update_stats_error.stats_record)

View File

@ -12,146 +12,33 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from octavia.api.drivers import exceptions # Copyright 2018 Rackspace, US Inc.
from octavia.api.drivers import provider_base as driver_base #
# 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 octavia_lib.api.drivers.provider_base as lib_provider_base
from octavia.api.drivers import provider_base
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
class TestProviderBase(base.TestCase): class TestProviderBase(base.TestCase):
"""Test base methods.
Tests that methods not implemented by the drivers raise
NotImplementedError.
"""
def setUp(self): def setUp(self):
super(TestProviderBase, self).setUp() super(TestProviderBase, self).setUp()
self.driver = driver_base.ProviderDriver()
def test_create_vip_port(self): # Silly test to check that debtcollector moves is working
self.assertRaises(exceptions.NotImplementedError, def test_provider_base_exists(self):
self.driver.create_vip_port, provider_base_class = provider_base.ProviderDriver()
False, False, False) self.assertIsInstance(provider_base_class,
lib_provider_base.ProviderDriver)
def test_loadbalancer_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_create,
False)
def test_loadbalancer_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_delete,
False)
def test_loadbalancer_failover(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_failover,
False)
def test_loadbalancer_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.loadbalancer_update,
False, False)
def test_listener_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_create,
False)
def test_listener_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_delete,
False)
def test_listener_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.listener_update,
False, False)
def test_pool_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_create,
False)
def test_pool_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_delete,
False)
def test_pool_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.pool_update,
False, False)
def test_member_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_create,
False)
def test_member_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_delete,
False)
def test_member_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_update,
False, False)
def test_member_batch_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.member_batch_update,
False)
def test_health_monitor_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_create,
False)
def test_health_monitor_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_delete,
False)
def test_health_monitor_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.health_monitor_update,
False, False)
def test_l7policy_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_create,
False)
def test_l7policy_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_delete,
False)
def test_l7policy_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7policy_update,
False, False)
def test_l7rule_create(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_create,
False)
def test_l7rule_delete(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_delete,
False)
def test_l7rule_update(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.l7rule_update,
False, False)
def test_get_supported_flavor_metadata(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.get_supported_flavor_metadata)
def test_validate_flavor(self):
self.assertRaises(exceptions.NotImplementedError,
self.driver.validate_flavor,
False)

View File

@ -0,0 +1,70 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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 signal
import mock
import octavia.api.drivers.driver_agent.driver_listener
from octavia.cmd import driver_agent
from octavia.tests.unit import base
class TestDriverAgentCMD(base.TestCase):
def setUp(self):
super(TestDriverAgentCMD, self).setUp()
@mock.patch('os.kill')
@mock.patch('octavia.cmd.driver_agent.CONF')
def test_handle_mutate_config(self, mock_conf, mock_os_kill):
driver_agent._handle_mutate_config(1, 2)
mock_conf.mutate_config_files.assert_called_once()
os_calls = [mock.call(1, signal.SIGHUP), mock.call(2, signal.SIGHUP)]
mock_os_kill.assert_has_calls(os_calls, any_order=True)
@mock.patch('signal.signal')
@mock.patch('octavia.cmd.driver_agent.multiprocessing')
@mock.patch('oslo_reports.guru_meditation_report.TextGuruMeditation.'
'setup_autorun')
@mock.patch('octavia.common.service.prepare_service')
def test_main(self, mock_prep_srvc, mock_gmr, mock_multiprocessing,
mock_signal):
mock_exit_event = mock.MagicMock()
mock_multiprocessing.Event.return_value = mock_exit_event
mock_status_listener_proc = mock.MagicMock()
mock_stats_listener_proc = mock.MagicMock()
mock_multiprocessing.Process.side_effect = [mock_status_listener_proc,
mock_stats_listener_proc,
mock_status_listener_proc,
mock_stats_listener_proc]
driver_agent.main()
mock_prep_srvc.assert_called_once()
mock_gmr.assert_called_once()
mock_status_listener_proc.start.assert_called_once()
mock_stats_listener_proc.start.assert_called_once()
process_calls = [mock.call(
args=mock_exit_event, name='status_listener',
target=(octavia.api.drivers.driver_agent.driver_listener.
status_listener)),
mock.call(
args=mock_exit_event, name='stats_listener',
target=(octavia.api.drivers.driver_agent.driver_listener.
stats_listener))]
mock_multiprocessing.Process.has_calls(process_calls, any_order=True)
# Test keyboard interrupt path
mock_stats_listener_proc.join.side_effect = [KeyboardInterrupt, None]
driver_agent.main()
mock_exit_event.set.assert_called_once()

View File

@ -0,0 +1,26 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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.
from octavia import opts
import octavia.tests.unit.base as base
class TestOpts(base.TestCase):
def setUp(self):
super(TestOpts, self).setUp()
def test_list_opts(self):
opts_list = opts.list_opts()[0]
self.assertIn('DEFAULT', opts_list)

View File

@ -0,0 +1,34 @@
# Copyright 2018 Rackspace, US Inc.
#
# 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
import octavia.tests.unit.base as base
from octavia import version
class TestVersion(base.TestCase):
def setUp(self):
super(TestVersion, self).setUp()
def test_vendor_str(self):
self.assertEqual("OpenStack Foundation", version.vendor_string())
def test_product_string(self):
self.assertEqual("OpenStack Octavia", version.product_string())
@mock.patch('pbr.version.VersionInfo.version_string', return_value='0.0.0')
def test_version_str(self, mock_pbr):
self.assertEqual('0.0.0', version.version_string_with_package())

View File

@ -31,6 +31,7 @@
export PROJECTS="openstack-dev/grenade $PROJECTS" export PROJECTS="openstack-dev/grenade $PROJECTS"
export PROJECTS="openstack/octavia $PROJECTS" export PROJECTS="openstack/octavia $PROJECTS"
export PROJECTS="openstack/octavia-lib $PROJECTS"
export PROJECTS="openstack/octavia-tempest-plugin $PROJECTS" export PROJECTS="openstack/octavia-tempest-plugin $PROJECTS"
export PROJECTS="openstack/python-octaviaclient $PROJECTS" export PROJECTS="openstack/python-octaviaclient $PROJECTS"
export DEVSTACK_PROJECT_FROM_GIT="python-octaviaclient $DEVSTACK_PROJECT_FROM_GIT" export DEVSTACK_PROJECT_FROM_GIT="python-octaviaclient $DEVSTACK_PROJECT_FROM_GIT"

View File

@ -43,6 +43,7 @@
export PROJECTS="openstack/diskimage-builder $PROJECTS" export PROJECTS="openstack/diskimage-builder $PROJECTS"
export PROJECTS="openstack/tripleo-image-elements $PROJECTS" export PROJECTS="openstack/tripleo-image-elements $PROJECTS"
export PROJECTS="openstack/neutron-lbaas $PROJECTS" export PROJECTS="openstack/neutron-lbaas $PROJECTS"
export PROJECTS="openstack/octavia-lib $PROJECTS"
export PROJECTS="openstack/octavia $PROJECTS" export PROJECTS="openstack/octavia $PROJECTS"
if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then

View File

@ -42,6 +42,7 @@
export PROJECTS="openstack/diskimage-builder $PROJECTS" export PROJECTS="openstack/diskimage-builder $PROJECTS"
export PROJECTS="openstack/tripleo-image-elements $PROJECTS" export PROJECTS="openstack/tripleo-image-elements $PROJECTS"
export PROJECTS="openstack/neutron-lbaas $PROJECTS" export PROJECTS="openstack/neutron-lbaas $PROJECTS"
export PROJECTS="openstack/octavia-lib $PROJECTS"
export PROJECTS="openstack/octavia $PROJECTS" export PROJECTS="openstack/octavia $PROJECTS"
if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then

View File

@ -0,0 +1,18 @@
---
features:
- |
The Stein release of Octavia introduces the octavia-lib python module.
This library enables provider drivers to integrate easier with the Octavia
API by providing a shared set of coding objects and interfaces.
upgrade:
- |
The Stein release of Octavia adds the driver-agent controller process.
This process is deployed along with the Octavia API process and uses
unix domain sockets for communication between the provider drivers using
octavia-lib and the driver-agent.
When upgrading to Stein, operators should make sure that the
/var/run/octavia directry is available for the driver-agent with the
appropriate ownership and permissions for the driver-agent and API
processes to access it. The operator may need to make sure the driver-agent
process starts after installation. For example, a systemd service may need
to be created and enabled for it.

View File

@ -44,6 +44,8 @@ castellan>=0.16.0 # Apache-2.0
tenacity>=4.9.0 # Apache-2.0 tenacity>=4.9.0 # Apache-2.0
distro>=1.2.0 # Apache-2.0 distro>=1.2.0 # Apache-2.0
jsonschema>=2.6.0 # MIT jsonschema>=2.6.0 # MIT
debtcollector>=1.19.0 # Apache-2.0
octavia-lib>=1.1.1 # Apache-2.0
#for the amphora api #for the amphora api
Flask!=0.11,>=0.10 # BSD Flask!=0.11,>=0.10 # BSD

View File

@ -49,6 +49,7 @@ console_scripts =
octavia-health-manager = octavia.cmd.health_manager:main octavia-health-manager = octavia.cmd.health_manager:main
octavia-housekeeping = octavia.cmd.house_keeping:main octavia-housekeeping = octavia.cmd.house_keeping:main
octavia-db-manage = octavia.db.migration.cli:main octavia-db-manage = octavia.db.migration.cli:main
octavia-driver-agent = octavia.cmd.driver_agent:main
amphora-agent = octavia.cmd.agent:main amphora-agent = octavia.cmd.agent:main
haproxy-vrrp-check = octavia.cmd.haproxy_vrrp_check:main haproxy-vrrp-check = octavia.cmd.haproxy_vrrp_check:main
octavia-status = octavia.cmd.status:main octavia-status = octavia.cmd.status:main

View File

@ -8,6 +8,7 @@
- openstack/diskimage-builder - openstack/diskimage-builder
- openstack/neutron-lbaas - openstack/neutron-lbaas
- openstack/octavia - openstack/octavia
- openstack/octavia-lib
- openstack/python-barbicanclient - openstack/python-barbicanclient
- openstack/python-octaviaclient - openstack/python-octaviaclient
- openstack/tripleo-image-elements - openstack/tripleo-image-elements
@ -78,5 +79,6 @@
- openstack-dev/grenade - openstack-dev/grenade
- openstack-infra/devstack-gate - openstack-infra/devstack-gate
- openstack/octavia - openstack/octavia
- openstack/octavia-lib
- openstack/octavia-tempest-plugin - openstack/octavia-tempest-plugin
- openstack/python-octaviaclient - openstack/python-octaviaclient