add k8s capacity test case 69/60969/8
authorYang (Gabriel) Yu <Gabriel.yuyang@huawei.com>
Wed, 15 Aug 2018 04:39:33 +0000 (12:39 +0800)
committerYang (Gabriel) Yu <Gabriel.yuyang@huawei.com>
Thu, 16 Aug 2018 12:25:07 +0000 (20:25 +0800)
JIRA: BOTTLENECK-243

Change-Id: I0f36aac10cf0e05560051c785ada397e0c97e112
Signed-off-by: Yang (Gabriel) Yu <Gabriel.yuyang@huawei.com>
testsuites/kubestone/__init__.py [new file with mode: 0644]
testsuites/kubestone/samples/__init__.py [new file with mode: 0644]
testsuites/kubestone/samples/deployment_sample.yaml [new file with mode: 0644]
testsuites/kubestone/samples/pod_sample.yaml [new file with mode: 0644]
testsuites/kubestone/stress_test.py [new file with mode: 0644]
testsuites/kubestone/testcases/__init__.py [new file with mode: 0644]
testsuites/kubestone/testcases/deployment_capacity.yaml [new file with mode: 0644]
utils/k8s_setup/k8s_config_pre.sh
utils/k8s_setup/k8s_utils.py

diff --git a/testsuites/kubestone/__init__.py b/testsuites/kubestone/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/testsuites/kubestone/samples/__init__.py b/testsuites/kubestone/samples/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/testsuites/kubestone/samples/deployment_sample.yaml b/testsuites/kubestone/samples/deployment_sample.yaml
new file mode 100644 (file)
index 0000000..f7f95de
--- /dev/null
@@ -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 (file)
index 0000000..3035cbb
--- /dev/null
@@ -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 (file)
index 0000000..7f5d75f
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/testsuites/kubestone/testcases/deployment_capacity.yaml b/testsuites/kubestone/testcases/deployment_capacity.yaml
new file mode 100644 (file)
index 0000000..2638211
--- /dev/null
@@ -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
index f41ba78..05c3f1c 100644 (file)
@@ -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.
 
index 7195bf2..a89889f 100644 (file)
@@ -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")