Merge "Change pool create scenario test to wait for operating status"
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 4b8f60b..5eac133 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -16,9 +16,11 @@
     fi
 
     go_path=$(find $DEST/tempest/.tox/tempest/ -name test_server.go)
-    bin_path=${go_path%.go}.bin
+    sudo mkdir -m755 -p /opt/octavia-tempest-plugin
+    sudo chown $STACK_USER /opt/octavia-tempest-plugin
     CGO_ENABLED=0 GOOS=linux go build \
-        -a -ldflags '-s -w -extldflags -static' -o $bin_path \
+        -a -ldflags '-s -w -extldflags -static' \
+        -o /opt/octavia-tempest-plugin/test_server.bin \
         ${DEST}/octavia-tempest-plugin/octavia_tempest_plugin/contrib/test_server/test_server.go
 }
 
@@ -26,11 +28,11 @@
     case "$2" in
         install)
             # Install dev library if
-            # - the release is more recent than stein (devstack in stein would
-            #   try to install it in a python2 env, but octavia-tempest-plugin is
-            #   now a python3-only project)
+            # - the release is more recent than train (devstack in train would
+            #   try to install it in a python2 env, but octavia-tempest-plugin
+            #   is now a python3-only project)
             # - or the user explicitly requests it (INSTALL_TEMPEST=True)
-            if [[ "$DEVSTACK_SERIES" != "stein" ]] || [[ "$(trueorfalse False INSTALL_TEMPEST)" == "True" ]]; then
+            if [[ ! "$DEVSTACK_SERIES" =~ (stein|train) ]] || [[ "$(trueorfalse False INSTALL_TEMPEST)" == "True" ]]; then
                 echo_summary "Installing octavia-tempest-plugin"
                 install_octavia_tempest_plugin
             fi
diff --git a/octavia_tempest_plugin/config.py b/octavia_tempest_plugin/config.py
index 77d2f6e..f44bf96 100644
--- a/octavia_tempest_plugin/config.py
+++ b/octavia_tempest_plugin/config.py
@@ -213,6 +213,10 @@
                default='/var/log/octavia-amphora.log',
                help='File path, on the tempest system, to the amphora admin '
                     'log file.'),
+    cfg.StrOpt('test_server_path',
+               default='/opt/octavia-tempest-plugin/test_server.bin',
+               help='Filesystem path to the test web server that will be '
+                    'installed in the web server VMs.'),
 ]
 
 lb_feature_enabled_group = cfg.OptGroup(name='loadbalancer-feature-enabled',
diff --git a/octavia_tempest_plugin/contrib/test_server/test_server.go b/octavia_tempest_plugin/contrib/test_server/test_server.go
index f8bc1e0..27b6b2c 100644
--- a/octavia_tempest_plugin/contrib/test_server/test_server.go
+++ b/octavia_tempest_plugin/contrib/test_server/test_server.go
@@ -16,60 +16,60 @@
 	"time"
 )
 
-var sess_cookie http.Cookie
+var sessCookie http.Cookie
 var resp string
 
