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:
parent
c00faa7edd
commit
8997def2b5
@ -23,7 +23,7 @@ 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
|
||||
https://docs.openstack.org/devstack/latest/plugins.html
|
||||
|
@ -45,7 +45,7 @@ ENABLED_SERVICES+=,neutron-metadata-agent,neutron-qos
|
||||
# Tempest (optional)
|
||||
#ENABLED_SERVICES+=,tempest
|
||||
# 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
|
||||
|
||||
# Create the stack user
|
||||
|
@ -29,6 +29,15 @@ function octaviaclient_install {
|
||||
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 {
|
||||
if use_library_from_git "diskimage-builder"; then
|
||||
GITREPO["diskimage-builder"]=$DISKIMAGE_BUILDER_REPO_URL
|
||||
@ -209,6 +218,9 @@ function octavia_configure {
|
||||
sudo mkdir -m 755 -p $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
|
||||
cp $OCTAVIA_DIR/etc/octavia.conf $OCTAVIA_CONF
|
||||
fi
|
||||
@ -477,6 +489,7 @@ function octavia_start {
|
||||
run_process $OCTAVIA_API "$OCTAVIA_API_BINARY $OCTAVIA_API_ARGS"
|
||||
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_HOUSEKEEPER "$OCTAVIA_HOUSEKEEPER_BINARY $OCTAVIA_HOUSEKEEPER_ARGS"
|
||||
run_process $OCTAVIA_HEALTHMANAGER "$OCTAVIA_HEALTHMANAGER_BINARY $OCTAVIA_HEALTHMANAGER_ARGS"
|
||||
@ -489,6 +502,7 @@ function octavia_stop {
|
||||
else
|
||||
stop_process $OCTAVIA_API
|
||||
fi
|
||||
stop_process $OCTAVIA_DRIVER_AGENT
|
||||
stop_process $OCTAVIA_CONSUMER
|
||||
stop_process $OCTAVIA_HOUSEKEEPER
|
||||
stop_process $OCTAVIA_HEALTHMANAGER
|
||||
@ -523,6 +537,9 @@ function octavia_cleanup {
|
||||
if [ ${OCTAVIA_CONF_DIR}x != x ] ; then
|
||||
sudo rm -rf ${OCTAVIA_CONF_DIR}
|
||||
fi
|
||||
if [ ${OCTAVIA_RUN_DIR}x != x ] ; then
|
||||
sudo rm -rf ${OCTAVIA_RUN_DIR}
|
||||
fi
|
||||
if [ ${OCTAVIA_AMP_SSH_KEY_PATH}x != x ] ; then
|
||||
rm -f ${OCTAVIA_AMP_SSH_KEY_PATH} ${OCTAVIA_AMP_SSH_KEY_PATH}.pub
|
||||
fi
|
||||
@ -635,6 +652,7 @@ if is_service_enabled $OCTAVIA; then
|
||||
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
# Perform installation of service source
|
||||
echo_summary "Installing octavia"
|
||||
octavia_lib_install
|
||||
octavia_install
|
||||
octaviaclient_install
|
||||
|
||||
|
@ -61,6 +61,7 @@ enable_service o-hm
|
||||
enable_service o-hk
|
||||
enable_service o-api
|
||||
enable_service o-api-ha
|
||||
enable_service o-da
|
||||
|
||||
OCTAVIA_USE_PREGENERATED_CERTS=True
|
||||
OCTAVIA_USE_PREGENERATED_SSH_KEY=True
|
||||
|
@ -73,6 +73,7 @@ enable_service o-cw
|
||||
enable_service o-hm
|
||||
enable_service o-hk
|
||||
enable_service o-api
|
||||
enable_service o-da
|
||||
|
||||
|
||||
# enable DVR
|
||||
|
@ -16,6 +16,7 @@ OCTAVIA_DHCLIENT_CONF=${OCTAVIA_DHCLIENT_CONF:-${OCTAVIA_DHCLIENT_DIR}/dhclient.
|
||||
OCTAVIA_CONF=${OCTAVIA_CONF:-${OCTAVIA_CONF_DIR}/octavia.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_RUN_DIR=${OCTAVIA_RUN_DIR:-"/var/run/octavia"}
|
||||
|
||||
OCTAVIA_AMPHORA_DRIVER=${OCTAVIA_AMPHORA_DRIVER:-"amphora_haproxy_rest_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_HOUSEKEEPER_BINARY=${OCTAVIA_HOUSEKEEPER_BINARY:-${OCTAVIA_BIN_DIR}/octavia-housekeeping}
|
||||
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_CONSUMER_ARGS=${OCTAVIA_CONSUMER_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_DRIVER_AGENT_ARGS=${OCTAVIA_DRIVER_AGENT_ARGS:-" --config-file $OCTAVIA_CONF"}
|
||||
|
||||
OCTAVIA_TEMPEST=${OCTAVIA_TEMPEST:-"disabled"}
|
||||
|
||||
@ -75,12 +78,18 @@ OCTAVIA_HOUSEKEEPER="o-hk"
|
||||
OCTAVIA_HEALTHMANAGER="o-hm"
|
||||
OCTAVIA_SERVICE="octavia"
|
||||
OCTAVIA_API_HAPROXY="o-api-ha"
|
||||
OCTAVIA_DRIVER_AGENT="o-da"
|
||||
|
||||
# Client settings
|
||||
GITREPO["python-octaviaclient"]=${OCTAVIACLIENT_REPO:-${GIT_BASE}/openstack/python-octaviaclient.git}
|
||||
GITBRANCH["python-octaviaclient"]=${OCTAVIACLIENT_BRANCH:-master}
|
||||
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_CONF=$NEUTRON_CONF_DIR/neutron_lbaas.conf
|
||||
OCTAVIA_SERVICE_PROVIDER=${OCTAVIA_SERVICE_PROVIDER:-"LOADBALANCERV2:Octavia:neutron_lbaas.drivers.octavia.driver.OctaviaDriver:default"}
|
||||
|
@ -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
|
||||
may change at any time.
|
||||
|
||||
* octavia.api.drivers.data_models
|
||||
* octavia.api.drivers.driver_lib
|
||||
* octavia.api.drivers.exceptions
|
||||
* octavia.api.drivers.provider_base
|
||||
* octavia_lib.api.drivers.data_models
|
||||
* octavia_lib.api.drivers.driver_lib
|
||||
* octavia_lib.api.drivers.exceptions
|
||||
* octavia_lib.api.drivers.provider_base
|
||||
* octavia_lib.common.constants
|
||||
|
||||
Octavia Provider Driver API
|
||||
===========================
|
||||
@ -1695,7 +1696,7 @@ Driver Support Library
|
||||
|
||||
Provider drivers need support for updating provisioning status, operating
|
||||
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::
|
||||
|
||||
@ -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
|
||||
is made available. At which point drivers will not import any code from
|
||||
Octavia.
|
||||
octavia-lib.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -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
|
||||
Neutron LBaaS.
|
||||
|
||||
Soon, Octavia will support third-party vendor drivers just like Neutron LBaaS,
|
||||
and will then fully replace Neutron LBaaS as the load balancing solution for
|
||||
OpenStack. At that time, third-party vendor drivers that presently "plug in" to
|
||||
Neutron LBaaS will plug in to Octavia instead.
|
||||
Octavia supports third-party vendor drivers just like Neutron LBaaS,
|
||||
and fully replaces Neutron LBaaS as the load balancing solution for
|
||||
OpenStack.
|
||||
|
||||
For further information on OpenStack Neutron LBaaS deprecation, please refer to
|
||||
https://wiki.openstack.org/wiki/Neutron/LBaaS/Deprecation.
|
||||
@ -119,7 +118,7 @@ A 10,000-foot overview of Octavia components
|
||||
:width: 660px
|
||||
: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
|
||||
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.
|
||||
|
||||
* **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:
|
||||
|
||||
* **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
|
||||
rotation.
|
||||
|
||||
* **Driver Agent** - The driver agent receives status and statistics updates
|
||||
from provider drivers.
|
||||
|
||||
* **network** - Octavia cannot accomplish what it does without manipulating
|
||||
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
|
||||
|
@ -1,16 +1,9 @@
|
||||
<?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">
|
||||
<!-- 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"
|
||||
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="11.6929in" height="8.26772in"
|
||||
viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB" class="st15">
|
||||
<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>
|
||||
|
||||
width="11.6929in" height="8.26772in" viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB"
|
||||
class="st17">
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.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}
|
||||
.st4 {font-size:1em}
|
||||
.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}
|
||||
.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}
|
||||
.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}
|
||||
.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}
|
||||
.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>
|
||||
|
||||
@ -35,162 +30,97 @@
|
||||
<g id="lend4">
|
||||
<path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/>
|
||||
</g>
|
||||
<marker id="mrkr4-41" class="st7" v:arrowType="4" v:arrowSize="2" v:setback="4.34" refX="4.34" orient="auto"
|
||||
markerUnits="strokeWidth" overflow="visible">
|
||||
<marker id="mrkr4-42" class="st7" refX="4.34" orient="auto" markerUnits="strokeWidth" overflow="visible">
|
||||
<use xlink:href="#lend4" transform="scale(2.26) "/>
|
||||
</marker>
|
||||
<marker id="mrkr4-43" class="st7" v:arrowType="4" v:arrowSize="2" v:setback="4.52" refX="-4.52" orient="auto"
|
||||
markerUnits="strokeWidth" overflow="visible">
|
||||
<marker id="mrkr4-44" class="st7" refX="-4.52" orient="auto" markerUnits="strokeWidth" overflow="visible">
|
||||
<use xlink:href="#lend4" transform="scale(-2.26,-2.26) "/>
|
||||
</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>
|
||||
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
|
||||
</v:userDefs>
|
||||
<g>
|
||||
<title>Page-1</title>
|
||||
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394"
|
||||
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)">
|
||||
<g id="shape1023-1" transform="translate(305.179,-99.2126)">
|
||||
<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"/>
|
||||
</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>
|
||||
<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"/>
|
||||
<text x="147.47" y="573.74" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Controller Worker Driver</text> </g>
|
||||
<g id="shape2-6" v:mID="2" v:groupContext="shape" transform="translate(451.214,-166.925)">
|
||||
<text x="147.47" y="573.74" class="st3">Controller Worker Driver</text> </g>
|
||||
<g id="shape2-6" transform="translate(449.049,-237.791)">
|
||||
<title>Square</title>
|
||||
<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"/>
|
||||
<text x="12.02" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Certificate<v:newlineChar/><tspan
|
||||
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape3-10" v:mID="3" v:groupContext="shape" transform="translate(571.214,-166.925)">
|
||||
<text x="12.02" y="547.85" class="st3">Certificate <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape3-10" transform="translate(569.049,-237.791)">
|
||||
<title>Square.3</title>
|
||||
<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"/>
|
||||
<text x="14.65" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Compute<v:newlineChar/><tspan
|
||||
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape4-14" v:mID="4" v:groupContext="shape" transform="translate(691.214,-166.925)">
|
||||
<text x="14.65" y="547.85" class="st3">Compute <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape4-14" transform="translate(689.049,-237.791)">
|
||||
<title>Square.4</title>
|
||||
<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"/>
|
||||
<text x="16.89" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Network<v:newlineChar/><tspan
|
||||
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape5-18" v:mID="5" v:groupContext="shape" transform="translate(331.214,-166.925)">
|
||||
<text x="16.89" y="547.85" class="st3">Network <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape5-18" transform="translate(329.049,-237.791)">
|
||||
<title>Square.5</title>
|
||||
<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"/>
|
||||
<text x="14.51" y="547.85" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Amphora<v:newlineChar/><tspan
|
||||
x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape1001-22" v:mID="1001" v:groupContext="shape" transform="translate(687.192,-53.5394)">
|
||||
<text x="14.51" y="547.85" class="st3">Amphora <tspan x="24.74" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="shape1001-22" transform="translate(685.028,-124.406)">
|
||||
<title>Ellipse.6</title>
|
||||
<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"/>
|
||||
<text x="21.52" y="567.98" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Neutron</text> </g>
|
||||
<g id="shape1002-25" v:mID="1002" v:groupContext="shape" transform="translate(567.192,-53.5394)">
|
||||
<text x="21.52" y="567.98" class="st3">Neutron</text> </g>
|
||||
<g id="shape1002-25" transform="translate(565.028,-124.406)">
|
||||
<title>Ellipse.5</title>
|
||||
<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"/>
|
||||
<text x="31" y="567.98" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Nova</text> </g>
|
||||
<g id="shape1003-28" v:mID="1003" v:groupContext="shape" transform="translate(447.192,-53.5394)">
|
||||
<text x="31" y="567.98" class="st3">Nova</text> </g>
|
||||
<g id="shape1003-28" transform="translate(445.028,-124.406)">
|
||||
<title>Ellipse.69</title>
|
||||
<desc>Barbican</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"/>
|
||||
<desc>Barbican / Castellan</desc>
|
||||
<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>
|
||||
<g id="shape1004-31" v:mID="1004" v:groupContext="shape" transform="translate(210.906,-330.27)">
|
||||
<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-32" transform="translate(208.742,-401.136)">
|
||||
<title>Ellipse.3</title>
|
||||
<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"/>
|
||||
<text x="33.21" y="559.58" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Oslo<v:newlineChar/><tspan
|
||||
x="13.9" dy="1.2em" class="st4">Messaging</tspan></text> </g>
|
||||
<g id="shape1006-35" v:mID="1006" v:groupContext="shape" v:layerMember="0" transform="translate(331.214,-272.031)">
|
||||
<text x="33.21" y="559.58" class="st3">Oslo <tspan x="13.9" dy="1.2em" class="st4">Messaging</tspan></text> </g>
|
||||
<g id="shape1006-36" transform="translate(329.049,-342.897)">
|
||||
<title>Dynamic connector.1006</title>
|
||||
<path d="M-8.68 588.19 L-9.04 588.19 L-29.47 588.19" class="st6"/>
|
||||
</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>
|
||||
<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 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>
|
||||
<path d="M7.09 595.28 L7.09 636.62" class="st8"/>
|
||||
</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>
|
||||
<path d="M7.09 595.28 L7.09 636.62" class="st8"/>
|
||||
</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>
|
||||
<path d="M0 588.19 L24.5 588.19" class="st8"/>
|
||||
<path d="M0 602.36 L24.5 602.36" class="st8"/>
|
||||
</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>
|
||||
<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>
|
||||
<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"/>
|
||||
<text x="33.64" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Octavia<v:newlineChar/><tspan
|
||||
x="46.39" dy="1.2em" class="st4">API</tspan></text> </g>
|
||||
<g id="shape15-69" v:mID="15" v:groupContext="shape" transform="translate(80.3899,0)">
|
||||
<text x="21.42" y="504.09" class="st3">Octavia API</text> </g>
|
||||
<g id="shape15-69" transform="translate(80.3899,-5.68434E-13)">
|
||||
<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,
|
||||
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
|
||||
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
|
||||
@ -202,25 +132,19 @@
|
||||
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
|
||||
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
|
||||
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 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>
|
||||
<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>
|
||||
<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"/>
|
||||
<text x="33.64" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Octavia<v:newlineChar/><tspan
|
||||
x="34.05" dy="1.2em" class="st4">Worker</tspan></text> </g>
|
||||
<g id="shape16-78" v:mID="16" v:groupContext="shape" transform="translate(80.3899,0)">
|
||||
<text x="33.64" y="534.38" class="st3">Octavia <tspan x="34.05" dy="1.2em" class="st4">Worker</tspan></text> </g>
|
||||
<g id="shape16-78" transform="translate(80.3899,0)">
|
||||
<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,
|
||||
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
|
||||
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
|
||||
@ -232,25 +156,19 @@
|
||||
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
|
||||
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
|
||||
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 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>
|
||||
<g id="shape7-83" v:mID="7" v:groupContext="shape">
|
||||
<g id="shape7-83">
|
||||
<title>Square.7</title>
|
||||
<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"/>
|
||||
<text x="36.47" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Health<v:newlineChar/><tspan
|
||||
x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
|
||||
<g id="shape17-87" v:mID="17" v:groupContext="shape" transform="translate(80.3899,0)">
|
||||
<text x="36.47" y="534.38" class="st3">Health <tspan x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
|
||||
<g id="shape17-87" transform="translate(80.3899,0)">
|
||||
<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,
|
||||
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
|
||||
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
|
||||
@ -262,25 +180,19 @@
|
||||
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
|
||||
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
|
||||
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 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>
|
||||
<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>
|
||||
<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"/>
|
||||
<text x="12.33" y="534.38" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Housekeeping<v:newlineChar/><tspan
|
||||
x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
|
||||
<g id="shape18-96" v:mID="18" v:groupContext="shape" transform="translate(80.3899,0)">
|
||||
<text x="12.33" y="534.38" class="st3">Housekeeping <tspan x="29.11" dy="1.2em" class="st4">Manager</tspan></text> </g>
|
||||
<g id="shape18-96" transform="translate(80.3899,0)">
|
||||
<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,
|
||||
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
|
||||
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
|
||||
@ -292,109 +204,88 @@
|
||||
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
|
||||
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
|
||||
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 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>
|
||||
<path d="M0 602.25 L16.77 602.4" class="st8"/>
|
||||
<path d="M0 588.27 L30.94 588.14" class="st8"/>
|
||||
</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>
|
||||
<path d="M0 588.3 L16.77 588.16" class="st8"/>
|
||||
</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>
|
||||
<path d="M0 603.96 L0 604.32 L0 621.46 L86.16 621.46" class="st6"/>
|
||||
</g>
|
||||
<g id="group1019-117" transform="translate(223.609,-244.57)" v:mID="1019" v:groupContext="group">
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
</v:userDefs>
|
||||
<g id="group1019-117" transform="translate(221.445,-315.436)">
|
||||
<title>Can.1019</title>
|
||||
<desc>Database</desc>
|
||||
<g id="shape1020-118" v:mID="1020" v:groupContext="shape">
|
||||
<g id="shape1020-118">
|
||||
<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"/>
|
||||
</g>
|
||||
<g id="shape1019-120" v:mID="1019" v:groupContext="groupContent">
|
||||
<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"/>
|
||||
<g id="shape1019-120">
|
||||
<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 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>
|
||||
<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"/>
|
||||
</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>
|
||||
<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"/>
|
||||
<text x="27.98" y="556.96" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Amphora</text> </g>
|
||||
<g id="group1054-128" transform="translate(359.038,-138.579)" v:mID="1054" v:groupContext="group">
|
||||
<text x="27.98" y="556.96" class="st3">Amphora</text> </g>
|
||||
<g id="group1054-128" transform="translate(356.874,-209.445)">
|
||||
<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>
|
||||
<path d="M-6.99 603.95 L-7 604.31 L-7.33 623.62" class="st13"/>
|
||||
</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>
|
||||
<path d="M-6.84 595.28 L-7.17 614.58" class="st8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="group1064-139" transform="translate(60.1975,-453.543)" v:mID="1064" v:groupContext="group">
|
||||
<title>Sheet.1064</title>
|
||||
<g id="shape1056-140" v:mID="1056" v:groupContext="shape">
|
||||
<title>Rectangle.1056</title>
|
||||
<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="68.2087" cy="491.339" width="136.42" height="18.8976"/>
|
||||
<rect x="0" y="481.89" width="136.417" height="113.386" class="st5"/>
|
||||
<text x="42.48" y="495.54" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Neutron</text> </g>
|
||||
<g id="shape1057-143" v:mID="1057" v:groupContext="shape" transform="translate(11.5157,-11.3386)">
|
||||
<title>Rectangle.1057</title>
|
||||
<desc>LBaaS v2 User API Handler</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="537.101" width="113.39" height="42.3913"/>
|
||||
<rect x="0" y="515.906" width="113.386" height="79.3701" class="st14"/>
|
||||
<text x="29.5" y="532.9" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>LBaaS v2<v:newlineChar/><tspan
|
||||
x="4.28" dy="1.2em" class="st4">User API Handler</tspan></text> </g>
|
||||
<g id="shape1058-147" v:mID="1058" v:groupContext="shape" transform="translate(20.9587,-17.0079)">
|
||||
<title>Rectangle.1058</title>
|
||||
<desc>Octavia 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="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 id="shape1056-139" transform="translate(83.7218,-411.024)">
|
||||
<title>Sheet.1056</title>
|
||||
<desc>Amphora Driver</desc>
|
||||
<rect x="0" y="552.756" width="85.0394" height="42.5197" class="st14"/>
|
||||
<text x="13.8" y="569.82" class="st3">Amphora <tspan x="24.04" dy="1.2em" class="st4">Driver</tspan></text> </g>
|
||||
<g id="group1057-143" transform="translate(69.5486,-212.598)">
|
||||
<title>Sheet.1057</title>
|
||||
<g id="shape1058-144">
|
||||
<title>Square.7</title>
|
||||
<desc>Driver Agent</desc>
|
||||
<rect x="0" y="481.89" width="113.386" height="113.386" class="st9"/>
|
||||
<text x="17.66" y="542.78" class="st3">Driver Agent</text> </g>
|
||||
<g id="shape19-147" transform="translate(80.3899,0)">
|
||||
<title>Sheet.19</title>
|
||||
<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,
|
||||
iVBORw0KGgoAAAANSUhEUgAAACEAAAAfCAYAAABplKSyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA
|
||||
AA7DAcdvqGQAAAI9SURBVFhHxZYhUxxBEIVXICIQCEQEAolERiAQCEREBAKBQCAQEQj+AzIyMiIiAhGBQCAQCATixAkEAqoQCERE
|
||||
BAJxvC9sX/UNb2/3iqpNV311M29mumd3evq2Go1G/x0r9o0V+8aKfWPFLsjmiv587s+CFach+yxOxHmhfxfX4qtYyGNtWNEhOxQ0
|
||||
4Lb+XanH5sVfETptNtXp7VjRIfstngUd+CMeBYEfRB5Du3Z+HFZ0yDbqAF1gQwfOj8OKgexjam+LCNIGm7gQ/45DtgDhq8SKIFsX
|
||||
T+KnIB/y6+7KpTgW+DlyccCKIPsl7gXnnR3PAsHJjyuBn4lrPY5lxapaFDjIDh1lMuYxx5aNZ8XJ69gEt+NAcGw7gqeddmSMndp4
|
||||
VqyqTYFTOlzDcJTZKNZ8ECTjQJRzgbG9vGa81omBjCfNjoJhw/wvaU5mas2wYiDbTY4yEyU7zW+qJbNvQkZi7osbkZ0FlOVxDUnr
|
||||
juqxcj5Ju1bOD7z4ej1pnNa/JSQlZ7yU1lDMmhIzbo5/g1Z8zfhw0AQJS1AKUlstuRPMtQXrjRDI+Fs+E9nZe2ATyzaWE0EWtYIz
|
||||
5tq5s24jF7wTFwesCDKSkw8U7j/fC+RAlyoasOk1sSp+iIm6krGio3aYg7Sx6/w4rOiQ8TS5eg5Tu+wz75vz47BiiWxORM3gWCIY
|
||||
tYSbxLGFxvcnSXjrfDms6JCxkS0RmxgU41ELuFUk9WIen4YV25CRbJ8Kjf+N9ax1xYp9Y8W+sWK/jKoXFAS4xJXVHUYAAAAASUVO
|
||||
RK5CYII="/>
|
||||
<rect x="0" y="564.279" width="32.9959" height="30.9961" class="st10"/>
|
||||
</g>
|
||||
<g id="shape1065-151" v:mID="1065" v:groupContext="shape" v:layerMember="0" transform="translate(121.32,-470.551)">
|
||||
<title>Dynamic connector.1065</title>
|
||||
<path d="M7.09 595.28 L7.09 638.1" class="st8"/>
|
||||
</g>
|
||||
<g id="shape1059-151" transform="translate(90.2835,-411.024)">
|
||||
<title>Dynamic connector.1059</title>
|
||||
<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>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 18 KiB |
@ -460,3 +460,23 @@
|
||||
# A URL representing messaging driver to use for notification. If not
|
||||
# specified, we fall back to the same configuration used for RPC.
|
||||
# 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
|
||||
|
@ -72,6 +72,7 @@ munch==2.2.0
|
||||
netaddr==0.7.19
|
||||
netifaces==0.10.4
|
||||
networkx==1.11
|
||||
octavia-lib==1.1.1
|
||||
openstacksdk==0.12.0
|
||||
os-client-config==1.29.0
|
||||
os-service-types==1.2.0
|
||||
|
@ -14,15 +14,17 @@
|
||||
|
||||
from jsonschema import exceptions as js_exceptions
|
||||
from jsonschema import validate
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
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 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.common import constants as consts
|
||||
from octavia.common import data_models
|
||||
@ -93,7 +95,7 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
|
||||
# expects
|
||||
vip_qos_policy_id = lb_dict.pop('vip_qos_policy_id', None)
|
||||
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
|
||||
|
||||
payload = {consts.LOAD_BALANCER_ID: lb_id,
|
||||
|
@ -15,270 +15,44 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# 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):
|
||||
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]
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
if recurse:
|
||||
if isinstance(getattr(self, attr), list):
|
||||
ret[attr] = []
|
||||
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
|
||||
BaseDataModel = moves.moved_class(lib_data_models.BaseDataModel,
|
||||
'BaseDataModel', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
return ret
|
||||
UnsetType = moves.moved_class(lib_data_models.UnsetType, 'UnsetType', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return self.to_dict() == other.to_dict()
|
||||
return False
|
||||
LoadBalancer = moves.moved_class(lib_data_models.LoadBalancer, 'LoadBalancer',
|
||||
__name__, version='Stein',
|
||||
removal_version='U')
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
Listener = moves.moved_class(lib_data_models.Listener, 'Listener', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict):
|
||||
return cls(**dict)
|
||||
Pool = moves.moved_class(lib_data_models.Pool, 'Pool', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
Member = moves.moved_class(lib_data_models.Member, 'Member', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
class UnsetType(object):
|
||||
def __bool__(self):
|
||||
return False
|
||||
__nonzero__ = __bool__
|
||||
HealthMonitor = moves.moved_class(lib_data_models.HealthMonitor,
|
||||
'HealthMonitor', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
def __repr__(self):
|
||||
return 'Unset'
|
||||
L7Policy = moves.moved_class(lib_data_models.L7Policy, 'L7Policy', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
L7Rule = moves.moved_class(lib_data_models.L7Rule, 'L7Rule', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
Unset = UnsetType()
|
||||
|
||||
|
||||
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
|
||||
VIP = moves.moved_class(lib_data_models.VIP, 'VIP', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
11
octavia/api/drivers/driver_agent/__init__.py
Normal file
11
octavia/api/drivers/driver_agent/__init__.py
Normal 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.
|
144
octavia/api/drivers/driver_agent/driver_listener.py
Normal file
144
octavia/api/drivers/driver_agent/driver_listener.py
Normal 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)
|
174
octavia/api/drivers/driver_agent/driver_updater.py
Normal file
174
octavia/api/drivers/driver_agent/driver_updater.py
Normal 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}
|
@ -12,144 +12,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from octavia.api.drivers import exceptions as driver_exceptions
|
||||
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
|
||||
import warnings
|
||||
|
||||
from debtcollector import moves
|
||||
|
||||
from octavia_lib.api.drivers import driver_lib as lib_driver_lib
|
||||
|
||||
|
||||
class DriverLibrary(object):
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
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(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)
|
||||
DriverLibrary = moves.moved_class(
|
||||
lib_driver_lib.DriverLibrary, 'DriverLibrary', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
@ -12,137 +12,30 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# 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):
|
||||
"""Catch all exception that drivers can raise.
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
This exception includes two strings: The user fault string and the
|
||||
optional operator fault string. The user fault string,
|
||||
"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.
|
||||
DriverError = moves.moved_class(lib_exceptions.DriverError, 'DriverError',
|
||||
__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 = _("An unknown driver error occurred.")
|
||||
operator_fault_string = _("An unknown driver error occurred.")
|
||||
NotImplementedError = moves.moved_class(
|
||||
lib_exceptions.NotImplementedError, 'NotImplementedError', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
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(DriverError, self).__init__(*args, **kwargs)
|
||||
UnsupportedOptionError = moves.moved_class(
|
||||
lib_exceptions.UnsupportedOptionError, 'UnsupportedOptionError', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
UpdateStatusError = moves.moved_class(
|
||||
lib_exceptions.UpdateStatusError, 'UpdateStatusError', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
||||
class NotImplementedError(Exception):
|
||||
"""Exception raised when a driver does not implement an API function.
|
||||
|
||||
: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)
|
||||
UpdateStatisticsError = moves.moved_class(
|
||||
lib_exceptions.UpdateStatisticsError, 'UpdateStatisticsError', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
@ -15,8 +15,8 @@
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.api.drivers import data_models
|
||||
from octavia.api.drivers import provider_base as driver_base
|
||||
from octavia_lib.api.drivers import data_models
|
||||
from octavia_lib.api.drivers import provider_base as driver_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -12,470 +12,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from octavia.api.drivers import exceptions
|
||||
import warnings
|
||||
|
||||
# This class describes the abstraction of a provider driver interface.
|
||||
# Load balancing provider drivers will implement this interface.
|
||||
from debtcollector import moves
|
||||
|
||||
from octavia_lib.api.drivers import provider_base as lib_provider_base
|
||||
|
||||
|
||||
class ProviderDriver(object):
|
||||
# name is for internal Octavia use and should not be used by drivers
|
||||
name = None
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
# Load Balancer
|
||||
def create_vip_port(self, loadbalancer_id, project_id, vip_dictionary):
|
||||
"""Creates a port for a load balancer VIP.
|
||||
|
||||
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.')
|
||||
ProviderDriver = moves.moved_class(
|
||||
lib_provider_base.ProviderDriver, 'ProviderDriver', __name__,
|
||||
version='Stein', removal_version='U')
|
||||
|
84
octavia/cmd/driver_agent.py
Normal file
84
octavia/cmd/driver_agent.py
Normal 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()
|
@ -601,6 +601,37 @@ audit_opts = [
|
||||
'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
|
||||
cfg.CONF.register_opts(core_opts)
|
||||
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(quota_opts, group='quotas')
|
||||
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.certmgr_opts, group='certificates')
|
||||
|
@ -12,48 +12,183 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
LB_ALGORITHM_ROUND_ROBIN = 'ROUND_ROBIN'
|
||||
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)
|
||||
from octavia_lib.common import constants as lib_consts
|
||||
|
||||
SESSION_PERSISTENCE_SOURCE_IP = 'SOURCE_IP'
|
||||
SESSION_PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE'
|
||||
SESSION_PERSISTENCE_APP_COOKIE = 'APP_COOKIE'
|
||||
SUPPORTED_SP_TYPES = (SESSION_PERSISTENCE_SOURCE_IP,
|
||||
SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
SESSION_PERSISTENCE_APP_COOKIE)
|
||||
##############################################################################
|
||||
# Constants common to the provider drivers moved to
|
||||
# octavia_lib.common.constants
|
||||
# These are deprecated, to be removed in the 'U' release
|
||||
##############################################################################
|
||||
# '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'
|
||||
HEALTH_MONITOR_TCP = 'TCP'
|
||||
HEALTH_MONITOR_HTTP = 'HTTP'
|
||||
HEALTH_MONITOR_HTTPS = 'HTTPS'
|
||||
HEALTH_MONITOR_TLS_HELLO = 'TLS-HELLO'
|
||||
HEALTH_MONITOR_UDP_CONNECT = 'UDP-CONNECT'
|
||||
SUPPORTED_HEALTH_MONITOR_TYPES = (HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS,
|
||||
HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP,
|
||||
HEALTH_MONITOR_TLS_HELLO,
|
||||
HEALTH_MONITOR_UDP_CONNECT)
|
||||
HEALTH_MONITOR_HTTP_METHOD_GET = 'GET'
|
||||
HEALTH_MONITOR_HTTP_METHOD_HEAD = 'HEAD'
|
||||
HEALTH_MONITOR_HTTP_METHOD_POST = 'POST'
|
||||
HEALTH_MONITOR_HTTP_METHOD_PUT = 'PUT'
|
||||
HEALTH_MONITOR_HTTP_METHOD_DELETE = 'DELETE'
|
||||
HEALTH_MONITOR_HTTP_METHOD_TRACE = 'TRACE'
|
||||
HEALTH_MONITOR_HTTP_METHOD_OPTIONS = 'OPTIONS'
|
||||
HEALTH_MONITOR_HTTP_METHOD_CONNECT = 'CONNECT'
|
||||
HEALTH_MONITOR_HTTP_METHOD_PATCH = 'PATCH'
|
||||
HEALTH_MONITOR_HTTP_DEFAULT_METHOD = HEALTH_MONITOR_HTTP_METHOD_GET
|
||||
# 'PING'
|
||||
HEALTH_MONITOR_PING = lib_consts.HEALTH_MONITOR_PING
|
||||
# 'TCP'
|
||||
HEALTH_MONITOR_TCP = lib_consts.HEALTH_MONITOR_TCP
|
||||
# 'HTTP'
|
||||
HEALTH_MONITOR_HTTP = lib_consts.HEALTH_MONITOR_HTTP
|
||||
# 'HTTPS'
|
||||
HEALTH_MONITOR_HTTPS = lib_consts.HEALTH_MONITOR_HTTPS
|
||||
# 'TLS-HELLO'
|
||||
HEALTH_MONITOR_TLS_HELLO = lib_consts.HEALTH_MONITOR_TLS_HELLO
|
||||
# 'UDP-CONNECT'
|
||||
HEALTH_MONITOR_UDP_CONNECT = lib_consts.HEALTH_MONITOR_UDP_CONNECT
|
||||
SUPPORTED_HEALTH_MONITOR_TYPES = lib_consts.SUPPORTED_HEALTH_MONITOR_TYPES
|
||||
|
||||
# 'GET'
|
||||
HEALTH_MONITOR_HTTP_METHOD_GET = lib_consts.HEALTH_MONITOR_HTTP_METHOD_GET
|
||||
# 'HEAD'
|
||||
HEALTH_MONITOR_HTTP_METHOD_HEAD = lib_consts.HEALTH_MONITOR_HTTP_METHOD_HEAD
|
||||
# 'POST'
|
||||
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 = (
|
||||
HEALTH_MONITOR_HTTP_METHOD_GET, HEALTH_MONITOR_HTTP_METHOD_HEAD,
|
||||
HEALTH_MONITOR_HTTP_METHOD_POST, HEALTH_MONITOR_HTTP_METHOD_PUT,
|
||||
HEALTH_MONITOR_HTTP_METHOD_DELETE, HEALTH_MONITOR_HTTP_METHOD_TRACE,
|
||||
HEALTH_MONITOR_HTTP_METHOD_OPTIONS, HEALTH_MONITOR_HTTP_METHOD_CONNECT,
|
||||
HEALTH_MONITOR_HTTP_METHOD_PATCH)
|
||||
SUPPORTED_HTTP_VERSIONS = [1.0, 1.1]
|
||||
lib_consts.SUPPORTED_HEALTH_MONITOR_HTTP_METHODS)
|
||||
|
||||
# 'REJECT'
|
||||
L7POLICY_ACTION_REJECT = lib_consts.L7POLICY_ACTION_REJECT
|
||||
# 'REDIRECT_TO_URL'
|
||||
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_HTTP_DEFAULT_METHOD = lib_consts.HEALTH_MONITOR_HTTP_METHOD_GET
|
||||
HEALTH_MONITOR_DEFAULT_URL_PATH = '/'
|
||||
TYPE = 'type'
|
||||
URL_PATH = 'url_path'
|
||||
@ -67,15 +202,6 @@ RISE_THRESHOLD = 'rise_threshold'
|
||||
UPDATE_STATS = 'UPDATE_STATS'
|
||||
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
|
||||
MIN_PORT_NUMBER = 1
|
||||
MAX_PORT_NUMBER = 65535
|
||||
@ -97,86 +223,27 @@ DEFAULT_TIMEOUT_MEMBER_CONNECT = 5000
|
||||
DEFAULT_TIMEOUT_MEMBER_DATA = 50000
|
||||
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
|
||||
# the provisioning_status table
|
||||
# Amphora has been allocated to a load balancer
|
||||
AMPHORA_ALLOCATED = 'ALLOCATED'
|
||||
# Amphora is being built
|
||||
AMPHORA_BOOTING = 'BOOTING'
|
||||
# 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)
|
||||
SUPPORTED_AMPHORA_STATUSES = (
|
||||
lib_consts.AMPHORA_ALLOCATED, lib_consts.AMPHORA_BOOTING, lib_consts.ERROR,
|
||||
lib_consts.AMPHORA_READY, lib_consts.DELETED, lib_consts.PENDING_CREATE,
|
||||
lib_consts.PENDING_DELETE)
|
||||
|
||||
AMPHORA_VM = 'VM'
|
||||
SUPPORTED_AMPHORA_TYPES = (AMPHORA_VM,)
|
||||
|
||||
# L7 constants
|
||||
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)
|
||||
DISTINGUISHED_NAME_FIELD_REGEX = lib_consts.DISTINGUISHED_NAME_FIELD_REGEX
|
||||
|
||||
# For redirect, only codes 301, 302, 303, 307 and 308 are # supported.
|
||||
SUPPORTED_L7POLICY_REDIRECT_HTTP_CODES = [301, 302, 303, 307, 308]
|
||||
|
||||
SUPPORTED_HTTP_VERSIONS = [1.0, 1.1]
|
||||
|
||||
MIN_POLICY_POSITION = 1
|
||||
# Largest a 32-bit integer can be, which is a limitation
|
||||
# here if you're using MySQL, as most probably are. This just needs
|
||||
@ -220,17 +287,14 @@ DELTAS = 'deltas'
|
||||
HEALTH_MON = 'health_mon'
|
||||
HEALTH_MONITOR = 'health_monitor'
|
||||
LISTENER = 'listener'
|
||||
LISTENERS = 'listeners'
|
||||
LISTENER_ID = 'listener_id'
|
||||
LOADBALANCER = 'loadbalancer'
|
||||
LOADBALANCERS = 'loadbalancers'
|
||||
LOADBALANCER_ID = 'loadbalancer_id'
|
||||
LOAD_BALANCER_ID = 'load_balancer_id'
|
||||
SERVER_GROUP_ID = 'server_group_id'
|
||||
ANTI_AFFINITY = 'anti-affinity'
|
||||
SOFT_ANTI_AFFINITY = 'soft-anti-affinity'
|
||||
MEMBER = 'member'
|
||||
MEMBERS = 'members'
|
||||
MEMBER_ID = 'member_id'
|
||||
COMPUTE_ID = 'compute_id'
|
||||
COMPUTE_OBJ = 'compute_obj'
|
||||
@ -239,7 +303,6 @@ AMPS_DATA = 'amps_data'
|
||||
NICS = 'nics'
|
||||
VIP = 'vip'
|
||||
POOL = 'pool'
|
||||
POOLS = 'pools'
|
||||
POOL_CHILD_COUNT = 'pool_child_count'
|
||||
POOL_ID = 'pool_id'
|
||||
L7POLICY = 'l7policy'
|
||||
@ -253,11 +316,8 @@ ADDED_PORTS = 'added_ports'
|
||||
PORTS = 'ports'
|
||||
MEMBER_PORTS = 'member_ports'
|
||||
TOPOLOGY = 'topology'
|
||||
HEALTHMONITORS = 'healthmonitors'
|
||||
HEALTH_MONITOR_ID = 'health_monitor_id'
|
||||
L7POLICIES = 'l7policies'
|
||||
L7POLICY_ID = 'l7policy_id'
|
||||
L7RULES = 'l7rules'
|
||||
L7RULE_ID = 'l7rule_id'
|
||||
LOAD_BALANCER_UPDATES = 'load_balancer_updates'
|
||||
LISTENER_UPDATES = 'listener_updates'
|
||||
@ -418,7 +478,6 @@ DEFAULT_VRRP_ID = 1
|
||||
VRRP_PROTOCOL_NUM = 112
|
||||
AUTH_HEADER_PROTOCOL_NUMBER = 51
|
||||
|
||||
|
||||
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'
|
||||
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',
|
||||
'LoadBalancerFlows': 'Load Balancer Flows',
|
||||
'ListenerFlows': 'Listener Flows',
|
||||
|
@ -45,6 +45,7 @@ def list_opts():
|
||||
('glance', octavia.common.config.glance_opts),
|
||||
('quotas', octavia.common.config.quota_opts),
|
||||
('audit', octavia.common.config.audit_opts),
|
||||
('driver_agent', octavia.common.config.driver_agent_opts),
|
||||
add_auth_opts(),
|
||||
]
|
||||
|
||||
|
@ -13,9 +13,12 @@
|
||||
# under the License.
|
||||
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 import data_models as driver_dm
|
||||
from octavia.api.drivers import exceptions
|
||||
from octavia.common import constants as consts
|
||||
from octavia.network import base as network_base
|
||||
from octavia.tests.unit.api.drivers import sample_data_models
|
||||
@ -101,6 +104,20 @@ class TestAmphoraDriver(base.TestRpc):
|
||||
consts.LOAD_BALANCER_UPDATES: lb_dict}
|
||||
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
|
||||
@mock.patch('oslo_messaging.RPCClient.cast')
|
||||
def test_listener_create(self, mock_cast):
|
||||
|
11
octavia/tests/unit/api/drivers/driver_agent/__init__.py
Normal file
11
octavia/tests/unit/api/drivers/driver_agent/__init__.py
Normal 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.
|
@ -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()
|
@ -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)
|
@ -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))
|
@ -12,239 +12,32 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from mock import call
|
||||
# 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 octavia_lib.api.drivers.driver_lib as lib_driver_lib
|
||||
|
||||
from octavia.api.drivers import driver_lib
|
||||
from octavia.api.drivers import exceptions as driver_exceptions
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit import base
|
||||
import octavia.tests.unit.base as base
|
||||
|
||||
|
||||
class TestDriverLib(base.TestCase):
|
||||
@mock.patch('octavia.db.repositories.L7RuleRepository')
|
||||
@mock.patch('octavia.db.repositories.L7PolicyRepository')
|
||||
@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):
|
||||
|
||||
def setUp(self):
|
||||
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')
|
||||
def test_check_for_lb_vip_deallocate(self, mock_get_driver):
|
||||
mock_repo = mock.MagicMock()
|
||||
mock_lb = mock.MagicMock()
|
||||
|
||||
# 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)
|
||||
# Silly test to check that debtcollector moves is working
|
||||
def test_driver_lib_exists(self):
|
||||
driver_lib_class = driver_lib.DriverLibrary()
|
||||
self.assertIsInstance(driver_lib_class, lib_driver_lib.DriverLibrary)
|
||||
|
@ -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)
|
@ -12,146 +12,33 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from octavia.api.drivers import exceptions
|
||||
from octavia.api.drivers import provider_base as driver_base
|
||||
# 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 octavia_lib.api.drivers.provider_base as lib_provider_base
|
||||
|
||||
from octavia.api.drivers import provider_base
|
||||
import octavia.tests.unit.base as base
|
||||
|
||||
|
||||
class TestProviderBase(base.TestCase):
|
||||
"""Test base methods.
|
||||
|
||||
Tests that methods not implemented by the drivers raise
|
||||
NotImplementedError.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestProviderBase, self).setUp()
|
||||
self.driver = driver_base.ProviderDriver()
|
||||
|
||||
def test_create_vip_port(self):
|
||||
self.assertRaises(exceptions.NotImplementedError,
|
||||
self.driver.create_vip_port,
|
||||
False, False, False)
|
||||
|
||||
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)
|
||||
# Silly test to check that debtcollector moves is working
|
||||
def test_provider_base_exists(self):
|
||||
provider_base_class = provider_base.ProviderDriver()
|
||||
self.assertIsInstance(provider_base_class,
|
||||
lib_provider_base.ProviderDriver)
|
||||
|
70
octavia/tests/unit/cmd/test_driver_agent.py
Normal file
70
octavia/tests/unit/cmd/test_driver_agent.py
Normal 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()
|
26
octavia/tests/unit/test_opts.py
Normal file
26
octavia/tests/unit/test_opts.py
Normal 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)
|
34
octavia/tests/unit/test_version.py
Normal file
34
octavia/tests/unit/test_version.py
Normal 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())
|
@ -31,6 +31,7 @@
|
||||
|
||||
export PROJECTS="openstack-dev/grenade $PROJECTS"
|
||||
export PROJECTS="openstack/octavia $PROJECTS"
|
||||
export PROJECTS="openstack/octavia-lib $PROJECTS"
|
||||
export PROJECTS="openstack/octavia-tempest-plugin $PROJECTS"
|
||||
export PROJECTS="openstack/python-octaviaclient $PROJECTS"
|
||||
export DEVSTACK_PROJECT_FROM_GIT="python-octaviaclient $DEVSTACK_PROJECT_FROM_GIT"
|
||||
|
@ -43,6 +43,7 @@
|
||||
export PROJECTS="openstack/diskimage-builder $PROJECTS"
|
||||
export PROJECTS="openstack/tripleo-image-elements $PROJECTS"
|
||||
export PROJECTS="openstack/neutron-lbaas $PROJECTS"
|
||||
export PROJECTS="openstack/octavia-lib $PROJECTS"
|
||||
export PROJECTS="openstack/octavia $PROJECTS"
|
||||
|
||||
if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then
|
||||
|
@ -42,6 +42,7 @@
|
||||
export PROJECTS="openstack/diskimage-builder $PROJECTS"
|
||||
export PROJECTS="openstack/tripleo-image-elements $PROJECTS"
|
||||
export PROJECTS="openstack/neutron-lbaas $PROJECTS"
|
||||
export PROJECTS="openstack/octavia-lib $PROJECTS"
|
||||
export PROJECTS="openstack/octavia $PROJECTS"
|
||||
|
||||
if ! [[ "$ZUUL_BRANCH" =~ "stable/newton"|"stable/ocata" ]]; then
|
||||
|
@ -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.
|
@ -44,6 +44,8 @@ castellan>=0.16.0 # Apache-2.0
|
||||
tenacity>=4.9.0 # Apache-2.0
|
||||
distro>=1.2.0 # Apache-2.0
|
||||
jsonschema>=2.6.0 # MIT
|
||||
debtcollector>=1.19.0 # Apache-2.0
|
||||
octavia-lib>=1.1.1 # Apache-2.0
|
||||
|
||||
#for the amphora api
|
||||
Flask!=0.11,>=0.10 # BSD
|
||||
|
@ -49,6 +49,7 @@ console_scripts =
|
||||
octavia-health-manager = octavia.cmd.health_manager:main
|
||||
octavia-housekeeping = octavia.cmd.house_keeping:main
|
||||
octavia-db-manage = octavia.db.migration.cli:main
|
||||
octavia-driver-agent = octavia.cmd.driver_agent:main
|
||||
amphora-agent = octavia.cmd.agent:main
|
||||
haproxy-vrrp-check = octavia.cmd.haproxy_vrrp_check:main
|
||||
octavia-status = octavia.cmd.status:main
|
||||
|
@ -8,6 +8,7 @@
|
||||
- openstack/diskimage-builder
|
||||
- openstack/neutron-lbaas
|
||||
- openstack/octavia
|
||||
- openstack/octavia-lib
|
||||
- openstack/python-barbicanclient
|
||||
- openstack/python-octaviaclient
|
||||
- openstack/tripleo-image-elements
|
||||
@ -78,5 +79,6 @@
|
||||
- openstack-dev/grenade
|
||||
- openstack-infra/devstack-gate
|
||||
- openstack/octavia
|
||||
- openstack/octavia-lib
|
||||
- openstack/octavia-tempest-plugin
|
||||
- openstack/python-octaviaclient
|
||||
|
Loading…
Reference in New Issue
Block a user