summaryrefslogtreecommitdiffstats
path: root/test-scheduler/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'test-scheduler/server/src')
-rw-r--r--test-scheduler/server/src/__init__.py8
-rw-r--r--test-scheduler/server/src/conductor_processor/__init__.py8
-rw-r--r--test-scheduler/server/src/conductor_processor/defaultTaskFile.json9
-rw-r--r--test-scheduler/server/src/conductor_processor/defaultWorkflowFile.json24
-rw-r--r--test-scheduler/server/src/conductor_processor/task.py28
-rw-r--r--test-scheduler/server/src/conductor_processor/workflow.py243
-rw-r--r--test-scheduler/server/src/env/context/context.yaml75
-rw-r--r--test-scheduler/server/src/env/service/ansible.yaml65
-rw-r--r--test-scheduler/server/src/env/service/greet.yaml68
-rw-r--r--test-scheduler/server/src/rest/__init__.py10
-rw-r--r--test-scheduler/server/src/rest/router.py485
-rw-r--r--test-scheduler/server/src/rest/test_service_demo.py77
-rw-r--r--test-scheduler/server/src/step/__init__.py8
-rw-r--r--test-scheduler/server/src/step/general_test_step.py87
-rw-r--r--test-scheduler/server/src/step/monitor.py57
-rw-r--r--test-scheduler/server/src/step/step_manager.py41
-rw-r--r--test-scheduler/server/src/step/test_step.py56
-rw-r--r--test-scheduler/server/src/step/workload.py46
-rw-r--r--test-scheduler/server/src/test_parser.py315
19 files changed, 1710 insertions, 0 deletions
diff --git a/test-scheduler/server/src/__init__.py b/test-scheduler/server/src/__init__.py
new file mode 100644
index 00000000..85d997a7
--- /dev/null
+++ b/test-scheduler/server/src/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/test-scheduler/server/src/conductor_processor/__init__.py b/test-scheduler/server/src/conductor_processor/__init__.py
new file mode 100644
index 00000000..02647edc
--- /dev/null
+++ b/test-scheduler/server/src/conductor_processor/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 Huawei Technologies Co.,Ltd. and others
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/test-scheduler/server/src/conductor_processor/defaultTaskFile.json b/test-scheduler/server/src/conductor_processor/defaultTaskFile.json
new file mode 100644
index 00000000..544ce530
--- /dev/null
+++ b/test-scheduler/server/src/conductor_processor/defaultTaskFile.json
@@ -0,0 +1,9 @@
+{
+ "name": "",
+ "retryCount": 6,
+ "timeOutSeconds": 1200,
+ "timeOutPolicy": "TIME_OUT_WF",
+ "retryLogic": "FIXED",
+ "retryDelaySeconds": 3,
+ "responseTimeOutSeconds": 3600
+}
diff --git a/test-scheduler/server/src/conductor_processor/defaultWorkflowFile.json b/test-scheduler/server/src/conductor_processor/defaultWorkflowFile.json
new file mode 100644
index 00000000..f70573f3
--- /dev/null
+++ b/test-scheduler/server/src/conductor_processor/defaultWorkflowFile.json
@@ -0,0 +1,24 @@
+{
+ "name": "workflow_demo_05",
+ "description": "run a workflow of yardstick test service",
+ "version": 1,
+ "tasks": [
+ {
+ "name": "http_yardstick_test",
+ "taskReferenceName": "ping_test",
+ "inputParameters": {
+ "http_request": {
+ "uri": "http://192.168.199.105:8080/greet",
+ "method": "GET"
+ }
+ },
+ "type": "HTTP"
+ }
+ ],
+ "outputParameters": {
+ "header": "${ping_test.output.response.headers}",
+ "response": "${ping_test.output.response.body}",
+ "status": "${ping_test.output.response.statusCode}"
+ },
+ "schemaVersion": 2
+}
diff --git a/test-scheduler/server/src/conductor_processor/task.py b/test-scheduler/server/src/conductor_processor/task.py
new file mode 100644
index 00000000..04616ade
--- /dev/null
+++ b/test-scheduler/server/src/conductor_processor/task.py
@@ -0,0 +1,28 @@
+##############################################################################
+# Copyright (c) 2018 Huawei Technologies Co.,Ltd. and others
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import json
+import os
+
+
+class TaskFile(object):
+ def __init__(self, taskName='task_0'):
+ self._defaultConfFile = self._getFilePath("defaultTaskFile.json")
+ with open(self._defaultConfFile) as defaultConf:
+ self._jsonObj = json.load(defaultConf)
+ self._jsonObj['name'] = taskName
+
+ def generateFromStep(self, stepObject):
+ self._jsonObj['name'] = stepObject.getName()
+ print "taskFile:", self._jsonObj['name']
+ return self._jsonObj
+
+ def _getFilePath(self, fileName):
+ dirPath = os.path.dirname(os.path.realpath(__file__))
+ return os.path.join(dirPath, fileName)
diff --git a/test-scheduler/server/src/conductor_processor/workflow.py b/test-scheduler/server/src/conductor_processor/workflow.py
new file mode 100644
index 00000000..da67e013
--- /dev/null
+++ b/test-scheduler/server/src/conductor_processor/workflow.py
@@ -0,0 +1,243 @@
+##############################################################################
+# Copyright (c) 2018 Huawei Technologies Co.,Ltd. and others
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import random
+import collections
+import re
+from src.conductor_processor.task import TaskFile
+
+
+class WorkflowFile(object):
+ def __init__(self, name):
+ self._name = "workflow_" + name + "(%s)" % getRandString(10)
+ self._description = ''
+ self._version = 1
+ self._schemaVersion = 2
+ self._tasks = []
+ self._outputParameters = {}
+
+ def getDict(self):
+ d = collections.OrderedDict()
+ d['name'] = self._name
+ d['description'] = self._description
+ d['version'] = self._version
+ d['schemaVersion'] = self._schemaVersion
+ d['tasks'] = self._tasks
+ d['outputParameters'] = self._outputParameters
+
+ return d
+
+ def generateMetaData(self, flowList, stepObjArr):
+ flowParser = FlowParser(flowList, stepObjArr)
+ self._tasks, taskMetaList = flowParser.parseMainFlow()
+ normalTasks = flowParser.getNormalTaskList()
+ for normalTask in normalTasks:
+ taskName = normalTask['name']
+ referenceName = normalTask['taskReferenceName']
+ self._outputParameters["%s(%s)" % (taskName, referenceName)] = \
+ "${%s.output.response.body}" % referenceName
+ return self.getDict(), taskMetaList
+
+
+class FlowParser(object):
+ def __init__(self, flowList, stepObjArr):
+ self._mainFlow = {}
+ self._subFlowDict = {}
+ self._stepObjArr = stepObjArr
+ self._normalTasks = []
+ for flow in flowList:
+ if flow['name'] == "main":
+ self._mainFlow = flow
+ else:
+ self._subFlowDict[flow['name']] = flow
+
+ def parseMainFlow(self):
+ return self.parseOrderList(self._mainFlow['orders'], self._stepObjArr)
+
+ def parse(self, obj, stepObjArr):
+ if isinstance(obj, str):
+ return self.parseFlow(obj, stepObjArr)
+ else:
+ return self.parseOrderList(obj, stepObjArr)
+
+ def parseFlow(self, flowName, stepObjArr):
+ orderList = self._subFlowDict[flowName]['orders']
+ return self.parseOrderList(orderList, stepObjArr)
+
+ def parseOrderList(self, orderList, stepObjArr):
+ tasks = []
+ taskMetaAllList = []
+ for order in orderList:
+ if order['type'] == "normal":
+ genTask = NormalTask(order, stepObjArr, self)
+ self._normalTasks.append(genTask)
+ elif order['type'] == "switch":
+ genTask = SwitchTask(order, stepObjArr, self)
+ elif order['type'] == "parallel":
+ genTask = ParallelTask(order, stepObjArr, self)
+ tasks.append(genTask.getDict())
+
+ if order['type'] == "parallel":
+ joinTask = genTask.getJoinTask()
+ tasks.append(joinTask.getDict())
+
+ taskMetaList = genTask.getTaskMetaList()
+ if taskMetaList is not None:
+ taskMetaAllList.extend(taskMetaList)
+ return tasks, taskMetaAllList
+
+ def getNormalTaskList(self):
+ normalTasksDict = []
+ for normalTask in self._normalTasks:
+ normalTasksDict.append(normalTask.getDict())
+ return normalTasksDict
+
+ def getNormalTask(self, stepId):
+ for normalTask in self._normalTasks:
+ if normalTask.getStepId() == stepId:
+ return normalTask
+ return None
+
+
+class BaseWorkflowTask(object):
+ def __init__(self, name):
+ self._name = name
+ self._taskReferenceName = self._name + "_task_%s" % getRandString(10)
+ self._type = ''
+ self._args = {}
+
+ def __str__(self):
+ dictObj = self.getDict()
+ return str(dictObj)
+
+ def getDict(self):
+ d1 = {
+ "name": self._name,
+ "taskReferenceName": self._taskReferenceName,
+ "type": self._type
+ }
+ return dict(d1, **self._args)
+
+ def getName(self):
+ return self._name
+
+ def getReferenceName(self):
+ return self._taskReferenceName
+
+ def getTaskMetaList(self):
+ taskFile = TaskFile()
+ return [taskFile.generateFromStep(self)]
+
+
+class NormalTask(BaseWorkflowTask):
+ def __init__(self, order, stepObjArr, flowParser):
+ relatedStepObj = stepObjArr[order['step'] - 1]
+ super(NormalTask, self).__init__(relatedStepObj.getName())
+ self._taskReferenceName = "task_%s" % getRandString(10)
+ self._stepId = relatedStepObj.getId()
+ self._type = "HTTP"
+ self._args['inputParameters'] = relatedStepObj.getArgs()
+ self._paramTransform(self._args['inputParameters'], flowParser)
+ print "NormalTask:----------------------\n", relatedStepObj.getArgs()
+
+ def _paramTransform(self, argsDict, flowParser):
+ for (k, v) in argsDict.items():
+ if isinstance(v, str):
+ if re.match("^\(\(\d+\..*\)\)", v):
+ v = v[2:-2]
+ stepId, outputParam = v.split(".")
+ stepId = int(stepId)
+ normalTask = flowParser.getNormalTask(stepId)
+ if normalTask is None:
+ continue
+ argsDict[k] = "${%s.output.response.body.%s}" % \
+ (normalTask.getReferenceName(), outputParam)
+ elif isinstance(v, dict):
+ self._paramTransform(v, flowParser)
+
+ def getStepId(self):
+ return self._stepId
+
+
+class SwitchTask(BaseWorkflowTask):
+ seqNumber = 0
+
+ def __init__(self, order, stepObjArr, flowParser):
+ super(SwitchTask, self).__init__("switch_" + str(SwitchTask.seqNumber))
+ SwitchTask.seqNumber = SwitchTask.seqNumber + 1
+ if 'name' in order:
+ self._name = order['name']
+ self._type = "DECISION"
+ caseValueParam = 'value'
+ order['value'] = order['value'][2:-2]
+ stepId, outputParam = order['value'].split(".")
+ stepId = int(stepId)
+ normalTask = flowParser.getNormalTask(stepId)
+ caseValue = "${%s.output.response.body.%s}" % \
+ (normalTask.getReferenceName(), outputParam)
+ self._args['inputParameters'] = {caseValueParam: caseValue}
+ self._args['caseValueParam'] = caseValueParam
+ self._args['decisionCases'] = {}
+ self._childTaskMetaList = []
+ for case, caseOrders in order['cases'].items():
+ self._args['decisionCases'][case], taskMetaList = \
+ flowParser.parse(caseOrders, stepObjArr)
+ if taskMetaList is not None:
+ self._childTaskMetaList.extend(taskMetaList)
+
+ def getTaskMetaList(self):
+ selfTaskMetaList = super(SwitchTask, self).getTaskMetaList()
+ selfTaskMetaList.extend(self._childTaskMetaList)
+ return selfTaskMetaList
+
+
+class ParallelTask(BaseWorkflowTask):
+ seqNumber = 0
+
+ def __init__(self, order, stepObjArr, flowParser):
+ InstSeqNumber = ParallelTask.seqNumber
+ super(ParallelTask, self).__init__("parallel_" + str(InstSeqNumber))
+ ParallelTask.seqNumber = ParallelTask.seqNumber + 1
+ if 'name' in order:
+ self._name = order['name']
+ self._type = "FORK_JOIN"
+ self._args['forkTasks'] = []
+ self._childTaskMetaList = []
+ lastTasksNameList = []
+ parallelList = order['parallel'].items()
+ parallelList.sort()
+ for key, orderList in parallelList:
+ print orderList
+ taskList, taskMetaList = flowParser.parse(orderList, stepObjArr)
+ self._args['forkTasks'].append(taskList)
+ lastTasksNameList.append(taskList[-1]['taskReferenceName'])
+ if taskMetaList is not None:
+ self._childTaskMetaList.extend(taskMetaList)
+ self._joinTaskObj = ParallelJoinTask(InstSeqNumber, lastTasksNameList)
+
+ def getTaskMetaList(self):
+ selfTaskMetaList = super(ParallelTask, self).getTaskMetaList()
+ selfTaskMetaList.extend(self._childTaskMetaList)
+ selfTaskMetaList.extend(self._joinTaskObj.getTaskMetaList())
+ return selfTaskMetaList
+
+ def getJoinTask(self):
+ return self._joinTaskObj
+
+
+class ParallelJoinTask(BaseWorkflowTask):
+ def __init__(self, seqNumber, joinOnList):
+ super(ParallelJoinTask, self).__init__(
+ "paralleljoin_" + str(seqNumber))
+ self._type = "JOIN"
+ self._args['joinOn'] = joinOnList
+
+
+def getRandString(length):
+ return "".join(random.choice(str("0123456789")) for i in range(length))
diff --git a/test-scheduler/server/src/env/context/context.yaml b/test-scheduler/server/src/env/context/context.yaml
new file mode 100644
index 00000000..656f56a0
--- /dev/null
+++ b/test-scheduler/server/src/env/context/context.yaml
@@ -0,0 +1,75 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+---
+
+hosts:
+ node26:
+ name: node26
+ ip: 192.168.1.26
+ port: 22
+ user: root
+ password: 123456
+
+ node27:
+ name: node27
+ ip: 192.168.1.27
+ port: 22
+ user: root
+ password: 123456
+
+ node28:
+ name: node28
+ ip: 192.168.1.28
+ port: 22
+ user: root
+ password: 123456
+
+ node29:
+ name: node29
+ ip: 192.168.1.29
+ port: 22
+ user: root
+ password: 123456
+
+ node30:
+ name: node30
+ ip: 192.168.1.30
+ port: 22
+ user: root
+ password: 123456
+
+ node31:
+ name: node31
+ ip: 192.168.1.31
+ port: 22
+ user: root
+ password: 123456
+
+ node32:
+ name: node32
+ ip: 192.168.1.32
+ port: 22
+ user: root
+ password: 123456
+
+
+cassandra:
+ name: node24
+ ip: 192.168.1.24
+ port: 22
+ user: root
+ password: 123456
+
+sprout:
+ name: node32
+ ip: 192.168.1.32
+ port: 22
+ user: root
+ password: 123456
diff --git a/test-scheduler/server/src/env/service/ansible.yaml b/test-scheduler/server/src/env/service/ansible.yaml
new file mode 100644
index 00000000..eb9edb36
--- /dev/null
+++ b/test-scheduler/server/src/env/service/ansible.yaml
@@ -0,0 +1,65 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+---
+
+ansible:
+ apis:
+ - method: POST
+ name: faultload
+ params:
+ - description: call user count
+ name: call_user
+ - description: workload time
+ name: duration
+ - description: faultload time
+ name: fault_duration
+ - description: register speed
+ name: initial_reg_rate
+ - description: multiplier
+ name: multiplier
+ - description: register user count
+ name: reg_user
+ baseuri: faultload
+ template:
+ uri: ((baseuri))
+ body:
+ call_user: ((call_user))
+ duration: ((duration))
+ fault_duration: ((fault_duration))
+ initial_reg_rate: ((initial_reg_rate))
+ multiplier: ((multiplier))
+ reg_user: ((reg_user))
+
+ - method: POST
+ name: workload
+ params:
+ - description: call user count
+ name: call_user
+ - description: workload time
+ name: duration
+ - description: register speed
+ name: initial_reg_rate
+ - description: multiplier
+ name: multiplier
+ - description: register user count
+ name: reg_user
+ baseuri: workload
+ template:
+ uri: ((baseuri))
+ body:
+ call_user: ((call_user))
+ duration: ((duration))
+ fault_duration: ((fault_duration))
+ initial_reg_rate: ((initial_reg_rate))
+ multiplier: ((multiplier))
+ reg_user: ((reg_user))
+
+ ip: 100.64.227.222
+ port: 9006
diff --git a/test-scheduler/server/src/env/service/greet.yaml b/test-scheduler/server/src/env/service/greet.yaml
new file mode 100644
index 00000000..e328e43a
--- /dev/null
+++ b/test-scheduler/server/src/env/service/greet.yaml
@@ -0,0 +1,68 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+---
+
+greet:
+ ip: t-scheduler-server
+ port: 5312
+ apis:
+ -
+ name: greet
+ method: GET
+ baseuri: greet
+ params:
+ - name: name
+ description: user name
+ template:
+ uri: ((baseuri))?name=((name))
+ response:
+ result: int
+ next:
+ aa: str
+ bb: int
+ -
+ name: answer
+ method: POST
+ baseuri: answer
+ params:
+ - name: ping
+ description: param ping
+ template:
+ uri: ((baseuri))
+ body:
+ ping: ((ping))
+
+ -
+ name: ten
+ method: GET
+ baseuri: ten
+ template:
+ uri: ((baseuri))
+
+
+ -
+ name: switch
+ method: GET
+ baseuri: switch
+ template:
+ uri: ((baseuri))
+ return:
+ -
+ result
+
+ -
+ name: switch_2
+ method: GET
+ baseuri: switch_2
+ template:
+ uri: ((baseuri))
+ return:
+ -
+ result
diff --git a/test-scheduler/server/src/rest/__init__.py b/test-scheduler/server/src/rest/__init__.py
new file mode 100644
index 00000000..27f99646
--- /dev/null
+++ b/test-scheduler/server/src/rest/__init__.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/test-scheduler/server/src/rest/router.py b/test-scheduler/server/src/rest/router.py
new file mode 100644
index 00000000..1ceeedd0
--- /dev/null
+++ b/test-scheduler/server/src/rest/router.py
@@ -0,0 +1,485 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from flask import Flask
+from flask import jsonify
+from flask import request
+from flask_cors import CORS
+import os
+import json
+import time
+import pyaml
+import yaml
+import traceback
+
+import src.test_parser as test_parser
+
+
+BASE_DIR = os.path.abspath(os.path.dirname(__file__))
+TESTSUITE_DIR = os.path.join(BASE_DIR, "..", "..", "test", "test_case")
+SERVICE_DIR = os.path.join(BASE_DIR, "..", "env", "service")
+CONTEXT_FILE_DIR = os.path.join(BASE_DIR, "..", "env", "context",
+ "context.yaml")
+app = Flask(__name__)
+CORS(app)
+
+
+###############
+# 1. EXECUTE API
+###########################################################################
+@app.route("/")
+def hello():
+ return "Hello, World! This is a greet from parser." + SERVICE_DIR
+
+
+@app.route("/execute/testcase", methods=['POST'])
+def runTestcase():
+ suiteName = request.values.get('suiteName')
+ caseName = request.values.get('caseName')
+ try:
+ casePath = os.path.join(TESTSUITE_DIR, suiteName, caseName)
+ if os.path.exists(casePath):
+ workflowId = test_parser.parse(casePath)
+ if workflowId is None or workflowId == '':
+ return jsonify({"code": 500, "error": "Server Error."})
+ return jsonify({"code": 200, "result": {"workflowId": workflowId}})
+ else:
+ return jsonify({"code": 300, "error": "no such test case: %s" %
+ (os.path.join(suiteName, caseName))})
+ except BaseException, e:
+ return returnServerError(e)
+
+
+@app.route("/story-content")
+def getStoryContent():
+ try:
+ story_name = request.args['story']
+ service_name = request.args['service']
+ storyFileDir = os.path.join("/tmp", "generate_workflow.json")
+ with open(storyFileDir, "r") as f:
+ storyContent = f.read()
+ except BaseException, e:
+ return returnServerError(e)
+
+ result = {"code": 200, "result":
+ {"service": service_name, "story": story_name,
+ "content": storyContent}}
+ return jsonify(result)
+
+
+###############
+# 2. TESTCASE CRUD
+###########################################################################
+@app.route("/testsuite/list")
+def getAllSuite():
+ res = []
+ id = 1
+ try:
+ for fileName in os.listdir(TESTSUITE_DIR):
+ suiteInfo = {}
+ suiteInfo["id"] = id
+ suiteInfo["testsuite"] = fileName
+ res.append(suiteInfo)
+ id = id + 1
+ except BaseException, e:
+ print e
+ app.logger.error(traceback.format_exc())
+ return jsonify({"code": 500, "error": "Server error"})
+
+ return jsonify({"code": 200, "result": res})
+
+
+@app.route("/testsuite/content")
+def getSuiteContent():
+ res = []
+ id = 1
+ try:
+ suiteName = request.values.get("suiteName")
+ exSuitePath = os.path.join(TESTSUITE_DIR, suiteName)
+ if os.path.exists(exSuitePath):
+ for fileName in os.listdir(exSuitePath):
+ tcInfo = {}
+ tcInfo["id"] = id
+ tcInfo["testcase"] = fileName
+ res.append(tcInfo)
+ id = id + 1
+ else:
+ return jsonify({"code": 300, "error": "no such test suite!"})
+ except BaseException, e:
+ print e
+ app.logger.error(traceback.format_exc())
+ return jsonify({"code": 500, "error": "Server error"})
+
+ return jsonify({"code": 200, "result": res})
+
+
+@app.route("/testcase/content")
+def getTCContent():
+ res = ""
+ editorRes = ""
+ try:
+ suiteName = request.values.get("suiteName")
+ caseName = request.values.get("caseName")
+ casePath = os.path.join(suiteName, caseName)
+ casePath = os.path.join(TESTSUITE_DIR, casePath)
+ if os.path.exists(casePath):
+ with open(casePath, "r") as f:
+ fileContent = f.read()
+ res = fileContent
+ editorRes = test_parser.getWebTestcase(yaml.load(res))
+ else:
+ return jsonify({"code": 300, "error": "no such file!"})
+ except BaseException, e:
+ print e
+ app.logger.error(traceback.format_exc())
+ return jsonify({"code": 500, "error": "Server error"})
+
+ return jsonify({"code": 200, "result":
+ {"content": res, "editorContent": editorRes}})
+
+
+@app.route("/testsuite/new", methods=['POST'])
+def addNewSuite():
+ try:
+ suiteName = request.values.get("suiteName")
+ for fileName in os.listdir(TESTSUITE_DIR):
+ if fileName == suiteName:
+ return jsonify({"code": 300,
+ "error": "testsuite already exists!"})
+ testSuitePath = os.path.join(TESTSUITE_DIR, suiteName)
+ os.mkdir(testSuitePath)
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 200, "result": "ok"})
+
+
+@app.route("/testsuite/delete", methods=['POST'])
+def deleteSuite():
+ try:
+ suiteName = request.values.get("suiteName")
+ for fileName in os.listdir(TESTSUITE_DIR):
+ if fileName == suiteName:
+ testSuitePath = os.path.join(TESTSUITE_DIR, fileName)
+ del_file(testSuitePath)
+ os.rmdir(testSuitePath)
+ return jsonify({"code": 200, "result": "ok"})
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 300, "error": "no such testsuite!"})
+
+
+def del_file(path):
+ for i in os.listdir(path):
+ path_file = os.path.join(path, i)
+ if os.path.isfile(path_file):
+ os.remove(path_file)
+ else:
+ del_file(path_file)
+
+
+@app.route("/testcase/new", methods=['POST'])
+def createTestcase():
+ try:
+ suiteName = request.values.get("suiteName")
+ caseName = request.values.get("caseName")
+ exSuitePath = os.path.join(TESTSUITE_DIR, suiteName)
+ if os.path.exists(exSuitePath):
+ for fileName in os.listdir(exSuitePath):
+ if fileName == caseName:
+ return jsonify({"code": 301,
+ "error": "testcase already exists!"})
+ casePath = os.path.join(exSuitePath, caseName)
+ with open(casePath, "w") as f:
+ # the next line is a placeholder.
+ print f
+ else:
+ return jsonify({"code": 300, "error": "no such test suite!"})
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 200, "result": "ok"})
+
+
+@app.route("/testcase/delete", methods=['POST'])
+def deleteTestcase():
+ try:
+ suiteName = request.values.get("suiteName")
+ caseName = request.values.get("caseName")
+ exSuitePath = os.path.join(TESTSUITE_DIR, suiteName)
+ if os.path.exists(exSuitePath):
+ for fileName in os.listdir(exSuitePath):
+ if fileName == caseName:
+ casePath = os.path.join(exSuitePath, caseName)
+ os.remove(casePath)
+ return jsonify({"code": 200, "result": "ok"})
+ return jsonify({"code": 301, "error": "no such test case!"})
+ else:
+ return jsonify({"code": 300, "error": "no such test suite!"})
+ except BaseException, e:
+ return returnServerError(e)
+
+
+@app.route("/testcase/save", methods=["POST"])
+def saveTCContent():
+ try:
+ suiteName = request.values.get("suiteName")
+ caseName = request.values.get("caseName")
+ stepList = json.loads(request.values.get("stepList"))
+ subflowList = json.loads(request.values.get("subflowList"))
+ mainOrdersList = json.loads(request.values.get("mainOrdersList"))
+ jsonObj = {"stepList": stepList, "subflowList": subflowList,
+ "mainOrdersList": mainOrdersList}
+ parseData = test_parser.parseWebTestcase(jsonObj)
+
+ casePath = os.path.join(suiteName, caseName)
+ casePath = os.path.join(TESTSUITE_DIR, casePath)
+ if os.path.exists(casePath):
+ with open(casePath, "w") as f:
+ pyaml.dump(parseData, f, safe=True)
+ else:
+ return jsonify({"code": 300, "error": "no such file!"})
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 200, "result": "save success"})
+
+
+###############
+# 3.1 API FOR SERVICE
+############################################################
+@app.route("/service/list")
+def getAllServices():
+ res = []
+ try:
+ for fileName in os.listdir(SERVICE_DIR):
+ serviceName = os.path.splitext(fileName)[0]
+ res.append(serviceName)
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 200, "result": res})
+
+
+@app.route("/service/content")
+def getServiceContent():
+ res = {}
+ try:
+ serviceName = request.values.get("serviceName")
+ for fileName in os.listdir(SERVICE_DIR):
+ if serviceName == os.path.splitext(fileName)[0]:
+ res["actions"] = []
+ filePath = os.path.join(SERVICE_DIR, fileName)
+ with open(filePath, "r") as f:
+ content = yaml.load(f)
+ apisArr = content[serviceName]['apis']
+ for i in range(len(apisArr)):
+ apisArr[i].pop("method")
+ apisArr[i].pop("baseuri")
+ res["actions"] = apisArr
+ except BaseException, e:
+ return returnServerError(e)
+
+ if res == {}:
+ return jsonify({"code": 300, "error": "no such service!"})
+
+ return jsonify({"code": 200, "result": res})
+
+
+def paramTransform(paramDict):
+ res = []
+ for (key, value) in paramDict.items():
+ paramJson = {}
+ paramJson["name"] = key
+ paramJson["description"] = value["help"]
+ if "params" in value:
+ paramJson["params"] = paramTransform(value["params"])
+ res.append(paramJson)
+ return res
+
+
+@app.route("/service/action_response")
+def actionResponse():
+ res = {}
+ try:
+ serviceName = request.values.get("serviceName")
+ actionName = request.values.get("actionName")
+ for fileName in os.listdir(SERVICE_DIR):
+ if serviceName == os.path.splitext(fileName)[0]:
+ res["responseParams"] = []
+ filePath = os.path.join(SERVICE_DIR, fileName)
+ with open(filePath, "r") as f:
+ content = yaml.load(f)
+ apisArr = content[serviceName]['apis']
+ for i in range(len(apisArr)):
+ if actionName == apisArr[i]['name'] and (
+ "response" in apisArr[i]):
+ res["responseParams"] = apisArr[i]["response"]
+ except BaseException, e:
+ return returnServerError(e)
+ if res == {}:
+ return jsonify({"code": 300, "error": "no such service!"})
+ return jsonify({"code": 200, "result": res})
+
+
+###############
+# 3.2 API FOR ENVIRONMENT SERVICE AND CONTEXT
+###########################################################################
+@app.route('/env/getAllServices')
+def getAllService():
+ res = []
+ id = 1
+ try:
+ for fileName in os.listdir(SERVICE_DIR):
+ item = {}
+ item['id'] = id
+ item['name'] = os.path.splitext(fileName)[0]
+ filePath = os.path.join(SERVICE_DIR, fileName)
+ filemt = time.localtime(os.stat(filePath).st_mtime)
+ item['time'] = time.strftime("%Y-%m-%d", filemt)
+ res.append(item)
+ id = id + 1
+ except BaseException, e:
+ return returnServerError(e)
+ return jsonify({"code": 200, "result": res})
+
+
+@app.route('/env/getService')
+def getService():
+ try:
+ serviceName = request.values.get('serviceName')
+ serviceFile = serviceName + '.yaml'
+ servicePath = os.path.join(SERVICE_DIR, serviceFile)
+ if os.path.exists(servicePath):
+ with open(servicePath, "r") as f:
+ serviceDict = yaml.load(f)
+ serviceDict = serviceDict[serviceName]
+ return jsonify({"code": 200, "result": serviceDict})
+ else:
+ return jsonify({"code": 300, "error": "no such service!"})
+ except BaseException, e:
+ return returnServerError(e)
+
+
+@app.route('/env/createService', methods=['POST'])
+def createService():
+ try:
+ name = str(request.values.get('name'))
+ ip = str(request.values.get('ip'))
+ port = int(request.values.get('port'))
+ apis = json.loads(request.values.get('apis'))
+ service = {
+ name: {
+ 'ip': ip,
+ 'port': port,
+ 'apis': apis
+ }
+ }
+ serviceJson = json.dumps(service, indent=True)
+ print serviceJson
+ app.logger.debug(service)
+
+ serviceFile = name + '.yaml'
+ servicePath = os.path.join(SERVICE_DIR, serviceFile)
+ with open(servicePath, 'w') as f:
+ pyaml.dump(service, f, safe=True)
+ except BaseException, e:
+ return returnServerError(e)
+ return jsonify({"code": 200, "result": "create success!"})
+
+
+@app.route('/env/editService', methods=['POST'])
+def editService():
+ try:
+ oldName = str(request.values.get('oldName'))
+ name = str(request.values.get('newName'))
+ ip = str(request.values.get('ip'))
+ port = int(request.values.get('port'))
+ apis = json.loads(request.values.get('apis'))
+ app.logger.debug(apis)
+ service = {
+ name: {
+ 'ip': ip,
+ 'port': port,
+ 'apis': apis
+ }
+ }
+ serviceJson = json.dumps(service, indent=True)
+ print serviceJson
+ app.logger.debug(service)
+
+ for fileName in os.listdir(SERVICE_DIR):
+ serviceName = os.path.splitext(fileName)[0]
+ if serviceName == oldName:
+ filePath = os.path.join(SERVICE_DIR, fileName)
+ os.remove(filePath)
+
+ serviceFile = name + '.yaml'
+ servicePath = os.path.join(SERVICE_DIR, serviceFile)
+ with open(servicePath, 'w') as f:
+ pyaml.dump(service, f, safe=True)
+ except BaseException, e:
+ return returnServerError(e)
+ return jsonify({"code": 200, "result": "edit success!"})
+
+
+@app.route('/env/deleteService', methods=['POST'])
+def deleteService():
+ try:
+ name = str(request.values.get('serviceName'))
+
+ for fileName in os.listdir(SERVICE_DIR):
+ serviceName = os.path.splitext(fileName)[0]
+ if serviceName == name:
+ filePath = os.path.join(SERVICE_DIR, fileName)
+ os.remove(filePath)
+ except BaseException, e:
+ return returnServerError(e)
+ return jsonify({"code": 200, "result": "delete success!"})
+
+
+@app.route('/env/getContext')
+def getContext():
+ try:
+ with open(CONTEXT_FILE_DIR, "r") as f:
+ fileContent = f.read()
+ res = fileContent
+ except BaseException, e:
+ return returnServerError(e)
+ return jsonify({"code": 200, "result": {"context": res}})
+
+
+@app.route('/env/editContext', methods=['POST'])
+def editContext():
+ try:
+ context = request.values.get("context")
+ test = yaml.load(context)
+ print test
+ with open(CONTEXT_FILE_DIR, "w") as f:
+ f.write(context)
+ except yaml.constructor.ConstructorError, e:
+ app.logger.error(traceback.format_exc())
+ return jsonify({"code": 500, "error":
+ "context content error: not a .yaml file!"})
+ except BaseException, e:
+ return returnServerError(e)
+
+ return jsonify({"code": 200, "result": "edit context success!"})
+###########################################################################
+
+
+def returnServerError(e, msg="Server Error"):
+ print e
+ app.logger.error(traceback.format_exc())
+ return jsonify({"code": 500, "error": msg})
+
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', port=5310)
diff --git a/test-scheduler/server/src/rest/test_service_demo.py b/test-scheduler/server/src/rest/test_service_demo.py
new file mode 100644
index 00000000..abd8609d
--- /dev/null
+++ b/test-scheduler/server/src/rest/test_service_demo.py
@@ -0,0 +1,77 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from flask import Flask
+from flask_cors import CORS
+from flask import request
+from flask import jsonify
+import time
+import json
+from random import randint
+
+app = Flask(__name__)
+CORS(app)
+
+
+@app.route("/greet")
+def greet():
+ return "hello"
+
+
+@app.route("/answer", methods=["POST"])
+def answer():
+ app.logger.debug(request.form)
+ app.logger.debug(request.data)
+ if jsonify(request.form) != {} and 'ping' in request.form:
+ return "answer: ping is: \"" + request.form['ping'] + "\" end."
+ elif request.data != "":
+ requestDict = json.loads(request.data)
+ if 'ping' in requestDict:
+ return "answer: the ping is: \"" + requestDict['ping'] + "\" end."
+ else:
+ return "answer ping is null"
+
+
+@app.route("/answer2", methods=["POST"])
+def answer2():
+ return "ok"
+
+
+@app.route("/five")
+def sleepFiveSeconds():
+ time.sleep(5)
+ return "five: receive the request."
+
+
+@app.route("/ten")
+def sleepTenSeconds():
+ time.sleep(10)
+ return "ten: receive the request."
+
+
+@app.route("/switch")
+def switchValue():
+ value = randint(0, 10)
+ if value > 4:
+ return jsonify({'code': 200, 'result': 'A'})
+ else:
+ return jsonify({'code': 200, 'result': 'B'})
+
+
+@app.route("/switch_2")
+def switchValue_2():
+ value = randint(0, 10)
+ if value > 4:
+ return jsonify({'code': 200, 'result': 'C'})
+ else:
+ return jsonify({'code': 200, 'result': 'D'})
+
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', port=5312, debug=True)
diff --git a/test-scheduler/server/src/step/__init__.py b/test-scheduler/server/src/step/__init__.py
new file mode 100644
index 00000000..85d997a7
--- /dev/null
+++ b/test-scheduler/server/src/step/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/test-scheduler/server/src/step/general_test_step.py b/test-scheduler/server/src/step/general_test_step.py
new file mode 100644
index 00000000..718982bc
--- /dev/null
+++ b/test-scheduler/server/src/step/general_test_step.py
@@ -0,0 +1,87 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from src.step.test_step import TestStep
+import os
+import yaml
+import re
+
+
+class GeneralTestStep(TestStep):
+ __step_type__ = "test"
+
+ def __init__(self, id, name, service, action, args, context):
+ super(GeneralTestStep, self).__init__(
+ self.__step_type__, id, name, service, action, args, context)
+ self._stepParse()
+ self.action()
+
+ def _contextTransform(self, argsDict):
+ for (k, v) in argsDict.items():
+ if isinstance(v, str):
+ if re.match('^\(\(context\..*\)\)', v):
+ v = v[10:-2]
+ layers = v.split(".")
+ contextData = self._context
+ for layer in layers:
+ contextData = contextData[layer]
+ argsDict[k] = contextData
+ elif isinstance(v, dict):
+ self._contextTransform(v)
+
+ def _stepParse(self):
+ self._args_temp = self._args
+ self._args = {}
+
+ # transform the service config
+ envFilePath = os.path.join(
+ self._getCurrentDir(), "..", "env",
+ "service", self._serviceName + ".yaml")
+ requestParam = {}
+ with open(envFilePath, 'r') as f:
+ conf = yaml.load(f)
+ conf = conf[self._serviceName]
+ for apiItem in conf["apis"]:
+ if apiItem['name'] == self._serviceInterface:
+ interfaceConf = apiItem
+ if interfaceConf is None:
+ return
+
+ # transform the args config
+ self._contextTransform(self._args_temp)
+
+ interfaceUri = interfaceConf['baseuri'] + \
+ interfaceConf['template']['uri'][11:]
+ interfaceUri = "http://%s:%s/%s" % (
+ conf['ip'], conf['port'], interfaceUri)
+ requestParam['uri'] = self._uriTransform(interfaceUri)
+
+ requestParam['method'] = interfaceConf['method']
+ if requestParam["method"] == "POST":
+ requestParam['body'] = interfaceConf['template']['body']
+ self._paramTransform(requestParam['body'], self._args_temp)
+ self._args['http_request'] = requestParam
+
+ def _uriTransform(self, uri):
+ return re.sub("\(\(.*?\)\)", self._uriResReplace, uri)
+
+ def _uriResReplace(self, match):
+ matchTrim = match.group()[2:-2]
+ return self._args_temp[matchTrim]
+
+ def _paramTransform(self, argsTemplate, argsDict):
+ for (k, v) in argsTemplate.items():
+ if isinstance(v, str):
+ if re.match('^\(\(.*\)\)', v):
+ argsTemplate[k] = argsDict[v[2:-2]]
+ elif isinstance(v, dict):
+ self._paramTransform(v, argsDict)
+
+ def start(self):
+ pass
diff --git a/test-scheduler/server/src/step/monitor.py b/test-scheduler/server/src/step/monitor.py
new file mode 100644
index 00000000..7322e5f7
--- /dev/null
+++ b/test-scheduler/server/src/step/monitor.py
@@ -0,0 +1,57 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import json
+import os
+from src.step.test_step import TestStep
+
+
+class MonitorStep(TestStep):
+ __step_type__ = 'monitor'
+
+ def __init__(self, name, service, action, args):
+ super(MonitorStep, self).__init__(name, service, action, args)
+ self._argsParse()
+ self.action()
+
+ def _argsParse(self):
+ if self._call == "REST":
+ currentDirPath = os.path.dirname(os.path.abspath(__file__))
+ envDirPath = os.path.abspath(os.path.join(
+ currentDirPath, os.pardir, os.pardir, 'env'))
+ envFilePath = os.path.join(
+ envDirPath, "%s.json" % self._service['name'])
+ with open(envFilePath) as f:
+ propDict = json.load(f)
+ self._args['ip'] = propDict['ip']
+ self._args['port'] = propDict['port']
+ self._args['api'] = "%s/%s" % (
+ propDict['api_map']['workload'], self._args['command'])
+ exclude = {'ip', 'port', 'api', 'command', 'method'}
+ self._args['req_body'] = {
+ key: value for key, value in
+ self._args.items() if key not in exclude}
+
+ def setUp(self):
+ print "monitor setUp"
+
+ def uninstall(self):
+ print "monitor uninstall"
+
+ def start(self):
+ print "monitor start...."
+
+ def stop(self):
+ print "monitor stop"
+
+
+if __name__ == "__main__":
+ service = {"name": "ansible", "call": "REST"}
+ monitor = MonitorStep(
+ "monitor_cpu", service, "start", **{"target": "abc:qq"})
diff --git a/test-scheduler/server/src/step/step_manager.py b/test-scheduler/server/src/step/step_manager.py
new file mode 100644
index 00000000..01e87ba7
--- /dev/null
+++ b/test-scheduler/server/src/step/step_manager.py
@@ -0,0 +1,41 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from src.step.test_step import TestStep
+import os
+import sys
+
+
+class TestStepManager(object):
+ def __init__(self, context):
+ self._context = context
+
+ currentDirPath = os.path.dirname(os.path.abspath(__file__))
+ sys.path.append(currentDirPath)
+
+ excludeFiles = ('__init__.py', 'step_manager.py', 'test_step.py')
+ for fileName in os.listdir(currentDirPath):
+ if os.path.isfile(os.path.join(currentDirPath, fileName)) and \
+ os.path.splitext(fileName)[1] == '.py' and \
+ fileName not in excludeFiles:
+ __import__(os.path.splitext(fileName)[0])
+
+ def getStepObj(self, type, id, name, service, action, args):
+ for subclass in TestStep.__subclasses__():
+ if type == subclass.__step_type__:
+ return subclass(id, name, service, action, args, self._context)
+
+
+if __name__ == "__main__":
+ tsMgr = TestStepManager()
+ args = {'command': 'greet', 'method': 'POST', 'args': {'name': 'leo'}}
+ stepObj = tsMgr.getStepObj('test', 1, 'test_cpu', {
+ 'name': 'greet', 'call': 'REST'}, 'start', args)
+ print stepObj
+ print stepObj.__class__.__mro__
diff --git a/test-scheduler/server/src/step/test_step.py b/test-scheduler/server/src/step/test_step.py
new file mode 100644
index 00000000..e69363ae
--- /dev/null
+++ b/test-scheduler/server/src/step/test_step.py
@@ -0,0 +1,56 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import os
+
+
+class TestStep(object):
+ def __init__(self, type, id, name, service, action, args, context):
+ self._type = type
+ self._id = id
+ self._name = name
+ self._serviceName = service['name']
+ self._serviceInterface = service['interface']
+ self._action = action
+ self._args = args
+ self._context = context
+
+ def getId(self):
+ return self._id
+
+ def getName(self):
+ return self._name
+
+ def getServiceName(self):
+ return self._serviceName
+
+ def getCallFunction(self):
+ return self._callType
+
+ def getArgs(self):
+ return self._args
+
+ def action(self):
+ f = getattr(self, self._action)
+ f()
+
+ def _argsParse(self):
+ pass
+
+ def _getCurrentDir(self):
+ return os.path.dirname(__file__)
+
+ def __str__(self):
+ return str(self.__dict__)
+
+
+if __name__ == "__main__":
+ args = {'command': 'start'}
+ stepObj = TestStep('test_cpu', 'ansible', 'REST', **args)
+ print stepObj
diff --git a/test-scheduler/server/src/step/workload.py b/test-scheduler/server/src/step/workload.py
new file mode 100644
index 00000000..b35819e1
--- /dev/null
+++ b/test-scheduler/server/src/step/workload.py
@@ -0,0 +1,46 @@
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import json
+import os
+from src.step.test_step import TestStep
+
+
+class WorkloadStep(TestStep):
+ __step_type__ = 'workload'
+
+ def __init__(self, id, name, service, action, args):
+ super(WorkloadStep, self).__init__(
+ self.__step_type__, id, name, service, action, args)
+ self._argsParse()
+ self._action()
+
+ def _argsParse(self):
+ if self._callType == "REST":
+ currentDirPath = os.path.dirname(os.path.abspath(__file__))
+ envDirPath = os.path.abspath(
+ os.path.join(currentDirPath, os.pardir, os.pardir, 'env'))
+ envFilePath = os.path.join(
+ envDirPath, "%s.json" % self._service['name'])
+ with open(envFilePath) as f:
+ propDict = json.load(f)
+ self._args['ip'] = propDict['ip']
+ self._args['port'] = propDict['port']
+ self._args['api'] = "%s/%s" % (
+ propDict['api_map']['workload'], self._args['command'])
+ exclude = {'ip', 'port', 'api', 'command', 'method'}
+ self._args['req_body'] = {
+ key: value for key, value in
+ self._args.items() if key not in exclude}
+
+ def _start(self):
+ print "workload start"
+
+ def _stop(self):
+ print "workload stop"
diff --git a/test-scheduler/server/src/test_parser.py b/test-scheduler/server/src/test_parser.py
new file mode 100644
index 00000000..af0134a7
--- /dev/null
+++ b/test-scheduler/server/src/test_parser.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import click
+import os
+import yaml
+import json
+import collections
+from src.step.step_manager import TestStepManager
+from src.conductor_processor.workflow import WorkflowFile
+from conductorclient.run_new_workflow import WorkflowMgr
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+CONDUCTOR_SERVER_ADDR = "http://conductor_conductor-server_1:8080"
+STORE_TASK_PATH = "/tmp/generate_task.json"
+STORE_WF_PATH = "/tmp/generate_workflow.json"
+
+
+@click.command()
+@click.option("--filepath", help="file path of test case")
+def cmdParse(filepath):
+ parse(filepath)
+
+
+def parse(filepath):
+ filePrefix, fileName = os.path.split(filepath)
+ print '------------ start to parse the test case:' + \
+ '%s ----------------' % fileName
+ with open(filepath) as f:
+ yaml_file = yaml.load(f)
+ parseTestcase(yaml_file['schema'], fileName)
+
+ workflowId = runWorkFlow()
+ print '------------------- parse executes end -------------------------'
+
+ return workflowId
+
+
+def parseTestcase(schema, tcName='testcase0'):
+ if schema is None:
+ return parseLog(False, reason='schema not found.')
+ steps = schema['steps']
+ if steps is None:
+ return parseLog(False, reason='steps is invalid.')
+ flows = schema['flows']
+ if flows is None:
+ return parseLog(False, reasion='flows is invalid.')
+ # steps is a list, step is dict. no json here.
+ # steps = sorted(steps, sortById)
+
+ # load context
+ contextDict = {}
+ contextDir = os.path.join(BASE_DIR, "env", "context", "context.yaml")
+ with open(contextDir, "r") as f:
+ contextDict = yaml.load(f)
+ #
+ testStepMgr = TestStepManager(contextDict)
+
+ stepObjArr = []
+ for step in steps:
+ if 'args' not in step:
+ step['args'] = {}
+ # type and action can be extended, default couple is 'test' & 'start'.
+ if 'type' not in step:
+ step['type'] = 'test'
+ step['action'] = 'start'
+
+ stepObj = testStepMgr.getStepObj(
+ step['type'], step['id'], step['name'], step['service'],
+ step['action'], step['args'])
+ stepObjArr.append(stepObj)
+
+ # generate workflow by 'flow' and 'step'
+ tcName = os.path.splitext(tcName)[0]
+ wfFileObj = WorkflowFile(tcName)
+ workflowDict, taskMetaList = wfFileObj.generateMetaData(flows, stepObjArr)
+
+ with open(STORE_TASK_PATH, 'w') as f:
+ f.write(json.dumps({'task_group': taskMetaList}, indent=True))
+ with open(STORE_WF_PATH, 'w') as f:
+ f.write(json.dumps(workflowDict, indent=True))
+
+
+def parseWebTestcase(webTestcase):
+ print 'parseWebTestcase----------------------------'
+
+ stepList = webTestcase['stepList']
+ mainOrdersList = webTestcase['mainOrdersList']
+ subflowList = webTestcase['subflowList']
+
+ parseData = collections.OrderedDict()
+ parseData['schema'] = collections.OrderedDict()
+ parseData['schema']['steps'] = []
+ parseData['schema']['flows'] = []
+
+ parseStepList = parseData['schema']['steps']
+ parseFlowList = parseData['schema']['flows']
+ stepIndexDict = {}
+ # parse stepList
+ for index in range(len(stepList)):
+ stepItem = stepList[index]
+ parseStep = collections.OrderedDict()
+
+ parseStep['id'] = index + 1
+ parseStep['name'] = stepItem['name']
+ parseStep['service'] = collections.OrderedDict()
+ parseStep['service']['name'] = stepItem['service']
+ parseStep['service']['interface'] = stepItem['action']
+ parseStep['action'] = 'start'
+ parseStep['args'] = {}
+ for paramItem in stepItem['params']:
+ parseStep['args'][paramItem['key']] = transParamString(
+ paramItem['value'])
+
+ parseStepList.append(parseStep)
+ stepIndexDict[parseStep['name']] = parseStep['id']
+ # parse flows
+ # parse mainflow
+ print stepIndexDict
+ typeDict = {1: 'normal', 2: 'switch', 3: 'parallel'}
+ mainFlow = collections.OrderedDict()
+ mainFlow['name'] = 'main'
+ mainFlow['orders'] = []
+ mainFlow['orders'] = parseOrderList(
+ mainOrdersList, stepIndexDict, typeDict)
+ parseFlowList.append(mainFlow)
+
+ # parse subflow
+ for subflowItem in subflowList:
+ replaceSubflow = collections.OrderedDict()
+ replaceSubflow['name'] = subflowItem['name']
+ replaceSubflow['orders'] = parseOrderList(
+ subflowItem['orderList'], stepIndexDict, typeDict)
+ parseFlowList.append(replaceSubflow)
+
+ print 'END parseWebTestcase----------------------------'
+ return parseData
+
+
+# parse orderlist from web edition to server edition
+def parseOrderList(orderList, stepIndexDict, typeDict):
+ replaceList = []
+ for orderItem in orderList:
+ replaceOrder = collections.OrderedDict()
+ orderType = typeDict[orderItem['type']]
+ replaceOrder['type'] = orderType
+ if orderType == 'normal':
+ stepId = stepIndexDict[orderItem['step']]
+ replaceOrder['step'] = stepId
+ elif orderType == 'switch':
+ replaceOrder['value'] = orderItem['value']
+ replaceOrder['cases'] = collections.OrderedDict()
+ for caseItem in orderItem['cases']:
+ caseValue = caseItem['value']
+ caseOrderType = caseItem['orderType']
+ caseOrderValue = caseItem['orderValue']
+ if caseOrderType == "step":
+ orderInCase = collections.OrderedDict()
+ orderInCase['type'] = 'normal'
+ orderInCase['step'] = stepIndexDict[caseOrderValue]
+ replaceOrder['cases'][caseValue] = [orderInCase]
+ else:
+ replaceOrder['cases'][caseValue] = caseOrderValue
+ else:
+ replaceOrder['parallel'] = collections.OrderedDict()
+ pIndex = 1
+ for branchItem in orderItem['branches']:
+ pKey = 'p' + str(pIndex)
+ branchOrderType = branchItem['orderType']
+ branchOrderValue = branchItem['orderValue']
+ if branchOrderType == "step":
+ replaceBranchItem = collections.OrderedDict()
+ replaceBranchItem['type'] = 'normal'
+ replaceBranchItem['step'] = stepIndexDict[branchOrderValue]
+ replaceOrder['parallel'][pKey] = [replaceBranchItem]
+ else:
+ replaceOrder['parallel'][pKey] = branchOrderValue
+ pIndex += 1
+ replaceList.append(replaceOrder)
+ return replaceList
+
+
+def transParamString(val):
+ if type(val) != str:
+ return val
+ if '.' not in val:
+ if val.isdigit():
+ return int(val)
+ try:
+ f = float(val)
+ return f
+ except ValueError:
+ return val
+
+
+def getWebTestcase(originTcDict):
+ print "getWebTestcase----------------------------------"
+ webTcDict = {
+ "stepList": [],
+ "mainOrdersList": [],
+ "subflowList": []
+ }
+ stepList = webTcDict['stepList']
+ subflowList = webTcDict['subflowList']
+ if originTcDict is None:
+ return webTcDict
+ originContent = originTcDict['schema']
+ originSteps = originContent['steps']
+ stepIndexDict = {}
+ # transform steps to stepList
+ for stepItem in originSteps:
+ replaceStep = {}
+ replaceStep['name'] = stepItem['name']
+ replaceStep['service'] = stepItem['service']['name']
+ replaceStep['action'] = stepItem['service']['interface']
+ replaceStep['params'] = []
+ if 'args' in stepItem:
+ for (key, value) in stepItem['args'].items():
+ replaceParam = {}
+ replaceParam['key'] = key
+ replaceParam['value'] = value
+ replaceStep['params'].append(replaceParam)
+ stepList.append(replaceStep)
+ stepIndexDict[stepItem['id']] = stepItem['name']
+
+ # transform main flow
+ originFlows = originContent['flows']
+ originMainflow = {}
+ for flowIndex in range(len(originFlows)):
+ flowItem = originFlows[flowIndex]
+ if flowItem['name'] == 'main':
+ originMainflow = flowItem
+ originFlows.pop(flowIndex)
+ break
+ typeDict = {'normal': 1, 'switch': 2, 'parallel': 3}
+ webTcDict['mainOrdersList'] = getOrderList(
+ originMainflow['orders'], stepIndexDict, typeDict)
+
+ # transform subflows
+ for originSubflow in originFlows:
+ replaceSubflow = {}
+ replaceSubflow['name'] = originSubflow['name']
+ replaceSubflow['orderList'] = getOrderList(
+ originSubflow['orders'], stepIndexDict, typeDict)
+ subflowList.append(replaceSubflow)
+
+ # return web edition of testcase
+ print "END getWebTestcase----------------------------------"
+ return webTcDict
+
+
+def getOrderList(originOrderList, stepIndexDict, typeDict):
+ replaceOrderList = []
+ for orderItem in originOrderList:
+ replaceOrderItem = {}
+ orderType = orderItem['type']
+ replaceOrderItem['type'] = typeDict[orderType]
+ if orderType == 'normal':
+ stepName = stepIndexDict[orderItem['step']]
+ replaceOrderItem['step'] = stepName
+ elif orderType == 'switch':
+ replaceOrderItem['value'] = orderItem['value']
+ replaceOrderItem['cases'] = []
+ for (caseValue, ordersInCase) in orderItem['cases'].items():
+ replaceCase = {}
+ replaceCase['value'] = caseValue
+ if type(ordersInCase) == list:
+ replaceCase['orderType'] = 'step'
+ caseStepName = stepIndexDict[ordersInCase[0]['step']]
+ replaceCase['orderValue'] = caseStepName
+ else:
+ replaceCase['orderType'] = 'flow'
+ replaceCase['orderValue'] = ordersInCase
+ replaceOrderItem['cases'].append(replaceCase)
+ else:
+ replaceOrderItem['branches'] = []
+ for paraIndex in orderItem['parallel']:
+ paraItem = orderItem['parallel'][paraIndex]
+ replaceBranch = {}
+ if type(paraItem) == list:
+ replaceBranch['orderType'] = 'step'
+ branchStepName = stepIndexDict[paraItem[0]['step']]
+ replaceBranch['orderValue'] = branchStepName
+ else:
+ replaceBranch['orderType'] = 'flow'
+ replaceBranch['orderValue'] = paraItem
+ replaceOrderItem['branches'].append(replaceBranch)
+ replaceOrderList.append(replaceOrderItem)
+
+ return replaceOrderList
+
+
+def runWorkFlow():
+ wfMgr = WorkflowMgr(CONDUCTOR_SERVER_ADDR)
+ wfMgr.setTaskDefFromFile(STORE_TASK_PATH)
+ wfMgr.setWorkflowFromFile(STORE_WF_PATH)
+ inputParam = {'input': 'fake'}
+ workflowId = wfMgr.startWorkflow(inputParam)
+ return workflowId
+
+
+def parseLog(flag, **msg):
+ return {'result': flag, 'message': msg}
+
+
+if __name__ == "__main__":
+ cmdParse()