# Copyright (c) 2019 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 __future__ import absolute_import import tobiko from tobiko import config from tobiko.openstack import heat from tobiko.openstack import octavia from tobiko.openstack.stacks import _centos from tobiko.openstack.stacks import _cirros from tobiko.openstack.stacks import _hot from tobiko.openstack.stacks import _neutron CONF = config.CONF class OctaviaVipNetworkStackFixture(_neutron.NetworkStackFixture): # Load Balancer VIP network must use port security (required by neutron to # support allowed address pairs on ports) port_security_enabled = True class OctaviaCentosServerStackFixture(_centos.CentosServerStackFixture): network_stack = tobiko.required_setup_fixture( OctaviaVipNetworkStackFixture) @property def user_data(self): # Launch a webserver on port 80 that replies the server name to the # client return ("#cloud-config\n" "packages:\n" "- httpd\n" "runcmd:\n" "- [ sh, -c, \"hostname > /var/www/html/id\" ]\n" "- [ systemctl, enable, --now, httpd ]\n") class OctaviaCirrosServerStackFixture(_cirros.CirrosServerStackFixture): network_stack = tobiko.required_setup_fixture( OctaviaVipNetworkStackFixture) @property def user_data(self): # Launch a webserver on port 80 that replies the server name to the # client # This webserver relies on the nc command which may fail if multiple # clients connect at the same time. For concurrency testing, # OctaviaCentosServerStackFixture is more suited to handle multiple # requests. return ( "#!/bin/sh\n" "sudo nc -k -p 80 -e echo -e \"HTTP/1.1 200 OK\r\n" "Content-Length: $(hostname | head -c-1 | wc -c )\r\n" "Server: $(hostname)\r\n" "Content-type: text/html; charset=utf-8\r\n" "Connection: close\r\n\r\n" "$(hostname)\"\n") class OctaviaServerStackFixture(OctaviaCirrosServerStackFixture): pass class OctaviaLoadbalancerStackFixture(heat.HeatStackFixture): template = _hot.heat_template_file('octavia/load_balancer.yaml') vip_network = tobiko.required_setup_fixture(OctaviaVipNetworkStackFixture) #: Floating IP network where the Neutron floating IP are created @property def floating_network(self) -> str: return self.vip_network.floating_network @property def has_floating_ip(self) -> bool: return bool(self.floating_network) ip_version = 4 provider = 'amphora' @property def vip_subnet_id(self): if self.ip_version == 4: return self.vip_network.ipv4_subnet_id else: return self.vip_network.ipv6_subnet_id def wait_for_active_loadbalancer(self): loadbalancer_id = self.stack.output_show( 'loadbalancer_id')['output']['output_value'] octavia.wait_for_status(status_key=octavia.PROVISIONING_STATUS, status=octavia.ACTIVE, get_client=octavia.get_loadbalancer, object_id=loadbalancer_id) def wait_for_update_loadbalancer(self): loadbalancer_id = self.stack.output_show( 'loadbalancer_id')['output']['output_value'] octavia.wait_for_status(status_key=octavia.PROVISIONING_STATUS, status=octavia.PENDING_UPDATE, get_client=octavia.get_loadbalancer, object_id=loadbalancer_id) class OctaviaListenerStackFixture(heat.HeatStackFixture): template = _hot.heat_template_file('octavia/listener.yaml') loadbalancer = tobiko.required_setup_fixture( OctaviaLoadbalancerStackFixture) lb_port = 80 lb_protocol = 'HTTP' @property def loadbalancer_id(self): return self.loadbalancer.loadbalancer_id @property def loadbalancer_provider(self): return self.loadbalancer.provider class OctaviaPoolStackFixture(heat.HeatStackFixture): template = _hot.heat_template_file('octavia/pool.yaml') listener = tobiko.required_setup_fixture( OctaviaListenerStackFixture) pool_protocol = 'HTTP' lb_algorithm = 'ROUND_ROBIN' hm_type = 'HTTP' # healthmonitor attributes hm_delay = 3 hm_max_retries = 4 hm_timeout = 3 #: whenever to create the health monitor has_monitor = True @property def listener_id(self): return self.listener.listener_id def wait_for_active_members(self): pool_id = self.stack.output_show('pool_id')['output']['output_value'] for member in octavia.list_members(pool_id=pool_id): self.wait_for_active_member(pool_id=pool_id, member_id=member['id']) def wait_for_active_member(self, pool_id, member_id, **kwargs): """Wait for the member to be active Waits for the member to have an ACTIVE provisioning status. :param member_id: the member id. :param pool_id: the pool id. """ octavia.wait_for_status(status_key=octavia.PROVISIONING_STATUS, status=octavia.ACTIVE, get_client=octavia.get_member, object_id=pool_id, member_id=member_id, **kwargs) class OctaviaMemberServerStackFixture(heat.HeatStackFixture): template = _hot.heat_template_file('octavia/member.yaml') pool = tobiko.required_setup_fixture(OctaviaPoolStackFixture) server_stack = tobiko.required_setup_fixture(OctaviaServerStackFixture) application_port = 80 ip_version = 4 @property def pool_id(self): return self.pool.pool_id @property def subnet_id(self): if self.ip_version == 4: return self.server_stack.network_stack.ipv4_subnet_id else: return self.server_stack.network_stack.ipv6_subnet_id @property def member_address(self): return [ fixed_ip['ip_address'] for fixed_ip in self.server_stack.fixed_ips if ((self.ip_version == 4 and ':' not in fixed_ip['ip_address']) or (self.ip_version == 6 and ':' in fixed_ip['ip_address'])) ][0] class OctaviaOtherServerStackFixture( OctaviaServerStackFixture): pass class OctaviaOtherMemberServerStackFixture( OctaviaMemberServerStackFixture): server_stack = tobiko.required_setup_fixture( OctaviaOtherServerStackFixture) # OVN provider stack fixtures class OctaviaOvnProviderLoadbalancerStackFixture( OctaviaLoadbalancerStackFixture): provider = 'ovn' class OctaviaOvnProviderListenerStackFixture(OctaviaListenerStackFixture): loadbalancer = tobiko.required_setup_fixture( OctaviaOvnProviderLoadbalancerStackFixture) lb_port = 22 lb_protocol = 'TCP' class OctaviaOvnProviderPoolStackFixture(OctaviaPoolStackFixture): listener = tobiko.required_setup_fixture( OctaviaOvnProviderListenerStackFixture) pool_protocol = 'TCP' lb_algorithm = 'SOURCE_IP_PORT' #: There is any health monitor available for OVN provider has_monitor = False class OctaviaOvnProviderMemberServerStackFixture( OctaviaMemberServerStackFixture): pool = tobiko.required_setup_fixture(OctaviaOvnProviderPoolStackFixture) application_port = 22 class OctaviaOvnProviderOtherMemberServerStackFixture( OctaviaOvnProviderMemberServerStackFixture): server_stack = tobiko.required_setup_fixture( OctaviaOtherServerStackFixture)