-type ConnectionCount struct {
-	mu         sync.Mutex
-	cur_conn   int
-	max_conn   int
-	total_conn int
+type connectionCount struct {
+	mu        sync.Mutex
+	curConn   int
+	maxConn   int
+	totalConn int
 }
 
-var scoreboard ConnectionCount
+var scoreboard connectionCount
 
-func (cc *ConnectionCount) open() {
+func (cc *connectionCount) open() {
 	cc.mu.Lock()
 	defer cc.mu.Unlock()
 
-	cc.cur_conn++
-	cc.total_conn++
+	cc.curConn++
+	cc.totalConn++
 }
 
-func (cc *ConnectionCount) close() {
+func (cc *connectionCount) close() {
 	cc.mu.Lock()
 	defer cc.mu.Unlock()
 
-	if cc.cur_conn > cc.max_conn {
-		cc.max_conn = cc.cur_conn
+	if cc.curConn > cc.maxConn {
+		cc.maxConn = cc.curConn
 	}
-	cc.cur_conn--
+	cc.curConn--
 }
 
-func (cc *ConnectionCount) stats() (int, int) {
+func (cc *connectionCount) stats() (int, int) {
 	cc.mu.Lock()
 	defer cc.mu.Unlock()
 
-	return cc.max_conn, cc.total_conn
+	return cc.maxConn, cc.totalConn
 }
 
-func (cc *ConnectionCount) reset() {
+func (cc *connectionCount) reset() {
 	cc.mu.Lock()
 	defer cc.mu.Unlock()
 
-	cc.max_conn = 0
-	cc.total_conn = 0
+	cc.maxConn = 0
+	cc.totalConn = 0
 }
 
-func root_handler(w http.ResponseWriter, r *http.Request) {
+func rootHandler(w http.ResponseWriter, r *http.Request) {
 	scoreboard.open()
 	defer scoreboard.close()
 
-	http.SetCookie(w, &sess_cookie)
+	http.SetCookie(w, &sessCookie)
 	io.WriteString(w, resp)
 }
 
-func slow_handler(w http.ResponseWriter, r *http.Request) {
+func slowHandler(w http.ResponseWriter, r *http.Request) {
 	scoreboard.open()
 	defer scoreboard.close()
 
@@ -79,59 +79,59 @@
 	}
 
 	time.Sleep(delay)
-	http.SetCookie(w, &sess_cookie)
+	http.SetCookie(w, &sessCookie)
 	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 statsHandler(w http.ResponseWriter, r *http.Request) {
+	http.SetCookie(w, &sessCookie)
+	maxConn, totalConn := scoreboard.stats()
+	fmt.Fprintf(w, "maxConn=%d\ntotalConn=%d\n", maxConn, totalConn)
 }
 
-func https_wrapper(base_handler func(http.ResponseWriter,
+func httpsWrapper(baseHandler 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)
+		baseHandler(w, r)
 	})
 }
 
-func reset_handler(w http.ResponseWriter, r *http.Request) {
-	http.SetCookie(w, &sess_cookie)
+func resetHandler(w http.ResponseWriter, r *http.Request) {
+	http.SetCookie(w, &sessCookie)
 	scoreboard.reset()
 	fmt.Fprintf(w, "reset\n")
 }
 
-func http_setup(id string) {
-	sess_cookie.Name = "JSESSIONID"
-	sess_cookie.Value = id
+func httpSetup(id string) {
+	sessCookie.Name = "JSESSIONID"
+	sessCookie.Value = id
 
-	http.HandleFunc("/", root_handler)
-	http.HandleFunc("/slow", slow_handler)
-	http.HandleFunc("/stats", stats_handler)
-	http.HandleFunc("/reset", reset_handler)
+	http.HandleFunc("/", rootHandler)
+	http.HandleFunc("/slow", slowHandler)
+	http.HandleFunc("/stats", statsHandler)
+	http.HandleFunc("/reset", resetHandler)
 }
 
-func http_serve(port int, id string) {
+func httpServe(port int, id string) {
 	portStr := fmt.Sprintf(":%d", port)
 	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) {
+func httpsServe(port int, id string, cert tls.Certificate,
+	certpool *x509.CertPool, serverCertPem string,
+	serverKeyPem 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))
+	mux.Handle("/", httpsWrapper(rootHandler))
+	mux.Handle("/slow", httpsWrapper(slowHandler))
+	mux.Handle("/stats", httpsWrapper(statsHandler))
+	mux.Handle("/reset", httpsWrapper(resetHandler))
 
-	var tls_config *tls.Config
+	var tlsConfig *tls.Config
 	if certpool != nil {
-		tls_config = &tls.Config{
+		tlsConfig = &tls.Config{
 			Certificates: []tls.Certificate{cert},
 			ClientAuth:   tls.RequireAndVerifyClientCert,
 			ClientCAs:    certpool,
@@ -147,7 +147,7 @@
 			},
 		}
 	} else {
-		tls_config = &tls.Config{
+		tlsConfig = &tls.Config{
 			Certificates: []tls.Certificate{cert},
 			ClientAuth:   tls.NoClientCert,
 			MinVersion:   tls.VersionTLS12,
@@ -160,21 +160,22 @@
 				tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
 				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
 			},
+			NextProtos: []string{"h2", "http/1.1", "http/1.0"},
 		}
 	}
-	tls_config.Rand = rand.Reader
+	tlsConfig.Rand = rand.Reader
 	portStr := fmt.Sprintf(":%d", port)
 	srv := &http.Server{
 		Addr:      portStr,
 		Handler:   mux,
-		TLSConfig: tls_config,
+		TLSConfig: tlsConfig,
 		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn,
 			http.Handler), 0),
 	}
-	log.Fatal(srv.ListenAndServeTLS(server_cert_pem, server_key_pem))
+	log.Fatal(srv.ListenAndServeTLS(serverCertPem, serverKeyPem))
 }
 
-func udp_serve(port int, id string) {
+func udpServe(port int, id string) {
 	portStr := fmt.Sprintf("0.0.0.0:%d", port)
 
 	pc, err := net.ListenPacket("udp", portStr)
@@ -202,44 +203,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,
+	httpsPortPtr := flag.Int("https_port", -1,
 		"HTTPS port to listen on, -1 is disabled.")
-	server_cert_pem := flag.String("cert", "",
+	serverCertPem := 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", "",
+	serverKey := flag.String("key", "", "Server side PEM format key.")
+	clientCaCertPem := flag.String("client_ca", "",
 		"Client side PEM format CA certificate.")
 
 	flag.Parse()
 
 	resp = fmt.Sprintf("%s", *idPtr)
 
-	http_setup(*idPtr)
+	httpSetup(*idPtr)
 
-	if *https_portPtr > -1 {
-		cert, err := tls.LoadX509KeyPair(*server_cert_pem, *server_key)
+	if *httpsPortPtr > -1 {
+		cert, err := tls.LoadX509KeyPair(*serverCertPem, *serverKey)
 		if err != nil {
-			fmt.Println("Error load server certificate and key.\n")
+			fmt.Println("Error load server certificate and key.")
 			os.Exit(1)
 		}
 		certpool := x509.NewCertPool()
-		if *client_ca_cert_pem != "" {
-			ca_pem, err := ioutil.ReadFile(*client_ca_cert_pem)
+		if *clientCaCertPem != "" {
+			caPem, err := ioutil.ReadFile(*clientCaCertPem)
 			if err != nil {
-				fmt.Println("Error load client side CA cert.\n")
+				fmt.Println("Error load client side CA cert.")
 				os.Exit(1)
 			}
-			if !certpool.AppendCertsFromPEM(ca_pem) {
+			if !certpool.AppendCertsFromPEM(caPem) {
 				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 httpsServe(*httpsPortPtr, *idPtr, cert, certpool,
+			*serverCertPem, *serverKey)
 	}
 
-	go http_serve(*portPtr, *idPtr)
-	udp_serve(*portPtr, *idPtr)
+	go httpServe(*portPtr, *idPtr)
+	udpServe(*portPtr, *idPtr)
 }
diff --git a/octavia_tempest_plugin/tests/test_base.py b/octavia_tempest_plugin/tests/test_base.py
index f260e88..669a33c 100644
--- a/octavia_tempest_plugin/tests/test_base.py
+++ b/octavia_tempest_plugin/tests/test_base.py
@@ -13,7 +13,6 @@
 #    under the License.
 
 import ipaddress
-import pkg_resources
 import random
 import shlex
 import string
@@ -849,8 +848,7 @@
 
     @classmethod
     def _install_start_webserver(cls, ip_address, ssh_key, start_id):
-        local_file = pkg_resources.resource_filename(
-            'octavia_tempest_plugin.contrib.test_server', 'test_server.bin')
+        local_file = CONF.load_balancer.test_server_path
         dest_file = '/dev/shm/test_server.bin'
 
         linux_client = remote_client.RemoteClient(
diff --git a/octavia_tempest_plugin/tests/waiters.py b/octavia_tempest_plugin/tests/waiters.py
index e0d9d2d..fa6c112 100644
--- a/octavia_tempest_plugin/tests/waiters.py
+++ b/octavia_tempest_plugin/tests/waiters.py
@@ -68,7 +68,7 @@
             LOG.info('{name}\'s status updated to {status}.'.format(
                 name=show_client.__name__, status=status))
             return object_details
-        elif object_details[status_key] == 'ERROR':
+        elif object_details[status_key] == 'ERROR' and not error_ok:
             message = ('{name} {field} updated to an invalid state of '
                        'ERROR'.format(name=show_client.__name__,
                                       field=status_key))
@@ -76,9 +76,9 @@
             if caller:
                 message = '({caller}) {message}'.format(caller=caller,
                                                         message=message)
-            if not error_ok:
-                raise exceptions.UnexpectedResponseCode(message)
-        elif int(time.time()) - start >= check_timeout:
+            raise exceptions.UnexpectedResponseCode(message)
+
+        if int(time.time()) - start >= check_timeout:
             message = (
                 '{name} {field} failed to update to {expected_status} within '
                 'the required time {timeout}. Current status of {name}: '
diff --git a/releasenotes/notes/test-server-path-3845f619090ba016.yaml b/releasenotes/notes/test-server-path-3845f619090ba016.yaml
new file mode 100644
index 0000000..51f79dd
--- /dev/null
+++ b/releasenotes/notes/test-server-path-3845f619090ba016.yaml
@@ -0,0 +1,7 @@
+---
+other:
+  - |
+    The Octavia tempest plugin now as a configuration setting for the path to
+    the test server. By default it will expect the test server to now be
+    located in /opt/octavia-tempest-plugin/test_server.bin. The devstack
+    plugin has been updated to place the test_server.bin in that location.