Merge "Remove all usage of six library"
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 54fca18..2a94e8b 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -3,11 +3,11 @@
# process, which may cause wedges in the gate later.
sphinxcontrib-apidoc>=0.2.0 # BSD
-sphinx!=1.6.6,!=1.6.7,>=1.6.2,!=2.1.0; # BSD
-openstackdocstheme>=1.18.1 # Apache-2.0
+sphinx>=2.0.0,!=2.1.0 # BSD
+openstackdocstheme>=2.2.1 # Apache-2.0
# releasenotes
-reno>=2.5.0 # Apache-2.0
+reno>=3.1.0 # Apache-2.0
# PDF Docs
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 5bbe295..ff6a826 100755
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -16,8 +16,6 @@
import os
import sys
-import openstackdocstheme
-
sys.path.insert(0, os.path.abspath('../..'))
sys.path.insert(0, os.path.abspath('.'))
@@ -49,16 +47,6 @@
project = u'octavia-tempest-plugin'
copyright = u'2017-2019, OpenStack Foundation'
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# Version info
-from octavia_tempest_plugin.version import version_info as octavia_tempest_ver
-release = octavia_tempest_ver.release_string()
-# The short X.Y version.
-version = octavia_tempest_ver.version_string()
-
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
@@ -67,14 +55,14 @@
add_module_names = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'native'
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['octavia_tempest_plugin.']
-repository_name = 'openstack/octavia-tempest-plugin'
-bug_project = '910'
-bug_tag = 'docs'
+openstackdocs_repo_name = 'openstack/octavia-tempest-plugin'
+openstackdocs_pdf_link = True
+openstackdocs_use_storyboard = True
apidoc_output_dir = '_build/modules'
apidoc_module_dir = '../../octavia_tempest_plugin'
@@ -90,7 +78,6 @@
html_theme = 'openstackdocs'
-html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
diff --git a/octavia_tempest_plugin/common/constants.py b/octavia_tempest_plugin/common/constants.py
index 0424cc5..16d6cdf 100644
--- a/octavia_tempest_plugin/common/constants.py
+++ b/octavia_tempest_plugin/common/constants.py
@@ -14,6 +14,7 @@
# API field names
ACTIVE_CONNECTIONS = 'active_connections'
+ALLOWED_CIDRS = 'allowed_cidrs'
AVAILABILITY_ZONE = 'availability_zone'
AVAILABILITY_ZONE_DATA = 'availability_zone_data'
AVAILABILITY_ZONE_PROFILE_ID = 'availability_zone_profile_id'
@@ -56,6 +57,7 @@
INSERT_HEADERS = 'insert_headers'
X_FORWARDED_FOR = 'X-Forwarded-For'
X_FORWARDED_PORT = 'X-Forwarded-Port'
+TAGS = 'tags'
TIMEOUT_CLIENT_DATA = 'timeout_client_data'
TIMEOUT_MEMBER_CONNECT = 'timeout_member_connect'
TIMEOUT_MEMBER_DATA = 'timeout_member_data'
diff --git a/octavia_tempest_plugin/common/decorators.py b/octavia_tempest_plugin/common/decorators.py
new file mode 100644
index 0000000..b484497
--- /dev/null
+++ b/octavia_tempest_plugin/common/decorators.py
@@ -0,0 +1,54 @@
+# Copyright 2020 Red Hat, Inc. All rights reserved.
+#
+# 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 wraps
+
+import testtools
+
+from oslo_utils import excutils
+from tempest import config
+from tempest.lib import exceptions
+
+CONF = config.CONF
+
+
+def skip_if_not_implemented(f):
+ """A decorator to raise a skip exception for not implemented features.
+
+ This decorator raises a skipException if the method raises a
+ NotImplemented exception. If "skip_if_not_implemented=False"
+ argument was passed to the method, the NotImplemented exception will
+ be raised.
+
+ @param skip_if_not_implemented: If True (default), raise skipException.
+ """
+ @wraps(f)
+ def wrapper(*func_args, **func_kwargs):
+
+ skip = func_kwargs.pop('skip_if_not_implemented', True)
+ if CONF.loadbalancer_feature_enabled.not_implemented_is_error:
+ skip = False
+ try:
+ return f(*func_args, **func_kwargs)
+ except exceptions.NotImplemented as e:
+ with excutils.save_and_reraise_exception():
+ if not skip:
+ raise
+ message = ("The configured provider driver '{driver}' "
+ "does not support a feature required for this "
+ "test.".format(
+ driver=CONF.load_balancer.provider))
+ if hasattr(e, 'resp_body'):
+ message = e.resp_body.get('faultstring', message)
+ raise testtools.TestCase.skipException(message)
+ return wrapper
diff --git a/octavia_tempest_plugin/config.py b/octavia_tempest_plugin/config.py
index 8573d89..601c6c8 100644
--- a/octavia_tempest_plugin/config.py
+++ b/octavia_tempest_plugin/config.py
@@ -195,14 +195,35 @@
cfg.StrOpt('availability_zone',
default=None,
help='Availability zone to use for creating servers.'),
+ cfg.StrOpt('availability_zone2',
+ default=None,
+ help='A second availability zone to use for creating servers.'),
+ cfg.StrOpt('availability_zone3',
+ default=None,
+ help='A third availability zone to use for creating servers.'),
cfg.BoolOpt('test_reuse_connection', default=True,
help='Reuse TCP connections while testing LB with '
'HTTP members (keep-alive).'),
+ # Log offloading specific options
+ cfg.StrOpt('tenant_flow_log_file',
+ default='/var/log/octavia-tenant-traffic.log',
+ help='File path, on the tempest system, to the tenant flow '
+ 'log file.'),
+ cfg.StrOpt('amphora_admin_log_file',
+ default='/var/log/octavia-amphora.log',
+ help='File path, on the tempest system, to the amphora admin '
+ 'log file.'),
]
lb_feature_enabled_group = cfg.OptGroup(name='loadbalancer-feature-enabled',
title='Enabled/Disabled LB features')
LBFeatureEnabledGroup = [
+ cfg.BoolOpt('not_implemented_is_error',
+ default=True,
+ help="When True, not-implemented responses from the API are "
+ "considered an error and test failure. This should be "
+ "used when a driver should support all of the Octavia "
+ "API features, such as the reference driver."),
cfg.BoolOpt('health_monitor_enabled',
default=True,
help="Whether Health Monitor is available with provider "
@@ -231,4 +252,8 @@
default=True,
help="Whether session persistence is supported with the "
"provider driver."),
+ cfg.BoolOpt('log_offload_enabled', default=False,
+ help="Whether the log offload tests will run. These require "
+ "the tempest instance have access to the log files "
+ "specified in the tempest configuration."),
]
diff --git a/octavia_tempest_plugin/contrib/test_server/README.rst b/octavia_tempest_plugin/contrib/test_server/README.rst
index da719b7..ba959f9 100644
--- a/octavia_tempest_plugin/contrib/test_server/README.rst
+++ b/octavia_tempest_plugin/contrib/test_server/README.rst
@@ -2,8 +2,8 @@
Amphorae test server
====================
-test_server is a static application that simulates an HTTP and a UDP server.
-
+test_server.bin is a static application that simulates HTTP, HTTPS, and UDP
+servers. This server can properly handle concurrent requests.
Building
--------
@@ -12,15 +12,55 @@
Install dependencies for Ubuntu/Debian:
+::
+
sudo apt-get install -y golang
Install dependencies for Centos (use golang 1.10 from go-toolset-7) and launch
a shell into the new environment:
+::
+
sudo yum install -y centos-release-scl
sudo yum install -y go-toolset-7-golang-bin glibc-static openssl-static zlib-static
scl enable go-toolset-7 bash
Build the binary:
+::
+
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w -extldflags -static' -o test_server.bin test_server.go
+
+
+Usage
+-----
+
+The usage string can be output from the command by running:
+
+::
+
+ ./test_server.bin --help
+
+Example output:
+
+::
+
+ Usage of ./test_server.bin:
+ -cert string
+ Server side PEM format certificate.
+ -client_ca string
+ Client side PEM format CA certificate.
+ -https_port int
+ HTTPS port to listen on, -1 is disabled. (default -1)
+ -id string
+ Server ID (default "1")
+ -key string
+ Server side PEM format key.
+ -port int
+ Port to listen on (default 8080)
+
+If -https_port is not specified, the server will not accept HTTPS requests.
+When --https_port is specified, -cert and -key are required parameters.
+If -https_port is specified, the -client_ca parameter is optional. When
+-client_ca is specified, it will configure the HTTPS port to require a valid
+client certificate to connect.
diff --git a/octavia_tempest_plugin/contrib/test_server/test_server.bin b/octavia_tempest_plugin/contrib/test_server/test_server.bin
index e3cc7ba..75ec2f2 100755
--- a/octavia_tempest_plugin/contrib/test_server/test_server.bin
+++ b/octavia_tempest_plugin/contrib/test_server/test_server.bin
Binary files differ
diff --git a/octavia_tempest_plugin/contrib/test_server/test_server.go b/octavia_tempest_plugin/contrib/test_server/test_server.go
index 8139580..f8bc1e0 100644
--- a/octavia_tempest_plugin/contrib/test_server/test_server.go
+++ b/octavia_tempest_plugin/contrib/test_server/test_server.go
@@ -1,11 +1,17 @@
package main
import (
+ "crypto/rand"
+ "crypto/tls"
+ "crypto/x509"
"flag"
"fmt"
"io"
+ "io/ioutil"
+ "log"
"net"
"net/http"
+ "os"
"sync"
"time"
)
@@ -83,13 +89,23 @@
fmt.Fprintf(w, "max_conn=%d\ntotal_conn=%d\n", max_conn, total_conn)
}
+func https_wrapper(base_handler func(http.ResponseWriter,
+ *http.Request)) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+ w.Header().Add("Strict-Transport-Security",
+ "max-age=66012000; includeSubDomains")
+ base_handler(w, r)
+ })
+}
+
func reset_handler(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &sess_cookie)
scoreboard.reset()
fmt.Fprintf(w, "reset\n")
}
-func http_serve(port int, id string) {
+func http_setup(id string) {
sess_cookie.Name = "JSESSIONID"
sess_cookie.Value = id
@@ -97,8 +113,65 @@
http.HandleFunc("/slow", slow_handler)
http.HandleFunc("/stats", stats_handler)
http.HandleFunc("/reset", reset_handler)
+}
+
+func http_serve(port int, id string) {
portStr := fmt.Sprintf(":%d", port)
- http.ListenAndServe(portStr, nil)
+ log.Fatal(http.ListenAndServe(portStr, nil))
+}
+
+func https_serve(port int, id string, cert tls.Certificate,
+ certpool *x509.CertPool, server_cert_pem string,
+ server_key_pem string) {
+ mux := http.NewServeMux()
+ mux.Handle("/", https_wrapper(root_handler))
+ mux.Handle("/slow", https_wrapper(slow_handler))
+ mux.Handle("/stats", https_wrapper(stats_handler))
+ mux.Handle("/reset", https_wrapper(reset_handler))
+
+ var tls_config *tls.Config
+ if certpool != nil {
+ tls_config = &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ ClientCAs: certpool,
+ MinVersion: tls.VersionTLS12,
+ CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384,
+ tls.CurveP256},
+ PreferServerCipherSuites: true,
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ }
+ } else {
+ tls_config = &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ ClientAuth: tls.NoClientCert,
+ MinVersion: tls.VersionTLS12,
+ CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384,
+ tls.CurveP256},
+ PreferServerCipherSuites: true,
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ }
+ }
+ tls_config.Rand = rand.Reader
+ portStr := fmt.Sprintf(":%d", port)
+ srv := &http.Server{
+ Addr: portStr,
+ Handler: mux,
+ TLSConfig: tls_config,
+ TLSNextProto: make(map[string]func(*http.Server, *tls.Conn,
+ http.Handler), 0),
+ }
+ log.Fatal(srv.ListenAndServeTLS(server_cert_pem, server_key_pem))
}
func udp_serve(port int, id string) {
@@ -129,11 +202,44 @@
func main() {
portPtr := flag.Int("port", 8080, "Port to listen on")
idPtr := flag.String("id", "1", "Server ID")
+ https_portPtr := flag.Int("https_port", -1,
+ "HTTPS port to listen on, -1 is disabled.")
+ server_cert_pem := flag.String("cert", "",
+ "Server side PEM format certificate.")
+ server_key := flag.String("key", "", "Server side PEM format key.")
+ client_ca_cert_pem := flag.String("client_ca", "",
+ "Client side PEM format CA certificate.")
flag.Parse()
resp = fmt.Sprintf("%s", *idPtr)
+ http_setup(*idPtr)
+
+ if *https_portPtr > -1 {
+ cert, err := tls.LoadX509KeyPair(*server_cert_pem, *server_key)
+ if err != nil {
+ fmt.Println("Error load server certificate and key.\n")
+ os.Exit(1)
+ }
+ certpool := x509.NewCertPool()
+ if *client_ca_cert_pem != "" {
+ ca_pem, err := ioutil.ReadFile(*client_ca_cert_pem)
+ if err != nil {
+ fmt.Println("Error load client side CA cert.\n")
+ os.Exit(1)
+ }
+ if !certpool.AppendCertsFromPEM(ca_pem) {
+ fmt.Println("Can't parse client side certificate authority")
+ os.Exit(1)
+ }
+ } else {
+ certpool = nil
+ }
+ go https_serve(*https_portPtr, *idPtr, cert, certpool,
+ *server_cert_pem, *server_key)
+ }
+
go http_serve(*portPtr, *idPtr)
udp_serve(*portPtr, *idPtr)
}
diff --git a/octavia_tempest_plugin/hacking/__init__.py b/octavia_tempest_plugin/hacking/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/octavia_tempest_plugin/hacking/__init__.py
diff --git a/octavia_tempest_plugin/hacking/checks.py b/octavia_tempest_plugin/hacking/checks.py
new file mode 100644
index 0000000..eec7476
--- /dev/null
+++ b/octavia_tempest_plugin/hacking/checks.py
@@ -0,0 +1,277 @@
+# Copyright (c) 2014 OpenStack Foundation.
+#
+# 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.
+
+
+"""
+Guidelines for writing new hacking checks
+
+ - Use only for Octavia specific tests. OpenStack general tests
+ should be submitted to the common 'hacking' module.
+ - Pick numbers in the range O3xx. Find the current test with
+ the highest allocated number and then pick the next value.
+ - Keep the test method code in the source file ordered based
+ on the O3xx value.
+ - List the new rule in the top level HACKING.rst file
+ - Add test cases for each new rule to
+ octavia/tests/unit/test_hacking.py
+
+"""
+
+import re
+
+from hacking import core
+
+
+_all_log_levels = {'critical', 'error', 'exception', 'info', 'warning'}
+_all_hints = {'_LC', '_LE', '_LI', '_', '_LW'}
+
+_log_translation_hint = re.compile(
+ r".*LOG\.(%(levels)s)\(\s*(%(hints)s)\(" % {
+ 'levels': '|'.join(_all_log_levels),
+ 'hints': '|'.join(_all_hints),
+ })
+
+assert_trueinst_re = re.compile(
+ r"(.)*assertTrue\(isinstance\((\w|\.|\'|\"|\[|\])+, "
+ r"(\w|\.|\'|\"|\[|\])+\)\)")
+assert_equal_in_end_with_true_or_false_re = re.compile(
+ r"assertEqual\((\w|[][.'\"])+ in (\w|[][.'\", ])+, (True|False)\)")
+assert_equal_in_start_with_true_or_false_re = re.compile(
+ r"assertEqual\((True|False), (\w|[][.'\"])+ in (\w|[][.'\", ])+\)")
+assert_equal_with_true_re = re.compile(
+ r"assertEqual\(True,")
+assert_equal_with_false_re = re.compile(
+ r"assertEqual\(False,")
+mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
+assert_equal_end_with_none_re = re.compile(r"(.)*assertEqual\(.+, None\)")
+assert_equal_start_with_none_re = re.compile(r".*assertEqual\(None, .+\)")
+assert_not_equal_end_with_none_re = re.compile(
+ r"(.)*assertNotEqual\(.+, None\)")
+assert_not_equal_start_with_none_re = re.compile(
+ r"(.)*assertNotEqual\(None, .+\)")
+revert_must_have_kwargs_re = re.compile(
+ r'[ ]*def revert\(.+,[ ](?!\*\*kwargs)\w+\):')
+untranslated_exception_re = re.compile(r"raise (?:\w*)\((.*)\)")
+no_eventlet_re = re.compile(r'(import|from)\s+[(]?eventlet')
+no_line_continuation_backslash_re = re.compile(r'.*(\\)\n')
+no_logging_re = re.compile(r'(import|from)\s+[(]?logging')
+import_mock_re = re.compile(r"\bimport[\s]+mock\b")
+import_from_mock_re = re.compile(r"\bfrom[\s]+mock[\s]+import\b")
+
+
+def _translation_checks_not_enforced(filename):
+ # Do not do these validations on tests
+ return any(pat in filename for pat in ["/tests/", "rally-jobs/plugins/"])
+
+
+@core.flake8ext
+def assert_true_instance(logical_line):
+ """Check for assertTrue(isinstance(a, b)) sentences
+
+ O316
+ """
+ if assert_trueinst_re.match(logical_line):
+ yield (0, "O316: assertTrue(isinstance(a, b)) sentences not allowed. "
+ "Use assertIsInstance instead.")
+
+
+@core.flake8ext
+def assert_equal_or_not_none(logical_line):
+ """Check for assertEqual(A, None) or assertEqual(None, A) sentences,
+
+ assertNotEqual(A, None) or assertNotEqual(None, A) sentences
+
+ O318
+ """
+ msg = ("O318: assertEqual/assertNotEqual(A, None) or "
+ "assertEqual/assertNotEqual(None, A) sentences not allowed")
+ res = (assert_equal_start_with_none_re.match(logical_line) or
+ assert_equal_end_with_none_re.match(logical_line) or
+ assert_not_equal_start_with_none_re.match(logical_line) or
+ assert_not_equal_end_with_none_re.match(logical_line))
+ if res:
+ yield (0, msg)
+
+
+@core.flake8ext
+def assert_equal_true_or_false(logical_line):
+ """Check for assertEqual(True, A) or assertEqual(False, A) sentences
+
+ O323
+ """
+ res = (assert_equal_with_true_re.search(logical_line) or
+ assert_equal_with_false_re.search(logical_line))
+ if res:
+ yield (0, "O323: assertEqual(True, A) or assertEqual(False, A) "
+ "sentences not allowed")
+
+
+@core.flake8ext
+def no_mutable_default_args(logical_line):
+ msg = "O324: Method's default argument shouldn't be mutable!"
+ if mutable_default_args.match(logical_line):
+ yield (0, msg)
+
+
+@core.flake8ext
+def assert_equal_in(logical_line):
+ """Check for assertEqual(A in B, True), assertEqual(True, A in B),
+
+ assertEqual(A in B, False) or assertEqual(False, A in B) sentences
+
+ O338
+ """
+ res = (assert_equal_in_start_with_true_or_false_re.search(logical_line) or
+ assert_equal_in_end_with_true_or_false_re.search(logical_line))
+ if res:
+ yield (0, "O338: Use assertIn/NotIn(A, B) rather than "
+ "assertEqual(A in B, True/False) when checking collection "
+ "contents.")
+
+
+@core.flake8ext
+def no_log_warn(logical_line):
+ """Disallow 'LOG.warn('
+
+ O339
+ """
+ if logical_line.startswith('LOG.warn('):
+ yield(0, "O339:Use LOG.warning() rather than LOG.warn()")
+
+
+@core.flake8ext
+def no_translate_logs(logical_line, filename):
+ """O341 - Don't translate logs.
+
+ Check for 'LOG.*(_(' and 'LOG.*(_Lx('
+
+ Translators don't provide translations for log messages, and operators
+ asked not to translate them.
+
+ * This check assumes that 'LOG' is a logger.
+
+ :param logical_line: The logical line to check.
+ :param filename: The file name where the logical line exists.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if _translation_checks_not_enforced(filename):
+ return
+
+ msg = "O341: Log messages should not be translated!"
+ match = _log_translation_hint.match(logical_line)
+ if match:
+ yield (logical_line.index(match.group()), msg)
+
+
+@core.flake8ext
+def check_raised_localized_exceptions(logical_line, filename):
+ """O342 - Untranslated exception message.
+
+ :param logical_line: The logical line to check.
+ :param filename: The file name where the logical line exists.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if _translation_checks_not_enforced(filename):
+ return
+
+ logical_line = logical_line.strip()
+ raised_search = untranslated_exception_re.match(logical_line)
+ if raised_search:
+ exception_msg = raised_search.groups()[0]
+ if exception_msg.startswith("\"") or exception_msg.startswith("\'"):
+ msg = "O342: Untranslated exception message."
+ yield (logical_line.index(exception_msg), msg)
+
+
+@core.flake8ext
+def check_no_eventlet_imports(logical_line):
+ """O345 - Usage of Python eventlet module not allowed.
+
+ :param logical_line: The logical line to check.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if no_eventlet_re.match(logical_line):
+ msg = 'O345 Usage of Python eventlet module not allowed'
+ yield logical_line.index('eventlet'), msg
+
+
+@core.flake8ext
+def check_line_continuation_no_backslash(logical_line, tokens):
+ """O346 - Don't use backslashes for line continuation.
+
+ :param logical_line: The logical line to check. Not actually used.
+ :param tokens: List of tokens to check.
+ :returns: None if the tokens don't contain any issues, otherwise a tuple
+ is yielded that contains the offending index in the logical
+ line and a message describe the check validation failure.
+ """
+ backslash = None
+ for token_type, text, start, end, orig_line in tokens:
+ m = no_line_continuation_backslash_re.match(orig_line)
+ if m:
+ backslash = (start[0], m.start(1))
+ break
+
+ if backslash is not None:
+ msg = 'O346 Backslash line continuations not allowed'
+ yield backslash, msg
+
+
+@core.flake8ext
+def revert_must_have_kwargs(logical_line):
+ """O347 - Taskflow revert methods must have \\*\\*kwargs.
+
+ :param logical_line: The logical line to check.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if revert_must_have_kwargs_re.match(logical_line):
+ msg = 'O347 Taskflow revert methods must have **kwargs'
+ yield 0, msg
+
+
+@core.flake8ext
+def check_no_logging_imports(logical_line):
+ """O348 - Usage of Python logging module not allowed.
+
+ :param logical_line: The logical line to check.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if no_logging_re.match(logical_line):
+ msg = 'O348 Usage of Python logging module not allowed, use oslo_log'
+ yield logical_line.index('logging'), msg
+
+
+@core.flake8ext
+def check_no_import_mock(logical_line):
+ """O349 - Test code must not import mock library.
+
+ :param logical_line: The logical line to check.
+ :returns: None if the logical line passes the check, otherwise a tuple
+ is yielded that contains the offending index in logical line
+ and a message describe the check validation failure.
+ """
+ if (import_mock_re.match(logical_line) or
+ import_from_mock_re.match(logical_line)):
+ msg = 'O349 Test code must not import mock library, use unittest.mock'
+ yield 0, msg
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/__init__.py b/octavia_tempest_plugin/services/load_balancer/v2/__init__.py
index d31d6cf..04cb473 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/__init__.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/__init__.py
@@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from octavia_tempest_plugin.services.load_balancer.v2.loadbalancer_client \
- import LoadbalancerClient
+from .loadbalancer_client import LoadbalancerClient
__all__ = ['LoadbalancerClient']
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/amphora_client.py b/octavia_tempest_plugin/services/load_balancer/v2/amphora_client.py
index 4094515..aed93b4 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/amphora_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/amphora_client.py
@@ -16,6 +16,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -28,6 +29,7 @@
stats_root_tag = 'amphora_stats'
base_uri = '/v2.0/octavia/{object}'
+ @skip_if_not_implemented
def show_amphora(self, amphora_id, query_params=None,
return_object_only=True):
"""Get amphora details.
@@ -69,6 +71,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_amphorae(self, query_params=None, return_object_only=True):
"""Get a list of amphora objects.
@@ -107,6 +110,7 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def get_amphora_stats(self, amphora_id, query_params=None,
return_object_only=True):
"""Get amphora statistics.
@@ -158,6 +162,7 @@
else:
return jsonutils.loads(body.decode('utf-8'))
+ @skip_if_not_implemented
def update_amphora_config(self, amphora_id):
"""Update the amphora agent configuration.
@@ -193,6 +198,7 @@
response, body = self.put(uri, '')
self.expected_success(202, response.status)
+ @skip_if_not_implemented
def amphora_failover(self, amphora_id):
"""Failover an amphora.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_capabilities_client.py b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_capabilities_client.py
index 92696a7..679af74 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_capabilities_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_capabilities_client.py
@@ -14,6 +14,7 @@
# under the License.
#
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
from octavia_tempest_plugin.services.load_balancer.v2 import provider_client
@@ -36,6 +37,7 @@
object=self.list_root_tag
)
+ @skip_if_not_implemented
def list_availability_zone_capabilities(self, provider, query_params=None,
return_object_only=True):
"""Get a list of provider availability zone capability objects.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_client.py b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_client.py
index c729f21..ab87a85 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_client.py
@@ -16,6 +16,7 @@
from oslo_log import log as logging
from tempest.lib import exceptions
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
LOG = logging.getLogger(__name__)
@@ -33,6 +34,7 @@
super(AvailabilityZoneClient, self).__init__(*args, **kwargs)
self.uri = self.base_uri.format(object=self.resource_path)
+ @skip_if_not_implemented
def create_availability_zone(self, name, availability_zone_profile_id,
description=Unset, enabled=Unset,
return_object_only=True):
@@ -75,6 +77,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_availability_zone(self, availability_zone_name, query_params=None,
return_object_only=True):
"""Get the availability zone details.
@@ -116,6 +119,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_availability_zones(self, query_params=None,
return_object_only=True):
"""Get a list of availability zone objects.
@@ -155,6 +159,7 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_availability_zone(self, availability_zone_name,
description=Unset, enabled=Unset,
return_object_only=True):
@@ -195,6 +200,7 @@
kwargs['obj_id'] = kwargs.pop('availability_zone_name')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_availability_zone(self, availability_zone_name,
ignore_errors=False):
"""Delete an availability zone.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_profile_client.py b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_profile_client.py
index 1aaab90..071b15f 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_profile_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/availability_zone_profile_client.py
@@ -16,6 +16,7 @@
from oslo_log import log as logging
from tempest.lib import exceptions
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
LOG = logging.getLogger(__name__)
@@ -33,6 +34,7 @@
super(AvailabilityZoneProfileClient, self).__init__(*args, **kwargs)
self.uri = self.base_uri.format(object=self.resource_path)
+ @skip_if_not_implemented
def create_availability_zone_profile(self, name, provider_name,
availability_zone_data,
return_object_only=True):
@@ -73,6 +75,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_availability_zone_profile(self, availability_zone_profile_id,
query_params=None,
return_object_only=True):
@@ -116,6 +119,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_availability_zone_profiles(self, query_params=None,
return_object_only=True):
"""Get a list of availability zone profile objects.
@@ -155,6 +159,7 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_availability_zone_profile(
self, availability_zone_profile_id, name=Unset,
provider_name=Unset, availability_zone_data=Unset,
@@ -199,6 +204,7 @@
kwargs['obj_id'] = kwargs.pop('availability_zone_profile_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_availability_zone_profile(self, availability_zone_profile_id,
ignore_errors=False):
"""Delete an availability zone profile.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/flavor_capabilities_client.py b/octavia_tempest_plugin/services/load_balancer/v2/flavor_capabilities_client.py
index 4c23042..eb07faf 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/flavor_capabilities_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/flavor_capabilities_client.py
@@ -13,6 +13,7 @@
# under the License.
#
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
from octavia_tempest_plugin.services.load_balancer.v2 import provider_client
@@ -34,6 +35,7 @@
object=self.list_root_tag
)
+ @skip_if_not_implemented
def list_flavor_capabilities(self, provider, query_params=None,
return_object_only=True):
"""Get a list of provider flavor capability objects.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/flavor_client.py b/octavia_tempest_plugin/services/load_balancer/v2/flavor_client.py
index 085da9e..8a87a33 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/flavor_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/flavor_client.py
@@ -16,6 +16,7 @@
from oslo_log import log as logging
from tempest.lib import exceptions
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
LOG = logging.getLogger(__name__)
@@ -27,6 +28,7 @@
root_tag = 'flavor'
list_root_tag = 'flavors'
+ @skip_if_not_implemented
def create_flavor(self, name, flavor_profile_id, description=Unset,
enabled=Unset, return_object_only=True):
"""Create a flavor.
@@ -67,6 +69,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_flavor(self, flavor_id, query_params=None,
return_object_only=True):
"""Get the flavor details.
@@ -108,6 +111,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_flavors(self, query_params=None, return_object_only=True):
"""Get a list of flavor objects.
@@ -146,6 +150,7 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_flavor(self, flavor_id, name=Unset, description=Unset,
enabled=Unset, return_object_only=True):
"""Update a flavor.
@@ -186,6 +191,7 @@
kwargs['obj_id'] = kwargs.pop('flavor_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_flavor(self, flavor_id, ignore_errors=False):
"""Delete a flavor.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/flavor_profile_client.py b/octavia_tempest_plugin/services/load_balancer/v2/flavor_profile_client.py
index 811cff8..bc0f2fb 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/flavor_profile_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/flavor_profile_client.py
@@ -16,6 +16,7 @@
from oslo_log import log as logging
from tempest.lib import exceptions
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
LOG = logging.getLogger(__name__)
@@ -27,6 +28,7 @@
root_tag = 'flavorprofile'
list_root_tag = 'flavorprofiles'
+ @skip_if_not_implemented
def create_flavor_profile(self, name, provider_name, flavor_data,
return_object_only=True):
"""Create a flavor profile.
@@ -65,6 +67,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_flavor_profile(self, flavorprofile_id, query_params=None,
return_object_only=True):
"""Get the flavor profile details.
@@ -106,6 +109,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_flavor_profiles(self, query_params=None, return_object_only=True):
"""Get a list of flavor profile objects.
@@ -144,6 +148,7 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_flavor_profile(
self, flavorprofile_id, name=Unset, provider_name=Unset,
flavor_data=Unset, return_object_only=True):
@@ -185,6 +190,7 @@
kwargs['obj_id'] = kwargs.pop('flavorprofile_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_flavor_profile(self, flavorprofile_id, ignore_errors=False):
"""Delete a flavor profile.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/healthmonitor_client.py b/octavia_tempest_plugin/services/load_balancer/v2/healthmonitor_client.py
index 70dce4c..4ce362e 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/healthmonitor_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/healthmonitor_client.py
@@ -14,6 +14,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -26,8 +27,9 @@
list_root_tag = 'healthmonitors'
resource_name = 'healthmonitor'
+ @skip_if_not_implemented
def create_healthmonitor(self, pool_id, type, delay, timeout, max_retries,
- max_retries_down=Unset, name=Unset,
+ max_retries_down=Unset, name=Unset, tags=Unset,
http_method=Unset, url_path=Unset,
expected_codes=Unset, admin_state_up=Unset,
return_object_only=True):
@@ -44,6 +46,7 @@
changing the operating status of the member to
ERROR.
:param name: Human-readable name of the resource.
+ :param tags: Human-readable tags of the resource.
:param http_method: The HTTP method that the health monitor uses for
requests.
:param url_path: The HTTP URL path of the request sent by the monitor
@@ -86,6 +89,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_healthmonitor(self, healthmonitor_id, query_params=None,
return_object_only=True):
"""Get healthmonitor details.
@@ -127,6 +131,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_healthmonitors(self, query_params=None, return_object_only=True):
"""Get a list of healthmonitor objects.
@@ -165,9 +170,10 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_healthmonitor(self, healthmonitor_id, delay=Unset,
timeout=Unset, max_retries=Unset,
- max_retries_down=Unset, name=Unset,
+ max_retries_down=Unset, name=Unset, tags=Unset,
http_method=Unset, url_path=Unset,
expected_codes=Unset, admin_state_up=Unset,
return_object_only=True):
@@ -183,6 +189,7 @@
changing the operating status of the member to
ERROR.
:param name: Human-readable name of the resource.
+ :param tags: Human-readable tags of the resource.
:param http_method: The HTTP method that the health monitor uses for
requests.
:param url_path: The HTTP URL path of the request sent by the monitor
@@ -226,6 +233,7 @@
kwargs['obj_id'] = kwargs.pop('healthmonitor_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_healthmonitor(self, healthmonitor_id, ignore_errors=False):
"""Delete a healthmonitor.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/l7policy_client.py b/octavia_tempest_plugin/services/load_balancer/v2/l7policy_client.py
index 674ec02..36eef40 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/l7policy_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/l7policy_client.py
@@ -14,6 +14,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -26,8 +27,9 @@
list_root_tag = 'l7policies'
resource_name = 'l7policy'
+ @skip_if_not_implemented
def create_l7policy(self, listener_id, action, name=Unset,
- description=Unset, admin_state_up=Unset,
+ description=Unset, tags=Unset, admin_state_up=Unset,
position=Unset, redirect_pool_id=Unset,
redirect_url=Unset, return_object_only=True):
"""Create a l7policy.
@@ -36,6 +38,7 @@
:param action: The l7policy action.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param position: The position of this policy on the listener.
@@ -77,6 +80,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_l7policy(self, l7policy_id, query_params=None,
return_object_only=True):
"""Get l7policy details.
@@ -118,6 +122,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_l7policies(self, query_params=None, return_object_only=True):
"""Get a list of l7policy objects.
@@ -156,8 +161,9 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_l7policy(self, l7policy_id, action=Unset, name=Unset,
- description=Unset, admin_state_up=Unset,
+ description=Unset, tags=Unset, admin_state_up=Unset,
position=Unset, redirect_pool_id=Unset,
redirect_url=Unset, return_object_only=True):
"""Update a l7policy.
@@ -166,6 +172,7 @@
:param action: The l7policy action.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param position: The position of this policy on the listener.
@@ -208,6 +215,7 @@
kwargs['obj_id'] = kwargs.pop('l7policy_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_l7policy(self, l7policy_id, ignore_errors=False):
"""Delete a l7policy.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/l7rule_client.py b/octavia_tempest_plugin/services/load_balancer/v2/l7rule_client.py
index 2ca1c71..da40af2 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/l7rule_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/l7rule_client.py
@@ -14,6 +14,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
from octavia_tempest_plugin.services.load_balancer.v2 import l7policy_client
@@ -38,7 +39,8 @@
object=self.list_root_tag
)
- def create_l7rule(self, l7policy_id, type, value, compare_type,
+ @skip_if_not_implemented
+ def create_l7rule(self, l7policy_id, type, value, compare_type, tags=Unset,
admin_state_up=Unset, key=Unset, invert=Unset,
return_object_only=True):
"""Create a l7rule.
@@ -47,6 +49,7 @@
:param type: The L7 rule type.
:param value: The value to use for the comparison.
:param compare_type: The comparison type for the L7 rule.
+ :param tags: The tags of the L7 rule.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param key: The key to use for the comparison.
@@ -86,6 +89,7 @@
kwargs['parent_id'] = kwargs.pop('l7policy_id')
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_l7rule(self, l7rule_id, l7policy_id, query_params=None,
return_object_only=True):
"""Get l7rule details.
@@ -129,6 +133,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_l7rules(self, l7policy_id, query_params=None,
return_object_only=True):
"""Get a list of l7rule objects.
@@ -170,9 +175,10 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_l7rule(self, l7rule_id, l7policy_id, type=Unset, value=Unset,
- compare_type=Unset, admin_state_up=Unset, key=Unset,
- invert=Unset, return_object_only=True):
+ compare_type=Unset, tags=Unset, admin_state_up=Unset,
+ key=Unset, invert=Unset, return_object_only=True):
"""Update a l7rule.
:param l7rule_id: The l7rule ID to update.
@@ -180,6 +186,7 @@
:param type: The L7 rule type.
:param value: The value to use for the comparison.
:param compare_type: The comparison type for the L7 rule.
+ :param tags: The tags of the L7 rule.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param key: The key to use for the comparison.
@@ -220,6 +227,7 @@
kwargs['parent_id'] = kwargs.pop('l7policy_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_l7rule(self, l7rule_id, l7policy_id, ignore_errors=False):
"""Delete a l7rule.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py b/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py
index 1cc17ff..a3f5958 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py
@@ -17,6 +17,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -28,16 +29,18 @@
root_tag = 'listener'
list_root_tag = 'listeners'
+ @skip_if_not_implemented
def create_listener(self, protocol, protocol_port, loadbalancer_id,
- name=Unset, description=Unset, admin_state_up=Unset,
- connection_limit=Unset, timeout_client_data=Unset,
+ name=Unset, description=Unset, tags=Unset,
+ admin_state_up=Unset, connection_limit=Unset,
+ timeout_client_data=Unset,
timeout_member_connect=Unset,
timeout_member_data=Unset, timeout_tcp_inspect=Unset,
insert_headers=Unset, default_pool_id=Unset,
default_tls_container_ref=Unset,
sni_container_refs=Unset, client_authentication=Unset,
client_ca_tls_container_ref=Unset,
- client_crl_container_ref=Unset,
+ client_crl_container_ref=Unset, allowed_cidrs=Unset,
return_object_only=True):
"""Create a listener.
@@ -46,6 +49,7 @@
:param loadbalancer_id: The ID of the load balancer.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param connection_limit: The maximum number of connections permitted
@@ -84,6 +88,7 @@
secret containing a PEM format CA
revocation list file for
TERMINATED_HTTPS listeners.
+ :param allowed_cidrs: A list of IPv4 or IPv6 CIDRs.
:param return_object_only: If True, the response returns the object
inside the root tag. False returns the full
response from the API.
@@ -118,6 +123,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_listener(self, listener_id, query_params=None,
return_object_only=True):
"""Get listener details.
@@ -159,6 +165,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_listeners(self, query_params=None, return_object_only=True):
"""Get a list of listener objects.
@@ -197,22 +204,24 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_listener(self, listener_id, name=Unset, description=Unset,
- admin_state_up=Unset, connection_limit=Unset,
- timeout_client_data=Unset,
+ tags=Unset, admin_state_up=Unset,
+ connection_limit=Unset, timeout_client_data=Unset,
timeout_member_connect=Unset,
timeout_member_data=Unset, timeout_tcp_inspect=Unset,
insert_headers=Unset, default_pool_id=Unset,
default_tls_container_ref=Unset,
sni_container_refs=Unset, client_authentication=Unset,
client_ca_tls_container_ref=Unset,
- client_crl_container_ref=Unset,
+ client_crl_container_ref=Unset, allowed_cidrs=Unset,
return_object_only=True):
"""Update a listener.
:param listener_id: The listener ID to update.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param connection_limit: The maximum number of connections permitted
@@ -251,6 +260,7 @@
secret containing a PEM format CA
revocation list file for
TERMINATED_HTTPS listeners.
+ :param allowed_cidrs: A list of IPv4 or IPv6 CIDRs.
:param return_object_only: If True, the response returns the object
inside the root tag. False returns the full
response from the API.
@@ -286,6 +296,7 @@
kwargs['obj_id'] = kwargs.pop('listener_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_listener(self, listener_id, ignore_errors=False):
"""Delete a listener.
@@ -322,6 +333,7 @@
return self._delete_obj(obj_id=listener_id,
ignore_errors=ignore_errors)
+ @skip_if_not_implemented
def get_listener_stats(self, listener_id, query_params=None,
return_object_only=True):
"""Get listener statistics.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/loadbalancer_client.py b/octavia_tempest_plugin/services/load_balancer/v2/loadbalancer_client.py
index 9a287cf..9499d89 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/loadbalancer_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/loadbalancer_client.py
@@ -17,6 +17,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -34,12 +35,14 @@
self.timeout = CONF.load_balancer.lb_build_timeout
self.build_interval = CONF.load_balancer.lb_build_interval
+ @skip_if_not_implemented
def create_loadbalancer(self, name=Unset, description=Unset,
admin_state_up=Unset, flavor_id=Unset,
listeners=Unset, project_id=Unset, provider=Unset,
vip_address=Unset, vip_network_id=Unset,
vip_port_id=Unset, vip_qos_policy_id=Unset,
- vip_subnet_id=Unset, return_object_only=True):
+ vip_subnet_id=Unset, return_object_only=True,
+ tags=Unset):
"""Create a loadbalancer.
:param name: Human-readable name of the resource.
@@ -56,6 +59,7 @@
:param vip_qos_policy_id: The ID of the QoS Policy which will apply to
the Virtual IP (VIP).
:param vip_subnet_id: The ID of the subnet for the Virtual IP (VIP).
+ :param tags: A human-readable tags of the resource.
:param return_object_only: If True, the response returns the object
inside the root tag. False returns the full
response from the API.
@@ -90,6 +94,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_loadbalancer(self, lb_id, query_params=None,
return_object_only=True):
"""Get loadbalancer details.
@@ -131,6 +136,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_loadbalancers(self, query_params=None, return_object_only=True):
"""Get a list of loadbalancer objects.
@@ -169,14 +175,17 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_loadbalancer(self, lb_id, name=Unset, description=Unset,
- admin_state_up=Unset, vip_qos_policy_id=Unset,
+ tags=Unset, admin_state_up=Unset,
+ vip_qos_policy_id=Unset,
return_object_only=True):
"""Update a loadbalancer.
:param lb_id: The loadbalancer ID to update.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param vip_qos_policy_id: The ID of the QoS Policy which will apply to
@@ -216,6 +225,7 @@
kwargs['obj_id'] = kwargs.pop('lb_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_loadbalancer(self, lb_id, cascade=False, ignore_errors=False):
"""Delete a loadbalancer.
@@ -255,6 +265,7 @@
ignore_errors=ignore_errors,
cascade=cascade)
+ @skip_if_not_implemented
def failover_loadbalancer(self, lb_id):
"""Failover a loadbalancer.
@@ -291,6 +302,7 @@
self.expected_success(202, response.status)
return
+ @skip_if_not_implemented
def get_loadbalancer_stats(self, lb_id, query_params=None,
return_object_only=True):
"""Get loadbalancer statistics.
@@ -341,6 +353,7 @@
else:
return jsonutils.loads(body.decode('utf-8'))
+ @skip_if_not_implemented
def get_loadbalancer_status(self, lb_id, query_params=None,
return_object_only=True):
"""Get a loadbalancer status tree.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/member_client.py b/octavia_tempest_plugin/services/load_balancer/v2/member_client.py
index 63e836a..c0d83da 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/member_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/member_client.py
@@ -16,6 +16,7 @@
from oslo_serialization import jsonutils
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
from octavia_tempest_plugin.services.load_balancer.v2 import pool_client
@@ -39,8 +40,10 @@
object=self.list_root_tag
)
+ @skip_if_not_implemented
def create_member(self, pool_id, address, protocol_port,
- name=Unset, admin_state_up=Unset, weight=Unset,
+ name=Unset, tags=Unset, admin_state_up=Unset,
+ weight=Unset,
backup=Unset, subnet_id=Unset, monitor_address=Unset,
monitor_port=Unset, return_object_only=True):
"""Create a member.
@@ -49,12 +52,15 @@
:param address: The IP address of the resource.
:param protocol_port: The protocol port number for the resource.
:param name: Human-readable name of the resource.
+ :param tags: Human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param weight: The weight of a member determines the portion of
requests or connections it services compared to the
other members of the pool.
:param backup: Is the member a backup?
+ :param subnet_id: The subnet ID which the member service
+ is accessible from
:param monitor_address: An alternate IP address used for health
monitoring a backend member.
:param monitor_port: An alternate protocol port used for health
@@ -94,6 +100,7 @@
kwargs['parent_id'] = kwargs.pop('pool_id')
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_member(self, member_id, pool_id, query_params=None,
return_object_only=True):
"""Get member details.
@@ -137,6 +144,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_members(self, pool_id, query_params=None,
return_object_only=True):
"""Get a list of member objects.
@@ -178,7 +186,8 @@
query_params=query_params,
return_object_only=return_object_only)
- def update_member(self, member_id, pool_id, name=Unset,
+ @skip_if_not_implemented
+ def update_member(self, member_id, pool_id, name=Unset, tags=Unset,
admin_state_up=Unset, weight=Unset, backup=Unset,
monitor_address=Unset, monitor_port=Unset,
return_object_only=True):
@@ -187,6 +196,7 @@
:param member_id: The member ID to update.
:param pool_id: The ID of the pool where the member lives.
:param name: Human-readable name of the resource.
+ :param tags: Human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param weight: The weight of a member determines the portion of
@@ -233,6 +243,7 @@
kwargs['parent_id'] = kwargs.pop('pool_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def update_members(self, pool_id, members_list):
"""Batch update all members on a pool.
@@ -272,6 +283,7 @@
self.expected_success(202, response.status)
return
+ @skip_if_not_implemented
def delete_member(self, member_id, pool_id, ignore_errors=False):
"""Delete a member.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py b/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
index 46ec38d..98f4bfa 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
@@ -14,6 +14,7 @@
from tempest import config
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
@@ -26,8 +27,10 @@
list_root_tag = 'pools'
resource_name = 'pool'
+ @skip_if_not_implemented
def create_pool(self, protocol, lb_algorithm, loadbalancer_id=Unset,
listener_id=Unset, name=Unset, description=Unset,
+ tags=Unset,
admin_state_up=Unset, session_persistence=Unset,
return_object_only=True):
"""Create a pool.
@@ -38,6 +41,7 @@
:param listener_id: The ID of the listener for the pool.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param session_persistence: A JSON object specifying the session
@@ -77,6 +81,7 @@
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
+ @skip_if_not_implemented
def show_pool(self, pool_id, query_params=None, return_object_only=True):
"""Get pool details.
@@ -117,6 +122,7 @@
query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def list_pools(self, query_params=None, return_object_only=True):
"""Get a list of pool objects.
@@ -155,8 +161,9 @@
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
+ @skip_if_not_implemented
def update_pool(self, pool_id, lb_algorithm=Unset, name=Unset,
- description=Unset, admin_state_up=Unset,
+ description=Unset, tags=Unset, admin_state_up=Unset,
session_persistence=Unset, return_object_only=True):
"""Update a pool.
@@ -164,6 +171,7 @@
:param lb_algorithm: The load balancing algorithm for the pool.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
+ :param tags: A human-readable tags of the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param session_persistence: A JSON object specifying the session
@@ -204,6 +212,7 @@
kwargs['obj_id'] = kwargs.pop('pool_id')
return self._update_object(**kwargs)
+ @skip_if_not_implemented
def delete_pool(self, pool_id, ignore_errors=False):
"""Delete a pool.
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/provider_client.py b/octavia_tempest_plugin/services/load_balancer/v2/provider_client.py
index cbef1df..319826e 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/provider_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/provider_client.py
@@ -13,6 +13,7 @@
# under the License.
#
+from octavia_tempest_plugin.common.decorators import skip_if_not_implemented
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
Unset = base_client.Unset
@@ -22,6 +23,7 @@
list_root_tag = 'providers'
+ @skip_if_not_implemented
def list_providers(self, query_params=None, return_object_only=True):
"""Get a list of provider objects.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_amphora.py b/octavia_tempest_plugin/tests/api/v2/test_amphora.py
index d1106e6..91241c6 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_amphora.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_amphora.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+import time
from uuid import UUID
from dateutil import parser
@@ -227,3 +229,63 @@
for new_amp in after_amphorae:
self.assertNotEqual(amphora_1[const.ID], new_amp[const.ID])
+
+ @testtools.skipIf(CONF.load_balancer.test_with_noop,
+ 'Log offload tests will not work in noop mode.')
+ @testtools.skipUnless(
+ CONF.loadbalancer_feature_enabled.log_offload_enabled,
+ 'Skipping log offload tests because tempest configuration '
+ '[loadbalancer-feature-enabled] log_offload_enabled is False.')
+ @decorators.idempotent_id('4e3c6fcb-5f83-4da1-8296-56f209eae30d')
+ def test_admin_log(self):
+ """Tests admin log offloading
+
+ * Create a listener
+ * Validates the listener config log message is present
+ """
+ listener_name = data_utils.rand_name("lb_member_listener1_admin_log")
+ protocol_port = '8124'
+ listener_kwargs = {
+ const.NAME: listener_name,
+ const.PROTOCOL: const.HTTP,
+ const.PROTOCOL_PORT: protocol_port,
+ const.LOADBALANCER_ID: self.lb_id,
+ }
+ listener = self.mem_listener_client.create_listener(**listener_kwargs)
+ listener_id = listener[const.ID]
+ self.addCleanup(
+ self.mem_listener_client.cleanup_listener,
+ listener_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ # We need to give the log subsystem time to commit the log
+ time.sleep(CONF.load_balancer.check_interval)
+
+ # Check for an amphora agent API call and code log entry
+ # One is logged via the gunicorn logging and the other via
+ # oslo.logging.
+ agent_found = False
+ client_found = False
+ with open(CONF.load_balancer.amphora_admin_log_file) as f:
+ for line in f:
+ if 'Octavia HaProxy Rest Client' in line:
+ client_found = True
+ if ' amphora-agent: ' in line:
+ agent_found = True
+ if client_found and agent_found:
+ break
+
+ self.assertTrue(
+ client_found,
+ 'Octavia user agent string was not found in: {0}'.format(
+ CONF.load_balancer.amphora_admin_log_file))
+
+ self.assertTrue(
+ agent_found, 'Amphora agent string was not found in: {0}'.format(
+ CONF.load_balancer.amphora_admin_log_file))
diff --git a/octavia_tempest_plugin/tests/api/v2/test_availability_zone.py b/octavia_tempest_plugin/tests/api/v2/test_availability_zone.py
index 023d5f5..29426c2 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_availability_zone.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_availability_zone.py
@@ -32,6 +32,15 @@
"""Test the availability zone object API."""
@classmethod
+ def skip_checks(cls):
+ super(AvailabilityZoneAPITest, cls).skip_checks()
+ if (CONF.load_balancer.availability_zone is None and
+ not CONF.load_balancer.test_with_noop):
+ raise cls.skipException(
+ 'Availability Zone API tests require an availability zone '
+ 'configured in the [load_balancer] availability_zone setting.')
+
+ @classmethod
def resource_setup(cls):
"""Setup resources needed by the tests."""
super(AvailabilityZoneAPITest, cls).resource_setup()
@@ -46,7 +55,7 @@
availability_zone_profile_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile-setup")
availability_zone_data = {
- const.COMPUTE_ZONE: 'my_compute_zone',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
@@ -118,6 +127,15 @@
self.assertEqual(self.availability_zone_profile_id,
availability_zone[const.AVAILABILITY_ZONE_PROFILE_ID])
+ # Test that availability_zones do not support tags
+ availability_zone_tags = ["Hello", "World"]
+ tags_availability_zone_kwargs = availability_zone_kwargs.copy()
+ tags_availability_zone_kwargs[const.TAGS] = availability_zone_tags
+ self.assertRaises(
+ TypeError,
+ self.lb_admin_availability_zone_client.create_availability_zone,
+ **tags_availability_zone_kwargs)
+
@decorators.idempotent_id('bba84c0c-2832-4c4c-90ff-d28acfe4ae36')
def test_availability_zone_list(self):
"""Tests availability zone list API and field filtering.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_availability_zone_profile.py b/octavia_tempest_plugin/tests/api/v2/test_availability_zone_profile.py
index 86ae066..f5e4b7e 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_availability_zone_profile.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_availability_zone_profile.py
@@ -32,6 +32,16 @@
class AvailabilityZoneProfileAPITest(test_base.LoadBalancerBaseTest):
"""Test the availability zone profile object API."""
+ @classmethod
+ def skip_checks(cls):
+ super(AvailabilityZoneProfileAPITest, cls).skip_checks()
+ if (CONF.load_balancer.availability_zone is None and
+ not CONF.load_balancer.test_with_noop):
+ raise cls.skipException(
+ 'Availability zone profile API tests require an availability '
+ 'zone configured in the [load_balancer] availability_zone '
+ 'setting in the tempest configuration file.')
+
@decorators.idempotent_id('e512b580-ef32-44c3-bbd2-efdc27ba2ea6')
def test_availability_zone_profile_create(self):
"""Tests availability zone profile create and basic show APIs.
@@ -53,7 +63,7 @@
availability_zone_profile_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile1-create")
availability_zone_data = {
- const.COMPUTE_ZONE: 'my_compute_zone',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
@@ -94,6 +104,17 @@
availability_zone_data_json,
availability_zone_profile[const.AVAILABILITY_ZONE_DATA])
+ # Testing that availability_zone_profiles do not support tags
+ availability_zone_profile_tags = ["Hello", "World"]
+ tags_availability_zone_profile_kwargs = (
+ availability_zone_profile_kwargs.copy())
+ tags_availability_zone_profile_kwargs[const.TAGS] = (
+ availability_zone_profile_tags)
+ az_profile_client = self.lb_admin_availability_zone_profile_client
+ self.assertRaises(TypeError,
+ az_profile_client.create_availability_zone_profile,
+ **tags_availability_zone_profile_kwargs)
+
@decorators.idempotent_id('ef7d1c45-e312-46ce-8dcb-f2fe26295658')
def test_availability_zone_profile_list(self):
"""Tests availability zone profile list API and field filtering.
@@ -116,12 +137,20 @@
raise self.skipException(
'Availability zone profiles are only available on '
'Octavia API version 2.14 or newer.')
+ if ((CONF.load_balancer.availability_zone2 is None or
+ CONF.load_balancer.availability_zone3 is None) and
+ not CONF.load_balancer.test_with_noop):
+ raise self.skipException(
+ 'Availability zone profile list API test requires the '
+ '[load_balancer] availability_zone, availability_zone2, and '
+ 'availability_zone3 settings be defined in the tempest '
+ 'configuration file.')
# Create availability zone profile 1
availability_zone_profile1_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile-list-1")
availability_zone_data1 = {
- const.COMPUTE_ZONE: 'my_compute_zone1',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data1_json = jsonutils.dumps(availability_zone_data1)
@@ -144,7 +173,7 @@
availability_zone_profile2_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile-list-2")
availability_zone_data2 = {
- const.COMPUTE_ZONE: 'my_compute_zone2',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone2,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data2_json = jsonutils.dumps(availability_zone_data2)
@@ -167,7 +196,7 @@
availability_zone_profile3_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile-list-3")
availability_zone_data3 = {
- const.COMPUTE_ZONE: 'my_compute_zone3',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone3,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data3_json = jsonutils.dumps(availability_zone_data3)
@@ -329,7 +358,7 @@
availability_zone_profile_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile1-show")
availability_zone_data = {
- const.COMPUTE_ZONE: 'my_compute_zone',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
@@ -390,11 +419,17 @@
raise self.skipException(
'Availability zone profiles are only available on '
'Octavia API version 2.14 or newer.')
+ if (CONF.load_balancer.availability_zone2 is None and
+ not CONF.load_balancer.test_with_noop):
+ raise self.skipException(
+ 'Availability zone profile update API tests requires '
+ '[load_balancer] availability_zone2 to be defined in the '
+ 'tempest configuration file.')
availability_zone_profile_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile1-update")
availability_zone_data = {
- const.COMPUTE_ZONE: 'my_compute_zone1',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
@@ -427,7 +462,7 @@
availability_zone_profile_name2 = data_utils.rand_name(
"lb_admin_availabilityzoneprofile1-update2")
availability_zone_data2 = {
- const.COMPUTE_ZONE: 'my_compute_zone2',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone2,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data2_json = jsonutils.dumps(availability_zone_data2)
@@ -495,7 +530,7 @@
availability_zone_profile_name = data_utils.rand_name(
"lb_admin_availabilityzoneprofile1-delete")
availability_zone_data = {
- const.COMPUTE_ZONE: 'my_compute_zone',
+ const.COMPUTE_ZONE: CONF.load_balancer.availability_zone,
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
}
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
diff --git a/octavia_tempest_plugin/tests/api/v2/test_flavor.py b/octavia_tempest_plugin/tests/api/v2/test_flavor.py
index be3ac76..4333121 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_flavor.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_flavor.py
@@ -105,6 +105,14 @@
self.assertEqual(self.flavor_profile_id,
flavor[const.FLAVOR_PROFILE_ID])
+ # Test that flavors do not support tags
+ flavor_tags = ["Hello", "World"]
+ tags_flavor_kwargs = flavor_kwargs.copy()
+ tags_flavor_kwargs[const.TAGS] = flavor_tags
+ self.assertRaises(TypeError,
+ self.lb_admin_flavor_client.create_flavor,
+ **tags_flavor_kwargs)
+
@decorators.idempotent_id('3ef040ee-fe7e-457b-a56f-8b152f7afa3b')
def test_flavor_list(self):
"""Tests flavor list API and field filtering.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_flavor_profile.py b/octavia_tempest_plugin/tests/api/v2/test_flavor_profile.py
index 3bd6e54..8f9c7d3 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_flavor_profile.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_flavor_profile.py
@@ -80,6 +80,15 @@
flavor_profile[const.PROVIDER_NAME])
self.assertEqual(flavor_data_json, flavor_profile[const.FLAVOR_DATA])
+ # Testing that flavor_profiles do not support tags
+ flavor_profile_tags = ["Hello", "World"]
+ tags_flavor_profile_kwargs = flavor_profile_kwargs.copy()
+ tags_flavor_profile_kwargs[const.TAGS] = flavor_profile_tags
+ self.assertRaises(
+ TypeError,
+ self.lb_admin_flavor_profile_client.create_flavor_profile,
+ **tags_flavor_profile_kwargs)
+
@decorators.idempotent_id('c4e17fdf-849a-4132-93ae-dfca21ce4444')
def test_flavor_profile_list(self):
"""Tests flavor profile list API and field filtering.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_healthmonitor.py b/octavia_tempest_plugin/tests/api/v2/test_healthmonitor.py
index 1feb9d9..5f4be23 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_healthmonitor.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_healthmonitor.py
@@ -107,6 +107,13 @@
const.ADMIN_STATE_UP: True,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ hw_tags = ["Hello", "World"]
+ hm_kwargs.update({
+ const.TAGS: hw_tags
+ })
+
# Test that a user without the loadbalancer role cannot
# create a healthmonitor
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -148,6 +155,10 @@
for item in equal_items:
self.assertEqual(hm_kwargs[item], hm[item])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(hm_kwargs[const.TAGS], hm[const.TAGS])
+
# Helper functions for test healthmonitor list
def _filter_hms_by_pool_id(self, hms, pool_ids):
return [hm for hm in hms
@@ -250,6 +261,13 @@
const.EXPECTED_CODES: '200-204',
const.ADMIN_STATE_UP: True,
}
+
+ if self.mem_healthmonitor_client.is_version_supported(
+ self.api_version, '2.5'):
+ hm1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ hm1_kwargs.update({const.TAGS: hm1_tags})
+
hm1 = self.mem_healthmonitor_client.create_healthmonitor(
**hm1_kwargs)
self.addCleanup(
@@ -286,6 +304,13 @@
const.EXPECTED_CODES: '200-204',
const.ADMIN_STATE_UP: True,
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ hm2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ hm2_kwargs.update({const.TAGS: hm2_tags})
+
hm2 = self.mem_healthmonitor_client.create_healthmonitor(
**hm2_kwargs)
self.addCleanup(
@@ -322,6 +347,13 @@
const.EXPECTED_CODES: '200-204',
const.ADMIN_STATE_UP: False,
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ hm3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ hm3_kwargs.update({const.TAGS: hm3_tags})
+
hm3 = self.mem_healthmonitor_client.create_healthmonitor(
**hm3_kwargs)
self.addCleanup(
@@ -455,6 +487,28 @@
self.assertEqual(hm1[const.NAME],
hms[0][const.NAME])
+ # Creating a list of 3 healthmonitors, each one contains different tags
+ if self.mem_healthmonitor_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_hms = [hm1, hm2, hm3]
+ test_list = []
+ for hm in list_of_hms:
+
+ # If tags "English" and "Creativity" are in the HM's tags
+ # and "Spanish" is not, add the HM to the list
+ if "English" in hm[const.TAGS] and "Creativity" in (
+ hm[const.TAGS]) and "Spanish" not in (
+ hm[const.TAGS]):
+ test_list.append(hm[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [hm1[const.NAME], hm3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in hm[const.TAGS]
+ for hm in list_of_hms]))
+
@decorators.idempotent_id('284e8d3b-7b2d-4697-9e41-580b3423c0b4')
def test_healthmonitor_show(self):
"""Tests healthmonitor show API.
@@ -607,6 +661,13 @@
const.ADMIN_STATE_UP: False,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ hw_tags = ["Hello", "World"]
+ hm_kwargs.update({
+ const.TAGS: hw_tags
+ })
+
hm = self.mem_healthmonitor_client.create_healthmonitor(**hm_kwargs)
self.addCleanup(
self.mem_healthmonitor_client.cleanup_healthmonitor,
@@ -643,6 +704,10 @@
for item in equal_items:
self.assertEqual(hm_kwargs[item], hm[item])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(hm_kwargs[const.TAGS], hm[const.TAGS])
+
# Test that a user, without the loadbalancer member role, cannot
# use this command
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -685,6 +750,14 @@
const.EXPECTED_CODES: '201,202',
const.ADMIN_STATE_UP: not hm_kwargs[const.ADMIN_STATE_UP],
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ hw_new_tags = ["Hola", "Mundo"]
+ hm_update_kwargs.update({
+ const.TAGS: hw_new_tags
+ })
+
hm = self.mem_healthmonitor_client.update_healthmonitor(
hm[const.ID], **hm_update_kwargs)
@@ -712,6 +785,10 @@
const.HTTP_METHOD, const.URL_PATH, const.EXPECTED_CODES,
const.ADMIN_STATE_UP]
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(hm_update_kwargs[const.TAGS], hm[const.TAGS])
+
for item in equal_items:
self.assertEqual(hm_update_kwargs[item], hm[item])
diff --git a/octavia_tempest_plugin/tests/api/v2/test_l7policy.py b/octavia_tempest_plugin/tests/api/v2/test_l7policy.py
index b1bccc2..419a20f 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_l7policy.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_l7policy.py
@@ -134,6 +134,14 @@
const.ADMIN_STATE_UP: True,
const.POSITION: 1,
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7_policy_tags = ["Hello", "World"]
+ l7policy_kwargs.update({
+ const.TAGS: l7_policy_tags
+ })
+
if url:
l7policy_kwargs[const.ACTION] = const.REDIRECT_TO_URL
l7policy_kwargs[const.REDIRECT_URL] = url
@@ -202,6 +210,11 @@
self.assertIsNone(l7policy.pop(const.REDIRECT_URL, None))
self.assertIsNone(l7policy.pop(const.REDIRECT_POOL_ID, None))
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7policy_kwargs[const.TAGS],
+ l7policy[const.TAGS])
+
@decorators.idempotent_id('42fa14ba-caf1-465e-ab36-27e7501f95ef')
def test_l7policy_list(self):
"""Tests l7policy list API and field filtering.
@@ -248,6 +261,13 @@
const.POSITION: 1,
const.ACTION: const.REJECT
}
+
+ if self.mem_l7policy_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7policy1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ l7policy1_kwargs.update({const.TAGS: l7policy1_tags})
+
l7policy1 = self.mem_l7policy_client.create_l7policy(
**l7policy1_kwargs)
self.addCleanup(
@@ -281,6 +301,13 @@
const.ACTION: const.REDIRECT_TO_POOL,
const.REDIRECT_POOL_ID: self.pool_id
}
+
+ if self.mem_l7policy_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7policy2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ l7policy2_kwargs.update({const.TAGS: l7policy2_tags})
+
l7policy2 = self.mem_l7policy_client.create_l7policy(
**l7policy2_kwargs)
self.addCleanup(
@@ -315,6 +342,13 @@
const.ACTION: const.REDIRECT_TO_URL,
const.REDIRECT_URL: l7_redirect_url
}
+
+ if self.mem_l7policy_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7policy3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ l7policy3_kwargs.update({const.TAGS: l7policy3_tags})
+
l7policy3 = self.mem_l7policy_client.create_l7policy(
**l7policy3_kwargs)
self.addCleanup(
@@ -473,6 +507,28 @@
self.assertEqual(l7policy1[const.DESCRIPTION],
l7policies[0][const.DESCRIPTION])
+ # Creating a list of 3 l7policies, each one contains different tags
+ if self.mem_l7policy_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_l7policies = [l7policy1, l7policy2, l7policy3]
+ test_list = []
+ for l7policy in list_of_l7policies:
+
+ # If tags "English" and "Creativity" are in the l7policy's tags
+ # and "Spanish" is not, add the l7policy to the list
+ if "English" in l7policy[const.TAGS] and "Creativity" in (
+ l7policy[const.TAGS]) and "Spanish" not in (
+ l7policy[const.TAGS]):
+ test_list.append(l7policy[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [l7policy1[const.NAME], l7policy3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in l7policy[const.TAGS]
+ for l7policy in list_of_l7policies]))
+
@decorators.idempotent_id('baaa8104-a037-4976-b908-82a0b3e08129')
def test_l7policy_show(self):
"""Tests l7policy show API.
@@ -630,6 +686,13 @@
const.REDIRECT_URL: l7_redirect_url,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7_policy_tags = ["Hello", "World"]
+ l7policy_kwargs.update({
+ const.TAGS: l7_policy_tags
+ })
+
l7policy = self.mem_l7policy_client.create_l7policy(**l7policy_kwargs)
self.addClassResourceCleanup(
self.mem_l7policy_client.cleanup_l7policy,
@@ -665,6 +728,11 @@
self.assertEqual(l7_redirect_url, l7policy[const.REDIRECT_URL])
self.assertIsNone(l7policy.pop(const.REDIRECT_POOL_ID, None))
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7policy_kwargs[const.TAGS],
+ l7policy[const.TAGS])
+
# Test that a user, without the load balancer member role, cannot
# use this command
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -706,6 +774,14 @@
const.ACTION: const.REDIRECT_TO_POOL,
const.REDIRECT_POOL_ID: self.pool_id,
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7_policy_new_tags = ["Hola", "Mundo"]
+ l7policy_update_kwargs.update({
+ const.TAGS: l7_policy_new_tags
+ })
+
l7policy = self.mem_l7policy_client.update_l7policy(
l7policy[const.ID], **l7policy_update_kwargs)
@@ -745,6 +821,11 @@
self.assertEqual(self.pool_id, l7policy[const.REDIRECT_POOL_ID])
self.assertIsNone(l7policy.pop(const.REDIRECT_URL, None))
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7policy_update_kwargs[const.TAGS],
+ l7policy[const.TAGS])
+
@decorators.idempotent_id('7925eb4b-94b6-4c28-98c2-fd0b4f0976cc')
def test_l7policy_delete(self):
"""Tests l7policy create and delete APIs.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_l7rule.py b/octavia_tempest_plugin/tests/api/v2/test_l7rule.py
index aaad8a0..c2526bf 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_l7rule.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_l7rule.py
@@ -143,6 +143,13 @@
const.INVERT: False,
}
+ if self.mem_l7policy_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7_rule_tags = ["Hello", "World"]
+ l7rule_kwargs.update({
+ const.TAGS: l7_rule_tags
+ })
+
# Test that a user without the load balancer role cannot
# create a l7rule
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -193,6 +200,11 @@
for item in equal_items:
self.assertEqual(l7rule_kwargs[item], l7rule[item])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7rule_kwargs[const.TAGS],
+ l7rule[const.TAGS])
+
@decorators.idempotent_id('69095254-f106-4fb6-9f54-7a78cc14fb51')
def test_l7rule_list(self):
"""Tests l7rule list API and field filtering.
@@ -232,6 +244,13 @@
const.COMPARE_TYPE: const.EQUAL_TO,
const.KEY: 'mykey2-list',
}
+
+ if self.mem_lb_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7rule1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ l7rule1_kwargs.update({const.TAGS: l7rule1_tags})
+
l7rule1 = self.mem_l7rule_client.create_l7rule(
**l7rule1_kwargs)
self.addCleanup(
@@ -263,6 +282,13 @@
const.COMPARE_TYPE: const.EQUAL_TO,
const.KEY: 'mykey1-list',
}
+
+ if self.mem_lb_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7rule2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ l7rule2_kwargs.update({const.TAGS: l7rule2_tags})
+
l7rule2 = self.mem_l7rule_client.create_l7rule(
**l7rule2_kwargs)
self.addCleanup(
@@ -294,6 +320,13 @@
const.COMPARE_TYPE: const.EQUAL_TO,
const.KEY: 'mykey3-list',
}
+
+ if self.mem_lb_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7rule3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ l7rule3_kwargs.update({const.TAGS: l7rule3_tags})
+
l7rule3 = self.mem_l7rule_client.create_l7rule(
**l7rule3_kwargs)
self.addCleanup(
@@ -414,6 +447,28 @@
self.assertEqual(l7rule1[const.VALUE],
l7rules[0][const.VALUE])
+ # Creating a list of 3 l7rules, each one contains different tags
+ if self.mem_l7rule_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_l7rules = [l7rule1, l7rule2, l7rule3]
+ test_list = []
+ for l7rule in list_of_l7rules:
+
+ # If tags "English" and "Creativity" are in the l7rule's tags
+ # and "Spanish" is not, add the l7rule to the list
+ if "English" in l7rule[const.TAGS] and "Creativity" in (
+ l7rule[const.TAGS]) and "Spanish" not in (
+ l7rule[const.TAGS]):
+ test_list.append(l7rule[const.VALUE])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ [l7rule1[const.VALUE], l7rule3[const.VALUE]], test_list)
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in l7rule[const.TAGS]
+ for l7rule in list_of_l7rules]))
+
@decorators.idempotent_id('b80b34c3-09fc-467b-8027-7350adb17070')
def test_l7rule_show(self):
"""Tests l7rule show API.
@@ -526,6 +581,13 @@
const.INVERT: False,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7_rule_tags = ["Hello", "World"]
+ l7rule_kwargs.update({
+ const.TAGS: l7_rule_tags
+ })
+
l7rule = self.mem_l7rule_client.create_l7rule(**l7rule_kwargs)
self.addClassResourceCleanup(
self.mem_l7rule_client.cleanup_l7rule,
@@ -557,6 +619,11 @@
equal_items = [const.ADMIN_STATE_UP, const.TYPE, const.VALUE,
const.COMPARE_TYPE, const.KEY, const.INVERT]
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7rule_kwargs[const.TAGS],
+ l7rule[const.TAGS])
+
for item in equal_items:
self.assertEqual(l7rule_kwargs[item], l7rule[item])
@@ -573,7 +640,7 @@
l7rule_check = self.mem_l7rule_client.show_l7rule(
l7rule[const.ID], l7policy_id=self.l7policy_id)
self.assertEqual(const.ACTIVE, l7rule_check[const.PROVISIONING_STATUS])
- self.assertEqual(False, l7rule_check[const.ADMIN_STATE_UP])
+ self.assertFalse(l7rule_check[const.ADMIN_STATE_UP])
# Test that a user, without the load balancer member role, cannot
# update this l7rule
@@ -588,7 +655,7 @@
l7rule_check = self.mem_l7rule_client.show_l7rule(
l7rule[const.ID], l7policy_id=self.l7policy_id)
self.assertEqual(const.ACTIVE, l7rule_check[const.PROVISIONING_STATUS])
- self.assertEqual(False, l7rule_check[const.ADMIN_STATE_UP])
+ self.assertFalse(l7rule_check[const.ADMIN_STATE_UP])
l7rule_update_kwargs = {
const.L7POLICY_ID: self.l7policy_id,
@@ -599,6 +666,13 @@
const.KEY: 'mykey-UPDATED',
const.INVERT: True,
}
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ l7rule_update_kwargs.update({
+ const.TAGS: ["Hola", "Mundo"]
+ })
+
l7rule = self.mem_l7rule_client.update_l7rule(
l7rule[const.ID], **l7rule_update_kwargs)
@@ -624,6 +698,12 @@
# Test changed items (which is all of them, for l7rules)
equal_items = [const.ADMIN_STATE_UP, const.TYPE, const.VALUE,
const.COMPARE_TYPE, const.KEY, const.INVERT]
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(l7rule_update_kwargs[const.TAGS],
+ l7rule[const.TAGS])
+
for item in equal_items:
self.assertEqual(l7rule_update_kwargs[item], l7rule[item])
diff --git a/octavia_tempest_plugin/tests/api/v2/test_listener.py b/octavia_tempest_plugin/tests/api/v2/test_listener.py
index 3a45656..db98958 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_listener.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_listener.py
@@ -59,6 +59,10 @@
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
+ cls.allowed_cidrs = ['192.0.1.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ cls.allowed_cidrs = ['2001:db8:a0b:12f0::/64']
+
@decorators.idempotent_id('88d0ec83-7b08-48d9-96e2-0df1d2f8cd98')
def test_listener_create(self):
"""Tests listener create and basic show APIs.
@@ -102,6 +106,25 @@
const.TIMEOUT_TCP_INSPECT: 50,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener_tags = [str(x) for x in range(100)]
+ listener_kwargs.update({
+ const.TAGS: listener_tags
+ })
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ # Test that CIDR IP version matches VIP IP version
+ bad_cidrs = ['192.0.1.0/24', '2001:db8:a0b:12f0::/64']
+ listener_kwargs.update({const.ALLOWED_CIDRS: bad_cidrs})
+ self.assertRaises(
+ exceptions.BadRequest,
+ self.mem_listener_client.create_listener,
+ **listener_kwargs)
+
+ listener_kwargs.update({const.ALLOWED_CIDRS: self.allowed_cidrs})
+
# Test that a user without the load balancer role cannot
# create a listener
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -135,9 +158,21 @@
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
- self.assertEqual(listener_name, listener[const.NAME])
- self.assertEqual(listener_description, listener[const.DESCRIPTION])
- self.assertTrue(listener[const.ADMIN_STATE_UP])
+ equal_items = [const.NAME, const.DESCRIPTION,
+ const.ADMIN_STATE_UP,
+ const.PROTOCOL, const.PROTOCOL_PORT,
+ const.CONNECTION_LIMIT]
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.1'):
+ equal_items.append(const.TIMEOUT_CLIENT_DATA)
+ equal_items.append(const.TIMEOUT_MEMBER_CONNECT)
+ equal_items.append(const.TIMEOUT_MEMBER_DATA)
+ equal_items.append(const.TIMEOUT_TCP_INSPECT)
+
+ for item in equal_items:
+ self.assertEqual(listener_kwargs[item], listener[item])
+
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
@@ -146,20 +181,21 @@
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
else:
self.assertEqual(const.ONLINE, listener[const.OPERATING_STATUS])
- self.assertEqual(self.protocol, listener[const.PROTOCOL])
- self.assertEqual(80, listener[const.PROTOCOL_PORT])
- self.assertEqual(200, listener[const.CONNECTION_LIMIT])
+
insert_headers = listener[const.INSERT_HEADERS]
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
+
if self.mem_listener_client.is_version_supported(
- self.api_version, '2.1'):
- self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
- self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
- self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
- self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
+ self.api_version, '2.5'):
+ self.assertCountEqual(listener_kwargs[const.TAGS],
+ listener[const.TAGS])
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ self.assertEqual(self.allowed_cidrs, listener[const.ALLOWED_CIDRS])
@decorators.idempotent_id('cceac303-4db5-4d5a-9f6e-ff33780a5f29')
def test_listener_create_on_same_port(self):
@@ -336,6 +372,12 @@
const.PROTOCOL_PORT: 80,
const.LOADBALANCER_ID: lb_id,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ listener1_kwargs.update({const.TAGS: listener1_tags})
+
listener1 = self.mem_listener_client.create_listener(
**listener1_kwargs)
self.addCleanup(
@@ -368,6 +410,12 @@
const.PROTOCOL_PORT: 81,
const.LOADBALANCER_ID: lb_id,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ listener2_kwargs.update({const.TAGS: listener2_tags})
+
listener2 = self.mem_listener_client.create_listener(
**listener2_kwargs)
self.addCleanup(
@@ -400,6 +448,12 @@
const.PROTOCOL_PORT: 82,
const.LOADBALANCER_ID: lb_id,
}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ listener3_kwargs.update({const.TAGS: listener3_tags})
+
listener3 = self.mem_listener_client.create_listener(
**listener3_kwargs)
self.addCleanup(
@@ -487,6 +541,9 @@
show_listener_response_fields.append('timeout_member_connect')
show_listener_response_fields.append('timeout_member_data')
show_listener_response_fields.append('timeout_tcp_inspect')
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ show_listener_response_fields.append('allowed_cidrs')
for field in show_listener_response_fields:
if field in (const.DEFAULT_POOL_ID, const.L7_POLICIES):
continue
@@ -542,6 +599,28 @@
self.assertEqual(listener1[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
+ # Creating a list of 3 listeners, each one contains different tags
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_listeners = [listener1, listener2, listener3]
+ test_list = []
+ for listener in list_of_listeners:
+
+ # If tags "English" and "Creativity" are in the listener's tags
+ # and "Spanish" is not, add the listener to the list
+ if "English" in listener[const.TAGS] and "Creativity" in (
+ listener[const.TAGS]) and "Spanish" not in (
+ listener[const.TAGS]):
+ test_list.append(listener[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [listener1[const.NAME], listener3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in listener[const.TAGS]
+ for listener in list_of_listeners]))
+
@decorators.idempotent_id('6e299eae-6907-4dfc-89c2-e57709d25d3d')
def test_listener_show(self):
"""Tests listener show API.
@@ -581,6 +660,17 @@
const.TIMEOUT_TCP_INSPECT: 50,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener_tags = ["hello", "world"]
+ listener_kwargs.update({
+ const.TAGS: listener_tags
+ })
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ listener_kwargs.update({const.ALLOWED_CIDRS: self.allowed_cidrs})
+
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
@@ -605,10 +695,32 @@
const.ONLINE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
+ equal_items = [const.NAME, const.DESCRIPTION,
+ const.ADMIN_STATE_UP,
+ const.PROTOCOL, const.PROTOCOL_PORT,
+ const.CONNECTION_LIMIT]
- self.assertEqual(listener_name, listener[const.NAME])
- self.assertEqual(listener_description, listener[const.DESCRIPTION])
- self.assertTrue(listener[const.ADMIN_STATE_UP])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.1'):
+ equal_items.append(const.TIMEOUT_CLIENT_DATA)
+ equal_items.append(const.TIMEOUT_MEMBER_CONNECT)
+ equal_items.append(const.TIMEOUT_MEMBER_DATA)
+ equal_items.append(const.TIMEOUT_TCP_INSPECT)
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(listener_kwargs[const.TAGS],
+ listener[const.TAGS])
+
+ for item in equal_items:
+ self.assertEqual(listener_kwargs[item], listener[item])
+
+ insert_headers = listener[const.INSERT_HEADERS]
+ self.assertTrue(
+ strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
+ self.assertTrue(
+ strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
+
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
@@ -617,21 +729,10 @@
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
else:
self.assertEqual(const.ONLINE, listener[const.OPERATING_STATUS])
- self.assertEqual(self.protocol, listener[const.PROTOCOL])
- self.assertEqual(81, listener[const.PROTOCOL_PORT])
- self.assertEqual(200, listener[const.CONNECTION_LIMIT])
- insert_headers = listener[const.INSERT_HEADERS]
- self.assertTrue(
- strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
- self.assertTrue(
- strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
if self.mem_listener_client.is_version_supported(
- self.api_version, '2.1'):
- self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
- self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
- self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
- self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
+ self.api_version, '2.12'):
+ self.assertEqual(self.allowed_cidrs, listener[const.ALLOWED_CIDRS])
# Test that a user with lb_admin role can see the listener
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -702,6 +803,17 @@
const.TIMEOUT_TCP_INSPECT: 50,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener_tags = ["Hello", "World"]
+ listener_kwargs.update({
+ const.TAGS: listener_tags
+ })
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ listener_kwargs.update({const.ALLOWED_CIDRS: self.allowed_cidrs})
+
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
@@ -743,6 +855,15 @@
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(listener_kwargs[const.TAGS],
+ listener[const.TAGS])
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ self.assertEqual(self.allowed_cidrs, listener[const.ALLOWED_CIDRS])
+
# Test that a user, without the load balancer member role, cannot
# use this command
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
@@ -799,6 +920,28 @@
const.TIMEOUT_TCP_INSPECT: 100,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ listener_updated_tags = ["Hola", "Mundo"]
+ listener_update_kwargs.update({
+ const.TAGS: listener_updated_tags
+ })
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ # Test that CIDR IP version matches VIP IP version
+ bad_cidrs = ['192.0.2.0/24', '2001:db8::/6']
+ listener_update_kwargs.update({const.ALLOWED_CIDRS: bad_cidrs})
+ self.assertRaises(
+ exceptions.BadRequest,
+ self.mem_listener_client.update_listener,
+ listener[const.ID], **listener_update_kwargs)
+
+ new_cidrs = ['192.0.2.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ new_cidrs = ['2001:db8::/64']
+ listener_update_kwargs.update({const.ALLOWED_CIDRS: new_cidrs})
+
listener = self.mem_listener_client.update_listener(
listener[const.ID], **listener_update_kwargs)
@@ -842,6 +985,18 @@
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(100, listener[const.TIMEOUT_TCP_INSPECT])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(listener_update_kwargs[const.TAGS],
+ listener[const.TAGS])
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ expected_cidrs = ['192.0.2.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ expected_cidrs = ['2001:db8::/64']
+ self.assertEqual(expected_cidrs, listener[const.ALLOWED_CIDRS])
+
@decorators.idempotent_id('16f11c82-f069-4592-8954-81b35a98e3b7')
def test_listener_delete(self):
"""Tests listener create and delete APIs.
diff --git a/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py b/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py
index b4dde1f..684eddb 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py
@@ -69,6 +69,13 @@
# vip_qos_policy_id=lb_qos_policy_id)
const.NAME: lb_name}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ lb_tags = ["Hello", "World"]
+ lb_kwargs.update({
+ const.TAGS: lb_tags
+ })
+
self._setup_lb_network_kwargs(lb_kwargs, ip_version, use_fixed_ip=True)
# Test that a user without the load balancer role cannot
@@ -139,6 +146,10 @@
except Exception:
pass
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(lb_kwargs[const.TAGS], lb[const.TAGS])
+
@decorators.idempotent_id('643ef031-c800-45f2-b229-3c8f8b37c829')
def test_load_balancer_delete(self):
"""Tests load balancer create and delete APIs.
@@ -263,17 +274,29 @@
lb_name = data_utils.rand_name("lb_member_lb2-list")
lb_description = data_utils.rand_name('B')
+ lb_admin_state_up = True
+ lb_provider = CONF.load_balancer.provider
+ lb_vip_network_id = self.lb_member_vip_net[const.ID]
- lb = self.mem_lb_client.create_loadbalancer(
- admin_state_up=True,
- description=lb_description,
+ lb_kwargs = {
+ const.ADMIN_STATE_UP: lb_admin_state_up,
+ const.DESCRIPTION: lb_description,
# TODO(johnsom) Fix test to use a real flavor
# flavor=lb_flavor,
- provider=CONF.load_balancer.provider,
- name=lb_name,
+ const.PROVIDER: lb_provider,
+ const.NAME: lb_name,
# TODO(johnsom) Add QoS
# vip_qos_policy_id=lb_qos_policy_id)
- vip_network_id=self.lb_member_vip_net[const.ID])
+ const.VIP_NETWORK_ID: lb_vip_network_id
+ }
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ lb_tags = ["English", "Mathematics", "Marketing", "Creativity"]
+ lb_kwargs.update({const.TAGS: lb_tags})
+
+ lb = self.mem_lb_client.create_loadbalancer(
+ **lb_kwargs)
+
self.addCleanup(
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
@@ -298,13 +321,25 @@
lb_name = data_utils.rand_name("lb_member_lb1-list")
lb_description = data_utils.rand_name('A')
+ lb_admin_state_up = True
+ lb_provider = CONF.load_balancer.provider
+ lb_vip_network_id = self.lb_member_vip_net[const.ID]
+
+ lb_kwargs = {
+ const.ADMIN_STATE_UP: lb_admin_state_up,
+ const.DESCRIPTION: lb_description,
+ const.PROVIDER: lb_provider,
+ const.NAME: lb_name,
+ const.VIP_NETWORK_ID: lb_vip_network_id,
+ }
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ lb_tags = ["English", "Spanish", "Soft_skills", "Creativity"]
+ lb_kwargs.update({const.TAGS: lb_tags})
lb = self.mem_lb_client.create_loadbalancer(
- admin_state_up=True,
- description=lb_description,
- provider=CONF.load_balancer.provider,
- name=lb_name,
- vip_network_id=self.lb_member_vip_net[const.ID])
+ **lb_kwargs)
+
self.addCleanup(
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
@@ -329,13 +364,26 @@
lb_name = data_utils.rand_name("lb_member_lb3-list")
lb_description = data_utils.rand_name('C')
+ lb_admin_state_up = False
+ lb_provider = CONF.load_balancer.provider
+ lb_vip_network_id = self.lb_member_vip_net[const.ID]
+
+ lb_kwargs = {
+ const.ADMIN_STATE_UP: lb_admin_state_up,
+ const.DESCRIPTION: lb_description,
+ const.PROVIDER: lb_provider,
+ const.NAME: lb_name,
+ const.VIP_NETWORK_ID: lb_vip_network_id,
+ }
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ lb_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ lb_kwargs.update({const.TAGS: lb_tags})
lb = self.mem_lb_client.create_loadbalancer(
- admin_state_up=False,
- description=lb_description,
- provider=CONF.load_balancer.provider,
- name=lb_name,
- vip_network_id=self.lb_member_vip_net[const.ID])
+ **lb_kwargs)
+
self.addCleanup(
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
@@ -442,6 +490,28 @@
self.assertEqual(lb2[const.DESCRIPTION], lbs[1][const.DESCRIPTION])
self.assertEqual(lb1[const.DESCRIPTION], lbs[0][const.DESCRIPTION])
+ # Creating a list of 3 LBs, each one contains different tags
+ if self.mem_lb_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_lbs = [lb1, lb2, lb3]
+ test_list = []
+ for lb in list_of_lbs:
+
+ # If tags "English" and "Creativity" are in the LB's tags
+ # and "Spanish" is not, add the LB to the list
+ if "English" in lb[const.TAGS] and "Creativity" in (
+ lb[const.TAGS]) and "Spanish" not in (
+ lb[const.TAGS]):
+ test_list.append(lb[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [lb1[const.NAME], lb3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in lb[const.TAGS]
+ for lb in list_of_lbs]))
+
@decorators.idempotent_id('826ae612-8717-4c64-a8a7-cb9570a85870')
def test_load_balancer_show(self):
"""Tests load balancer show API.
@@ -561,6 +631,13 @@
# vip_qos_policy_id=lb_qos_policy_id)
const.NAME: lb_name}
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ lb_tags = ["Hello", "World"]
+ lb_kwargs.update({
+ const.TAGS: lb_tags
+ })
+
self._setup_lb_network_kwargs(lb_kwargs, 4, use_fixed_ip=True)
lb = self.mem_lb_client.create_loadbalancer(**lb_kwargs)
@@ -594,6 +671,10 @@
self.assertEqual(lb_kwargs[const.VIP_SUBNET_ID],
lb[const.VIP_SUBNET_ID])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(lb_kwargs[const.TAGS], lb[const.TAGS])
+
new_name = data_utils.rand_name("lb_member_lb1-update")
new_description = data_utils.arbitrary_string(size=255,
base_text='new')
@@ -624,13 +705,26 @@
self.assertEqual(const.ACTIVE, lb_check[const.PROVISIONING_STATUS])
self.assertFalse(lb_check[const.ADMIN_STATE_UP])
- lb = self.mem_lb_client.update_loadbalancer(
- lb[const.ID],
- admin_state_up=True,
- description=new_description,
+ admin_state_up = True
+
+ lb_update_kwargs = {
+ # const.ID: lb[const.ID],
+ const.ADMIN_STATE_UP: admin_state_up,
+ const.DESCRIPTION: new_description,
# TODO(johnsom) Add QoS
# vip_qos_policy_id=lb_qos_policy_id)
- name=new_name)
+ const.NAME: new_name
+ }
+
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.5'):
+ new_tags = ["Hola", "Mundo"]
+ lb_update_kwargs.update({
+ const.TAGS: new_tags
+ })
+
+ lb = self.mem_lb_client.update_loadbalancer(
+ lb[const.ID], **lb_update_kwargs)
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb[const.ID], const.PROVISIONING_STATUS,
@@ -641,6 +735,7 @@
self.assertTrue(lb[const.ADMIN_STATE_UP])
self.assertEqual(new_description, lb[const.DESCRIPTION])
self.assertEqual(new_name, lb[const.NAME])
+ self.assertCountEqual(lb_update_kwargs[const.TAGS], lb[const.TAGS])
# TODO(johnsom) Add QoS
# Attempt to clean up so that one full test run doesn't start 10+
diff --git a/octavia_tempest_plugin/tests/api/v2/test_member.py b/octavia_tempest_plugin/tests/api/v2/test_member.py
index a279495..583f93e 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_member.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_member.py
@@ -153,6 +153,13 @@
const.BACKUP: False,
})
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ member_tags = ["hello", "world"]
+ member_kwargs.update({
+ const.TAGS: member_tags
+ })
+
if self.lb_member_vip_subnet:
member_kwargs[const.SUBNET_ID] = self.lb_member_vip_subnet[
const.ID]
@@ -197,6 +204,11 @@
self.api_version, '2.1'):
equal_items.append(const.BACKUP)
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(member_kwargs[const.TAGS],
+ member[const.TAGS])
+
if monitor:
equal_items += [const.MONITOR_ADDRESS, const.MONITOR_PORT]
if const.SUBNET_ID in member_kwargs:
@@ -247,6 +259,13 @@
const.ADDRESS: '192.0.2.1',
const.PROTOCOL_PORT: 101,
}
+
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ member1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ member1_kwargs.update({const.TAGS: member1_tags})
+
member1 = self.mem_member_client.create_member(
**member1_kwargs)
self.addCleanup(
@@ -278,6 +297,13 @@
const.ADDRESS: '192.0.2.1',
const.PROTOCOL_PORT: 100,
}
+
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ member2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ member2_kwargs.update({const.TAGS: member2_tags})
+
member2 = self.mem_member_client.create_member(
**member2_kwargs)
self.addCleanup(
@@ -309,6 +335,13 @@
const.ADDRESS: '192.0.2.1',
const.PROTOCOL_PORT: 102,
}
+
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ member3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ member3_kwargs.update({const.TAGS: member3_tags})
+
member3 = self.mem_member_client.create_member(
**member3_kwargs)
self.addCleanup(
@@ -433,6 +466,28 @@
self.assertEqual(member1[const.PROTOCOL_PORT],
members[0][const.PROTOCOL_PORT])
+ # Creating a list of 3 members, each one contains different tags
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_members = [member1, member2, member3]
+ test_list = []
+ for member in list_of_members:
+
+ # If tags "English" and "Creativity" are in the member's tags
+ # and "Spanish" is not, add the member to the list
+ if "English" in member[const.TAGS] and "Creativity" in (
+ member[const.TAGS]) and "Spanish" not in (
+ member[const.TAGS]):
+ test_list.append(member[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [member1[const.NAME], member3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in member[const.TAGS]
+ for member in list_of_members]))
+
@decorators.idempotent_id('7674ae04-7e92-44ef-9adf-40718d7ec705')
def test_member_show(self):
"""Tests member show API.
@@ -563,6 +618,13 @@
const.BACKUP: False,
})
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ member_tags = ["Hello", "World"]
+ member_kwargs.update({
+ const.TAGS: member_tags
+ })
+
if self.lb_member_vip_subnet:
member_kwargs[const.SUBNET_ID] = self.lb_member_vip_subnet[
const.ID]
@@ -609,6 +671,11 @@
self.api_version, '2.1'):
equal_items.append(const.BACKUP)
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(member_kwargs[const.TAGS],
+ member[const.TAGS])
+
if monitor:
equal_items += [const.MONITOR_ADDRESS, const.MONITOR_PORT]
if const.SUBNET_ID in member_kwargs:
@@ -672,6 +739,14 @@
member_update_kwargs.update({
const.BACKUP: not member[const.BACKUP]
})
+
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ new_tags = ["Hola", "Mundo"]
+ member_update_kwargs.update({
+ const.TAGS: new_tags
+ })
+
if monitor:
member_update_kwargs[const.MONITOR_ADDRESS] = '192.0.2.3'
member_update_kwargs[const.MONITOR_PORT] = member[
@@ -709,6 +784,11 @@
self.api_version, '2.1'):
equal_items.append(const.BACKUP)
+ if self.mem_member_client.is_version_supported(
+ self.api_version, '2.5'):
+ self.assertCountEqual(member_update_kwargs[const.TAGS],
+ member[const.TAGS])
+
if monitor:
equal_items += [const.MONITOR_ADDRESS, const.MONITOR_PORT]
for item in equal_items:
diff --git a/octavia_tempest_plugin/tests/api/v2/test_pool.py b/octavia_tempest_plugin/tests/api/v2/test_pool.py
index fd3053d..88e0119 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_pool.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_pool.py
@@ -104,6 +104,13 @@
const.PROTOCOL: self.protocol,
const.LB_ALGORITHM: self.lb_algorithm,
}
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ pool_tags = ["Hello", "World"]
+ pool_kwargs.update({
+ const.TAGS: pool_tags
+ })
+
if self.lb_feature_enabled.session_persistence_enabled:
pool_kwargs[const.SESSION_PERSISTENCE] = {
const.TYPE: const.SESSION_PERSISTENCE_APP_COOKIE,
@@ -179,6 +186,9 @@
self.assertEqual(pool_sp_cookie_name,
pool[const.SESSION_PERSISTENCE][
const.COOKIE_NAME])
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ self.assertCountEqual(pool_kwargs[const.TAGS],
+ pool[const.TAGS])
@decorators.idempotent_id('6959a32e-fb34-4f3e-be68-8880c6450016')
def test_pool_list(self):
@@ -222,6 +232,13 @@
const.LB_ALGORITHM: self.lb_algorithm,
const.LOADBALANCER_ID: lb_id,
}
+
+ if self.mem_pool_client.is_version_supported(
+ self.api_version, '2.5'):
+ pool1_tags = ["English", "Mathematics",
+ "Marketing", "Creativity"]
+ pool1_kwargs.update({const.TAGS: pool1_tags})
+
if self.lb_feature_enabled.session_persistence_enabled:
pool1_kwargs[const.SESSION_PERSISTENCE] = {
const.TYPE: const.SESSION_PERSISTENCE_APP_COOKIE,
@@ -260,6 +277,13 @@
const.LB_ALGORITHM: self.lb_algorithm,
const.LOADBALANCER_ID: lb_id,
}
+
+ if self.mem_pool_client.is_version_supported(
+ self.api_version, '2.5'):
+ pool2_tags = ["English", "Spanish",
+ "Soft_skills", "Creativity"]
+ pool2_kwargs.update({const.TAGS: pool2_tags})
+
if self.lb_feature_enabled.session_persistence_enabled:
pool2_kwargs[const.SESSION_PERSISTENCE] = {
const.TYPE: const.SESSION_PERSISTENCE_APP_COOKIE,
@@ -298,6 +322,13 @@
# No session persistence, just so there's one test for that
const.LOADBALANCER_ID: lb_id,
}
+
+ if self.mem_pool_client.is_version_supported(
+ self.api_version, '2.5'):
+ pool3_tags = ["English", "Project_management",
+ "Communication", "Creativity"]
+ pool3_kwargs.update({const.TAGS: pool3_tags})
+
pool3 = self.mem_pool_client.create_pool(
**pool3_kwargs)
self.addCleanup(
@@ -418,6 +449,28 @@
self.assertEqual(pool1[const.DESCRIPTION],
pools[0][const.DESCRIPTION])
+ # Creating a list of 3 pools, each one contains different tags
+ if self.mem_pool_client.is_version_supported(
+ self.api_version, '2.5'):
+ list_of_pools = [pool1, pool2, pool3]
+ test_list = []
+ for pool in list_of_pools:
+
+ # If tags "English" and "Creativity" are in the pool's tags
+ # and "Spanish" is not, add the pool to the list
+ if "English" in pool[const.TAGS] and "Creativity" in (
+ pool[const.TAGS]) and "Spanish" not in (
+ pool[const.TAGS]):
+ test_list.append(pool[const.NAME])
+
+ # Tests if only the first and the third ones have those tags
+ self.assertEqual(
+ test_list, [pool1[const.NAME], pool3[const.NAME]])
+
+ # Tests that filtering by an empty tag will return an empty list
+ self.assertTrue(not any(["" in pool[const.TAGS]
+ for pool in list_of_pools]))
+
@decorators.idempotent_id('b7932438-1aea-4175-a50c-984fee1c0cad')
def test_pool_show(self):
"""Tests pool show API.
@@ -535,6 +588,13 @@
const.LB_ALGORITHM: self.lb_algorithm,
const.LOADBALANCER_ID: self.lb_id,
}
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ pool_tags = ["Hello", "World"]
+ pool_kwargs.update({
+ const.TAGS: pool_tags
+ })
+
if self.lb_feature_enabled.session_persistence_enabled:
pool_kwargs[const.SESSION_PERSISTENCE] = {
const.TYPE: const.SESSION_PERSISTENCE_APP_COOKIE,
@@ -610,6 +670,10 @@
pool_check[const.PROVISIONING_STATUS])
self.assertFalse(pool_check[const.ADMIN_STATE_UP])
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ self.assertCountEqual(pool_kwargs[const.TAGS],
+ pool[const.TAGS])
+
new_name = data_utils.rand_name("lb_member_pool1-UPDATED")
new_description = data_utils.arbitrary_string(size=255,
base_text='new')
@@ -619,6 +683,13 @@
const.ADMIN_STATE_UP: True,
const.LB_ALGORITHM: self.lb_algorithm,
}
+
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ new_tags = ["Hola", "Mundo"]
+ pool_update_kwargs.update({
+ const.TAGS: new_tags
+ })
+
if self.lb_feature_enabled.session_persistence_enabled:
pool_update_kwargs[const.SESSION_PERSISTENCE] = {
const.TYPE: const.SESSION_PERSISTENCE_HTTP_COOKIE,
@@ -650,6 +721,10 @@
self.assertIsNone(
pool[const.SESSION_PERSISTENCE].get(const.COOKIE_NAME))
+ if self.mem_lb_client.is_version_supported(self.api_version, '2.5'):
+ self.assertCountEqual(pool_update_kwargs[const.TAGS],
+ pool[const.TAGS])
+
# Also test removing a Session Persistence
if self.lb_feature_enabled.session_persistence_enabled:
pool_update_kwargs = {
diff --git a/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py b/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
index 84bfc20..a753a5c 100644
--- a/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
+++ b/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
@@ -749,8 +749,8 @@
CONF.load_balancer.build_timeout)
# Test that no client certificate fails to connect
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*certificate required.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get,
'https://{0}:{1}'.format(self.lb_vip_address, LISTENER1_TCP_PORT),
timeout=12, verify=False)
@@ -764,8 +764,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*revoked.*", requests.get,
+ self.assertRaises(
+ requests.exceptions.SSLError, requests.get,
'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER1_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -836,8 +836,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*revoked.*", requests.get,
+ self.assertRaises(
+ requests.exceptions.SSLError, requests.get,
'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER1_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -954,15 +954,15 @@
CONF.load_balancer.build_timeout)
# Test that no client certificate fails to connect to listener1
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*certificate required.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get,
'https://{0}:{1}'.format(self.lb_vip_address, LISTENER1_TCP_PORT),
timeout=12, verify=False)
# Test that no client certificate fails to connect to listener2
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*certificate required.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get,
'https://{0}:{1}'.format(self.lb_vip_address, LISTENER2_TCP_PORT),
timeout=12, verify=False)
@@ -976,8 +976,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*revoked.*", requests.get,
+ self.assertRaises(
+ requests.exceptions.SSLError, requests.get,
'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER1_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -992,8 +992,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*revoked.*", requests.get,
+ self.assertRaises(
+ requests.exceptions.SSLError, requests.get,
'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER2_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -1040,8 +1040,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*decrypt error.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get, 'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER2_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -1056,8 +1056,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*decrypt error.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get, 'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER1_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -1072,8 +1072,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*decrypt error.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get, 'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER2_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
@@ -1088,8 +1088,8 @@
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()))
- self.assertRaisesRegex(
- requests.exceptions.SSLError, ".*decrypt error.*",
+ self.assertRaises(
+ requests.exceptions.SSLError,
requests.get, 'https://{0}:{1}'.format(self.lb_vip_address,
LISTENER1_TCP_PORT),
timeout=12, verify=False, cert=(cert_file.name,
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py b/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
index b37ab57..fbfe930 100644
--- a/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import requests
+
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -290,3 +292,144 @@
'in Octavia API version 2.1 or newer')
self._test_ipv6_vip_ipv6_members_traffic(const.UDP, 8080)
+
+ @decorators.idempotent_id('84b23f68-4bc3-49e5-8372-60c25fe69613')
+ def test_listener_with_allowed_cidrs(self):
+ """Tests traffic through a loadbalancer with allowed CIDRs set.
+
+ * Set up listener with allowed CIDRS (allow all) on a loadbalancer.
+ * Set up pool on a loadbalancer
+ * Set up members on a loadbalancer.
+ * Test traffic to ensure it is balanced properly.
+ * Update allowed CIDRs to restrict traffic to a small subnet.
+ * Assert loadbalancer does not respond to client requests.
+ """
+
+ if not self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ raise self.skipException('Allowed CIDRS in listeners is only '
+ 'available on Octavia API version 2.12 '
+ 'or newer.')
+
+ listener_name = data_utils.rand_name("lb_member_listener2_cidrs")
+ listener_port = 8080
+ listener_kwargs = {
+ const.NAME: listener_name,
+ const.PROTOCOL: self.protocol,
+ const.PROTOCOL_PORT: listener_port,
+ const.LOADBALANCER_ID: self.lb_id,
+ const.ALLOWED_CIDRS: ['::/0']
+ }
+ listener = self.mem_listener_client.create_listener(**listener_kwargs)
+ listener_id = listener[const.ID]
+ self.addCleanup(
+ self.mem_listener_client.cleanup_listener,
+ listener_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ pool_name = data_utils.rand_name("lb_member_pool3_cidrs")
+ pool_kwargs = {
+ const.NAME: pool_name,
+ const.PROTOCOL: self.protocol,
+ const.LB_ALGORITHM: self.lb_algorithm,
+ const.LISTENER_ID: listener_id,
+ }
+ pool = self.mem_pool_client.create_pool(**pool_kwargs)
+ pool_id = pool[const.ID]
+ self.addCleanup(
+ self.mem_pool_client.cleanup_pool,
+ pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ # Set up Member 1 for Webserver 1
+ member1_name = data_utils.rand_name("lb_member_member1-cidrs-traffic")
+ member1_kwargs = {
+ const.POOL_ID: pool_id,
+ const.NAME: member1_name,
+ const.ADMIN_STATE_UP: True,
+ const.ADDRESS: self.webserver1_ip,
+ const.PROTOCOL_PORT: 80,
+ }
+ if self.lb_member_1_subnet:
+ member1_kwargs[const.SUBNET_ID] = self.lb_member_1_subnet[const.ID]
+
+ member1 = self.mem_member_client.create_member(
+ **member1_kwargs)
+ self.addCleanup(
+ self.mem_member_client.cleanup_member,
+ member1[const.ID], pool_id=pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+ waiters.wait_for_status(
+ self.mem_lb_client.show_loadbalancer, self.lb_id,
+ const.PROVISIONING_STATUS, const.ACTIVE,
+ CONF.load_balancer.check_interval,
+ CONF.load_balancer.check_timeout)
+
+ # Set up Member 2 for Webserver 2
+ member2_name = data_utils.rand_name("lb_member_member2-cidrs-traffic")
+ member2_kwargs = {
+ const.POOL_ID: pool_id,
+ const.NAME: member2_name,
+ const.ADMIN_STATE_UP: True,
+ const.ADDRESS: self.webserver2_ip,
+ const.PROTOCOL_PORT: 80,
+ }
+ if self.lb_member_2_subnet:
+ member2_kwargs[const.SUBNET_ID] = self.lb_member_2_subnet[const.ID]
+
+ member2 = self.mem_member_client.create_member(**member2_kwargs)
+ self.addCleanup(
+ self.mem_member_client.cleanup_member,
+ member2[const.ID], pool_id=pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+ waiters.wait_for_status(
+ self.mem_lb_client.show_loadbalancer, self.lb_id,
+ const.PROVISIONING_STATUS, const.ACTIVE,
+ CONF.load_balancer.check_interval,
+ CONF.load_balancer.check_timeout)
+
+ # Send some traffic
+ self.check_members_balanced(
+ self.lb_vip_address, protocol_port=listener_port)
+
+ listener_kwargs = {
+ const.LISTENER_ID: listener_id,
+ const.ALLOWED_CIDRS: ['2001:db8:a0b:12f0::/128']
+ }
+ self.mem_listener_client.update_listener(**listener_kwargs)
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ url_for_vip = 'http://[{}]:{}/'.format(self.lb_vip_address,
+ listener_port)
+
+ # NOTE: Before we start with the consistent response check, we must
+ # wait until Neutron completes the SG update.
+ # See https://bugs.launchpad.net/neutron/+bug/1866353.
+ def expect_conn_error(url):
+ try:
+ requests.Session().get(url)
+ except requests.exceptions.ConnectionError:
+ return True
+ return False
+
+ waiters.wait_until_true(expect_conn_error, url=url_for_vip)
+
+ # Assert that the server is consistently unavailable
+ self.assertConsistentResponse(
+ (None, None), url_for_vip, repeat=3, conn_error=True)
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_listener.py b/octavia_tempest_plugin/tests/scenario/v2/test_listener.py
index bb3df64..c056bd0 100644
--- a/octavia_tempest_plugin/tests/scenario/v2/test_listener.py
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_listener.py
@@ -96,6 +96,10 @@
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
+ cls.allowed_cidrs = ['192.0.1.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ cls.allowed_cidrs = ['2001:db8:a0b:12f0::/64']
+
@decorators.idempotent_id('4a874014-b7d1-49a4-ac9a-2400b3434700')
def test_listener_CRUD(self):
"""Tests listener create, read, update, delete
@@ -134,6 +138,9 @@
const.TIMEOUT_MEMBER_DATA: 1000,
const.TIMEOUT_TCP_INSPECT: 50,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ listener_kwargs.update({const.ALLOWED_CIDRS: self.allowed_cidrs})
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addCleanup(
@@ -176,6 +183,9 @@
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ self.assertEqual(self.allowed_cidrs, listener[const.ALLOWED_CIDRS])
# Listener update
new_name = data_utils.rand_name("lb_member_listener1-update")
@@ -204,6 +214,13 @@
const.TIMEOUT_TCP_INSPECT: 100,
})
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ new_cidrs = ['192.0.2.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ new_cidrs = ['2001:db8::/64']
+ listener_update_kwargs.update({const.ALLOWED_CIDRS: new_cidrs})
+
listener = self.mem_listener_client.update_listener(
listener[const.ID], **listener_update_kwargs)
@@ -249,6 +266,12 @@
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(100, listener[const.TIMEOUT_TCP_INSPECT])
+ if self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ expected_cidrs = ['192.0.2.0/24']
+ if CONF.load_balancer.test_with_ipv6:
+ expected_cidrs = ['2001:db8::/64']
+ self.assertEqual(expected_cidrs, listener[const.ALLOWED_CIDRS])
# Listener delete
waiters.wait_for_status(
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_pool.py b/octavia_tempest_plugin/tests/scenario/v2/test_pool.py
index 5e0622c..720e80a 100644
--- a/octavia_tempest_plugin/tests/scenario/v2/test_pool.py
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_pool.py
@@ -167,8 +167,8 @@
}
if self.lb_feature_enabled.pool_algorithms_enabled:
- pool_update_kwargs[const.LB_ALGORITHM] = \
- const.LB_ALGORITHM_LEAST_CONNECTIONS
+ pool_update_kwargs[const.LB_ALGORITHM] = (
+ const.LB_ALGORITHM_LEAST_CONNECTIONS)
if self.protocol == const.HTTP and (
self.lb_feature_enabled.session_persistence_enabled):
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py b/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py
index 0d49d67..7dd4a29 100644
--- a/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py
@@ -12,15 +12,22 @@
# License for the specific language governing permissions and limitations
# under the License.
+import datetime
+import ipaddress
+import requests
+import shlex
import testtools
+import time
from oslo_log import log as logging
+from oslo_utils import uuidutils
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from octavia_tempest_plugin.common import constants as const
from octavia_tempest_plugin.tests import test_base
+from octavia_tempest_plugin.tests import validators
from octavia_tempest_plugin.tests import waiters
CONF = config.CONF
@@ -198,6 +205,7 @@
protocol_port=protocol_port,
protocol=protocol)
+ @decorators.attr(type=['smoke', 'slow'])
@testtools.skipIf(CONF.load_balancer.test_with_noop,
'Traffic tests will not work in noop mode.')
@decorators.idempotent_id('6751135d-e15a-4e22-89f4-bfcc3408d424')
@@ -799,3 +807,280 @@
'in Octavia API version 2.1 or newer')
self._test_mixed_ipv4_ipv6_members_traffic(const.UDP, 8080)
+
+ @testtools.skipIf(CONF.load_balancer.test_with_noop,
+ 'Log offload tests will not work in noop mode.')
+ @testtools.skipUnless(
+ CONF.loadbalancer_feature_enabled.log_offload_enabled,
+ 'Skipping log offload tests because tempest configuration '
+ '[loadbalancer-feature-enabled] log_offload_enabled is False.')
+ @testtools.skipUnless(
+ CONF.loadbalancer_feature_enabled.l7_protocol_enabled,
+ 'Log offload tests require l7_protocol_enabled.')
+ @decorators.idempotent_id('571dddd9-f5bd-404e-a799-9df7ac9e2fa9')
+ def test_tenant_flow_log(self):
+ """Tests tenant flow log offloading
+
+ * Set up a member on a loadbalancer.
+ * Sends a request to the load balancer.
+ * Validates the flow log record for the request.
+ """
+ listener_name = data_utils.rand_name("lb_member_listener1_tenant_flow")
+ protocol_port = '8123'
+ listener_kwargs = {
+ const.NAME: listener_name,
+ const.PROTOCOL: const.HTTP,
+ const.PROTOCOL_PORT: protocol_port,
+ const.LOADBALANCER_ID: self.lb_id,
+ }
+ listener = self.mem_listener_client.create_listener(**listener_kwargs)
+ listener_id = listener[const.ID]
+ self.addCleanup(
+ self.mem_listener_client.cleanup_listener,
+ listener_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ pool_name = data_utils.rand_name("lb_member_pool1_tenant_flow")
+ pool_kwargs = {
+ const.NAME: pool_name,
+ const.PROTOCOL: const.HTTP,
+ const.LB_ALGORITHM: const.LB_ALGORITHM_SOURCE_IP,
+ const.LISTENER_ID: listener_id,
+ }
+ pool = self.mem_pool_client.create_pool(**pool_kwargs)
+ pool_id = pool[const.ID]
+ self.addCleanup(
+ self.mem_pool_client.cleanup_pool,
+ pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ # Set up Member for Webserver 1
+ member_name = data_utils.rand_name("lb_member_member-tenant_flow")
+ member_kwargs = {
+ const.POOL_ID: pool_id,
+ const.NAME: member_name,
+ const.ADMIN_STATE_UP: True,
+ const.ADDRESS: self.webserver1_ip,
+ const.PROTOCOL_PORT: 80,
+ }
+ if self.lb_member_1_subnet:
+ member_kwargs[const.SUBNET_ID] = self.lb_member_1_subnet[const.ID]
+
+ member = self.mem_member_client.create_member(**member_kwargs)
+ member_id = member[const.ID]
+ self.addCleanup(
+ self.mem_member_client.cleanup_member,
+ member[const.ID], pool_id=pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+ waiters.wait_for_status(
+ self.mem_lb_client.show_loadbalancer, self.lb_id,
+ const.PROVISIONING_STATUS, const.ACTIVE,
+ CONF.load_balancer.check_interval,
+ CONF.load_balancer.check_timeout)
+
+ project_id = self.os_roles_lb_member.credentials.project_id
+ unique_request_id = uuidutils.generate_uuid()
+ LOG.info('Tenant flow logging unique request ID is: %s',
+ unique_request_id)
+
+ # Make the request
+ URL = 'http://{0}:{1}/{2}'.format(
+ self.lb_vip_address, protocol_port, unique_request_id)
+ validators.validate_URL_response(URL, expected_status_code=200)
+
+ # We need to give the log subsystem time to commit the log
+ time.sleep(CONF.load_balancer.check_interval)
+
+ # Get the tenant log entry
+ log_line = None
+ with open(CONF.load_balancer.tenant_flow_log_file) as f:
+ for line in f:
+ if unique_request_id in line:
+ log_line = line
+ break
+ self.assertIsNotNone(
+ log_line, 'Tenant log entry was not found in {0}.'.format(
+ CONF.load_balancer.tenant_flow_log_file))
+
+ # Remove the syslog prefix
+ log_line = log_line[log_line.index(project_id):]
+
+ # Split the line into the log format fields
+ fields = shlex.split(log_line)
+
+ # Validate the fields
+ self.assertEqual(project_id, fields[0]) # project_id
+ self.assertEqual(self.lb_id, fields[1]) # loadbalancer_id
+ self.assertEqual(listener_id, fields[2]) # listener_id
+ ipaddress.ip_address(fields[3]) # client_ip
+ self.assertGreaterEqual(int(fields[4]), 0) # client_port
+ self.assertLessEqual(int(fields[4]), 65535) # client_port
+ datetime.datetime.strptime(fields[5],
+ '%d/%b/%Y:%H:%M:%S.%f') # date_time
+ request_string = 'GET /{0} HTTP/1.1'.format(unique_request_id)
+ self.assertEqual(request_string, fields[6]) # request_string
+ self.assertEqual('200', fields[7]) # http_status
+ self.assertTrue(fields[8].isdigit()) # bytes_read
+ self.assertTrue(fields[9].isdigit()) # bytes_uploaded
+ self.assertEqual('-', fields[10]) # client_cert_verify
+ self.assertEqual("", fields[11]) # cert_dn
+ pool_string = '{0}:{1}'.format(pool_id, listener_id)
+ self.assertEqual(pool_string, fields[12]) # pool_id
+ self.assertEqual(member_id, fields[13]) # member_id
+ self.assertTrue(fields[14].isdigit()) # processing_time
+ self.assertEqual('----', fields[15]) # term_state
+
+ @testtools.skipIf(CONF.load_balancer.test_with_noop,
+ 'Traffic tests will not work in noop mode.')
+ @decorators.idempotent_id('13b0f2de-9934-457b-8be0-f1bffc6915a0')
+ def test_listener_with_allowed_cidrs(self):
+ """Tests traffic through a loadbalancer with allowed CIDRs set.
+
+ * Set up listener with allowed CIDRS (allow all) on a loadbalancer.
+ * Set up pool on a loadbalancer
+ * Set up members on a loadbalancer.
+ * Test traffic to ensure it is balanced properly.
+ * Update allowed CIDRs to restrict traffic to a small subnet.
+ * Assert loadbalancer does not respond to client requests.
+ """
+
+ if not self.mem_listener_client.is_version_supported(
+ self.api_version, '2.12'):
+ raise self.skipException('Allowed CIDRS in listeners is only '
+ 'available on Octavia API version 2.12 '
+ 'or newer.')
+
+ listener_name = data_utils.rand_name("lb_member_listener2_cidrs")
+ listener_port = 8080
+ listener_kwargs = {
+ const.NAME: listener_name,
+ const.PROTOCOL: self.protocol,
+ const.PROTOCOL_PORT: listener_port,
+ const.LOADBALANCER_ID: self.lb_id,
+ const.ALLOWED_CIDRS: ['0.0.0.0/0']
+ }
+ listener = self.mem_listener_client.create_listener(**listener_kwargs)
+ listener_id = listener[const.ID]
+ self.addCleanup(
+ self.mem_listener_client.cleanup_listener,
+ listener_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ pool_name = data_utils.rand_name("lb_member_pool3_cidrs")
+ pool_kwargs = {
+ const.NAME: pool_name,
+ const.PROTOCOL: self.protocol,
+ const.LB_ALGORITHM: self.lb_algorithm,
+ const.LISTENER_ID: listener_id,
+ }
+ pool = self.mem_pool_client.create_pool(**pool_kwargs)
+ pool_id = pool[const.ID]
+ self.addCleanup(
+ self.mem_pool_client.cleanup_pool,
+ pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ # Set up Member 1 for Webserver 1
+ member1_name = data_utils.rand_name("lb_member_member1-cidrs-traffic")
+ member1_kwargs = {
+ const.POOL_ID: pool_id,
+ const.NAME: member1_name,
+ const.ADMIN_STATE_UP: True,
+ const.ADDRESS: self.webserver1_ip,
+ const.PROTOCOL_PORT: 80,
+ }
+ if self.lb_member_1_subnet:
+ member1_kwargs[const.SUBNET_ID] = self.lb_member_1_subnet[const.ID]
+
+ member1 = self.mem_member_client.create_member(
+ **member1_kwargs)
+ self.addCleanup(
+ self.mem_member_client.cleanup_member,
+ member1[const.ID], pool_id=pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+ waiters.wait_for_status(
+ self.mem_lb_client.show_loadbalancer, self.lb_id,
+ const.PROVISIONING_STATUS, const.ACTIVE,
+ CONF.load_balancer.check_interval,
+ CONF.load_balancer.check_timeout)
+
+ # Set up Member 2 for Webserver 2
+ member2_name = data_utils.rand_name("lb_member_member2-cidrs-traffic")
+ member2_kwargs = {
+ const.POOL_ID: pool_id,
+ const.NAME: member2_name,
+ const.ADMIN_STATE_UP: True,
+ const.ADDRESS: self.webserver2_ip,
+ const.PROTOCOL_PORT: 80,
+ }
+ if self.lb_member_2_subnet:
+ member2_kwargs[const.SUBNET_ID] = self.lb_member_2_subnet[const.ID]
+
+ member2 = self.mem_member_client.create_member(**member2_kwargs)
+ self.addCleanup(
+ self.mem_member_client.cleanup_member,
+ member2[const.ID], pool_id=pool_id,
+ lb_client=self.mem_lb_client, lb_id=self.lb_id)
+ waiters.wait_for_status(
+ self.mem_lb_client.show_loadbalancer, self.lb_id,
+ const.PROVISIONING_STATUS, const.ACTIVE,
+ CONF.load_balancer.check_interval,
+ CONF.load_balancer.check_timeout)
+
+ # Send some traffic
+ self.check_members_balanced(
+ self.lb_vip_address, protocol_port=listener_port)
+
+ listener_kwargs = {
+ const.LISTENER_ID: listener_id,
+ const.ALLOWED_CIDRS: ['192.0.1.0/32']
+ }
+ self.mem_listener_client.update_listener(**listener_kwargs)
+ waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+ self.lb_id, const.PROVISIONING_STATUS,
+ const.ACTIVE,
+ CONF.load_balancer.build_interval,
+ CONF.load_balancer.build_timeout)
+
+ url_for_vip = 'http://{}:{}/'.format(
+ self.lb_vip_address, listener_port)
+
+ # NOTE: Before we start with the consistent response check, we must
+ # wait until Neutron completes the SG update.
+ # See https://bugs.launchpad.net/neutron/+bug/1866353.
+ def expect_conn_error(url):
+ try:
+ requests.Session().get(url)
+ except requests.exceptions.ConnectionError:
+ return True
+ return False
+
+ waiters.wait_until_true(expect_conn_error, url=url_for_vip)
+
+ # Assert that the server is consistently unavailable
+ self.assertConsistentResponse(
+ (None, None), url_for_vip, repeat=3, conn_error=True)
diff --git a/octavia_tempest_plugin/tests/test_base.py b/octavia_tempest_plugin/tests/test_base.py
index f7b7376..d2c4014 100644
--- a/octavia_tempest_plugin/tests/test_base.py
+++ b/octavia_tempest_plugin/tests/test_base.py
@@ -502,6 +502,8 @@
if CONF.load_balancer.test_with_noop:
lb_kwargs[const.VIP_NETWORK_ID] = (
cls.lb_member_vip_net[const.ID])
+ if ip_version == 6:
+ lb_kwargs[const.VIP_ADDRESS] = lb_vip_address
else:
lb_kwargs[const.VIP_NETWORK_ID] = cls.lb_member_vip_net[const.ID]
lb_kwargs[const.VIP_SUBNET_ID] = None
@@ -1098,7 +1100,8 @@
protocol_port=protocol_port)
def assertConsistentResponse(self, response, url, method='GET', repeat=10,
- redirect=False, timeout=2, **kwargs):
+ redirect=False, timeout=2,
+ conn_error=False, **kwargs):
"""Assert that a request to URL gets the expected response.
:param response: Expected response in format (status_code, content).
@@ -1111,6 +1114,7 @@
:param redirect: Is the request a redirect? If true, assume the passed
content should be the next URL in the chain.
:param timeout: Optional seconds to wait for the server to send data.
+ :param conn_error: Optional Expect a connection error?
:return: boolean success status
@@ -1120,6 +1124,13 @@
response_code, response_content = response
for i in range(0, repeat):
+ if conn_error:
+ self.assertRaises(
+ requests.exceptions.ConnectionError, session.request,
+ method, url, allow_redirects=not redirect, timeout=timeout,
+ **kwargs)
+ continue
+
req = session.request(method, url, allow_redirects=not redirect,
timeout=timeout, **kwargs)
if response_code:
diff --git a/octavia_tempest_plugin/tests/waiters.py b/octavia_tempest_plugin/tests/waiters.py
index eb7410a..e0d9d2d 100644
--- a/octavia_tempest_plugin/tests/waiters.py
+++ b/octavia_tempest_plugin/tests/waiters.py
@@ -210,3 +210,26 @@
timeout=check_timeout))
raise exceptions.TimeoutException(message)
time.sleep(check_interval)
+
+
+def wait_until_true(func, timeout=60, sleep=1, **kwargs):
+ """Wait until callable predicate is evaluated as True
+
+ :param func: Callable deciding whether waiting should continue.
+ :param timeout: Timeout in seconds how long should function wait.
+ :param sleep: Polling interval for results in seconds.
+ """
+ start = int(time.time())
+ while True:
+ try:
+ ret = func(**kwargs)
+ if ret:
+ return
+ except Exception as e:
+ LOG.error(e)
+
+ if int(time.time()) - start >= timeout:
+ message = "Timed out after {timeout} seconds waiting".format(
+ timeout=timeout)
+ raise exceptions.TimeoutException(message)
+ time.sleep(sleep)
diff --git a/releasenotes/notes/Add-amphora-admin-log-offloading-012aca8151ebbc54.yaml b/releasenotes/notes/Add-amphora-admin-log-offloading-012aca8151ebbc54.yaml
new file mode 100644
index 0000000..9099bc6
--- /dev/null
+++ b/releasenotes/notes/Add-amphora-admin-log-offloading-012aca8151ebbc54.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Add an optional amphora admin log offloading scenario test.
diff --git a/releasenotes/notes/add-tags-api-tests-1130aab82bb0f7f2.yaml b/releasenotes/notes/add-tags-api-tests-1130aab82bb0f7f2.yaml
new file mode 100644
index 0000000..513a63f
--- /dev/null
+++ b/releasenotes/notes/add-tags-api-tests-1130aab82bb0f7f2.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ API tests for tags were added.
diff --git a/releasenotes/notes/add-tenant-flow-log-offload-scenario-test-39bf9bf51f70d7bb.yaml b/releasenotes/notes/add-tenant-flow-log-offload-scenario-test-39bf9bf51f70d7bb.yaml
new file mode 100644
index 0000000..6a49c97
--- /dev/null
+++ b/releasenotes/notes/add-tenant-flow-log-offload-scenario-test-39bf9bf51f70d7bb.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Adds an optional tenant flow log offload scenario test.
diff --git a/releasenotes/notes/skip-az-api-tests-if-azs-not-configured-c5d06cdcf29beeb5.yaml b/releasenotes/notes/skip-az-api-tests-if-azs-not-configured-c5d06cdcf29beeb5.yaml
new file mode 100644
index 0000000..84b1480
--- /dev/null
+++ b/releasenotes/notes/skip-az-api-tests-if-azs-not-configured-c5d06cdcf29beeb5.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ Fixed availability zone API tests to skip if the required availability
+ zones are not defined in the tempest configuration file and the test run
+ is not using no-op drivers.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index a346f01..8437073 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -43,8 +43,9 @@
]
# openstackdocstheme options
-repository_name = 'openstack/octavia-tempest-plugin'
-use_storyboard = True
+openstackdocs_repo_name = 'openstack/octavia-tempest-plugin'
+openstackdocs_auto_name = False
+openstackdocs_use_storyboard = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -102,7 +103,7 @@
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'native'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
diff --git a/requirements.txt b/requirements.txt
index 3dfa266..8376df6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,4 +15,5 @@
requests>=2.14.2 # Apache-2.0
tempest>=17.1.0 # Apache-2.0
tenacity>=4.4.0 # Apache-2.0
+testtools>=2.2.0 # MIT
keystoneauth1>=3.3.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 3e5d216..d7d3196 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -19,6 +19,7 @@
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
[global]
setup-hooks =
diff --git a/test-requirements.txt b/test-requirements.txt
index 354a020..2125ea0 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking>=3.0,<3.1.0;python_version>='3.5' # Apache-2.0
+hacking>=3.0.1,<3.1.0;python_version>='3.5' # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
python-subunit>=1.0.0 # Apache-2.0/BSD
diff --git a/tox.ini b/tox.ini
index 5d1a40e..8854d21 100644
--- a/tox.ini
+++ b/tox.ini
@@ -41,6 +41,7 @@
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
-r{toxinidir}/doc/requirements.txt
whitelist_externals = rm
commands =
@@ -80,6 +81,24 @@
# [H904]: Delay string interpolations at logging calls
enable-extensions=H106,H203,H204,H205,H904
+[flake8:local-plugins]
+extension =
+ O316 = checks:assert_true_instance
+ O318 = checks:assert_equal_or_not_none
+ O323 = checks:assert_equal_true_or_false
+ O324 = checks:no_mutable_default_args
+ O338 = checks:assert_equal_in
+ O339 = checks:no_log_warn
+ O341 = checks:no_translate_logs
+ O342 = checks:check_raised_localized_exceptions
+ O345 = checks:check_no_eventlet_imports
+ O346 = checks:check_line_continuation_no_backslash
+ O347 = checks:revert_must_have_kwargs
+ O348 = checks:check_no_logging_imports
+ O349 = checks:check_no_import_mock
+paths =
+ ./octavia_tempest_plugin/hacking
+
[testenv:genconfig]
whitelist_externals = mkdir
commands =
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index 60d9150..511e3c0 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -1,10 +1,40 @@
- nodeset:
+ name: octavia-single-node-ubuntu-bionic
+ nodes:
+ - name: controller
+ label: nested-virt-ubuntu-bionic
+ groups:
+ - name: tempest
+ nodes:
+ - controller
+
+- nodeset:
+ name: octavia-single-node-centos-7
+ nodes:
+ - name: controller
+ label: nested-virt-centos-7
+ groups:
+ - name: tempest
+ nodes:
+ - controller
+
+- nodeset:
+ name: octavia-single-node-centos-8
+ nodes:
+ - name: controller
+ label: nested-virt-centos-8
+ groups:
+ - name: tempest
+ nodes:
+ - controller
+
+- nodeset:
name: octavia-two-node
nodes:
- name: controller
- label: ubuntu-bionic
+ label: nested-virt-ubuntu-bionic
- name: controller2
- label: ubuntu-bionic
+ label: nested-virt-ubuntu-bionic
groups:
- name: controller
nodes:
@@ -142,6 +172,7 @@
- job:
name: octavia-dsvm-live-base
parent: octavia-dsvm-base
+ nodeset: octavia-single-node-ubuntu-bionic
timeout: 9000
required-projects:
- openstack/diskimage-builder
@@ -176,6 +207,7 @@
- job:
name: octavia-dsvm-live-base-ipv6-only
parent: octavia-dsvm-base-ipv6-only
+ nodeset: octavia-single-node-ubuntu-bionic
timeout: 9000
required-projects:
- openstack/diskimage-builder
@@ -309,6 +341,7 @@
g-api: true
g-reg: true
key: true
+ memory_tracker: false
mysql: true
n-api: true
n-api-meta: true
@@ -324,7 +357,6 @@
o-cw: true
o-hm: true
o-hk: true
- peakmem_tracker: true
placement-api: true
q-agt: true
q-dhcp: true
@@ -365,12 +397,18 @@
"$TEMPEST_CONFIG":
load_balancer:
test_with_noop: True
+ # AZ API tests with no-op need AZs configured but they do not
+ # need to actually exist in Nova due to the no-op driver.
+ availability_zone: bogus-az-1
+ availability_zone2: bogus-az-2
+ availability_zone3: bogus-az-3
post-config:
$OCTAVIA_CONF:
controller_worker:
amphora_driver: amphora_noop_driver
compute_driver: compute_noop_driver
network_driver: network_noop_driver
+ image_driver: image_noop_driver
certificates:
cert_manager: local_cert_manager
devstack_services:
@@ -408,6 +446,11 @@
USE_PYTHON3: False
- job:
+ name: octavia-v2-dsvm-noop-api-stable-ussuri
+ parent: octavia-v2-dsvm-noop-api
+ override-checkout: stable/ussuri
+
+- job:
name: octavia-v2-dsvm-noop-api-stable-train
parent: octavia-v2-dsvm-noop-api
override-checkout: stable/train
@@ -426,6 +469,10 @@
$OCTAVIA_CONF:
api_settings:
api_v1_enabled: False
+ test-config:
+ "$TEMPEST_CONFIG":
+ loadbalancer-feature-enabled:
+ log_offload_enabled: True
tempest_concurrency: 2
tempest_test_regex: ^octavia_tempest_plugin.tests.scenario.v2
tox_envlist: all
@@ -463,6 +510,14 @@
vars:
devstack_localrc:
USE_PYTHON3: False
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
+
+- job:
+ name: octavia-v2-dsvm-scenario-stable-ussuri
+ parent: octavia-v2-dsvm-scenario
+ override-checkout: stable/ussuri
- job:
name: octavia-v2-dsvm-scenario-stable-train
@@ -473,6 +528,15 @@
name: octavia-v2-dsvm-scenario-stable-stein
parent: octavia-v2-dsvm-scenario
override-checkout: stable/stein
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
+ vars:
+ devstack_local_conf:
+ test-config:
+ "$TEMPEST_CONFIG":
+ loadbalancer-feature-enabled:
+ log_offload_enabled: False
# Legacy jobs for the transition to the act-stdby two node jobs
- job:
@@ -504,6 +568,9 @@
USE_PYTHON3: False
LIBVIRT_TYPE: kvm
LIBVIRT_CPU_MODE: host-passthrough
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
- job:
name: octavia-v2-act-stdby-dsvm-scenario-two-node
@@ -513,6 +580,10 @@
tempest_test_regex: ^octavia_tempest_plugin.tests.scenario.v2
tox_envlist: all
devstack_local_conf:
+ post-config:
+ $OCTAVIA_CONF:
+ nova:
+ enable_anti_affinity: True
test-config:
"$TEMPEST_CONFIG":
load_balancer:
@@ -521,7 +592,7 @@
- job:
name: octavia-v2-dsvm-py2-scenario-centos-7
parent: octavia-v2-dsvm-py2-scenario
- nodeset: devstack-single-node-centos-7
+ nodeset: octavia-single-node-centos-7
vars:
devstack_localrc:
OCTAVIA_AMP_BASE_OS: centos
@@ -531,6 +602,7 @@
- job:
name: octavia-v2-dsvm-scenario-centos-8
parent: octavia-v2-dsvm-scenario
+ nodeset: octavia-single-node-centos-8
vars:
devstack_localrc:
OCTAVIA_AMP_BASE_OS: centos
@@ -581,6 +653,11 @@
- ^octavia_tempest_plugin/tests/(?!barbican_scenario/|\w+\.py).*
- job:
+ name: octavia-v2-dsvm-tls-barbican-stable-ussuri
+ parent: octavia-v2-dsvm-tls-barbican
+ override-checkout: stable/ussuri
+
+- job:
name: octavia-v2-dsvm-tls-barbican-stable-train
parent: octavia-v2-dsvm-tls-barbican
override-checkout: stable/train
@@ -589,15 +666,20 @@
name: octavia-v2-dsvm-tls-barbican-stable-stein
parent: octavia-v2-dsvm-tls-barbican
override-checkout: stable/stein
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
- job:
name: octavia-v2-dsvm-tls-barbican-stable-rocky
parent: octavia-v2-dsvm-tls-barbican
+ nodeset: openstack-single-node-xenial
override-checkout: stable/rocky
- job:
name: octavia-v2-dsvm-tls-barbican-stable-queens
parent: octavia-v2-dsvm-tls-barbican
+ nodeset: openstack-single-node-xenial
override-checkout: stable/queens
- job:
@@ -621,6 +703,14 @@
vars:
devstack_localrc:
USE_PYTHON3: False
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
+
+- job:
+ name: octavia-v2-dsvm-spare-pool-stable-ussuri
+ parent: octavia-v2-dsvm-spare-pool
+ override-checkout: stable/ussuri
- job:
name: octavia-v2-dsvm-spare-pool-stable-train
@@ -631,6 +721,9 @@
name: octavia-v2-dsvm-spare-pool-stable-stein
parent: octavia-v2-dsvm-spare-pool
override-checkout: stable/stein
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
- job:
name: octavia-v2-dsvm-cinder-amphora
@@ -689,6 +782,7 @@
- job:
name: octavia-v2-dsvm-scenario-centos-7
parent: octavia-v2-dsvm-py2-scenario-centos-7
+ nodeset: octavia-single-node-centos-7
- job:
name: octavia-v2-act-stdby-iptables-dsvm-scenario
@@ -723,11 +817,14 @@
vars:
devstack_localrc:
USE_PYTHON3: False
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
- job:
name: octavia-v2-act-stdby-iptables-dsvm-py2-scenario-centos-7
parent: octavia-v2-act-stdby-iptables-dsvm-py2-scenario
- nodeset: devstack-single-node-centos-7
+ nodeset: octavia-single-node-centos-7
vars:
devstack_localrc:
USE_PYTHON3: False
@@ -758,6 +855,11 @@
tox_envlist: all
- job:
+ name: octavia-v2-act-stdby-dsvm-scenario-stable-ussuri
+ parent: octavia-v2-act-stdby-dsvm-scenario
+ override-checkout: stable/ussuri
+
+- job:
name: octavia-v2-act-stdby-dsvm-scenario-stable-train
parent: octavia-v2-act-stdby-dsvm-scenario
override-checkout: stable/train
@@ -766,3 +868,6 @@
name: octavia-v2-act-stdby-dsvm-scenario-stable-stein
parent: octavia-v2-act-stdby-dsvm-scenario
override-checkout: stable/stein
+ required-projects:
+ - name: openstack/diskimage-builder
+ override-checkout: 2.30.0
diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml
index 6b1fcc0..ec11f85 100644
--- a/zuul.d/projects.yaml
+++ b/zuul.d/projects.yaml
@@ -9,11 +9,17 @@
check:
jobs:
- octavia-v2-dsvm-noop-api
+ - octavia-v2-dsvm-noop-api-stable-ussuri
- octavia-v2-dsvm-noop-api-stable-train
- octavia-v2-dsvm-noop-api-stable-stein
- octavia-v2-dsvm-scenario
+ - octavia-v2-dsvm-scenario-stable-ussuri
- octavia-v2-dsvm-scenario-stable-train
- octavia-v2-dsvm-scenario-stable-stein
+ - octavia-v2-dsvm-tls-barbican
+ - octavia-v2-dsvm-tls-barbican-stable-ussuri
+ - octavia-v2-dsvm-tls-barbican-stable-train
+ - octavia-v2-dsvm-tls-barbican-stable-stein
- octavia-v2-dsvm-scenario-ipv6-only:
voting: false
- octavia-v2-dsvm-scenario-centos-8:
@@ -22,18 +28,16 @@
voting: false
- octavia-v2-act-stdby-dsvm-scenario:
voting: false
+ - octavia-v2-act-stdby-dsvm-scenario-stable-ussuri:
+ voting: false
- octavia-v2-act-stdby-dsvm-scenario-stable-train:
voting: false
- octavia-v2-act-stdby-dsvm-scenario-stable-stein:
voting: false
- - octavia-v2-dsvm-tls-barbican:
- voting: false
- - octavia-v2-dsvm-tls-barbican-stable-train:
- voting: false
- - octavia-v2-dsvm-tls-barbican-stable-stein:
- voting: false
- octavia-v2-dsvm-spare-pool:
voting: false
+ - octavia-v2-dsvm-spare-pool-stable-ussuri:
+ voting: false
- octavia-v2-dsvm-spare-pool-stable-train:
voting: false
- octavia-v2-dsvm-spare-pool-stable-stein:
@@ -45,8 +49,14 @@
queue: octavia
jobs:
- octavia-v2-dsvm-noop-api
+ - octavia-v2-dsvm-noop-api-stable-ussuri
- octavia-v2-dsvm-noop-api-stable-train
- octavia-v2-dsvm-noop-api-stable-stein
- octavia-v2-dsvm-scenario
+ - octavia-v2-dsvm-scenario-stable-ussuri
- octavia-v2-dsvm-scenario-stable-train
- octavia-v2-dsvm-scenario-stable-stein
+ - octavia-v2-dsvm-tls-barbican
+ - octavia-v2-dsvm-tls-barbican-stable-ussuri
+ - octavia-v2-dsvm-tls-barbican-stable-train
+ - octavia-v2-dsvm-tls-barbican-stable-stein