Create basic traffic balancing scenario test

This patch implements a basic traffic test for the Octavia Tempest Plugin.

Change-Id: If6ecf7b429e3dfab00342af0fde0dfdc875e907d
Story: 2001387
Task: 19588
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 4f83192..547a616 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_load_balancer.py
@@ -248,7 +248,7 @@
         pretest_lb_ids = [lb['id'] for lb in pretest_lbs]
 
         lb_name = data_utils.rand_name("lb_member_lb2-list")
-        lb_description = 'B'
+        lb_description = data_utils.rand_name('B')
 
         lb = self.mem_lb_client.create_loadbalancer(
             admin_state_up=True,
@@ -276,7 +276,7 @@
         time.sleep(1)
 
         lb_name = data_utils.rand_name("lb_member_lb1-list")
-        lb_description = 'A'
+        lb_description = data_utils.rand_name('A')
 
         lb = self.mem_lb_client.create_loadbalancer(
             admin_state_up=True,
@@ -299,7 +299,7 @@
         time.sleep(1)
 
         lb_name = data_utils.rand_name("lb_member_lb3-list")
-        lb_description = 'C'
+        lb_description = data_utils.rand_name('C')
 
         lb = self.mem_lb_client.create_loadbalancer(
             admin_state_up=False,
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py b/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py
new file mode 100644
index 0000000..8014f88
--- /dev/null
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_traffic_ops.py
@@ -0,0 +1,173 @@
+# Copyright 2018 GoDaddy
+#
+#    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 oslo_log import log as logging
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+import testtools
+
+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__)
+
+
+@testtools.skipUnless(
+    CONF.validation.run_validation,
+    'Traffic tests will not work without run_validation enabled.')
+class TrafficOperationsScenarioTest(test_base.LoadBalancerBaseTestWithCompute):
+
+    @classmethod
+    def resource_setup(cls):
+        """Setup resources needed by the tests."""
+        super(TrafficOperationsScenarioTest, cls).resource_setup()
+
+        lb_name = data_utils.rand_name("lb_member_lb1_operations")
+        lb_kwargs = {const.PROVIDER: CONF.load_balancer.provider,
+                     const.NAME: lb_name}
+
+        # TODO(rm_work): Make this work with ipv6 and split this test for both
+        ip_version = 4
+        cls._setup_lb_network_kwargs(lb_kwargs, ip_version)
+
+        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)
+
+        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]
+
+        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)
+
+        listener_name = data_utils.rand_name("lb_member_listener1_operations")
+        listener_kwargs = {
+            const.NAME: listener_name,
+            const.PROTOCOL: const.HTTP,
+            const.PROTOCOL_PORT: '80',
+            const.LOADBALANCER_ID: cls.lb_id,
+        }
+        listener = cls.mem_listener_client.create_listener(**listener_kwargs)
+        cls.listener_id = listener[const.ID]
+        cls.addClassResourceCleanup(
+            cls.mem_listener_client.cleanup_listener,
+            cls.listener_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)
+
+        pool_name = data_utils.rand_name("lb_member_pool1_operations")
+        pool_kwargs = {
+            const.NAME: pool_name,
+            const.PROTOCOL: const.HTTP,
+            const.LB_ALGORITHM: const.LB_ALGORITHM_ROUND_ROBIN,
+            const.LISTENER_ID: cls.listener_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)
+
+    @testtools.skipIf(CONF.load_balancer.test_with_noop,
+                      'Traffic tests will not work in noop mode.')
+    @decorators.idempotent_id('6751135d-e15a-4e22-89f4-bfcc3408d424')
+    def test_traffic(self):
+        """Tests sending traffic through a loadbalancer
+
+        * Create a fully populated loadbalancer.
+        * Test traffic to ensure it is balanced properly.
+        """
+        # Set up Member 1 for Webserver 1
+        member1_name = data_utils.rand_name("lb_member_member1-traffic")
+        member1_kwargs = {
+            const.POOL_ID: self.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.addClassResourceCleanup(
+            self.mem_member_client.cleanup_member,
+            member1[const.ID], pool_id=self.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-traffic")
+        member2_kwargs = {
+            const.POOL_ID: self.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.addClassResourceCleanup(
+            self.mem_member_client.cleanup_member,
+            member2[const.ID], pool_id=self.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)
diff --git a/octavia_tempest_plugin/tests/test_base.py b/octavia_tempest_plugin/tests/test_base.py
index bacc9fd..ff4dce7 100644
--- a/octavia_tempest_plugin/tests/test_base.py
+++ b/octavia_tempest_plugin/tests/test_base.py
@@ -15,11 +15,13 @@
 import ipaddress
 import pkg_resources
 import random
+import requests
 import shlex
 import six
 import string
 import subprocess
 import tempfile
+import time
 
 from oslo_log import log as logging
 from oslo_utils import uuidutils
@@ -189,175 +191,6 @@
             LOG.debug('Octavia Setup: lb_member_2_ipv6_subnet = {}'.format(
                 cls.lb_member_2_ipv6_subnet[const.ID]))
 
-        # If validation is disabled in this cloud, we won't be able to
-        # start the webservers, so don't even boot them.
-        if not CONF.validation.run_validation:
-            return
-
-        # Create a keypair for the webservers
-        keypair_name = data_utils.rand_name('lb_member_keypair')
-        result = cls.lb_mem_keypairs_client.create_keypair(
-            name=keypair_name)
-        cls.lb_member_keypair = result['keypair']
-        LOG.info('lb_member_keypair: {}'.format(cls.lb_member_keypair))
-        cls.addClassResourceCleanup(
-            waiters.wait_for_not_found,
-            cls.lb_mem_keypairs_client.delete_keypair,
-            cls.lb_mem_keypairs_client.show_keypair,
-            keypair_name)
-
-        if (CONF.load_balancer.enable_security_groups and
-                CONF.network_feature_enabled.port_security):
-            # Set up the security group for the webservers
-            SG_name = data_utils.rand_name('lb_member_SG')
-            cls.lb_member_sec_group = (
-                cls.lb_mem_SG_client.create_security_group(
-                    name=SG_name)['security_group'])
-            cls.addClassResourceCleanup(
-                waiters.wait_for_not_found,
-                cls.lb_mem_SG_client.delete_security_group,
-                cls.lb_mem_SG_client.show_security_group,
-                cls.lb_member_sec_group['id'])
-
-            # Create a security group rule to allow 80-81 (test webservers)
-            SGr = cls.lb_mem_SGr_client.create_security_group_rule(
-                direction='ingress',
-                security_group_id=cls.lb_member_sec_group['id'],
-                protocol='tcp',
-                ethertype='IPv4',
-                port_range_min=80,
-                port_range_max=81)['security_group_rule']
-            cls.addClassResourceCleanup(
-                waiters.wait_for_not_found,
-                cls.lb_mem_SGr_client.delete_security_group_rule,
-                cls.lb_mem_SGr_client.show_security_group_rule,
-                SGr['id'])
-            # Create a security group rule to allow 22 (ssh)
-            SGr = cls.lb_mem_SGr_client.create_security_group_rule(
-                direction='ingress',
-                security_group_id=cls.lb_member_sec_group['id'],
-                protocol='tcp',
-                ethertype='IPv4',
-                port_range_min=22,
-                port_range_max=22)['security_group_rule']
-            cls.addClassResourceCleanup(
-                waiters.wait_for_not_found,
-                cls.lb_mem_SGr_client.delete_security_group_rule,
-                cls.lb_mem_SGr_client.show_security_group_rule,
-                SGr['id'])
-            if CONF.load_balancer.test_with_ipv6:
-                # Create a security group rule to allow 80-81 (test webservers)
-                SGr = cls.lb_mem_SGr_client.create_security_group_rule(
-                    direction='ingress',
-                    security_group_id=cls.lb_member_sec_group['id'],
-                    protocol='tcp',
-                    ethertype='IPv6',
-                    port_range_min=80,
-                    port_range_max=81)['security_group_rule']
-                cls.addClassResourceCleanup(
-                    waiters.wait_for_not_found,
-                    cls.lb_mem_SGr_client.delete_security_group_rule,
-                    cls.lb_mem_SGr_client.show_security_group_rule,
-                    SGr['id'])
-                # Create a security group rule to allow 22 (ssh)
-                SGr = cls.lb_mem_SGr_client.create_security_group_rule(
-                    direction='ingress',
-                    security_group_id=cls.lb_member_sec_group['id'],
-                    protocol='tcp',
-                    ethertype='IPv6',
-                    port_range_min=22,
-                    port_range_max=22)['security_group_rule']
-                cls.addClassResourceCleanup(
-                    waiters.wait_for_not_found,
-                    cls.lb_mem_SGr_client.delete_security_group_rule,
-                    cls.lb_mem_SGr_client.show_security_group_rule,
-                    SGr['id'])
-
-            LOG.info('lb_member_sec_group: {}'.format(cls.lb_member_sec_group))
-
-        # Create webserver 1 instance
-        server_details = cls._create_webserver('lb_member_webserver1',
-                                               cls.lb_member_1_net)
-
-        cls.lb_member_webserver1 = server_details['server']
-        cls.webserver1_ip = server_details.get('ipv4_address')
-        cls.webserver1_ipv6 = server_details.get('ipv6_address')
-        cls.webserver1_public_ip = server_details['public_ipv4_address']
-
-        LOG.debug('Octavia Setup: lb_member_webserver1 = {}'.format(
-            cls.lb_member_webserver1[const.ID]))
-        LOG.debug('Octavia Setup: webserver1_ip = {}'.format(
-            cls.webserver1_ip))
-        LOG.debug('Octavia Setup: webserver1_ipv6 = {}'.format(
-            cls.webserver1_ipv6))
-        LOG.debug('Octavia Setup: webserver1_public_ip = {}'.format(
-            cls.webserver1_public_ip))
-
-        cls._install_start_webserver(cls.webserver1_public_ip,
-                                     cls.lb_member_keypair['private_key'], 1)
-
-        # Validate webserver 1
-        cls._validate_webserver(cls.webserver1_public_ip, 1)
-
-        # Create webserver 2 instance
-        server_details = cls._create_webserver('lb_member_webserver2',
-                                               cls.lb_member_2_net)
-
-        cls.lb_member_webserver2 = server_details['server']
-        cls.webserver2_ip = server_details.get('ipv4_address')
-        cls.webserver2_ipv6 = server_details.get('ipv6_address')
-        cls.webserver2_public_ip = server_details['public_ipv4_address']
-
-        LOG.debug('Octavia Setup: lb_member_webserver2 = {}'.format(
-            cls.lb_member_webserver2[const.ID]))
-        LOG.debug('Octavia Setup: webserver2_ip = {}'.format(
-            cls.webserver2_ip))
-        LOG.debug('Octavia Setup: webserver2_ipv6 = {}'.format(
-            cls.webserver2_ipv6))
-        LOG.debug('Octavia Setup: webserver2_public_ip = {}'.format(
-            cls.webserver2_public_ip))
-
-        cls._install_start_webserver(cls.webserver2_public_ip,
-                                     cls.lb_member_keypair['private_key'], 5)
-
-        # Validate webserver 2
-        cls._validate_webserver(cls.webserver2_public_ip, 5)
-
-    @classmethod
-    def _install_start_webserver(cls, ip_address, ssh_key, start_id):
-        local_file = pkg_resources.resource_filename(
-            'octavia_tempest_plugin.contrib.httpd', 'httpd.bin')
-        dest_file = '/dev/shm/httpd.bin'
-
-        linux_client = remote_client.RemoteClient(
-            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key)
-        linux_client.validate_authentication()
-
-        with tempfile.NamedTemporaryFile() as key:
-            key.write(ssh_key.encode('utf-8'))
-            key.flush()
-            cmd = ("scp -v -o UserKnownHostsFile=/dev/null "
-                   "-o StrictHostKeyChecking=no "
-                   "-o ConnectTimeout={0} -o ConnectionAttempts={1} "
-                   "-i {2} {3} {4}@{5}:{6}").format(
-                CONF.load_balancer.scp_connection_timeout,
-                CONF.load_balancer.scp_connection_attempts,
-                key.name, local_file, CONF.validation.image_ssh_user,
-                ip_address, dest_file)
-            args = shlex.split(cmd)
-            subprocess_args = {'stdout': subprocess.PIPE,
-                               'stderr': subprocess.STDOUT,
-                               'cwd': None}
-            proc = subprocess.Popen(args, **subprocess_args)
-            stdout, stderr = proc.communicate()
-            if proc.returncode != 0:
-                raise exceptions.CommandFailed(proc.returncode, cmd,
-                                               stdout, stderr)
-        linux_client.exec_command('sudo screen -d -m {0} -port 80 '
-                                  '-id {1}'.format(dest_file, start_id))
-        linux_client.exec_command('sudo screen -d -m {0} -port 81 '
-                                  '-id {1}'.format(dest_file, start_id + 1))
-
     @classmethod
     def _create_networks(cls):
         """Creates networks, subnets, and routers used in tests.
@@ -519,6 +352,173 @@
                 cls.lb_mem_subnet_client.show_subnet,
                 cls.lb_member_2_ipv6_subnet['id'])
 
+    @classmethod
+    def _setup_lb_network_kwargs(cls, lb_kwargs, ip_version):
+        if cls.lb_member_vip_subnet:
+            ip_index = data_utils.rand_int_id(start=10, end=100)
+            if ip_version == 4:
+                network = ipaddress.IPv4Network(
+                    six.u(CONF.load_balancer.vip_subnet_cidr))
+                lb_vip_address = str(network[ip_index])
+                subnet_id = cls.lb_member_vip_subnet[const.ID]
+            else:
+                network = ipaddress.IPv6Network(
+                    six.u(CONF.load_balancer.vip_ipv6_subnet_cidr))
+                lb_vip_address = str(network[ip_index])
+                subnet_id = cls.lb_member_vip_ipv6_subnet[const.ID]
+            lb_kwargs[const.VIP_SUBNET_ID] = subnet_id
+            lb_kwargs[const.VIP_ADDRESS] = lb_vip_address
+            if CONF.load_balancer.test_with_noop:
+                lb_kwargs[const.VIP_NETWORK_ID] = (
+                    cls.lb_member_vip_net[const.ID])
+        else:
+            lb_kwargs[const.VIP_NETWORK_ID] = cls.lb_member_vip_net[const.ID]
+            lb_kwargs[const.VIP_SUBNET_ID] = None
+
+
+class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
+    @classmethod
+    def resource_setup(cls):
+        super(LoadBalancerBaseTestWithCompute, cls).resource_setup()
+        # If validation is disabled in this cloud, we won't be able to
+        # start the webservers, so don't even boot them.
+        if not CONF.validation.run_validation:
+            return
+
+        # Create a keypair for the webservers
+        keypair_name = data_utils.rand_name('lb_member_keypair')
+        result = cls.lb_mem_keypairs_client.create_keypair(
+            name=keypair_name)
+        cls.lb_member_keypair = result['keypair']
+        LOG.info('lb_member_keypair: {}'.format(cls.lb_member_keypair))
+        cls.addClassResourceCleanup(
+            waiters.wait_for_not_found,
+            cls.lb_mem_keypairs_client.delete_keypair,
+            cls.lb_mem_keypairs_client.show_keypair,
+            keypair_name)
+
+        if (CONF.load_balancer.enable_security_groups and
+                CONF.network_feature_enabled.port_security):
+            # Set up the security group for the webservers
+            SG_name = data_utils.rand_name('lb_member_SG')
+            cls.lb_member_sec_group = (
+                cls.lb_mem_SG_client.create_security_group(
+                    name=SG_name)['security_group'])
+            cls.addClassResourceCleanup(
+                waiters.wait_for_not_found,
+                cls.lb_mem_SG_client.delete_security_group,
+                cls.lb_mem_SG_client.show_security_group,
+                cls.lb_member_sec_group['id'])
+
+            # Create a security group rule to allow 80-81 (test webservers)
+            SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                direction='ingress',
+                security_group_id=cls.lb_member_sec_group['id'],
+                protocol='tcp',
+                ethertype='IPv4',
+                port_range_min=80,
+                port_range_max=81)['security_group_rule']
+            cls.addClassResourceCleanup(
+                waiters.wait_for_not_found,
+                cls.lb_mem_SGr_client.delete_security_group_rule,
+                cls.lb_mem_SGr_client.show_security_group_rule,
+                SGr['id'])
+            # Create a security group rule to allow 22 (ssh)
+            SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                direction='ingress',
+                security_group_id=cls.lb_member_sec_group['id'],
+                protocol='tcp',
+                ethertype='IPv4',
+                port_range_min=22,
+                port_range_max=22)['security_group_rule']
+            cls.addClassResourceCleanup(
+                waiters.wait_for_not_found,
+                cls.lb_mem_SGr_client.delete_security_group_rule,
+                cls.lb_mem_SGr_client.show_security_group_rule,
+                SGr['id'])
+            if CONF.load_balancer.test_with_ipv6:
+                # Create a security group rule to allow 80-81 (test webservers)
+                SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                    direction='ingress',
+                    security_group_id=cls.lb_member_sec_group['id'],
+                    protocol='tcp',
+                    ethertype='IPv6',
+                    port_range_min=80,
+                    port_range_max=81)['security_group_rule']
+                cls.addClassResourceCleanup(
+                    waiters.wait_for_not_found,
+                    cls.lb_mem_SGr_client.delete_security_group_rule,
+                    cls.lb_mem_SGr_client.show_security_group_rule,
+                    SGr['id'])
+                # Create a security group rule to allow 22 (ssh)
+                SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                    direction='ingress',
+                    security_group_id=cls.lb_member_sec_group['id'],
+                    protocol='tcp',
+                    ethertype='IPv6',
+                    port_range_min=22,
+                    port_range_max=22)['security_group_rule']
+                cls.addClassResourceCleanup(
+                    waiters.wait_for_not_found,
+                    cls.lb_mem_SGr_client.delete_security_group_rule,
+                    cls.lb_mem_SGr_client.show_security_group_rule,
+                    SGr['id'])
+
+            LOG.info('lb_member_sec_group: {}'.format(cls.lb_member_sec_group))
+
+        # Create webserver 1 instance
+        server_details = cls._create_webserver('lb_member_webserver1',
+                                               cls.lb_member_1_net)
+
+        cls.lb_member_webserver1 = server_details['server']
+        cls.webserver1_ip = server_details.get('ipv4_address')
+        cls.webserver1_ipv6 = server_details.get('ipv6_address')
+        cls.webserver1_public_ip = server_details['public_ipv4_address']
+
+        LOG.debug('Octavia Setup: lb_member_webserver1 = {}'.format(
+            cls.lb_member_webserver1[const.ID]))
+        LOG.debug('Octavia Setup: webserver1_ip = {}'.format(
+            cls.webserver1_ip))
+        LOG.debug('Octavia Setup: webserver1_ipv6 = {}'.format(
+            cls.webserver1_ipv6))
+        LOG.debug('Octavia Setup: webserver1_public_ip = {}'.format(
+            cls.webserver1_public_ip))
+
+        # Create webserver 2 instance
+        server_details = cls._create_webserver('lb_member_webserver2',
+                                               cls.lb_member_2_net)
+
+        cls.lb_member_webserver2 = server_details['server']
+        cls.webserver2_ip = server_details.get('ipv4_address')
+        cls.webserver2_ipv6 = server_details.get('ipv6_address')
+        cls.webserver2_public_ip = server_details['public_ipv4_address']
+
+        LOG.debug('Octavia Setup: lb_member_webserver2 = {}'.format(
+            cls.lb_member_webserver2[const.ID]))
+        LOG.debug('Octavia Setup: webserver2_ip = {}'.format(
+            cls.webserver2_ip))
+        LOG.debug('Octavia Setup: webserver2_ipv6 = {}'.format(
+            cls.webserver2_ipv6))
+        LOG.debug('Octavia Setup: webserver2_public_ip = {}'.format(
+            cls.webserver2_public_ip))
+
+        # Set up serving on webserver 1
+        cls._install_start_webserver(cls.webserver1_public_ip,
+                                     cls.lb_member_keypair['private_key'], 1)
+
+        # Validate webserver 1
+        cls._validate_webserver(cls.webserver1_public_ip, 1)
+
+        # Set up serving on webserver 2
+        cls._install_start_webserver(cls.webserver2_public_ip,
+                                     cls.lb_member_keypair['private_key'], 5)
+
+        # Validate webserver 2
+        cls._validate_webserver(cls.webserver2_public_ip, 5)
+
+    @classmethod
+    def _create_networks(cls):
+        super(LoadBalancerBaseTestWithCompute, cls)._create_networks()
         # Create a router for the subnets (required for the floating IP)
         router_name = data_utils.rand_name("lb_member_router")
         result = cls.lb_mem_routers_client.create_router(
@@ -654,31 +654,87 @@
         return webserver_details
 
     @classmethod
+    def _install_start_webserver(cls, ip_address, ssh_key, start_id):
+        local_file = pkg_resources.resource_filename(
+            'octavia_tempest_plugin.contrib.httpd', 'httpd.bin')
+        dest_file = '/dev/shm/httpd.bin'
+
+        linux_client = remote_client.RemoteClient(
+            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key)
+        linux_client.validate_authentication()
+
+        with tempfile.NamedTemporaryFile() as key:
+            key.write(ssh_key.encode('utf-8'))
+            key.flush()
+            cmd = ("scp -v -o UserKnownHostsFile=/dev/null "
+                   "-o StrictHostKeyChecking=no "
+                   "-o ConnectTimeout={0} -o ConnectionAttempts={1} "
+                   "-i {2} {3} {4}@{5}:{6}").format(
+                CONF.load_balancer.scp_connection_timeout,
+                CONF.load_balancer.scp_connection_attempts,
+                key.name, local_file, CONF.validation.image_ssh_user,
+                ip_address, dest_file)
+            args = shlex.split(cmd)
+            subprocess_args = {'stdout': subprocess.PIPE,
+                               'stderr': subprocess.STDOUT,
+                               'cwd': None}
+            proc = subprocess.Popen(args, **subprocess_args)
+            stdout, stderr = proc.communicate()
+            if proc.returncode != 0:
+                raise exceptions.CommandFailed(proc.returncode, cmd,
+                                               stdout, stderr)
+        linux_client.exec_command('sudo screen -d -m {0} -port 80 '
+                                  '-id {1}'.format(dest_file, start_id))
+        linux_client.exec_command('sudo screen -d -m {0} -port 81 '
+                                  '-id {1}'.format(dest_file, start_id + 1))
+
+    @classmethod
     def _validate_webserver(cls, ip_address, start_id):
         URL = 'http://{0}'.format(ip_address)
         validators.validate_URL_response(URL, expected_body=str(start_id))
         URL = 'http://{0}:81'.format(ip_address)
         validators.validate_URL_response(URL, expected_body=str(start_id + 1))
 
-    @classmethod
-    def _setup_lb_network_kwargs(cls, lb_kwargs, ip_version):
-        if cls.lb_member_vip_subnet:
-            ip_index = data_utils.rand_int_id(start=10, end=100)
-            if ip_version == 4:
-                network = ipaddress.IPv4Network(
-                    six.u(CONF.load_balancer.vip_subnet_cidr))
-                lb_vip_address = str(network[ip_index])
-                subnet_id = cls.lb_member_vip_subnet[const.ID]
-            else:
-                network = ipaddress.IPv6Network(
-                    six.u(CONF.load_balancer.vip_ipv6_subnet_cidr))
-                lb_vip_address = str(network[ip_index])
-                subnet_id = cls.lb_member_vip_ipv6_subnet[const.ID]
-            lb_kwargs[const.VIP_SUBNET_ID] = subnet_id
-            lb_kwargs[const.VIP_ADDRESS] = lb_vip_address
-            if CONF.load_balancer.test_with_noop:
-                lb_kwargs[const.VIP_NETWORK_ID] = (
-                    cls.lb_member_vip_net[const.ID])
-        else:
-            lb_kwargs[const.VIP_NETWORK_ID] = cls.lb_member_vip_net[const.ID]
-            lb_kwargs[const.VIP_SUBNET_ID] = None
+    def _wait_for_lb_functional(self, vip_address):
+        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)
+                time.sleep(1)
+                return
+            except Exception:
+                LOG.warning('Server is not passing initial traffic. Waiting.')
+                time.sleep(1)
+        LOG.error('Server did not begin passing traffic within the timeout '
+                  'period. Failing test.')
+        raise Exception()
+
+    def _check_members_balanced(self, vip_address):
+        session = requests.Session()
+        response_counts = {}
+
+        self._wait_for_lb_functional(vip_address)
+
+        # Send a number requests to lb vip
+        for i in range(20):
+            try:
+                r = session.get('http://{0}'.format(vip_address),
+                                timeout=2)
+
+                if r.content in response_counts:
+                    response_counts[r.content] += 1
+                else:
+                    response_counts[r.content] = 1
+
+            except Exception:
+                LOG.exception('Failed to send request to loadbalancer vip')
+                raise Exception('Failed to connect to lb')
+
+        LOG.debug('Loadbalancer response totals: %s', response_counts)
+        # Ensure the correct number of members
+        self.assertEqual(2, len(response_counts))
+
+        # Ensure both members got the same number of responses
+        self.assertEqual(1, len(set(response_counts.values())))