summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYang (Gabriel) Yu <Gabriel.yuyang@huawei.com>2018-08-15 12:39:33 +0800
committerYang (Gabriel) Yu <Gabriel.yuyang@huawei.com>2018-08-16 20:25:07 +0800
commiteacf878ec5372a6db4596c9cbc78ef96bf2453b9 (patch)
treee1d525b9c645ee10fc26f203e5b899d0e34cd9e6
parent480c3c51a0a2bcc67ac5ca1d96cb63153bf9fff8 (diff)
add k8s capacity test case
JIRA: BOTTLENECK-243 Change-Id: I0f36aac10cf0e05560051c785ada397e0c97e112 Signed-off-by: Yang (Gabriel) Yu <Gabriel.yuyang@huawei.com>
-rw-r--r--testsuites/kubestone/__init__.py0
-rw-r--r--testsuites/kubestone/samples/__init__.py0
-rw-r--r--testsuites/kubestone/samples/deployment_sample.yaml21
-rw-r--r--testsuites/kubestone/samples/pod_sample.yaml11
-rw-r--r--testsuites/kubestone/stress_test.py156
-rw-r--r--testsuites/kubestone/testcases/__init__.py0
-rw-r--r--testsuites/kubestone/testcases/deployment_capacity.yaml25
-rw-r--r--utils/k8s_setup/k8s_config_pre.sh2
-rw-r--r--utils/k8s_setup/k8s_utils.py53
9 files changed, 260 insertions, 8 deletions
diff --git a/testsuites/kubestone/__init__.py b/testsuites/kubestone/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuites/kubestone/__init__.py
diff --git a/testsuites/kubestone/samples/__init__.py b/testsuites/kubestone/samples/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuites/kubestone/samples/__init__.py
diff --git a/testsuites/kubestone/samples/deployment_sample.yaml b/testsuites/kubestone/samples/deployment_sample.yaml
new file mode 100644
index 0000000..f7f95de
--- /dev/null
+++ b/testsuites/kubestone/samples/deployment_sample.yaml
@@ -0,0 +1,21 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nginx-deployment
+ labels:
+ app: nginx
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.7.9
+ ports:
+ - containerPort: 80
diff --git a/testsuites/kubestone/samples/pod_sample.yaml b/testsuites/kubestone/samples/pod_sample.yaml
new file mode 100644
index 0000000..3035cbb
--- /dev/null
+++ b/testsuites/kubestone/samples/pod_sample.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: myapp-pod
+ labels:
+ app: myapp
+spec:
+ containers:
+ - name: myapp-container
+ image: busybox
+ command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
diff --git a/testsuites/kubestone/stress_test.py b/testsuites/kubestone/stress_test.py
new file mode 100644
index 0000000..7f5d75f
--- /dev/null
+++ b/testsuites/kubestone/stress_test.py
@@ -0,0 +1,156 @@
+from kubernetes import client, config
+from utils.k8s_setup import k8s_utils
+
+import os
+import datetime
+import uuid
+
+import utils.logger as log
+import yaml
+import argparse
+
+LOG = log.Logger(__name__).getLogger()
+
+parser = argparse.ArgumentParser(description='kubestone (k8s stress) tests')
+parser.add_argument("-c", "--TEST_CASE",
+ help="The path of test case in form of yaml")
+args = parser.parse_args()
+TEST_CASE = args.TEST_CASE
+TEST_CASE_NAME, TEST_CASE_FORMAT = os.path.splitext(
+ os.path.basename(TEST_CASE))
+OUT_FILE = ("/tmp/bottlenecks_kubestone_" +
+ TEST_CASE_NAME + "_" + str(uuid.uuid4()) + ".out")
+
+
+def test_step_result(num, success_num, during_seconds, result):
+ testdata = {}
+ test_result = {}
+ test_result["number_of_deployments"] = float(num)
+ test_result["success_deployments"] = success_num
+ test_result["success_rate"] = success_num / num
+ test_result["duration_time"] = during_seconds
+ test_result["result"] = result
+ testdata["data_body"] = test_result
+ testdata["testcase"] = TEST_CASE_NAME
+ return testdata
+
+
+def main():
+ INSTALLER_TYPE = os.getenv("INSTALLER_TYPE")
+ K8S_CONFIG_PATH = os.getenv("K8S_CONFIG_PATH")
+ K8S_APPS_API_VERSION = os.getenv("K8S_APPS_API_VERSION")
+ K8S_CORE_API_VERSION = os.getenv("K8S_CORE_API_VERSION")
+ # Get k8s config. If provided in the path indicated by
+ # K8S_CONFIG_PATH, only return the path.
+ if K8S_CONFIG_PATH:
+ k8s_utils.get_config_path(
+ K8S_CONFIG_PATH=K8S_CONFIG_PATH)
+ else:
+ if INSTALLER_TYPE:
+ K8S_CONFIG_PATH = k8s_utils.get_config_path(
+ INSTALLER_TYPE=INSTALLER_TYPE)
+ else:
+ k8s_utils.get_config_path()
+
+ config.load_kube_config(K8S_CONFIG_PATH)
+
+ # Initiate api clients
+ if K8S_APPS_API_VERSION:
+ apps_api = k8s_utils.get_apps_api(K8S_APPS_API_VERSION)
+ else:
+ apps_api = k8s_utils.get_apps_api()
+
+ if K8S_CORE_API_VERSION:
+ core_api = k8s_utils.get_core_api(K8S_CORE_API_VERSION)
+ else:
+ core_api = k8s_utils.get_core_api()
+
+ # Read test case in the form of yaml
+ with open(TEST_CASE) as test_case_file:
+ test_case_yaml = yaml.load(test_case_file)
+ if test_case_yaml['template']:
+ if test_case_yaml['template'].lower() == 'none':
+ deployment_yaml = test_case_yaml
+ else:
+ with open(test_case_yaml['template']) as deployment_file:
+ deployment_yaml = yaml.load(deployment_file)
+ else:
+ deployment_yaml = test_case_yaml
+
+ name = deployment_yaml['metadata']['name']
+ namespace = deployment_yaml['namespace']
+ body = client.V1Deployment()
+ body.api_version = deployment_yaml['apiVersion']
+ body.kind = deployment_yaml['kind']
+ body.metadata = deployment_yaml['metadata']
+ body.spec = deployment_yaml['spec']
+ pretty = True
+
+ # Create namespace
+ namespace_existed = k8s_utils.get_namespace_status(namespace)
+ if namespace_existed[0] == 0 and \
+ 'exception' not in namespace_existed[1].lower():
+ namespace_read = core_api.read_namespace(namespace, pretty=pretty)
+ LOG.info('Namespace {} already exist: \n{}'.format(
+ namespace, namespace_read))
+ else:
+ namespace_body = client.V1Namespace()
+ namespace_body.metadata = {'name': namespace}
+ namespace_created = core_api.create_namespace(
+ namespace_body, pretty=pretty)
+ LOG.info('Namespace has been created:\n{}'.format(
+ namespace_created))
+
+ # Create deployment
+ deployment_existed = k8s_utils.get_deployment_status(name, namespace)
+ if deployment_existed[0] == 0 and \
+ 'exception' not in deployment_existed[1].lower():
+ deployment_read = apps_api.read_namespaced_deployment(
+ name, namespace, pretty=pretty)
+ LOG.info('Deployment {}@{} already exist.'.format(name, namespace))
+ LOG.info('Discription of this deployment is:\n{}'.format(
+ deployment_read))
+ else:
+ deployment_created = apps_api.create_namespaced_deployment(
+ namespace, body, pretty=pretty)
+ LOG.info('Deployment has been created:\n{}'.format(
+ deployment_created))
+
+ # Scale the deployment
+ scaling_steps = deployment_yaml['scaling_steps'].split(',')
+ for step in scaling_steps:
+ start_time = datetime.datetime.now()
+
+ step = int(step)
+ body.spec['replicas'] = step
+ api_response = apps_api.patch_namespaced_deployment_scale(
+ name, namespace, body, pretty=pretty)
+ LOG.info("Deployment replicas is to be scaled to: %s" % step)
+ pods_number = k8s_utils.get_available_pods(name, namespace)
+ while pods_number != step:
+ pods_number = k8s_utils.get_available_pods(name, namespace)
+ LOG.info("Number of available pods are {} out of {}".format(
+ pods_number, step))
+ api_response = apps_api.read_namespaced_deployment_scale(
+ name, namespace, pretty=pretty)
+ LOG.info(
+ "Deployment {}-scaling finished:\n{}".format(
+ step, api_response))
+
+ end_time = datetime.datetime.now()
+ duration_seconds = (start_time - end_time).seconds
+ if pods_number == step:
+ criteria = 'PASS'
+ else:
+ criteria = 'FAIL'
+ test_result_body = test_step_result(
+ step, pods_number, duration_seconds, criteria)
+ k8s_utils.write_json(test_result_body, OUT_FILE)
+ if api_response:
+ LOG.info("Deployment scaling test has been successfuly executed.")
+ LOG.info("Testing results written in: {}".format(OUT_FILE))
+ return
+
+
+if __name__ == '__main__':
+ main()
diff --git a/testsuites/kubestone/testcases/__init__.py b/testsuites/kubestone/testcases/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuites/kubestone/testcases/__init__.py
diff --git a/testsuites/kubestone/testcases/deployment_capacity.yaml b/testsuites/kubestone/testcases/deployment_capacity.yaml
new file mode 100644
index 0000000..2638211
--- /dev/null
+++ b/testsuites/kubestone/testcases/deployment_capacity.yaml
@@ -0,0 +1,25 @@
+apiVersion: apps/v1
+kind: Deployment
+namespace: bottlenecks-kubestone
+test_type: Horizontal-Scaling
+scaling_steps: 10, 50, 100, 200
+template: None
+metadata:
+ name: nginx-deployment
+ labels:
+ app: nginx
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.7.9
+ ports:
+ - containerPort: 80
diff --git a/utils/k8s_setup/k8s_config_pre.sh b/utils/k8s_setup/k8s_config_pre.sh
index f41ba78..05c3f1c 100644
--- a/utils/k8s_setup/k8s_config_pre.sh
+++ b/utils/k8s_setup/k8s_config_pre.sh
@@ -7,7 +7,7 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-K8S_CONFIG="/tmp/k8s_conig"
+K8S_CONFIG="/tmp/k8s_config"
usage="Script to prepare kubenetes test configurations.
diff --git a/utils/k8s_setup/k8s_utils.py b/utils/k8s_setup/k8s_utils.py
index 7195bf2..a89889f 100644
--- a/utils/k8s_setup/k8s_utils.py
+++ b/utils/k8s_setup/k8s_utils.py
@@ -9,25 +9,29 @@
##############################################################################
import os
+import commands
+import json
import utils.logger as log
from kubernetes import client, watch
LOG = log.Logger(__name__).getLogger()
INSTALLER_TYPE = os.getenv("INSTALLER_TYPE")
+K8S_UTILS = "/home/opnfv/bottlenecks/utils/k8s_setup"
-def get_config_path(INSTALLER_TYPE=None, CONFIG_PATH="/tmp/k8s_config"):
+def get_config_path(INSTALLER_TYPE=None, K8S_CONFIG_PATH="/tmp/k8s_config"):
if INSTALLER_TYPE:
- CMD = "bash k8s_config_pre.sh -i " + INSTALLER_TYPE + \
- " -c " + CONFIG_PATH
+ CMD = "bash " + K8S_UTILS + "/k8s_config_pre.sh -i " \
+ + INSTALLER_TYPE + \
+ " -c " + K8S_CONFIG_PATH
LOG.info("Executing command: " + CMD)
- CONFIG_PATH = os.popen(CMD)
+ os.popen(CMD)
else:
- if not os.path.exists(CONFIG_PATH):
+ if not os.path.exists(K8S_CONFIG_PATH):
raise Exception("Must at least specify the path \
of k8s config!")
- return CONFIG_PATH
+ return K8S_CONFIG_PATH
def get_core_api(version='v1'):
@@ -35,10 +39,39 @@ def get_core_api(version='v1'):
API = client.CoreV1Api()
LOG.info(API)
else:
- raise Exception("Must input a validate verison!")
+ raise Exception("Must input a valid verison!")
return API
+def get_apps_api(version='v1'):
+ if version.lower() == 'v1':
+ API = client.AppsV1Api()
+ LOG.info(API)
+ else:
+ raise Exception("Must input a valid verison!")
+ return API
+
+
+def get_namespace_status(namespace):
+ CMD = ("kubectl get ns | grep %s" % namespace)
+ namespace_existed = commands.getstatusoutput(CMD)
+ return namespace_existed
+
+
+def get_deployment_status(name, namespace):
+ CMD = ("kubectl get deployment --namespace={} | grep {}".format(
+ namespace, name))
+ deployment_existed = commands.getstatusoutput(CMD)
+ return deployment_existed
+
+
+def get_available_pods(name, namespace):
+ CMD = ("kubectl get deployment --namespace={} | grep {}".format(
+ namespace, name) + " | awk '{print $5}'")
+ available_pods = commands.getstatusoutput(CMD)
+ return int(available_pods[1])
+
+
def watch_namespace(namespace, count=3, stop=None, request_timeout=0):
w = watch.Watch()
LOG.debug("Watch object generated: {}".format(w))
@@ -54,3 +87,9 @@ def watch_namespace(namespace, count=3, stop=None, request_timeout=0):
if not count:
LOG.info("Ended.\n")
w.stop()
+
+
+def write_json(data, file_name):
+ with open(file_name, "a") as f:
+ f.write(json.dumps(data, f))
+ f.write("\n")