diff --git a/devstack/files/debs/octavia b/devstack/files/debs/octavia new file mode 100644 index 0000000000..f5267c2664 --- /dev/null +++ b/devstack/files/debs/octavia @@ -0,0 +1 @@ +golang diff --git a/devstack/files/rpms/octavia b/devstack/files/rpms/octavia new file mode 100644 index 0000000000..f5267c2664 --- /dev/null +++ b/devstack/files/rpms/octavia @@ -0,0 +1 @@ +golang diff --git a/octavia/tests/contrib/httpd.go b/octavia/tests/contrib/httpd.go new file mode 100644 index 0000000000..23814e301c --- /dev/null +++ b/octavia/tests/contrib/httpd.go @@ -0,0 +1,87 @@ +package main + +import ( + "flag" + "fmt" + "io" + "net/http" + "sync" + "time" +) + +var sess_cookie http.Cookie +var resp string + +type ConnectionCount struct { + mu sync.Mutex + cur_conn int + max_conn int + total_conn int +} + +var scoreboard ConnectionCount + +func (cc *ConnectionCount) open() { + cc.mu.Lock() + defer cc.mu.Unlock() + + cc.cur_conn++ + cc.total_conn++ +} + +func (cc *ConnectionCount) close() { + cc.mu.Lock() + defer cc.mu.Unlock() + + if cc.cur_conn > cc.max_conn { + cc.max_conn = cc.cur_conn + } + cc.cur_conn-- +} + +func (cc *ConnectionCount) stats() (int, int) { + cc.mu.Lock() + defer cc.mu.Unlock() + + return cc.max_conn, cc.total_conn +} + +func root_handler(w http.ResponseWriter, r *http.Request) { + scoreboard.open() + defer scoreboard.close() + + http.SetCookie(w, &sess_cookie) + io.WriteString(w, resp) +} + +func slow_handler(w http.ResponseWriter, r *http.Request) { + scoreboard.open() + defer scoreboard.close() + + time.Sleep(3 * time.Second) + http.SetCookie(w, &sess_cookie) + io.WriteString(w, resp) +} + +func stats_handler(w http.ResponseWriter, r *http.Request) { + http.SetCookie(w, &sess_cookie) + max_conn, total_conn := scoreboard.stats() + fmt.Fprintf(w, "max_conn=%d\ntotal_conn=%d\n", max_conn, total_conn) +} + +func main() { + portPtr := flag.Int("port", 8080, "TCP port to listen on") + idPtr := flag.Int("id", 1, "Server ID") + + flag.Parse() + + resp = fmt.Sprintf("server%d", *idPtr) + sess_cookie.Name = "JESSIONID" + sess_cookie.Value = fmt.Sprintf("%d", *idPtr) + + http.HandleFunc("/", root_handler) + http.HandleFunc("/slow", slow_handler) + http.HandleFunc("/stats", stats_handler) + portStr := fmt.Sprintf(":%d", *portPtr) + http.ListenAndServe(portStr, nil) +} diff --git a/octavia/tests/tempest/v1/scenario/base.py b/octavia/tests/tempest/v1/scenario/base.py index 47b820b7f7..8e3c5d3c63 100644 --- a/octavia/tests/tempest/v1/scenario/base.py +++ b/octavia/tests/tempest/v1/scenario/base.py @@ -17,7 +17,9 @@ try: from http import cookiejar as cookielib except ImportError: import cookielib +import os import shlex +import shutil import socket import subprocess import tempfile @@ -47,6 +49,9 @@ from octavia.tests.tempest.v1.clients import pools_client config = config.CONF LOG = logging.getLogger(__name__) +HTTPD_SRC = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../../../contrib/httpd.go')) class BaseTestCase(manager.NetworkScenarioTest): @@ -221,6 +226,18 @@ class BaseTestCase(manager.NetworkScenarioTest): waiters.wait_for_server_status(self.servers_client, value, 'ACTIVE') + def _build_static_httpd(self): + """Compile test httpd as a static binary + + returns file path of resulting binary file + """ + builddir = tempfile.mkdtemp() + shutil.copyfile(HTTPD_SRC, os.path.join(builddir, 'httpd.go')) + self.execute('go build -ldflags ' + '"-linkmode external -extldflags -static" ' + 'httpd.go', cwd=builddir) + return os.path.join(builddir, 'httpd') + def _start_servers(self): """Start one or more backends @@ -236,46 +253,30 @@ class BaseTestCase(manager.NetworkScenarioTest): ip_address=ip, private_key=private_key) - # Write a backend's response into a file - resp = ('echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n' - 'Set-Cookie:JSESSIONID=%(s_id)s\r\nConnection: close\r\n' - 'Content-Type: text/html; ' - 'charset=UTF-8\r\n\r\n%(server)s"; cat >/dev/null') + httpd = self._build_static_httpd() - with tempfile.NamedTemporaryFile() as script: - script.write(resp % {'s_id': server_name[-1], - 'server': server_name}) - script.flush() - with tempfile.NamedTemporaryFile() as key: - key.write(private_key) - key.flush() - self.copy_file_to_host(script.name, - "/tmp/script1", - ip, - username, key.name) + with tempfile.NamedTemporaryFile() as key: + key.write(private_key) + key.flush() + self.copy_file_to_host(httpd, + "/dev/shm/httpd", + ip, + username, key.name) - # Start netcat - start_server = ('while true; do ' - 'sudo nc -ll -p %(port)s -e sh /tmp/%(script)s; ' - 'done > /dev/null &') - cmd = start_server % {'port': self.port1, - 'script': 'script1'} + # Start httpd + start_server = ('sudo sh -c "ulimit -n 100000; nohup ' + '/dev/shm/httpd -id %(id)s ' + '-port %(port)s &"') + cmd = start_server % {'id': server_name[-1], + 'port': self.port1} ssh_client.exec_command(cmd) + # In single server case run a second server on an alternate port if len(self.server_ips) == 1: - with tempfile.NamedTemporaryFile() as script: - script.write(resp % {'s_id': 2, - 'server': 'server2'}) - script.flush() - with tempfile.NamedTemporaryFile() as key: - key.write(private_key) - key.flush() - self.copy_file_to_host(script.name, - "/tmp/script2", ip, - username, key.name) - cmd = start_server % {'port': self.port2, - 'script': 'script2'} + cmd = start_server % {'id': '2', + 'port': self.port2} ssh_client.exec_command(cmd) + # Allow ssh_client connection to fall out of scope def _create_listener(self, load_balancer_id, default_pool_id=None): """Create a listener with HTTP protocol listening on port 80.""" @@ -754,9 +755,13 @@ class BaseTestCase(manager.NetworkScenarioTest): "-i %(pkey)s %(file1)s %(dest)s" % {'pkey': pkey, 'file1': file_from, 'dest': dest}) + return self.execute(cmd) + + def execute(self, cmd, cwd=None): args = shlex.split(cmd.encode('utf-8')) subprocess_args = {'stdout': subprocess.PIPE, - 'stderr': subprocess.STDOUT} + 'stderr': subprocess.STDOUT, + 'cwd': cwd} proc = subprocess.Popen(args, **subprocess_args) stdout, stderr = proc.communicate() if proc.returncode != 0: