summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lcm/pub/config/config.py3
-rw-r--r--lcm/pub/msapi/activiti.py16
-rw-r--r--lcm/pub/nfvi/vim/vimadaptor.py5
-rw-r--r--lcm/pub/utils/restcall.py12
-rw-r--r--lcm/pub/utils/toscaparser/basemodel.py70
-rw-r--r--lcm/pub/utils/toscaparser/nsdmodel.py158
-rw-r--r--lcm/settings.py1
-rw-r--r--lcm/urls.py6
-rw-r--r--lcm/workflows/auto_deploy.py38
-rw-r--r--lcm/workflows/build_in.py117
-rw-r--r--lcm/workflows/tests.py46
-rw-r--r--lcm/workflows/views.py31
12 files changed, 455 insertions, 48 deletions
diff --git a/lcm/pub/config/config.py b/lcm/pub/config/config.py
index 85ba24d8..f501d338 100644
--- a/lcm/pub/config/config.py
+++ b/lcm/pub/config/config.py
@@ -61,6 +61,9 @@ SDC_BASE_URL = "https://127.0.0.1:1234/api"
SDC_USER = "admin"
SDC_PASSWD = "admin"
+# [workflow]
+DEPLOY_WORKFLOW_WHEN_START = True
+
diff --git a/lcm/pub/msapi/activiti.py b/lcm/pub/msapi/activiti.py
index 69320d1c..b3a12205 100644
--- a/lcm/pub/msapi/activiti.py
+++ b/lcm/pub/msapi/activiti.py
@@ -14,7 +14,7 @@
import json
from lcm.pub.exceptions import NSLCMException
-from lcm.pub.utils.restcall import req_by_msb
+from lcm.pub.utils import restcall
"""
Input:
@@ -29,14 +29,16 @@ Output:
"processId": "string"
}
"""
-def deploy_workflow(content):
- content_str = json.JSONEncoder().encode(content)
- ret = req_by_msb("/api/workflow/v1/package", "POST", content_str)
+def deploy_workflow(file_path):
+ file_name = file_path.split("/")[-1]
+ file_data = {
+ 'file': open(file_path, 'rb'),
+ 'filename': file_name}
+ ret = restcall.upload_by_msb("api/workflow/v1/package", "POST", file_data)
if ret[0] != 0:
raise NSLCMException("Status code is %s, detail is %s.", ret[2], ret[1])
return json.JSONDecoder().decode(ret[1])
-
"""
Input:
None
@@ -49,7 +51,7 @@ Output:
"""
def undeploy_workflow(deploy_id):
uri = "api/workflow/v1/package/{deployId}".format(deployId=deploy_id)
- ret = req_by_msb(uri, "DELETE")
+ ret = restcall.req_by_msb(uri, "DELETE")
if ret[0] != 0:
raise NSLCMException("Status code is %s, detail is %s.", ret[2], ret[1])
return json.JSONDecoder().decode(ret[1])
@@ -70,7 +72,7 @@ Output:
"""
def exec_workflow(content):
content_str = json.JSONEncoder().encode(content)
- ret = req_by_msb("api/workflow/v1/process/instance", "POST", content_str)
+ ret = restcall.req_by_msb("api/workflow/v1/process/instance", "POST", content_str)
if ret[0] != 0:
raise NSLCMException("Status code is %s, detail is %s.", ret[2], ret[1])
return json.JSONDecoder().decode(ret[1])
diff --git a/lcm/pub/nfvi/vim/vimadaptor.py b/lcm/pub/nfvi/vim/vimadaptor.py
index 5f08d0b1..6f63a48c 100644
--- a/lcm/pub/nfvi/vim/vimadaptor.py
+++ b/lcm/pub/nfvi/vim/vimadaptor.py
@@ -35,10 +35,7 @@ class VimAdaptor:
def create_api(self, connectInfo):
vimtype = connectInfo['vimtype'] if 'vimtype' in connectInfo else None
logger.info("call %s, vimtype=%s" % (fun_name(), vimtype))
- if vimtype == const.VIM_OPENSTACK:
- from lcm.pub.nfvi.vim.api.openstack.api import OpenstackApi
- self.apiImpl = OpenstackApi()
- elif vimtype == const.VIM_VMWARE:
+ if vimtype in (const.VIM_OPENSTACK, const.VIM_VMWARE):
from lcm.pub.nfvi.vim.api.multivim.api import MultiVimApi
self.apiImpl = MultiVimApi()
else:
diff --git a/lcm/pub/utils/restcall.py b/lcm/pub/utils/restcall.py
index bfb02d6b..0ef20561 100644
--- a/lcm/pub/utils/restcall.py
+++ b/lcm/pub/utils/restcall.py
@@ -81,6 +81,18 @@ def req_by_msb(resource, method, content=''):
base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT)
return call_req(base_url, "", "", rest_no_auth, resource, method, content)
+def upload_by_msb(resource, method, file_data={}):
+ headers = {'Content-Type': 'application/octet-stream'}
+ full_url = "http://%s:%s/%s" % (MSB_SERVICE_IP, MSB_SERVICE_PORT, resource)
+ http = httplib2.Http()
+ resp, resp_content = http.request(full_url,
+ method=method.upper(), body=file_data, headers=headers)
+ resp_status, resp_body = resp['status'], resp_content.decode('UTF-8')
+ if resp_status not in status_ok_list:
+ logger.error("Status code is %s, detail is %s.", resp_status, resp_body)
+ return [1, "Failed to upload file.", resp_status]
+ logger.debug("resp_body=%s", resp_body)
+ return [0, resp_body, resp_status]
def combine_url(base_url, resource):
full_url = None
diff --git a/lcm/pub/utils/toscaparser/basemodel.py b/lcm/pub/utils/toscaparser/basemodel.py
index e1f841ff..e624bd08 100644
--- a/lcm/pub/utils/toscaparser/basemodel.py
+++ b/lcm/pub/utils/toscaparser/basemodel.py
@@ -105,23 +105,6 @@ class BaseInfoModel(object):
if 'metadata' in tosca.tpl:
self.metadata = copy.deepcopy(tosca.tpl['metadata'])
- def buildProperties(self, nodeTemplate, parsed_params):
- properties = {}
- isMappingParams = parsed_params and len(parsed_params) > 0
- for k, item in nodeTemplate.get_properties().items():
- properties[k] = item.value
- if isinstance(item.value, GetInput):
- if item.value.result() and isMappingParams:
- properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result())
- else:
- tmp = {}
- tmp[item.value.name] = item.value.input_name
- properties[k] = tmp
- if 'attributes' in nodeTemplate.entity_tpl:
- for k, item in nodeTemplate.entity_tpl['attributes'].items():
- properties[k] = str(item)
- return properties
-
def buildProperties(self, nodeTemplate, parsed_params):
properties = {}
isMappingParams = parsed_params and len(parsed_params) > 0
@@ -198,6 +181,13 @@ class BaseInfoModel(object):
def isPnf(self, node):
return node['nodeType'].upper().find('.PNF.') >= 0 or node['nodeType'].upper().endswith('.PNF')
+ def isCp(self, node):
+ return node['nodeType'].upper().find('.CP.') >= 0 or node['nodeType'].upper().endswith('.CP')
+
+ def isVl(self, node):
+ return node['nodeType'].upper().find('.VIRTUALLINK.') >= 0 or node['nodeType'].upper().find('.VL.') >= 0 or \
+ node['nodeType'].upper().endswith('.VIRTUALLINK') or node['nodeType'].upper().endswith('.VL')
+
def get_requirement_node_name(self, req_value):
return self.get_prop_from_obj(req_value, 'node')
@@ -211,6 +201,13 @@ class BaseInfoModel(object):
def getNodeDependencys(self, node):
return self.getRequirementByName(node, 'dependency')
+ def getVirtualLinks(self, node):
+ return self.getRequirementByName(node, 'virtualLink')
+
+ def getVirtualbindings(self, node):
+ return self.getRequirementByName(node, 'virtualbinding')
+
+
def getRequirementByName(self, node, requirementName):
requirements = []
if 'requirements' in node:
@@ -229,3 +226,42 @@ class BaseInfoModel(object):
rets.append({"key_name":key, "vl_id":self.get_requirement_node_name(value)})
return rets
+ def _verify_value(self, value, inputs, parsed_params):
+ if isinstance(value, str):
+ return self._verify_string(inputs, parsed_params, value)
+ if isinstance(value, list) or isinstance(value, dict):
+ return self._verify_object(value, inputs, parsed_params)
+ return value
+
+ def _verify_object(self, value, inputs, parsed_params):
+ s = self._verify_string(inputs, parsed_params, json.dumps(value))
+ return json.loads(s)
+
+ def _get_input_name(self, getInput):
+ input_name = getInput.split(':')[1]
+ input_name = input_name.strip()
+ return input_name.replace('"', '').replace('}', '')
+
+ def _verify_string(self, inputs, parsed_params, value):
+ getInputs = re.findall(r'{"get_input": "[a-zA-Z_0-9]+"}', value)
+ for getInput in getInputs:
+ input_name = self._get_input_name(getInput)
+ if parsed_params and input_name in parsed_params:
+ value = value.replace(getInput, json.dumps(parsed_params[input_name]))
+ else:
+ for input_def in inputs:
+ if input_def.default and input_name == input_def.name:
+ value = value.replace(getInput, json.dumps(input_def.default))
+ return value
+
+ def get_node_vl_id(self, node):
+ vl_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualLinks(node))
+ if len(vl_ids) > 0:
+ return vl_ids[0]
+ return ""
+
+ def get_node_by_name(self, node_templates, name):
+ for node in node_templates:
+ if node['name'] == name:
+ return node
+ return None \ No newline at end of file
diff --git a/lcm/pub/utils/toscaparser/nsdmodel.py b/lcm/pub/utils/toscaparser/nsdmodel.py
index 7d7dafad..1838014d 100644
--- a/lcm/pub/utils/toscaparser/nsdmodel.py
+++ b/lcm/pub/utils/toscaparser/nsdmodel.py
@@ -19,16 +19,11 @@ class EtsiNsdInfoModel(BaseInfoModel):
self.vnfs = self._get_all_vnf(nodeTemplates)
self.pnfs = self._get_all_pnf(nodeTemplates)
- # self.vls = self.get_all_vl(nodeTemplates)
- # self.cps = self.get_all_cp(nodeTemplates)
- # self.routers = self.get_all_router(nodeTemplates)
- # self.fps = self._get_all_fp(nodeTemplates)
- # self.vnffgs = self._get_all_vnffg(tosca.topology_template.groups)
- # self.server_groups = self.get_all_server_group(tosca.topology_template.groups)
- # self.ns_exposed = self.get_all_endpoint_exposed(tosca.topology_template)
- # self.policies = self._get_policies_scaling(tosca.topology_template.policies)
- # self.ns_flavours = self.get_all_flavour(tosca.topology_template.groups)
- # self.nested_ns = self.get_all_nested_ns(nodeTemplates)
+ self.vls = self.get_all_vl(nodeTemplates)
+ self.cps = self.get_all_cp(nodeTemplates)
+ self.routers = self.get_all_router(nodeTemplates)
+ self.fps = self._get_all_fp(nodeTemplates)
+
def buildInputs(self, top_inputs):
ret = {}
@@ -98,4 +93,145 @@ class EtsiNsdInfoModel(BaseInfoModel):
req_node_name = self.get_requirement_node_name(value)
if req_node_name != None and req_node_name == node['name']:
cps.append(tmpnode)
- return cps \ No newline at end of file
+ return cps
+
+ def get_all_vl(self, nodeTemplates):
+ vls = []
+ for node in nodeTemplates:
+ if self.isVl(node):
+ vl = {}
+ vl['vl_id'] = node['name']
+ vl['description'] = node['description']
+ vl['properties'] = node['properties']
+ vl['route_external'] = False
+ vl['route_id'] = self._get_vl_route_id(node)
+ vls.append(vl)
+ if self._isExternalVL(node):
+ vl = {}
+ vl['vl_id'] = node['name']
+ vl['description'] = node['description']
+ vl['properties'] = node['properties']
+ vl['route_external'] = True
+ vls.append(vl)
+ return vls
+
+ def _get_vl_route_id(self, node):
+ route_ids = map(lambda x: self.get_requirement_node_name(x),
+ self.getRequirementByName(node, 'virtual_route'))
+ if len(route_ids) > 0:
+ return route_ids[0]
+ return ""
+
+ def _isExternalVL(self, node):
+ return node['nodeType'].upper().find('.ROUTEEXTERNALVL') >= 0
+
+ def get_all_cp(self, nodeTemplates):
+ cps = []
+ for node in nodeTemplates:
+ if self.isCp(node):
+ cp = {}
+ cp['cp_id'] = node['name']
+ cp['cpd_id'] = node['name']
+ cp['description'] = node['description']
+ cp['properties'] = node['properties']
+ cp['vl_id'] = self.get_node_vl_id(node)
+ binding_node_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualbindings(node))
+ # cp['vnf_id'] = self._filter_vnf_id(binding_node_ids, nodeTemplates)
+ cp['pnf_id'] = self._filter_pnf_id(binding_node_ids, nodeTemplates)
+ vls = self.buil_cp_vls(node)
+ if len(vls) > 1:
+ cp['vls'] = vls
+ cps.append(cp)
+ return cps
+
+ def buil_cp_vls(self, node):
+ return map(lambda x: self._build_cp_vl(x), self.getVirtualLinks(node))
+
+ def _build_cp_vl(self, req):
+ cp_vl = {}
+ cp_vl['vl_id'] = self.get_prop_from_obj(req, 'node')
+ relationship = self.get_prop_from_obj(req, 'relationship')
+ if relationship != None:
+ properties = self.get_prop_from_obj(relationship, 'properties')
+ if properties != None and isinstance(properties, dict):
+ for key, value in properties.items():
+ cp_vl[key] = value
+ return cp_vl
+
+ def _filter_pnf_id(self, node_ids, node_templates):
+ for node_id in node_ids:
+ node = self.get_node_by_name(node_templates, node_id)
+ if self.isPnf(node):
+ return node_id
+ return ""
+
+ def get_all_router(self, nodeTemplates):
+ rets = []
+ for node in nodeTemplates:
+ if self._isRouter(node):
+ ret = {}
+ ret['router_id'] = node['name']
+ ret['description'] = node['description']
+ ret['properties'] = node['properties']
+ ret['external_vl_id'] = self._get_router_external_vl_id(node)
+ ret['external_ip_addresses'] = self._get_external_ip_addresses(node)
+
+ rets.append(ret)
+ return rets
+
+ def _isRouter(self, node):
+ return node['nodeType'].upper().find('.ROUTER.') >= 0 or node['nodeType'].upper().endswith('.ROUTER')
+
+ def _get_router_external_vl(self, node):
+ return self.getRequirementByName(node, 'external_virtual_link')
+
+ def _get_router_external_vl_id(self, node):
+ ids = map(lambda x: self.get_requirement_node_name(x), self._get_router_external_vl(node))
+ if len(ids) > 0:
+ return ids[0]
+ return ""
+
+ def _get_external_ip_addresses(self, node):
+ external_vls = self._get_router_external_vl(node)
+ if len(external_vls) > 0:
+ if 'relationship' in external_vls[0] and 'properties' in external_vls[0]['relationship'] and 'router_ip_address' in external_vls[0]['relationship']['properties']:
+ return external_vls[0]['relationship']['properties']['router_ip_address']
+ return []
+
+ def _get_all_fp(self, nodeTemplates):
+ fps = []
+ for node in nodeTemplates:
+ if self._isFp(node):
+ fp = {}
+ fp['fp_id'] = node['name']
+ fp['description'] = node['description']
+ fp['properties'] = node['properties']
+ fp['forwarder_list'] = self._getForwarderList(node, nodeTemplates)
+
+ fps.append(fp)
+ return fps
+
+ def _isFp(self, node):
+ return node['nodeType'].upper().find('.FP.') >= 0 or node['nodeType'].upper().find('.SFP.') >= 0 or node[
+ 'nodeType'].upper().endswith('.FP') or node['nodeType'].upper().endswith('.SFP')
+
+ def _getForwarderList(self, node, node_templates):
+ forwarderList = []
+ if 'requirements' in node:
+ for item in node['requirements']:
+ for key, value in item.items():
+ if key == 'forwarder':
+ tmpnode = self.get_node_by_req(node_templates, value)
+ type = 'cp' if self.isCp(tmpnode) else 'vnf'
+ req_node_name = self.get_requirement_node_name(value)
+ if isinstance(value, dict) and 'capability' in value:
+ forwarderList.append(
+ {"type": type, "node_name": req_node_name, "capability": value['capability']})
+ else:
+ forwarderList.append({"type": type, "node_name": req_node_name, "capability": ""})
+
+ return forwarderList
+
+ def get_node_by_req(self, node_templates, req):
+ req_node_name = self.get_requirement_node_name(req)
+ return self.get_node_by_name(node_templates, req_node_name)
diff --git a/lcm/settings.py b/lcm/settings.py
index f338ac29..1cf13714 100644
--- a/lcm/settings.py
+++ b/lcm/settings.py
@@ -136,6 +136,7 @@ LOGGING = {
if 'test' in sys.argv:
config.REG_TO_MSB_WHEN_START = False
+ config.DEPLOY_WORKFLOW_WHEN_START = False
DATABASES = {}
DATABASES['default'] = {
'ENGINE': 'django.db.backends.sqlite3',
diff --git a/lcm/urls.py b/lcm/urls.py
index 9471def9..b0333513 100644
--- a/lcm/urls.py
+++ b/lcm/urls.py
@@ -14,6 +14,7 @@
from django.conf.urls import include, url
from lcm.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM
+from lcm.pub.config.config import DEPLOY_WORKFLOW_WHEN_START
urlpatterns = [
url(r'^', include('lcm.samples.urls')),
@@ -31,3 +32,8 @@ if REG_TO_MSB_WHEN_START:
import json
from lcm.pub.utils.restcall import req_by_msb
req_by_msb(REG_TO_MSB_REG_URL, "POST", json.JSONEncoder().encode(REG_TO_MSB_REG_PARAM))
+
+# deploy workflow when startup
+if DEPLOY_WORKFLOW_WHEN_START:
+ from lcm.workflows import auto_deploy
+ auto_deploy.deploy_workflow_on_startup()
diff --git a/lcm/workflows/auto_deploy.py b/lcm/workflows/auto_deploy.py
new file mode 100644
index 00000000..8813542b
--- /dev/null
+++ b/lcm/workflows/auto_deploy.py
@@ -0,0 +1,38 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import traceback
+
+from lcm.pub.database.models import WFPlanModel
+from lcm.pub.msapi import activiti
+
+logger = logging.getLogger(__name__)
+
+def deploy_workflow_on_startup():
+ try:
+ if WFPlanModel.objects.filter():
+ logger.warn("Workflow is already deployed.")
+ return
+ file_path = "TODO:"
+ deploy_info = activiti.deploy_workflow(file_path)
+ WFPlanModel(
+ deployed_id=deploy_info["deployedId"],
+ process_id=deploy_info["processId"],
+ status=deploy_info["status"],
+ message=deploy_info["message"],
+ plan_name="ns_instantiate").save()
+ logger.info("Deploy workflow successfully.")
+ except:
+ logger.error(traceback.format_exc())
+
diff --git a/lcm/workflows/build_in.py b/lcm/workflows/build_in.py
new file mode 100644
index 00000000..b24c7af8
--- /dev/null
+++ b/lcm/workflows/build_in.py
@@ -0,0 +1,117 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import traceback
+
+from lcm.pub.utils.syscomm import fun_name
+from lcm.pub.utils.values import ignore_case_get
+from lcm.pub.utils import restcall
+from lcm.pub.exceptions import NSLCMException
+
+logger = logging.getLogger(__name__)
+
+RESULT_OK, RESULT_NG = "0", "1"
+
+"""
+format of input_data
+{
+ "jobId": uuid of job,
+ "nsInstanceId": id of ns instance,
+ "object_context": json format of nsd,
+ "object_additionalParamForNs": json format of additional parameters for ns,
+ "object_additionalParamForVnf": json format of additional parameters for vnf,
+ "vlCount": int type of VL count,
+ "vnfCount: int type of VNF count,
+ "sfcCount": int type of SFC count,
+ "sdnControllerId": uuid of SDN controller
+}
+"""
+def run_ns_instantiate(input_data):
+ logger.debug("Enter %s, input_data is %s", fun_name(), input_data)
+ job_id = ignore_case_get(input_data, "jobId")
+ ns_inst_id = ignore_case_get(input_data, "nsInstanceId")
+ nsd_json = ignore_case_get(input_data, "object_context")
+ ns_param_json = ignore_case_get(input_data, "object_additionalParamForNs")
+ vnf_param_json = ignore_case_get(input_data, "object_additionalParamForVnf")
+ vl_count = ignore_case_get(input_data, "vlCount")
+ vnf_count = ignore_case_get(input_data, "vnfCount")
+ sfc_count = ignore_case_get(input_data, "sfcCount")
+ sdnc_id = ignore_case_get(input_data, "sdnControllerId")
+
+ update_job(job_id, 10, "0", "Start to create VL")
+ for i in range(vl_count):
+ create_vl(ns_inst_id, i + 1, nsd_json, ns_param_json)
+
+ update_job(job_id, 30, "0", "Start to create VNF")
+ for i in range(vnf_count):
+ create_vnf()
+ wait_until_job_done()
+
+ update_job(job_id, 70, "0", "Start to create SFC")
+ for i in range(sfc_count):
+ create_sfc()
+ wait_until_job_done()
+
+ update_job(job_id, 90, "0", "Start to post deal")
+ post_deal()
+
+ update_job(job_id, 100, "0", "Create NS successfully.")
+
+def create_vl(ns_inst_id, vl_index, nsd, ns_param):
+ uri = "api/nslcm/v1/ns/vls"
+ data = json.JSONEncoder().encode({
+ "nsInstanceId": ns_inst_id,
+ "vlIndex": vl_index,
+ "context": nsd,
+ "additionalParamForNs": ns_param
+ })
+
+ ret = restcall.req_by_msb(uri, "POST", data)
+ if ret[0] != 0:
+ logger.error("Failed to call create_vl(%s): %s", vl_index, ret[1])
+ raise NSLCMException("Failed to call create_vl(index is %s)" % vl_index)
+
+ result = str(ret[1]["result"])
+ detail = ret[1]["detail"]
+ vl_id = ret[1]["vlId"]
+ if result != RESULT_OK:
+ logger.error("Failed to create VL(%s): %s", vl_id, detail)
+ raise NSLCMException("Failed to create VL(%s)" % vl_id)
+
+ logger.debug("Create VL(%s) successfully.", vl_id)
+
+def create_vnf():
+ # TODO:
+ pass
+
+def create_sfc():
+ # TODO:
+ pass
+
+def post_deal():
+ # TODO:
+ pass
+
+def update_job(job_id, progress, errcode, desc):
+ uri = "api/nslcm/v1/jobs/{jobId}".format(jobId=job_id)
+ data = json.JSONEncoder().encode({
+ "progress": progress,
+ "errcode": errcode,
+ "desc": desc
+ })
+ restcall.req_by_msb(uri, "POST", data)
+
+def wait_until_job_done():
+ # TODO:
+ pass
diff --git a/lcm/workflows/tests.py b/lcm/workflows/tests.py
index 31651624..bba2d7ac 100644
--- a/lcm/workflows/tests.py
+++ b/lcm/workflows/tests.py
@@ -14,18 +14,60 @@
import unittest
import json
+import mock
+import os
from django.test import Client
from rest_framework import status
+from lcm.pub.utils import restcall
+from lcm.pub.database.models import WFPlanModel
class WorkflowViewTest(unittest.TestCase):
def setUp(self):
self.client = Client()
+ WFPlanModel.objects.filter().delete()
def tearDown(self):
pass
- def test_deploy_workflow(self):
+ @mock.patch.object(restcall, 'upload_by_msb')
+ def test_deploy_workflow(self, mock_upload_by_msb):
+ mock_upload_by_msb.return_value = [0, json.JSONEncoder().encode({
+ "status": "1",
+ "message": "2",
+ "deployedId": "3",
+ "processId": "4"
+ }), '202']
response = self.client.post("/api/nslcm/v1/workflow",
- {"filePath": "/home/init.zip"}, format='json')
+ {"filePath": os.path.abspath(__file__)}, format='json')
self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code, response.content)
+ self.assertEqual(1, len(WFPlanModel.objects.filter(deployed_id="3")))
+
+ @mock.patch.object(restcall, 'upload_by_msb')
+ @mock.patch.object(restcall, 'call_req')
+ def test_force_deploy_workflow(self, mock_call_req, mock_upload_by_msb):
+ mock_call_req.return_value = [0, json.JSONEncoder().encode({
+ "status": "1",
+ "message": "2"
+ }), '202']
+ mock_upload_by_msb.return_value = [0, json.JSONEncoder().encode({
+ "status": "2",
+ "message": "3",
+ "deployedId": "4",
+ "processId": "5"
+ }), '202']
+ WFPlanModel(deployed_id="1", process_id="2", status="3", message="4").save()
+ response = self.client.post("/api/nslcm/v1/workflow",
+ {"filePath": os.path.abspath(__file__), "forceDeploy": "True"}, format='json')
+ self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code, response.content)
+ self.assertEqual(0, len(WFPlanModel.objects.filter(deployed_id="1")))
+ self.assertEqual(1, len(WFPlanModel.objects.filter(deployed_id="4")))
+
+ def test_deploy_workflow_when_already_deployed(self):
+ WFPlanModel(deployed_id="1", process_id="2", status="3", message="4").save()
+ response = self.client.post("/api/nslcm/v1/workflow",
+ {"filePath": os.path.abspath(__file__)}, format='json')
+ self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code, response.content)
+ self.assertEqual({'msg': 'Already deployed.'}, json.loads(response.content))
+
+
diff --git a/lcm/workflows/views.py b/lcm/workflows/views.py
index 92f029fb..41779e87 100644
--- a/lcm/workflows/views.py
+++ b/lcm/workflows/views.py
@@ -14,14 +14,16 @@
import logging
import traceback
+import sys
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
-from lcm.pub.database import models
+from lcm.pub.database.models import WFPlanModel
from lcm.pub.utils.syscomm import fun_name
from lcm.pub.utils.values import ignore_case_get
+from lcm.pub.msapi import activiti
logger = logging.getLogger(__name__)
@@ -30,16 +32,31 @@ logger = logging.getLogger(__name__)
@api_view(http_method_names=['POST'])
def deploy_workflow(request, *args, **kwargs):
logger.debug("Enter %s", fun_name())
- file_path = ignore_case_get(request.data, "filePath")
- logger.debug("file_path is %s", file_path)
- ret = None
try:
- ret = [0, "TODO"]
+ file_path = ignore_case_get(request.data, "filePath")
+ force_deploy = ignore_case_get(request.data, "forceDeploy")
+ logger.debug("file_path is %s, force_deploy is %s", file_path, force_deploy)
+ if force_deploy.upper() == "TRUE":
+ plans = WFPlanModel.objects.filter()
+ if len(plans) > 0:
+ activiti.undeploy_workflow(plans[0].deployed_id)
+ plans.delete()
+ else:
+ if WFPlanModel.objects.filter():
+ logger.warn("Already deployed.")
+ return Response(data={'msg': 'Already deployed.'}, status=status.HTTP_202_ACCEPTED)
+ deploy_info = activiti.deploy_workflow(file_path)
+ WFPlanModel(
+ deployed_id=deploy_info["deployedId"],
+ process_id=deploy_info["processId"],
+ status=deploy_info["status"],
+ message=deploy_info["message"],
+ plan_name="ns_instantiate").save()
except:
logger.error(traceback.format_exc())
return Response(data={'error': str(sys.exc_info())}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
- logger.debug("Leave %s, Return value is %s", fun_name(), ret)
- return Response(data=ret[1], status=status.HTTP_202_ACCEPTED)
+ logger.debug("Leave %s", fun_name())
+ return Response(data={'msg': 'OK'}, status=status.HTTP_202_ACCEPTED)