Add a TLS scenario using Barbican
This patch adds a TLS load balancer scenario test using Barbican. Story: 1627383 Task: 5149 Change-Id: I7013888f94261d94e1cd4c3167dc84da7125d1da
This commit is contained in:
parent
f3f25825c2
commit
0a0f9b342a
@ -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.
|
||||
|
||||
sphinxcontrib-apidoc # BSD
|
||||
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||
sphinx>=1.6.2,!=1.6.6,!=1.6.7 # BSD
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
|
||||
|
88
octavia_tempest_plugin/common/barbican_client_mgr.py
Normal file
88
octavia_tempest_plugin/common/barbican_client_mgr.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2019 Rackspace US 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 barbicanclient import client
|
||||
from keystoneauth1 import identity
|
||||
from keystoneauth1 import session
|
||||
from oslo_log import log as logging
|
||||
from tempest.lib.common.utils import data_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BarbicanClientManager(object):
|
||||
"""Class for interacting with the barbican service.
|
||||
|
||||
This class is an abstraction for interacting with the barbican service.
|
||||
This class currently uses the barbican client code to access barbican due
|
||||
to the following reasons:
|
||||
1. Octavia users typically load secrets into barbican via the client.
|
||||
2. The barbican-tempest-plugin is lightly tested (no py3 tests, etc.).
|
||||
3. barbican-tempest-plugin is not in global requirements.
|
||||
|
||||
This led to the decision to not use the service client in the
|
||||
barbican-tempest-plugin.
|
||||
|
||||
In the future it may be better to use the barbican-tempest-plugin
|
||||
service client or the openstacksdk.
|
||||
"""
|
||||
|
||||
def __init__(self, tempest_client_mgr):
|
||||
"""Setup the barbican client.
|
||||
|
||||
:param tempest_client_mgr: A tempest client manager object, such as
|
||||
os_primary.
|
||||
"""
|
||||
# Convert the tempest credential passed in into a keystone session
|
||||
auth_provider = tempest_client_mgr.auth_provider
|
||||
cert_validation = False
|
||||
if not auth_provider.dscv:
|
||||
cert_validation = auth_provider.ca_certs
|
||||
credentials = tempest_client_mgr.credentials
|
||||
keystone_auth = identity.v3.Token(
|
||||
auth_url=auth_provider.auth_url,
|
||||
token=auth_provider.get_token(),
|
||||
project_id=credentials.project_id,
|
||||
project_name=credentials.project_name,
|
||||
project_domain_id=credentials.project_domain_id,
|
||||
project_domain_name=credentials.project_domain_name)
|
||||
id_session = session.Session(auth=keystone_auth,
|
||||
verify=cert_validation)
|
||||
|
||||
# Setup the barbican client
|
||||
self.barbican = client.Client(session=id_session)
|
||||
|
||||
def store_secret(self, pkcs12_secret):
|
||||
"""Store a secret in barbican.
|
||||
|
||||
:param pkcs12_secret: A pkcs12 secret.
|
||||
:returns: The barbican secret_ref.
|
||||
"""
|
||||
p12_secret = self.barbican.secrets.create()
|
||||
p12_secret.name = data_utils.rand_name("lb_member_barbican_pkcs12")
|
||||
p12_secret.payload = pkcs12_secret
|
||||
secret_ref = p12_secret.store()
|
||||
LOG.debug('Secret {0} has ref {1}'.format(p12_secret.name, secret_ref))
|
||||
return secret_ref
|
||||
|
||||
def delete_secret(self, secret_ref):
|
||||
self.barbican.secrets.delete(secret_ref)
|
||||
|
||||
def add_acl(self, secret_ref, user_id):
|
||||
acl_entity = self.barbican.acls.create(entity_ref=secret_ref,
|
||||
users=[user_id],
|
||||
project_access=True)
|
||||
acl_ref = acl_entity.submit()
|
||||
LOG.debug('Secret ACL {0} added user {1}'.format(acl_ref, user_id))
|
||||
return acl_ref
|
130
octavia_tempest_plugin/common/cert_utils.py
Normal file
130
octavia_tempest_plugin/common/cert_utils.py
Normal file
@ -0,0 +1,130 @@
|
||||
# Copyright 2018 Rackspace US 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.
|
||||
|
||||
import datetime
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
import OpenSSL
|
||||
|
||||
|
||||
def generate_ca_cert_and_key():
|
||||
"""Creates a CA cert and key for testing.
|
||||
|
||||
:returns: The cryptography CA cert and CA key objects.
|
||||
"""
|
||||
|
||||
ca_key = rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=2048, backend=default_backend())
|
||||
|
||||
subject = issuer = x509.Name([
|
||||
x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
|
||||
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
|
||||
x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, u"ca_cert.example.com"),
|
||||
])
|
||||
|
||||
ca_cert = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
issuer
|
||||
).public_key(
|
||||
ca_key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.datetime.utcnow()
|
||||
).not_valid_after(
|
||||
datetime.datetime.utcnow() + datetime.timedelta(days=10)
|
||||
).add_extension(
|
||||
x509.SubjectAlternativeName([x509.DNSName(u"ca_cert.example.com")]),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.BasicConstraints(ca=True, path_length=None),
|
||||
critical=True,
|
||||
).sign(ca_key, hashes.SHA256(), default_backend())
|
||||
|
||||
return ca_cert, ca_key
|
||||
|
||||
|
||||
def generate_server_cert_and_key(ca_cert, ca_key, server_uuid):
|
||||
"""Creates a server cert and key for testing.
|
||||
|
||||
:param ca_cert: A cryptography CA certificate (x509) object.
|
||||
:param ca_key: A cryptography CA key (x509) object.
|
||||
:param server_uuid: A UUID identifying the server.
|
||||
:returns: The cryptography server cert and key objects.
|
||||
"""
|
||||
|
||||
server_key = rsa.generate_private_key(
|
||||
public_exponent=65537, key_size=2048, backend=default_backend())
|
||||
|
||||
subject = x509.Name([
|
||||
x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
|
||||
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
|
||||
x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, u"{}.example.com".format(
|
||||
server_uuid)),
|
||||
])
|
||||
|
||||
server_cert = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
ca_cert.subject
|
||||
).public_key(
|
||||
server_key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.datetime.utcnow()
|
||||
).not_valid_after(
|
||||
datetime.datetime.utcnow() + datetime.timedelta(days=10)
|
||||
).add_extension(
|
||||
x509.SubjectAlternativeName(
|
||||
[x509.DNSName(u"{}.example.com".format(server_uuid))]),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.BasicConstraints(ca=False, path_length=None),
|
||||
critical=True,
|
||||
).sign(ca_key, hashes.SHA256(), default_backend())
|
||||
|
||||
return server_cert, server_key
|
||||
|
||||
|
||||
def generate_pkcs12_bundle(server_cert, server_key):
|
||||
"""Creates a pkcs12 formated bundle.
|
||||
|
||||
Note: This uses pyOpenSSL as the cryptography package does not yet
|
||||
support creating pkcs12 bundles. The currently un-released
|
||||
2.5 version of cryptography supports reading pkcs12, but not
|
||||
creation. This method should be updated to only use
|
||||
cryptography once it supports creating pkcs12 bundles.
|
||||
|
||||
:param server_cert: A cryptography certificate (x509) object.
|
||||
:param server_key: A cryptography key (x509) object.
|
||||
:returns: A pkcs12 bundle.
|
||||
"""
|
||||
# TODO(johnsom) Replace with cryptography once it supports creating pkcs12
|
||||
pkcs12 = OpenSSL.crypto.PKCS12()
|
||||
pkcs12.set_privatekey(
|
||||
OpenSSL.crypto.PKey.from_cryptography_key(server_key))
|
||||
pkcs12.set_certificate(OpenSSL.crypto.X509.from_cryptography(server_cert))
|
||||
return pkcs12.export()
|
@ -14,9 +14,13 @@
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from octavia_tempest_plugin.common import constants as const
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
service_available_group = cfg.OptGroup(name='service_available',
|
||||
title='Available OpenStack Services')
|
||||
|
||||
@ -27,6 +31,19 @@ ServiceAvailableGroup = [
|
||||
"to be available."),
|
||||
]
|
||||
|
||||
# Pull in the service_available for barbican if it is not defined.
|
||||
# If the barbican tempest plugin isn't loaded, this won't load from
|
||||
# tempest.conf.
|
||||
try:
|
||||
if cfg.CONF.service_available.barbican is not None:
|
||||
LOG.info('Barbican service_available state: {}'.format(
|
||||
cfg.CONF.service_available.barbican))
|
||||
except cfg.NoSuchOptError:
|
||||
ServiceAvailableGroup.append(
|
||||
cfg.BoolOpt('barbican', default=False,
|
||||
help="Whether or not the barbican service is expected to "
|
||||
"be available."))
|
||||
|
||||
octavia_group = cfg.OptGroup(name='load_balancer',
|
||||
title='load-balancer service options')
|
||||
|
||||
@ -54,6 +71,9 @@ OctaviaGroup = [
|
||||
default=300,
|
||||
help='Timeout in seconds to wait for non-load-balancer '
|
||||
'resources to build'),
|
||||
cfg.StrOpt('octavia_svc_username', default='admin',
|
||||
help='The service_auth username the Octavia services are using'
|
||||
'to access other OpenStack services.'),
|
||||
# load-balancer specific options
|
||||
cfg.IntOpt('check_interval',
|
||||
default=5,
|
||||
@ -152,4 +172,8 @@ LBFeatureEnabledGroup = [
|
||||
default=True,
|
||||
help="Whether Health Monitor is available with provider"
|
||||
" driver or not."),
|
||||
cfg.BoolOpt('terminated_tls_enabled',
|
||||
default=True,
|
||||
help="Whether TLS termination is available with provider "
|
||||
"driver or not."),
|
||||
]
|
||||
|
@ -0,0 +1,274 @@
|
||||
# Copyright 2019 Rackspace US 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.
|
||||
|
||||
import base64
|
||||
import socket
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from OpenSSL.crypto import X509
|
||||
from OpenSSL import SSL
|
||||
|
||||
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_lib.common import constants as lib_consts
|
||||
|
||||
from octavia_tempest_plugin.common import barbican_client_mgr
|
||||
from octavia_tempest_plugin.common import cert_utils
|
||||
from octavia_tempest_plugin.common import constants as const
|
||||
from octavia_tempest_plugin.tests import test_base
|
||||
from octavia_tempest_plugin.tests import waiters
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TLSWithBarbicanTest(test_base.LoadBalancerBaseTestWithCompute):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TLSWithBarbicanTest, cls).skip_checks()
|
||||
if not CONF.loadbalancer_feature_enabled.terminated_tls_enabled:
|
||||
raise cls.skipException('[loadbalancer-feature-enabled] '
|
||||
'"terminated_tls_enabled" is False in '
|
||||
'the tempest configuration. TLS tests '
|
||||
'will be skipped.')
|
||||
if not CONF.validation.run_validation:
|
||||
raise cls.skipException('Traffic tests will not work without '
|
||||
'run_validation enabled.')
|
||||
if not CONF.service_available.barbican:
|
||||
raise cls.skipException('TLS with Barbican tests require the '
|
||||
'barbican service.')
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
"""Setup resources needed by the tests."""
|
||||
super(TLSWithBarbicanTest, cls).resource_setup()
|
||||
|
||||
# Create a CA self-signed cert and key
|
||||
cls.ca_cert, ca_key = cert_utils.generate_ca_cert_and_key()
|
||||
|
||||
LOG.debug('CA Cert: %s' % cls.ca_cert.public_bytes(
|
||||
serialization.Encoding.PEM))
|
||||
LOG.debug('CA private Key: %s' % ca_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()))
|
||||
LOG.debug('CA public Key: %s' % ca_key.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo))
|
||||
|
||||
# Create a server cert and key
|
||||
cls.server_uuid = uuidutils.generate_uuid()
|
||||
server_cert, server_key = cert_utils.generate_server_cert_and_key(
|
||||
cls.ca_cert, ca_key, cls.server_uuid)
|
||||
|
||||
LOG.debug('Server Cert: %s' % server_cert.public_bytes(
|
||||
serialization.Encoding.PEM))
|
||||
LOG.debug('Server private Key: %s' % server_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()))
|
||||
server_public_key = server_key.public_key()
|
||||
LOG.debug('Server public Key: %s' % server_public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo))
|
||||
|
||||
# Create the pkcs12 bundle
|
||||
pkcs12 = cert_utils.generate_pkcs12_bundle(server_cert, server_key)
|
||||
LOG.debug('Server PKCS12 bundle: %s' % base64.b64encode(pkcs12))
|
||||
|
||||
# Load the secret into the barbican service under the
|
||||
# os_roles_lb_member tenant
|
||||
barbican_mgr = barbican_client_mgr.BarbicanClientManager(
|
||||
cls.os_roles_lb_member)
|
||||
|
||||
cls.secret_ref = barbican_mgr.store_secret(pkcs12)
|
||||
cls.addClassResourceCleanup(barbican_mgr.delete_secret, cls.secret_ref)
|
||||
|
||||
# Set the barbican ACL if the Octavia API version doesn't do it
|
||||
# automatically.
|
||||
if not cls.mem_lb_client.is_version_supported(
|
||||
cls.api_version, '2.1'):
|
||||
user_list = cls.os_admin.users_v3_client.list_users(
|
||||
name=CONF.load_balancer.octavia_svc_username)
|
||||
msg = 'Only one user named "{0}" should exist, {1} found.'.format(
|
||||
CONF.load_balancer.octavia_svc_username,
|
||||
len(user_list['users']))
|
||||
assert 1 == len(user_list['users']), msg
|
||||
barbican_mgr.add_acl(cls.secret_ref, user_list['users'][0]['id'])
|
||||
|
||||
# Setup a load balancer for the tests to use
|
||||
lb_name = data_utils.rand_name("lb_member_lb1-tls")
|
||||
lb_kwargs = {const.PROVIDER: CONF.load_balancer.provider,
|
||||
const.NAME: lb_name}
|
||||
|
||||
# TODO(johnsom) Update for IPv6
|
||||
cls._setup_lb_network_kwargs(lb_kwargs, 4)
|
||||
|
||||
lb = cls.mem_lb_client.create_loadbalancer(**lb_kwargs)
|
||||
cls.lb_id = lb[const.ID]
|
||||
cls.addClassResourceCleanup(
|
||||
cls.mem_lb_client.cleanup_loadbalancer,
|
||||
cls.lb_id)
|
||||
|
||||
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
|
||||
cls.lb_id, const.PROVISIONING_STATUS,
|
||||
const.ACTIVE,
|
||||
CONF.load_balancer.lb_build_interval,
|
||||
CONF.load_balancer.lb_build_timeout)
|
||||
|
||||
if CONF.validation.connect_method == 'floating':
|
||||
port_id = lb[const.VIP_PORT_ID]
|
||||
result = cls.lb_mem_float_ip_client.create_floatingip(
|
||||
floating_network_id=CONF.network.public_network_id,
|
||||
port_id=port_id)
|
||||
floating_ip = result['floatingip']
|
||||
LOG.info('lb1_floating_ip: {}'.format(floating_ip))
|
||||
cls.addClassResourceCleanup(
|
||||
waiters.wait_for_not_found,
|
||||
cls.lb_mem_float_ip_client.delete_floatingip,
|
||||
cls.lb_mem_float_ip_client.show_floatingip,
|
||||
floatingip_id=floating_ip['id'])
|
||||
cls.lb_vip_address = floating_ip['floating_ip_address']
|
||||
else:
|
||||
cls.lb_vip_address = lb[const.VIP_ADDRESS]
|
||||
|
||||
pool_name = data_utils.rand_name("lb_member_pool1-tls")
|
||||
pool_kwargs = {
|
||||
const.NAME: pool_name,
|
||||
const.PROTOCOL: const.HTTP,
|
||||
const.LB_ALGORITHM: const.LB_ALGORITHM_ROUND_ROBIN,
|
||||
const.LOADBALANCER_ID: cls.lb_id,
|
||||
}
|
||||
pool = cls.mem_pool_client.create_pool(**pool_kwargs)
|
||||
cls.pool_id = pool[const.ID]
|
||||
cls.addClassResourceCleanup(
|
||||
cls.mem_pool_client.cleanup_pool,
|
||||
cls.pool_id,
|
||||
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||
|
||||
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
|
||||
cls.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-tls")
|
||||
member1_kwargs = {
|
||||
const.POOL_ID: cls.pool_id,
|
||||
const.NAME: member1_name,
|
||||
const.ADMIN_STATE_UP: True,
|
||||
const.ADDRESS: cls.webserver1_ip,
|
||||
const.PROTOCOL_PORT: 80,
|
||||
}
|
||||
if cls.lb_member_1_subnet:
|
||||
member1_kwargs[const.SUBNET_ID] = cls.lb_member_1_subnet[const.ID]
|
||||
|
||||
member1 = cls.mem_member_client.create_member(
|
||||
**member1_kwargs)
|
||||
cls.addClassResourceCleanup(
|
||||
cls.mem_member_client.cleanup_member,
|
||||
member1[const.ID], pool_id=cls.pool_id,
|
||||
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||
waiters.wait_for_status(
|
||||
cls.mem_lb_client.show_loadbalancer, cls.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-tls")
|
||||
member2_kwargs = {
|
||||
const.POOL_ID: cls.pool_id,
|
||||
const.NAME: member2_name,
|
||||
const.ADMIN_STATE_UP: True,
|
||||
const.ADDRESS: cls.webserver2_ip,
|
||||
const.PROTOCOL_PORT: 80,
|
||||
}
|
||||
if cls.lb_member_2_subnet:
|
||||
member2_kwargs[const.SUBNET_ID] = cls.lb_member_2_subnet[const.ID]
|
||||
|
||||
member2 = cls.mem_member_client.create_member(
|
||||
**member2_kwargs)
|
||||
cls.addClassResourceCleanup(
|
||||
cls.mem_member_client.cleanup_member,
|
||||
member2[const.ID], pool_id=cls.pool_id,
|
||||
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||
waiters.wait_for_status(
|
||||
cls.mem_lb_client.show_loadbalancer, cls.lb_id,
|
||||
const.PROVISIONING_STATUS, const.ACTIVE,
|
||||
CONF.load_balancer.check_interval,
|
||||
CONF.load_balancer.check_timeout)
|
||||
|
||||
@decorators.idempotent_id('887ece26-0f7b-4933-89ab-5bb00b106ee0')
|
||||
def test_basic_tls_traffic(self):
|
||||
|
||||
listener_name = data_utils.rand_name("lb_member_listener1-tls")
|
||||
listener_kwargs = {
|
||||
const.NAME: listener_name,
|
||||
const.PROTOCOL: lib_consts.PROTOCOL_TERMINATED_HTTPS,
|
||||
const.PROTOCOL_PORT: '443',
|
||||
const.LOADBALANCER_ID: self.lb_id,
|
||||
const.DEFAULT_POOL_ID: self.pool_id,
|
||||
const.DEFAULT_TLS_CONTAINER_REF: self.secret_ref,
|
||||
}
|
||||
listener = self.mem_listener_client.create_listener(**listener_kwargs)
|
||||
self.listener_id = listener[const.ID]
|
||||
self.addCleanup(
|
||||
self.mem_listener_client.cleanup_listener,
|
||||
self.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)
|
||||
|
||||
# Test HTTPS listener load balancing.
|
||||
# Note: certificate validation tests will follow this test
|
||||
self.check_members_balanced(self.lb_vip_address, protocol='https',
|
||||
verify=False)
|
||||
|
||||
def _verify_cb(connection, x509, errno, errdepth, retcode):
|
||||
"""Callback for certificate validation."""
|
||||
# don't validate names of root certificates
|
||||
if errdepth != 0:
|
||||
return True
|
||||
if errno == 0:
|
||||
# Make sure the certificate is the one we generated
|
||||
self.assertEqual('{}.example.com'.format(self.server_uuid),
|
||||
x509.get_subject().commonName)
|
||||
else:
|
||||
LOG.error('Certificate with CN: {0} failed validation with '
|
||||
'OpenSSL verify errno {1}'.format(
|
||||
x509.get_subject().commonName, errno))
|
||||
return False
|
||||
return True
|
||||
|
||||
context = SSL.Context(SSL.SSLv23_METHOD)
|
||||
context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
_verify_cb)
|
||||
ca_store = context.get_cert_store()
|
||||
ca_store.add_cert(X509.from_cryptography(self.ca_cert))
|
||||
sock = socket.socket()
|
||||
sock = SSL.Connection(context, sock)
|
||||
sock.connect((self.lb_vip_address, 443))
|
||||
# Validate the certificate is signed by the ca_cert we created
|
||||
sock.do_handshake()
|
@ -766,13 +766,15 @@ class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
|
||||
URL = 'http://{0}:81'.format(ip_address)
|
||||
validators.validate_URL_response(URL, expected_body=str(start_id + 1))
|
||||
|
||||
def _wait_for_lb_functional(self, vip_address):
|
||||
def _wait_for_lb_functional(self, vip_address,
|
||||
protocol='http', verify=True):
|
||||
session = requests.Session()
|
||||
start = time.time()
|
||||
|
||||
while time.time() - start < CONF.load_balancer.build_timeout:
|
||||
try:
|
||||
session.get("http://{0}".format(vip_address), timeout=2)
|
||||
session.get("{0}://{1}".format(protocol, vip_address),
|
||||
timeout=2, verify=verify)
|
||||
time.sleep(1)
|
||||
return
|
||||
except Exception:
|
||||
@ -782,20 +784,21 @@ class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
|
||||
'period. Failing test.')
|
||||
raise Exception()
|
||||
|
||||
def check_members_balanced(self, vip_address, traffic_member_count=2):
|
||||
def check_members_balanced(self, vip_address, traffic_member_count=2,
|
||||
protocol='http', verify=True):
|
||||
session = requests.Session()
|
||||
response_counts = {}
|
||||
|
||||
if ipaddress.ip_address(vip_address).version == 6:
|
||||
vip_address = '[{}]'.format(vip_address)
|
||||
|
||||
self._wait_for_lb_functional(vip_address)
|
||||
self._wait_for_lb_functional(vip_address, protocol, verify)
|
||||
|
||||
# Send a number requests to lb vip
|
||||
for i in range(20):
|
||||
try:
|
||||
r = session.get('http://{0}'.format(vip_address),
|
||||
timeout=2)
|
||||
r = session.get('{0}://{1}'.format(protocol, vip_address),
|
||||
timeout=2, verify=verify)
|
||||
|
||||
if r.content in response_counts:
|
||||
response_counts[r.content] += 1
|
||||
|
@ -2,13 +2,17 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
cryptography>=2.1 # BSD/Apache-2.0
|
||||
python-dateutil>=2.5.3 # BSD
|
||||
ipaddress>=1.0.17;python_version<'3.3' # PSF
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
octavia-lib>=1.0.0 # Apache-2.0
|
||||
oslo.config>=5.2.0 # Apache-2.0
|
||||
oslo.log>=3.36.0 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
python-barbicanclient>=4.5.2 # Apache-2.0
|
||||
pyOpenSSL>=17.1.0 # Apache-2.0
|
||||
requests>=2.14.2 # Apache-2.0
|
||||
six>=1.10.0 # MIT
|
||||
tempest>=17.1.0 # Apache-2.0
|
||||
|
@ -87,17 +87,13 @@
|
||||
parent: octavia-dsvm-base
|
||||
timeout: 9000
|
||||
required-projects:
|
||||
- openstack/barbican
|
||||
- openstack/diskimage-builder
|
||||
- openstack/python-barbicanclient
|
||||
vars:
|
||||
devstack_localrc:
|
||||
DIB_LOCAL_ELEMENTS: openstack-ci-mirrors
|
||||
devstack_services:
|
||||
barbican: true
|
||||
neutron-qos: true
|
||||
devstack_plugins:
|
||||
barbican: https://git.openstack.org/openstack/barbican.git
|
||||
neutron: https://git.openstack.org/openstack/neutron.git
|
||||
zuul_copy_output:
|
||||
'/var/log/dib-build' : logs
|
||||
@ -108,9 +104,7 @@
|
||||
nodeset: octavia-two-node
|
||||
timeout: 9000
|
||||
required-projects:
|
||||
- openstack/barbican
|
||||
- openstack/diskimage-builder
|
||||
- openstack/python-barbicanclient
|
||||
host-vars:
|
||||
controller:
|
||||
devstack_localrc:
|
||||
@ -143,7 +137,6 @@
|
||||
OCTAVIA_NODES: "main:{{ hostvars['controller']['nodepool']['private_ipv4'] }},second:{{ hostvars['controller2']['nodepool']['private_ipv4'] }}"
|
||||
OCTAVIA_USE_PREGENERATED_CERTS: true
|
||||
devstack_plugins:
|
||||
barbican: https://git.openstack.org/openstack/barbican.git
|
||||
neutron: https://git.openstack.org/openstack/neutron.git
|
||||
octavia: https://git.openstack.org/openstack/octavia.git
|
||||
controller2:
|
||||
@ -191,7 +184,7 @@
|
||||
api_v1_enabled: False
|
||||
devstack_services:
|
||||
base: false
|
||||
barbican: true
|
||||
barbican: false
|
||||
dstat: true
|
||||
g-api: true
|
||||
g-reg: true
|
||||
@ -362,6 +355,20 @@
|
||||
OCTAVIA_AMP_BASE_OS: ubuntu
|
||||
OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID: bionic
|
||||
|
||||
- job:
|
||||
name: octavia-v2-dsvm-tls-barbican
|
||||
parent: octavia-v2-dsvm-scenario
|
||||
required-projects:
|
||||
- openstack/barbican
|
||||
- openstack/diskimage-builder
|
||||
- openstack/python-barbicanclient
|
||||
vars:
|
||||
tempest_test_regex: ^octavia_tempest_plugin.tests.barbican_scenario.v2
|
||||
devstack_services:
|
||||
barbican: true
|
||||
devstack_plugins:
|
||||
barbican: https://git.openstack.org/openstack/barbican.git
|
||||
|
||||
# Temporary transitional aliases for gates used in other repos
|
||||
# Remove once octavia has transitioned job names
|
||||
- job:
|
||||
|
@ -23,6 +23,8 @@
|
||||
voting: false
|
||||
- octavia-v2-dsvm-py2-scenario-two-node:
|
||||
voting: false
|
||||
- octavia-v2-dsvm-tls-barbican:
|
||||
voting: false
|
||||
gate:
|
||||
queue: octavia
|
||||
jobs:
|
||||
|
Loading…
x
Reference in New Issue
Block a user