diff options
83 files changed, 6578 insertions, 3409 deletions
diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..7b59118f --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +# Copyright 2016 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. + +# Micro service of network service life cycle management. diff --git a/assembly.xml b/assembly.xml index afb97eff..5e7aef75 100644 --- a/assembly.xml +++ b/assembly.xml @@ -31,6 +31,7 @@ <include>**/*.wsdl</include> <include>**/*.xsd</include> <include>**/*.bpel</include> + <include>**/*.yml</include> </includes> </fileSet> <fileSet> diff --git a/docker/Dockerfile b/docker/Dockerfile index 28b259c9..ec325137 100755 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -30,7 +30,7 @@ ADD . /service WORKDIR /service # get binary zip from nexus - vfc-nfvo-lcm -RUN wget -q -O vfc-nfvo-lcm.zip 'https://nexus.onap.org/service/local/artifact/maven/redirect?r=staging&g=org.onap.vfc.nfvo.lcm&a=vfc-nfvo-lcm&v=LATEST&e=zip' && \ +RUN wget -q -O vfc-nfvo-lcm.zip 'https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.vfc.nfvo.lcm&a=vfc-nfvo-lcm&v=LATEST&e=zip' && \ unzip vfc-nfvo-lcm.zip && \ rm -rf vfc-nfvo-lcm.zip diff --git a/docs/platform/APIs/NSLCM_API/NSLCM_API_Specification_v0.1.rst b/docs/platform/APIs/NSLCM_API/NSLCM_API_Specification_v0.1.rst index 7188dc7f..6ed6196a 100644 --- a/docs/platform/APIs/NSLCM_API/NSLCM_API_Specification_v0.1.rst +++ b/docs/platform/APIs/NSLCM_API/NSLCM_API_Specification_v0.1.rst @@ -1,1086 +1,651 @@ Network services lifecycle management northbound APIs
-==========
-
- {
- "swagger": "2.0",
-
- "info": {
-
- "version": "1.0.0",
-
- "title": "ONAP VFC Network Service Lifecycle Management API",
-
- "description": "VFC Network Service Lifecycle Management Rest API.",
-
- "contact": {
-
- "name": "ONAP VFC team",
-
- "email": "onap-discuss@lists.onap.org",
-
- "url": "https://gerrit.onap.org/r/#/admin/projects/vfc/nfvo/lcm"
-
- }
-
- },
-
- "basePath": "/api/nslcm/v1",
-
- "schemes": [
-
- "http",
- "https"
-
- ],
-
- "consumes": [
-
- "application/json"
-
- ],
- "produces": [
-
- "application/json"
-
- ],
- "paths": {
-
- "/ns": {
-
- "post": {
-
- "tags": [
-
- "ns"
-
- ],
- "summary": "ns create",
-
- "description": "ns create",
-
- "operationId": "ns_create",
-
- "parameters": [
-
- {
-
- "in": "body",
-
- "name": "NSCreateRequest",
-
- "description": "NS Instance Create Request",
-
- "required": true,
-
- "schema": {
-
- "$ref": "#/definitions/NsCreateRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "200": {
-
- "description": "successful operation",
-
- "schema": {
-
- "$ref": "#/definitions/NsCreateResponse"
-
- }
-
- }
-
- }
-
- },
-
- "get": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns get",
-
- "description": "ns get",
-
- "operationId": "ns_get",
-
- "parameters": [],
-
- "responses": {
-
- "200": {
-
- "description": "successful operation",
-
- "schema": {
-
- "$ref": "#/definitions/NsInfo"
-
- }
-
- }
-
- }
-
- }
-
- },
-
- "/ns/{nsInstanceId}/Instantiate": {
-
- "post": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns Instantiate",
-
- "description": "ns Instantiate",
-
- "operationId": "ns_Instantiate",
-
- "parameters": [
-
- {
- "required": true,
-
- "type": "string",
-
- "description": "",
-
- "name": "nsInstanceId",
-
- "in": "path"
-
- },
- {
-
- "in": "body",
-
- "name": "NSInstantiateRequest",
-
- "description": "NS Instantiate Request Body",
-
- "required": true,
-
- "schema": {
-
- "$ref": "#/definitions/NsInstantiateRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "200": {
-
- "description": "",
-
- "schema": {
-
- "$ref": "#/definitions/JobInfo"
-
- }
-
- },
-
- "201": {
-
- "description": "Invalid Request"
-
- }
-
- }
-
- }
-
- },
-
- "/ns/{nsInstanceId}/scale": {
-
- "post": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns scale",
-
- "description": "ns scale",
-
- "operationId": "ns_scale",
-
- "parameters": [
-
- {
-
- "required": true,
-
- "type": "string",
-
- "description": "",
-
- "name": "nsInstanceId",
-
- "in": "path"
-
- },
-
- {
-
- "in": "body",
-
- "name": "ScaleNSRequest",
-
- "description": "Scale NS Request Body",
-
- "required": true,
-
- "schema": {
-
- "$ref": "#/definitions/NsScaleRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "200": {
-
- "description": "",
-
- "schema": {
-
- "$ref": "#/definitions/JobInfo"
-
- }
-
- },
-
- "201": {
-
- "description": "Invalid Request"
-
- }
-
- }
-
- }
-
- },
-
- "/ns/{ns_instance_id}/heal": {
-
- "post": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns heal",
-
- "description": "ns heal",
-
- "operationId": "ns_heal",
-
- "parameters": [
-
- {
- "required": true,
-
- "type": "string",
-
- "description": "Identifier of the NS instance.",
-
- "name": "ns_instance_id",
-
- "in": "path"
-
- },
-
- {
-
- "in": "body",
-
- "name": "healVnfData",
-
- "description": "healVnfData",
-
- "required": true,
-
- "schema": {
-
- "$ref": "#/definitions/NsHealRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "202": {
-
- "description": "",
-
- "schema": {
-
- "$ref": "#/definitions/JobInfo"
-
- }
-
- },
-
- "500": {
-
- "description": "the url is invalid"
-
- }
-
- }
-
- }
-
- },
-
- "/ns/{ns_instance_id}/terminate": {
-
- "post": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns terminate",
-
- "description": "ns terminate",
-
- "operationId": "ns_terminate",
-
- "parameters": [
-
- {
-
- "required": true,
-
- "type": "string",
-
- "description": "Identifier of the NS instance.",
-
- "name": "ns_instance_id",
-
- "in": "path"
-
- },
-
- {
- "in": "body",
-
- "name": "NsTerminateRequest",
-
- "description": "NsTerminateRequest",
-
- "required": true,
-
- "schema": {
-
- "$ref": "#/definitions/NsTerminateRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "202": {
-
- "description": "",
-
- "schema": {
-
- "$ref": "#/definitions/JobInfo"
-
- }
-
- },
-
- "500": {
-
- "description": "the url is invalid"
-
- }
-
- }
-
- }
-
- },
- "/ns/{ns_instance_id}": {
-
- "delete": {
-
- "tags": [
-
- "ns"
-
- ],
-
- "summary": "ns delete",
-
- "description": "ns delete",
-
- "operationId": "ns_delete",
-
- "parameters": [
-
- {
- "required": true,
-
- "type": "string",
-
- "description": "Identifier of the NS instance.",
-
- "name": "ns_instance_id",
-
- "in": "path"
-
- }
-
- ],
-
- "responses": {
-
- "204": {
-
- "description": "The NS instance resource and the associated NS identifier were deleted successfully."
-
- }
-
- }
-
- }
-
- },
-
- "/jobs/{jobId}": {
-
- "post": {
-
- "tags": [
-
- "job"
-
- ],
-
- "summary": "jobstatus",
-
- "description": "",
-
- "operationId": "jobstatus",
-
- "parameters": [
-
- {
- "required": true,
-
- "type": "string",
-
- "description": "",
-
- "name": "jobId",
-
- "in": "path"
-
- },
-
- {
-
- "in": "body",
-
- "name": "body",
-
- "description": "request param",
-
- "required": true,
-
+=====================================================
+
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "ONAP VFC Network Service Lifecycle Management API",
+ "description": "VFC Network Service Lifecycle Management Rest API.",
+ "contact": {
+ "name": "ONAP VFC team",
+ "email": "onap-discuss@lists.onap.org",
+ "url": "https://gerrit.onap.org/r/#/admin/projects/vfc/nfvo/lcm"
+ }
+ },
+ "basePath": "/api/nslcm/v1",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/ns": {
+ "post": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns create",
+ "description": "ns create",
+ "operationId": "ns_create",
+ "parameters": [
+ {
+ "in": "body",
+ "name": "NSCreateRequest",
+ "description": "NS Instance Create Request",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/NsCreateRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/NsCreateResponse"
+ }
+ },
+ "404": {
+ "description": "URL not found"
+ }
+ }
+ },
+ "get": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns get",
+ "description": "ns get",
+ "operationId": "ns_instantces_get",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "job response message id",
+ "name": "csarId",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/NsInstancesInfo"
+ }
+ },
+ "404": {
+ "description": "URL not found"
+ }
+ }
+ }
+ },
+ "/ns/{nsInstanceId}/Instantiate": {
+ "post": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns Instantiate",
+ "description": "ns Instantiate",
+ "operationId": "ns_Instantiate",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "",
+ "name": "nsInstanceId",
+ "in": "path"
+ },
+ {
+ "in": "body",
+ "name": "NSInstantiateRequest",
+ "description": "NS Instantiate Request Body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/NsInstantiateRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "201": {
+ "description": "Invalid Request"
+ },
+ "404": {
+ "description": "URL not found"
+ }
+ }
+ }
+ },
+ "/ns/{nsInstanceId}/scale": {
+ "post": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns scale",
+ "description": "ns scale",
+ "operationId": "ns_scale",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "",
+ "name": "nsInstanceId",
+ "in": "path"
+ },
+ {
+ "in": "body",
+ "name": "ScaleNSRequest",
+ "description": "Scale NS Request Body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/NsScaleRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "201": {
+ "description": "Invalid Request"
+ },
+ "404": {
+ "description": "URL not found"
+ }
+ }
+ }
+ },
+ "/ns/{nsInstanceId}/heal": {
+ "post": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns heal",
+ "description": "ns heal",
+ "operationId": "ns_heal",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "Identifier of the NS instance.",
+ "name": "nsInstanceId",
+ "in": "path"
+ },
+ {
+ "in": "body",
+ "name": "healVnfData",
+ "description": "healVnfData",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/NsHealRequest"
+ }
+ }
+ ],
+ "responses": {
+ "202": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "404": {
+ "description": "URL not found"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/ns/{nsInstanceId}/terminate": {
+ "post": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns terminate",
+ "description": "ns terminate",
+ "operationId": "ns_terminate",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "Identifier of the NS instance.",
+ "name": "nsInstanceId",
+ "in": "path"
+ },
+ {
+ "in": "body",
+ "name": "NsTerminateRequest",
+ "description": "NsTerminateRequest",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/NsTerminateRequest"
+ }
+ }
+ ],
+ "responses": {
+ "202": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/ns/{nsInstanceId}": {
+ "get": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns get",
+ "description": "ns get",
+ "operationId": "ns_instance_get",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "Identifier of the NS instance.",
+ "name": "nsInstanceId",
+ "in": "path"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/NsInstanceInfo"
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "ns"
+ ],
+ "summary": "ns delete",
+ "description": "ns delete",
+ "operationId": "ns_delete",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "Identifier of the NS instance.",
+ "name": "nsInstanceId",
+ "in": "path"
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "The NS instance resource and the associated NS identifier were deleted successfully."
+ }
+ }
+ }
+ },
+ "/jobs/{jobId}": {
+ "get": {
+ "tags": [
+ "job"
+ ],
+ "summary": "jobstatus",
+ "description": "",
+ "operationId": "get_jobstatus",
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "job Id",
+ "name": "jobId",
+ "in": "path"
+ },
+ {
+ "required": true,
+ "type": "string",
+ "description": "job response message id",
+ "name": "responseId",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "202": {
+ "description": "",
"schema": {
-
- "$ref": "#/definitions/JobProgressRequest"
-
- }
-
- }
-
- ],
-
- "responses": {
-
- "202": {
-
- "description": ""
-
- }
-
- }
-
- }
-
- }
-
- },
-
- "definitions": {
-
- "NsCreateRequest": {
-
- "type": "object",
-
- "properties": {
-
- "context":{
-
- "type": "object",
-
- "properties": {
-
+ "$ref": "#/definitions/JobDetailInfo"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "NsCreateRequest": {
+ "type": "object",
+ "properties": {
+ "context":{
+ "type": "object",
+ "properties": {
"globalCustomerId":{
-
"type": "string",
"description": "the global customer id"
-
},
-
"serviceType":{
"type": "string",
-
"description": "service type"
-
}
-
}
-
- },
-
- "csarId": {
-
- "type": "string",
-
- "description": "the NS package ID"
-
- },
-
- "nsName": {
-
- "type": "string"
-
- },
-
- "description": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "NsCreateResponse": {
-
- "type": "object",
-
- "properties": {
-
- "nsInstanceId": {
-
- "type": "string"
-
- }
-
- }
-
- },
- "NsInstantiateRequest": {
-
- "type": "object",
-
- "properties": {
-
- "LocationConstraints": {
-
- "type": "array",
-
- "items": {
-
- "$ref": "#/definitions/LocationConstraint"
-
- }
-
- },
-
- "additionalParamForNs": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "LocationConstraint": {
-
- "type": "object",
-
- "properties": {
-
- "vnfProfileId": {
-
- "type": "string"
-
- },
- "locationConstraints": {
-
- "type": "object",
-
- "properties": {
-
- "vimid": {
-
- "type": "string"
-
- }
-
- }
-
- }
-
- }
-
- },
-
- "NsScaleRequest": {
-
+ },
+ "csarId": {
+ "type": "string",
+ "description": "the NS package ID"
+ },
+ "nsName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ },
+ "NsCreateResponse": {
+ "type": "object",
+ "properties": {
+ "nsInstanceId": {
+ "type": "string"
+ }
+ }
+ },
+ "NsInstantiateRequest": {
+ "type": "object",
+ "properties": {
+ "LocationConstraints": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/LocationConstraint"
+ }
+ },
+ "additionalParamForNs": {
+ "type": "string"
+ }
+ }
+ },
+ "LocationConstraint": {
+ "type": "object",
+ "properties": {
+ "vnfProfileId": {
+ "type": "string"
+ },
+ "locationConstraints": {
+ "type": "object",
+ "properties": {
+ "vimid": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "NsScaleRequest": {
+ "type": "object",
+ "properties": {
+ "scaleType": {
+ "type": "string"
+ },
+ "scaleNsByStepsData": {
+ "$ref": "#/definitions/NsScaleByStepsData"
+ }
+ }
+ },
+ "NsScaleByStepsData": {
+ "type": "object",
+ "properties": {
+ "scalingDirection": {
+ "type": "string"
+ },
+ "aspectId": {
+ "type": "string"
+ },
+ "numberOfSteps": {
+ "type": "integer"
+ }
+ }
+ },
+ "NsHealRequest": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceId": {
+ "type": "string"
+ },
+ "cause": {
+ "type": "string"
+ },
+ "additionalParams": {
+ "type": "object",
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "actionvminfo": {
+ "type": "object",
+ "properties": {
+ "vmid": {
+ "type": "string"
+ },
+ "vmname": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "NsTerminateRequest": {
+ "type": "object",
+ "properties": {
+ "terminationType": {
+ "type": "string"
+ },
+ "gracefulTerminationTimeout": {
+ "type": "string"
+ }
+ }
+ },
+ "JobInfo": {
+ "type": "object",
+ "properties": {
+ "jobId": {
+ "type": "string"
+ }
+ }
+ },
+ "NsInstancesInfo":{
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/NsInstanceInfo"
+ }
+ },
+ "NsInstanceInfo": {
+ "type": "object",
+ "properties": {
+ "nsInstanceId": {
+ "type": "string"
+ },
+ "nsName": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "nsdId": {
+ "type": "string"
+ },
+ "vnfInfo": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/vnfInfo"
+ }
+ },
+ "vlInfo": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/vlInfo"
+ }
+ },
+ "vnffgInfo": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/vnffgInfo"
+ }
+ },
+ "nsState": {
+ "type": "string"
+ }
+ }
+ },
+ "vnfInfo": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceId": {
+ "type": "string"
+ },
+ "vnfInstanceName": {
+ "type": "string"
+ },
+ "vnfdId": {
+ "type": "string"
+ }
+ }
+ },
+ "vlInfo": {
+ "type": "object",
+ "properties": {
+ "vlInstanceId": {
+ "type": "string"
+ },
+ "vlInstanceName": {
+ "type": "string"
+ },
+ "vldId": {
+ "type": "string"
+ },
+ "relatedCpInstanceId": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/cpInfo"
+ }
+ }
+ }
+ },
+ "cpInfo": {
+ "type": "object",
+ "properties": {
+ "cpInstanceId": {
+ "type": "string"
+ },
+ "cpInstanceName": {
+ "type": "string"
+ },
+ "cpdId": {
+ "type": "string"
+ }
+ }
+ },
+ "vnffgInfo": {
+ "type": "object",
+ "properties": {
+ "vnffgInstanceId": {
+ "type": "string"
+ },
+ "vnfId": {
+ "type": "string"
+ },
+ "pnfId": {
+ "type": "string"
+ },
+ "virtualLinkId": {
+ "type": "string"
+ },
+ "cpId": {
+ "type": "string"
+ },
+ "nfp": {
+ "type": "string"
+ }
+ }
+ },
+ "jobResponseInfo": {
"type": "object",
-
"properties": {
-
- "scaleType": {
-
- "type": "string"
-
+ "status": {
+ "type": "string"
+ },
+ "progress":{
+ "type": "string"
},
-
- "scaleNsByStepsData": {
-
- "$ref": "#/definitions/NsScaleByStepsData"
-
- }
-
- }
-
- },
-
- "NsScaleByStepsData": {
-
- "type": "object",
-
- "properties": {
-
- "scalingDirection": {
-
- "type": "string"
-
+ "statusDescription": {
+ "type": "string"
},
-
- "aspectId": {
-
- "type": "string"
-
+ "errorCode": {
+ "type": "string"
},
-
- "numberOfSteps": {
-
- "type": "integer"
-
- }
-
+ "responseId": {
+ "type": "string"
+ }
}
-
- },
-
- "NsHealRequest": {
-
- "type": "object",
-
- "properties": {
-
- "vnfInstanceId": {
-
- "type": "string"
-
- },
-
- "cause": {
-
- "type": "string"
-
- },
-
- "additionalParams": {
-
- "type": "object",
-
- "properties": {
-
- "action": {
-
+ },
+ "JobDetailInfo":{
+ "type": "object",
+ "properties": {
+ "jobId": {
"type": "string"
-
},
-
- "actionvminfo": {
-
- "type": "object",
-
- "properties": {
-
- "vmid": {
-
- "type": "string"
-
- },
-
- "vmname": {
-
- "type": "string"
-
- }
-
- }
-
- }
-
+ "responseDescriptor":
+ {
+ "type":"object",
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "progress":{
+ "type": "string"
+ },
+ "statusDescription": {
+ "type": "string"
+ },
+ "errorCode": {
+ "type": "string"
+ },
+ "responseId": {
+ "type": "string"
+ },
+ "responseHistoryList": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/jobResponseInfo"
+ }
+ }
+ }
}
-
- }
-
- }
-
- },
-
- "NsTerminateRequest": {
-
- "type": "object",
-
- "properties": {
-
- "terminationType": {
-
- "type": "string"
-
- },
-
- "gracefulTerminationTimeout": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "JobInfo": {
-
- "type": "object",
-
- "properties": {
-
- "jobId": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "JobProgressRequest": {
-
- "type": "object",
-
- "properties": {
-
- "progress": {
-
- "type": "string"
-
- },
-
- "desc": {
-
- "type": "string"
-
- },
-
- "errcode": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "NsInfo": {
-
- "type": "object",
-
- "properties": {
-
- "nsInstanceId": {
-
- "type": "string"
-
- },
-
- "nsName": {
-
- "type": "string"
-
- },
-
- "description": {
-
- "type": "string"
-
- },
-
- "nsdId": {
-
- "type": "string"
-
- },
-
- "vnfInfo": {
-
- "type": "array",
-
- "items": {
-
- "$ref": "#/definitions/vnfInfo"
-
- }
-
- },
-
- "vlInfo": {
-
- "type": "array",
-
- "items": {
-
- "$ref": "#/definitions/vlInfo"
-
- }
-
- },
-
- "vnffgInfo": {
-
- "type": "array",
-
- "items": {
-
- "$ref": "#/definitions/vnffgInfo"
-
- }
-
- },
-
- "nsState": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "vnfInfo": {
-
- "type": "object",
-
- "properties": {
-
- "vnfInstanceId": {
-
- "type": "string"
-
- },
-
- "vnfInstanceName": {
-
- "type": "string"
-
- },
-
- "vnfdId": {
-
- "type": "string"
-
- }
-
- }
-
- },
-
- "vlInfo": {
-
- "type": "object",
-
- "properties": {
-
- "vlInstanceId": {
-
- "type": "string"
-
- },
-
- "vlInstanceName": {
-
- "type": "string"
-
- },
-
- "vldId": {
-
- "type": "string"
-
- },
-
- "relatedCpInstanceId": {
-
- "type": "array",
-
- "items": {
-
- "$ref": "#/definitions/cpInfo"
-
- }
-
- }
-
- }
-
- },
-
- "cpInfo": {
-
- "type": "object",
-
- "properties": {
-
- "cpInstanceId": {
-
- "type": "string"
-
- },
-
- "cpInstanceName": {
-
- "type": "string"
-
- },
-
- "cpdId": {
-
- "type": "string"
-
- }
-
- }
-
- },
- "vnffgInfo": {
-
- "type": "object",
-
- "properties": {
-
- "vnffgInstanceId": {
-
- "type": "string"
-
- },
-
- "vnfId": {
-
- "type": "string"
-
- },
- "pnfId": {
-
- "type": "string"
-
- },
-
- "virtualLinkId": {
-
- "type": "string"
-
- },
-
- "cpId": {
-
- "type": "string"
-
- },
-
- "nfp": {
-
- "type": "string"
-
- }
-
- }
-
- }
-
- }
-
- }
\ No newline at end of file + }
+ }
+ }
+}
\ No newline at end of file diff --git a/docs/platform/APIs/VNFLCM_API/VNFLCM_API.rst b/docs/platform/APIs/VNFLCM_API/VNFLCM_API.rst index 8d9cff6a..c5c63c85 100644 --- a/docs/platform/APIs/VNFLCM_API/VNFLCM_API.rst +++ b/docs/platform/APIs/VNFLCM_API/VNFLCM_API.rst @@ -8,8 +8,9 @@ **1 Scope**
=============
- The scope of the present document is to describe the GVNFM exposed
- API specification over Or-Vnfm reference point and Ve-Vnfm-vnf reference point.
+The scope of the present document is to describe the GVNFM exposed API specification over Or-Vnfm reference point and Ve-Vnfm-vnf reference point.
+Some content has been updated, about the API Swagger definition, you can find here 'GVNFM_LCM_APIs<https://gerrit.onap.org/r/gitweb?p=vfc/gvnfm/vnflcm.git;a=blob;f=lcm/lcm/swagger/swagger.json;h=f098d282927d3535f5e9e6950f26b9171e04d30c;hb=HEAD>'.
+
**2 Terms, Definitions and Abbreviations**
===========================================
@@ -1681,4 +1682,4 @@ Table 2-1 abbreviations .. |image0| image:: VNFM_API.png
:width: 5.07047in
- :height: 5.6320
\ No newline at end of file + :height: 5.6320in
\ No newline at end of file diff --git a/docs/platform/APIs/VNFMDriver_API/VNFM_Driver_API.rst b/docs/platform/APIs/VNFMDriver_API/VNFM_Driver_API.rst index c289626d..a88e29e0 100644 --- a/docs/platform/APIs/VNFMDriver_API/VNFM_Driver_API.rst +++ b/docs/platform/APIs/VNFMDriver_API/VNFM_Driver_API.rst @@ -8,6 +8,8 @@ **1. Scope**
==============
The scope of the present document is to describe the VNFM driver integrated related API specification.
+Some content has been updated, about the API Swagger definition, you can find here 'VNFM driver development related API<https://gerrit.onap.org/r/gitweb?p=vfc/nfvo/lcm.git;a=blob;f=lcm/swagger/vfc.vnfdriver.swagger.json;h=fc35adbdc75df1307ca2c43a11bfb472da2a27c6;hb=HEAD>'
+
**2. Terms, Definitions and Abbreviations**
=============================================
diff --git a/docs/platform/APIs/index.rst b/docs/platform/APIs/index.rst index c6f561be..816bad25 100644 --- a/docs/platform/APIs/index.rst +++ b/docs/platform/APIs/index.rst @@ -3,12 +3,12 @@ VF-C Offered APIs -================= +================== List VF-C APIs offered. VFC Northbound API ----------- +------------------ Network services lifecycle management APIs @@ -20,7 +20,7 @@ Network services lifecycle management APIs VNFM Integration APIs ---------------- +--------------------- VNFM Driver Integration Related APIs @@ -32,7 +32,7 @@ VNFM Driver Integration Related APIs GVNFM Northbound & Southbound APIs ------------ +---------------------------------- GVNFM Northbound & Southbound APIs for VNF Integration diff --git a/docs/platform/Auto-healing.png b/docs/platform/Auto-healing.png Binary files differnew file mode 100644 index 00000000..6ebb8910 --- /dev/null +++ b/docs/platform/Auto-healing.png diff --git a/docs/platform/Instantiation.png b/docs/platform/Instantiation.png Binary files differnew file mode 100644 index 00000000..b26347ca --- /dev/null +++ b/docs/platform/Instantiation.png diff --git a/docs/platform/Termination.png b/docs/platform/Termination.png Binary files differnew file mode 100644 index 00000000..311d78a4 --- /dev/null +++ b/docs/platform/Termination.png diff --git a/docs/platform/architecture.rst b/docs/platform/architecture.rst index dc7ef09b..ca015792 100644 --- a/docs/platform/architecture.rst +++ b/docs/platform/architecture.rst @@ -4,22 +4,22 @@ VF-C Architecture ----------------- -Following is the VF-C architecture. +VF-C High Level Architecture. |image0| .. |image0| image:: vfc-arc.png :width: 5.97047in - :height: 5.63208in + :height: 4.63208in - -VF-C as one controller in ONAP includes two components NFV-O and GVNFM. +As you can see in this picture, VF-C has many dependencies with other projects, such as SO, Policy, A&AI, SDC, DCAE, Multi-cloud and so on. + +* NFVO provides north bound interface to SO to take part in fulfilling the orchestration and operation of end2end service.And provides standard south bound interface to VNFMs. -For NFV-O, it provides north bound interface to SO to take part in fulfilling the orchestration and operation of end2end service.and provides standard south bound interface to VNFMs. +* GVNFM provides LCM for VNFs which do not require a vendor VNFM and works with NFV-O component to take part in fulfilling the LCM of NS. -For GVNFM, it provides LCM for VNFs which do not require a vendor VNFM and works with NFV-O component to take part in fulfilling the LCM of NS. +* VF-C provides VNFM driver interfaces, vendor can implement these integrates to integrate with VF-C. Now, VF-C has integrated with three vendor VNFM, including ZTE, Huawe, Nokia. -In addition, VF-C provides interface to Policy and works with DCAE for Close Loop Automation. +* In addition, VF-C also provides interface to Policy and works with DCAE for Close Loop Automation. -As you can see in this picture,VF-C has many dependencies with other projects,such as SO,Policy,A&AI,SDC,DCAE,Multi-cloud and so on.
\ No newline at end of file diff --git a/docs/platform/consumedapis.rst b/docs/platform/consumedapis.rst index 2aef466e..08439551 100644 --- a/docs/platform/consumedapis.rst +++ b/docs/platform/consumedapis.rst @@ -10,6 +10,7 @@ References to APIs offered by other components - DCAE: Use DCAE Ves collector API to report FCAPS data to DCAE - MSB: Use MSB to register and find microservice - Modeling: Use nfvparser API to parse tosca template + - Multicloud: Use Multicloud API to CRUD virtual resource diff --git a/docs/platform/delivery.rst b/docs/platform/delivery.rst index 13d1e0f8..5f717146 100644 --- a/docs/platform/delivery.rst +++ b/docs/platform/delivery.rst @@ -6,23 +6,21 @@ VF-C includes the following components in R1. .. |image0| image:: components.png :width: 5.97047in - :height: 5.63208in + :height: 4.63208in VF-C includes several components in ONAP R1. -Catalog is used to store the package distributed by SDC, it is a runtime catalog. - -Workflow include two micro service, one is workflow manage service and the other is workflow-activiti engine service, this two service will onboard workflow to workflow engine and parse workflow. +* Catalog is used to store the package distributed by SDC, it is a runtime catalog. +* Workflow include two micro service, one is workflow manage service and the other is workflow-activiti engine service, this two service will onboard workflow to workflow engine and parse workflow. -For NS lifecycle manager,it mainly completes the NS lifecycle management,such as NS Instantiation/termination and auto-healing. +* For NS lifecycle manager,it mainly completes the NS lifecycle management,such as NS Instantiation/termination and auto-healing. -For Resource manager, it will communicate with NS lifecycle manager to update instance data to A&AI. +* For Resource manager, it will communicate with NS lifecycle manager to update instance data to A&AI. -In VF-C southbound, it includes Gvnfmdriver and SVNFM driver which will interact with GVNFM and Vendor VNFM respectively to execute VNF lifecycle management, -VF-C provides vnfm driver northbound api,then Vendor can implement this APIs to integrate with VF-C. +* In VF-C southbound, it includes Gvnfmdriver and SVNFM driver which will interact with GVNFM and Vendor VNFM respectively to execute VNF lifecycle management,VF-C provides vnfm driver northbound api,then Vendor can implement this APIs to integrate with VF-C. -For the EMS driver,it can collect VNF lay’s Fcaps data from Vendor EMS, and then translate and put these data to DCAE Vescollector +* For the EMS driver,it can collect VNF lay’s Fcaps data from Vendor EMS, and then translate and put these data to DCAE Vescollector For the Amsterdam release, VF-C includes the following components: @@ -44,4 +42,30 @@ GVNFM - vfc-gvnfm-vnfres Workflow - workflow-engine-mgr-service - - activiti-extension
\ No newline at end of file + - activiti-extension + +VF-C support VolTE use case in R1 and R2, following are the vVoLTE releated Workflow in VF-C. + +* VoLTE Use Case Instantiation In VF-C + +|image1| + +.. |image1| image:: Instantiation.png + :width: 5.97047in + :height: 5.63208in + +* VoLTE Use Case Termination in VF-C + +|image2| + +.. |image2| image:: Termination.png + :width: 5.97047in + :height: 5.63208in + +* VoLTE Use Case Auto-healing in VF-C + +|image3| + +.. |image3| image:: Auto-healing.png + :width: 5.97047in + :height: 5.63208in
\ No newline at end of file diff --git a/docs/platform/index.rst b/docs/platform/index.rst index 12fa0be0..364a4c94 100644 --- a/docs/platform/index.rst +++ b/docs/platform/index.rst @@ -15,6 +15,30 @@ As part of the integration between OpenECOMP and OPEN-O, VF-C leverages ETSI NFV * support microservice architecture and model driven resource orchestration and management +|image0| + +.. |image0| image:: vfc-component.png + :width: 3.97047in + :height: 2.63208in + +VF-C as one controller in ONAP includes two components NFV-O and GVNFM. + +* NFVO component + + * compliant with ETSI NFV MANO architecture and information model, + * providing resource orchestration and full life cycle management and FCAPS for NS, + * providing standard south bound interface to VNFMs, + * providing north bound interface to SO, to take part in fulfilling the orchestraion and operation of end2end service, + * providing interface and work with DCAE and Policy for Close Loop Automation. + +* GVNFM component + + * compliant with ETSI NFV MANO architecture and information model + * providing full life cycle management and FCAPS for VNFs which do not require a vendor VNFM, + * providing interface and work with NFV-O component, to take part in fulfiiling the LCM and FCAPS management of NS, + * providing interface and work with DCAE and Policy for Close Loop Automation. + + .. toctree:: :maxdepth: 1 diff --git a/docs/platform/vfc-component.png b/docs/platform/vfc-component.png Binary files differnew file mode 100644 index 00000000..8fbb83ca --- /dev/null +++ b/docs/platform/vfc-component.png diff --git a/docs/release-notes.rst.bak b/docs/release-notes.rst.bak deleted file mode 100644 index 79cf95d4..00000000 --- a/docs/release-notes.rst.bak +++ /dev/null @@ -1,77 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. http://creativecommons.org/licenses/by/4.0 - - -VF-C Release Notes -================== - -.. note:: - * This Release Notes must be updated each time the team decides to Release new artifacts. - * The scope of this Release Notes is for this particular component. In other words, each ONAP component has its Release Notes. - * This Release Notes is cumulative, the most recently Released artifact is made visible in the top of this Release Notes. - * Except the date and the version number, all the other sections are optional but there must be at least one section describing the purpose of this new release. - * This note must be removed after content has been added. - -VF-C includes two main component:NFV-O and GVNFM, can implement life cycle management and FCAPS of VNF and NS. VF-C takes part in end2end service orchestration and close loop automatiion by working with SO,DCAE and Policy. -VF-C also provides standard south bound interface to VNFMs and can integration with multi vendor VNFMs via drivers. - - - -Version: 1.0.0 --------------- - - -:Release Date: 2017-11-16 - - - -**New Features** - - - NS lifecycle management, including NS instance creation,termination and healing - - VNF lifecycle management, including VNF nstance creation,termination and healing - - VNF FCAPS, collecting FCAPS data from vendor EMS - - VNFM Integration, integration with specific VNFMs of vendors to deploy commercial VNFs - - VNF Integration, integration with VNF via GVNFM - -released components: - -NFVO - - vfc-nfvo-lcm - - vfc-nfvo-catalog - - vfc-nfvo-resmgr - - vfc-nfvo-driver-emsdriver - - vfc-nfvo-driver-gvnfm-gvnfmadapter - - vfc-nfvo-driver-gvnfm-jujudriver - - vfc-nfvo-driver-svnfm-ztedriver - - vfc-nfvo-driver-svnfm-huaweidriver - - vfc-nfvo-driver-svnfm-nokiadriver - - vfc-nfvo-driver-sfc-ztesfcdriver -GVNFM - - vfc-gvnfm-vnflcm - - vfc-gvnfm-vnfmgr - - vfc-gvnfm-vnfres -Workflow - - workflow-engine-mgr-service - - activiti-extension - -**Bug Fixes** - -This is the initial release -**Known Issues** - -None - -**Security Issues** - -None -**Upgrade Notes** - -This is the initial release -**Deprecation Notes** - -This is the initial release -**Other** - -=========== - -End of Release Notes diff --git a/lcm/__init__.py b/lcm/__init__.py index 5580cc3d..a1f872b9 100644 --- a/lcm/__init__.py +++ b/lcm/__init__.py @@ -11,3 +11,6 @@ # 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 pymysql + +pymysql.install_as_MySQLdb() diff --git a/lcm/jobs/serializers.py b/lcm/jobs/serializers.py new file mode 100644 index 00000000..61fa2dda --- /dev/null +++ b/lcm/jobs/serializers.py @@ -0,0 +1,48 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class JobHistorySerializer(serializers.Serializer): + status = serializers.CharField(help_text="Status of job", required=True) + progress = serializers.CharField(help_text="Progress of job", required=True) + statusDescription = serializers.CharField(help_text="Description of job", required=False, allow_null=True) + errorCode = serializers.CharField(help_text="Error code of job", required=False, allow_null=True) + responseId = serializers.CharField(help_text="Response index of job", required=True) + + +class JobDescriptorSerializer(serializers.Serializer): + status = serializers.CharField(help_text="Status of job", required=True) + progress = serializers.CharField(help_text="Progress of job", required=True) + statusDescription = serializers.CharField(help_text="Description of job", required=False, allow_null=True) + errorCode = serializers.CharField(help_text="Error code of job", required=False, allow_null=True) + responseId = serializers.CharField(help_text="Response index of job", required=True) + responseHistoryList = JobHistorySerializer(help_text="History of job", many=True) + + +class JobQueryRespSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="UUID of job", required=True) + responseDescriptor = JobDescriptorSerializer(help_text="Descriptor of job", required=False) + + +class JobUpdReqSerializer(serializers.Serializer): + progress = serializers.CharField(help_text="Progress of job", required=True) + desc = serializers.CharField(help_text="Desc of job", required=False) + errcode = serializers.CharField(help_text="Error code of job", required=False) + + +class JobUpdRespSerializer(serializers.Serializer): + result = serializers.CharField(help_text="Result of job update", required=True) + msg = serializers.CharField(help_text="Detail of job update", required=False) diff --git a/lcm/jobs/tests/tests.py b/lcm/jobs/tests/tests.py index 7b97c724..84ae29f9 100644 --- a/lcm/jobs/tests/tests.py +++ b/lcm/jobs/tests/tests.py @@ -27,14 +27,17 @@ class JobsViewTest(TestCase): def test_job(self): JobModel(jobid=self.job_id, jobtype='VNF', jobaction='INST', resid='1').save() - JobStatusModel(indexid=1, jobid=self.job_id, status='inst', progress=20, descp='inst').save() + JobStatusModel(indexid=1, jobid=self.job_id, status='inst', progress=20, descp='inst', errcode="0").save() response = self.client.get("/api/nslcm/v1/jobs/%s" % self.job_id) - self.failUnlessEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(status.HTTP_200_OK, response.status_code, response.data) + self.assertIn('jobId', response.data) + self.assertIn('responseDescriptor', response.data) def test_non_exiting_job(self): job_id = 'test_new_job_id' JobModel(jobid=self.job_id, jobtype='VNF', jobaction='INST', resid='1').save() - JobStatusModel(indexid=1, jobid=self.job_id, status='inst', progress=20, descp='inst').save() + JobStatusModel(indexid=1, jobid=self.job_id, status='inst', progress=20, descp='inst', errcode="0").save() response = self.client.get("/api/nslcm/v1/jobs/%s" % job_id) + self.assertEqual(status.HTTP_200_OK, response.status_code) self.assertIn('jobId', response.data) self.assertNotIn('responseDescriptor', response.data) diff --git a/lcm/jobs/urls.py b/lcm/jobs/urls.py index 6919d05f..3ddfc8a9 100644 --- a/lcm/jobs/urls.py +++ b/lcm/jobs/urls.py @@ -11,13 +11,13 @@ # 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. -from django.conf.urls import patterns, url +from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns from lcm.jobs.views import JobView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/jobs/(?P<job_id>[0-9a-zA-Z_-]+)$', JobView.as_view()), - ) +urlpatterns = [ + url(r'^api/nslcm/v1/jobs/(?P<job_id>[0-9a-zA-Z_-]+)$', JobView.as_view()), +] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/jobs/views.py b/lcm/jobs/views.py index acbb5fe9..7439b630 100644 --- a/lcm/jobs/views.py +++ b/lcm/jobs/views.py @@ -12,34 +12,87 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import traceback +from drf_yasg import openapi from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema from lcm.jobs.job_get import GetJobInfoService from lcm.pub.utils.jobutil import JobUtil from lcm.pub.utils.values import ignore_case_get +from lcm.jobs.serializers import JobUpdReqSerializer, JobUpdRespSerializer +from lcm.jobs.serializers import JobQueryRespSerializer +from lcm.pub.exceptions import NSLCMException logger = logging.getLogger(__name__) class JobView(APIView): + @swagger_auto_schema( + manual_parameters=[ + openapi.Parameter('responseId', + openapi.IN_QUERY, + "responseId", + type=openapi.TYPE_INTEGER + ), + ], + responses={ + status.HTTP_200_OK: JobQueryRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request, job_id): - response_id = ignore_case_get(request.META, 'responseId') - ret = GetJobInfoService(job_id, response_id).do_biz() - return Response(data=ret) + try: + response_id = ignore_case_get(request.META, 'responseId') + ret = GetJobInfoService(job_id, response_id).do_biz() + resp_serializer = JobQueryRespSerializer(data=ret) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + return Response(data=resp_serializer.data, status=status.HTTP_200_OK) + except Exception as e: + logger.error(traceback.format_exc()) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @swagger_auto_schema( + request_body=JobUpdReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: JobUpdRespSerializer() + } + ) def post(self, request, job_id): try: logger.debug("Enter JobView:post, %s, %s ", job_id, request.data) + + req_serializer = JobUpdReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + jobs = JobUtil.query_job_status(job_id) - if len(jobs) > 0 and jobs[-1].errcode == '255': - return Response(data={'result': 'ok'}) - progress = request.data.get('progress') - desc = request.data.get('desc', '%s' % progress) - errcode = '0' if request.data.get('errcode') in ('true', 'active') else '255' - logger.debug("errcode=%s", errcode) - JobUtil.add_job_status(job_id, progress, desc, error_code=errcode) - return Response(data={'result': 'ok'}) + if not jobs: + raise NSLCMException("Job(%s) does not exist.") + + if jobs[-1].errcode != '255': + progress = request.data.get('progress') + desc = request.data.get('desc', '%s' % progress) + errcode = '0' if request.data.get('errcode') in ('true', 'active') else '255' + logger.debug("errcode=%s", errcode) + JobUtil.add_job_status(job_id, progress, desc, error_code=errcode) + + resp_serializer = JobUpdRespSerializer(data={'result': 'ok'}) + if not resp_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + + return Response(data=resp_serializer.data, status=status.HTTP_202_ACCEPTED) except Exception as e: - return Response(data={'result': 'error', 'msg': e.message}) + resp_serializer = JobUpdRespSerializer(data={ + 'result': 'error', + 'msg': e.message}) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data={ + 'result': 'error', + 'msg': resp_serializer.errors}, status=status.HTTP_202_ACCEPTED) + return Response(data=resp_serializer.data, status=status.HTTP_202_ACCEPTED) diff --git a/lcm/log.yml b/lcm/log.yml new file mode 100644 index 00000000..4ae7ab16 --- /dev/null +++ b/lcm/log.yml @@ -0,0 +1,50 @@ +version: 1 +disable_existing_loggers: False + +loggers: + nslcm: + handlers: [nslcmlocal_handler, nslcm_handler] + level: "DEBUG" + propagate: False + django: + handlers: [django_handler] + level: "DEBUG" + propagate: False +handlers: + nslcmlocal_handler: + level: "DEBUG" + class: + "logging.handlers.RotatingFileHandler" + filename: "logs/runtime_nslcm.log" + formatter: + "standard" + maxBytes: 52428800 + backupCount: 10 + nslcm_handler: + level: "DEBUG" + class: + "logging.handlers.RotatingFileHandler" + filename: "/var/log/onap/vfc/nslcm/runtime_nslcm.log" + formatter: + "mdcFormat" + maxBytes: 52428800 + backupCount: 10 + django_handler: + level: "DEBUG" + class: + "logging.handlers.RotatingFileHandler" + filename: "logs/django.log" + formatter: + "standard" + maxBytes: 52428800 + backupCount: 10 +formatters: + standard: + format: + "%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s" + mdcFormat: + format: + "%(asctime)s|||||%(name)s||%(thread)s||%(funcName)s||%(levelname)s||%(message)s||||%(mdc)s \t" + mdcfmt: "{requestID} {invocationID} {serviceName} {serviceIP}" + datefmt: "%Y-%m-%d %H:%M:%S" + (): onaplogging.mdcformatter.MDCFormatter diff --git a/lcm/middleware.py b/lcm/middleware.py new file mode 100644 index 00000000..7bf6868f --- /dev/null +++ b/lcm/middleware.py @@ -0,0 +1,60 @@ +# Copyright (c) 2017-2018 ZTE, Inc. +# +# 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. + +import uuid +from onaplogging.mdcContext import MDC + +from lcm.pub.config.config import FORWARDED_FOR_FIELDS, SERVICE_NAME + + +class LogContextMiddleware(object): + # the last IP behind multiple proxies, if no exist proxies + # get local host ip. + def _getLastIp(self, request): + + ip = "" + try: + for field in FORWARDED_FOR_FIELDS: + if field in request.META: + if ',' in request.META[field]: + parts = request.META[field].split(',') + ip = parts[-1].strip().split(":")[0] + else: + ip = request.META[field].split(":")[0] + + if ip == "": + ip = request.META.get("HTTP_HOST").split(":")[0] + + except Exception: + pass + + return ip + + def process_request(self, request): + # Fetch TRANSACTIONID Id and pass to plugin server + ReqeustID = request.META.get("HTTP_X_TRANSACTIONID", None) + if ReqeustID is None: + ReqeustID = uuid.uuid3(uuid.NAMESPACE_URL, SERVICE_NAME) + request.META["HTTP_X_TRANSACTIONID"] = ReqeustID + MDC.put("requestID", ReqeustID) + # generate the unique id + InovocationID = uuid.uuid3(uuid.NAMESPACE_DNS, SERVICE_NAME) + MDC.put("invocationID", InovocationID) + MDC.put("serviceName", SERVICE_NAME) + # access ip + MDC.put("serviceIP", self._getLastIp(request)) + + return None + + def process_response(self, request, response): + MDC.clear() + return response diff --git a/lcm/ns/data/scalemapping.json b/lcm/ns/data/scalemapping.json index 410332c9..07e74b6e 100644 --- a/lcm/ns/data/scalemapping.json +++ b/lcm/ns/data/scalemapping.json @@ -1,55 +1,55 @@ { "scale_options": [ { - "ns_instanceId":"23", + "nsd_id":"23", "ns_scale_aspect": "TIC_EDGE_IMS", - "ns_scale_info_list": [ + "ns_scale_info": [ { "step": "1", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_zte_cscf", - "vnf_scaleAspectId": "gpu", + "vnfd_id":"nf_zte_cscf", + "vnf_scaleAspectId": "gsu", "numberOfSteps": "1" }, { - "vnfInstanceId":"nf_zte_hss", + "vnfd_id":"nf_zte_hss", "vnf_scaleAspectId": "gpu", - "numberOfSteps": "1" + "numberOfSteps": "3" } ] }, { "step": "2", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_zte_cscf", + "vnfd_id":"nf_zte_cscf", "vnf_scaleAspectId": "mpu", - "numberOfSteps": "1" + "numberOfSteps": "2" }, { - "vnfInstanceId":"nf_zte_hss", + "vnfd_id":"nf_zte_hss", "vnf_scaleAspectId": "mpu", - "numberOfSteps": "1" + "numberOfSteps": "4" } ] } ] }, { - "ns_instanceId":"23", + "nsd_id":"23", "ns_scale_aspect": "TIC_EDGE_HW", - "ns_scale_info_list": [ + "ns_scale_info": [ { "step": "4", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_hw_cscf", - "vnf_scaleAspectId": "gpu", + "vnfd_id":"nf_hw_cscf", + "vnf_scaleAspectId": "gsu", "numberOfSteps": "1" }, { - "vnfInstanceId":"nf_hw_hss", + "vnfd_id":"nf_hw_hss", "vnf_scaleAspectId": "gpu", "numberOfSteps": "1" } @@ -57,14 +57,14 @@ }, { "step": "6", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_HW_cscf", + "vnfd_id":"nf_HW_cscf", "vnf_scaleAspectId": "gpu", "numberOfSteps": "1" }, { - "vnfInstanceId":"nf_HW_hss", + "vnfd_id":"nf_HW_hss", "vnf_scaleAspectId": "gpu", "numberOfSteps": "1" } @@ -73,19 +73,19 @@ ] }, { - "ns_instanceId":"235", + "nsd_id":"235", "ns_scale_aspect": "TIC_EDGE_HW", - "ns_scale_info_list": [ + "ns_scale_info": [ { "step": "4", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_hw_cscf", + "vnfd_id":"nf_hw_cscf", "vnf_scaleAspectId": "gpu", "numberOfSteps": "123" }, { - "vnfInstanceId":"nf_hw_hss", + "vnfd_id":"nf_hw_hss", "vnf_scaleAspectId": "gpu", "numberOfSteps": "456" } @@ -93,14 +93,14 @@ }, { "step": "6", - "vnf_scale_list":[ + "vnf_scale_info":[ { - "vnfInstanceId":"nf_HW_cscf", + "vnfd_id":"nf_HW_cscf", "vnf_scaleAspectId": "gpu", "numberOfSteps": "1" }, { - "vnfInstanceId":"nf_HW_hss", + "vnfd_id":"nf_HW_hss", "vnf_scaleAspectId": "gpu", "numberOfSteps": "1" } diff --git a/lcm/ns/ns_manual_scale.py b/lcm/ns/ns_manual_scale.py index 4fcfbbe3..71897aee 100644 --- a/lcm/ns/ns_manual_scale.py +++ b/lcm/ns/ns_manual_scale.py @@ -23,7 +23,7 @@ from lcm.pub.database.models import JobModel, NSInstModel from lcm.pub.exceptions import NSLCMException from lcm.pub.utils.jobutil import JobUtil, JOB_MODEL_STATUS from lcm.pub.utils.values import ignore_case_get -from lcm.pub.utils.scaleaspect import get_scale_vnf_data +from lcm.pub.utils.scaleaspect import get_scale_vnf_data_info_list JOB_ERROR = 255 SCALE_TYPE = ("SCALE_NS", "SCALE_VNF") @@ -45,7 +45,8 @@ class NSManualScaleService(threading.Thread): self.do_biz() except NSLCMException as e: JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) - except: + except Exception as ex: + logger.error(ex.message) logger.error(traceback.format_exc()) JobUtil.add_job_status(self.job_id, JOB_ERROR, 'ns scale fail') finally: @@ -54,35 +55,45 @@ class NSManualScaleService(threading.Thread): def do_biz(self): self.update_job(1, desc='ns scale start') self.update_ns_status(NS_INST_STATUS.SCALING) - self.get_and_check_params() + self.check_and_set_params() self.do_vnfs_scale() self.update_job(100, desc='ns scale success') - def get_and_check_params(self): + def check_and_set_params(self): self.scale_type = ignore_case_get(self.request_data, 'scaleType') if not self.scale_type or self.scale_type != SCALE_TYPE[0]: - logger.error('scaleType parameter does not exist or value is incorrect. It must be SCALE_NS.') - raise NSLCMException('scaleType parameter does not exist or value incorrect. It must be SCALE_NS.') + logger.error( + 'scaleType parameter does not exist or value is incorrect. It must be SCALE_NS.') + raise NSLCMException( + 'scaleType parameter does not exist or value incorrect. It must be SCALE_NS.') # Get data if SCALE_NS self.scale_ns_data = ignore_case_get(self.request_data, 'scaleNsData') - self.scale_vnf_data = get_scale_vnf_data(self.scale_ns_data, self.ns_instance_id) + self.scale_vnf_data = get_scale_vnf_data_info_list( + self.scale_ns_data, self.ns_instance_id) logger.debug('scale_vnf_data = %s' % self.scale_vnf_data) # Get data if SCALE_VNF if not self.scale_vnf_data: - logger.error('scaleVnfData parameter does not exist or value incorrect') - raise NSLCMException('scaleVnfData parameter does not exist or value incorrect') + logger.error( + 'scaleVnfData parameter does not exist or value incorrect') + raise NSLCMException( + 'scaleVnfData parameter does not exist or value incorrect') def do_vnfs_scale(self): for i in range(len(self.scale_vnf_data)): - vnf_scale_params = self.prepare_vnf_scale_params(self.scale_vnf_data[i]) + vnf_scale_params = self.prepare_vnf_scale_params( + self.scale_vnf_data[i]) count = len(self.scale_vnf_data) progress_range = [11 + 80 / count * i, 10 + 80 / count * (i + 1)] status = self.do_vnf_scale(vnf_scale_params, progress_range) if status is JOB_MODEL_STATUS.FINISHED: - logger.info('nf[%s] scale handle end' % vnf_scale_params.get('vnfInstanceId')) - self.update_job(progress_range[1], - desc='nf[%s] scale handle end' % vnf_scale_params.get('vnfInstanceId')) + logger.info( + 'nf[%s] scale handle end' % + vnf_scale_params.get('vnfInstanceId')) + self.update_job( + progress_range[1], + desc='nf[%s] scale handle end' % + vnf_scale_params.get('vnfInstanceId')) else: logger.error('nf scale failed') raise NSLCMException('nf scale failed') @@ -101,7 +112,10 @@ class NSManualScaleService(threading.Thread): nf_inst_id = vnf_scale_params.get('vnfInstanceId') nf_service = NFManualScaleService(nf_inst_id, vnf_scale_params) nf_service.start() - self.update_job(progress_range[0], desc='nf[%s] scale handle start' % nf_inst_id) + self.update_job( + progress_range[0], + desc='nf[%s] scale handle start' % + nf_inst_id) status = self.wait_job_finish(nf_service.job_id) return status @@ -125,4 +139,6 @@ class NSManualScaleService(threading.Thread): JobUtil.add_job_status(self.job_id, progress, desc) def update_ns_status(self, status): - NSInstModel.objects.filter(id=self.ns_instance_id).update(status=status) + NSInstModel.objects.filter( + id=self.ns_instance_id).update( + status=status) diff --git a/lcm/ns/ns_terminate.py b/lcm/ns/ns_terminate.py index 57ba659b..5b93f58d 100644 --- a/lcm/ns/ns_terminate.py +++ b/lcm/ns/ns_terminate.py @@ -53,7 +53,8 @@ class TerminateNsService(threading.Thread): JobUtil.add_job_status(self.job_id, 100, "ns terminate ends.", '') except NSLCMException as e: JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) - except: + except Exception as ex: + logger.error(ex.message) logger.error(traceback.format_exc()) JobUtil.add_job_status(self.job_id, JOB_ERROR, "ns terminate fail.") diff --git a/lcm/ns/serializers.py b/lcm/ns/serializers.py new file mode 100644 index 00000000..a3b14127 --- /dev/null +++ b/lcm/ns/serializers.py @@ -0,0 +1,132 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class ContextSerializer(serializers.Serializer): + globalCustomerId = serializers.CharField(help_text="Global customer ID", required=False, allow_null=True) + serviceType = serializers.CharField(help_text="Service type", required=False, allow_null=True) + + +class CreateNsReqSerializer(serializers.Serializer): + csarId = serializers.CharField(help_text="Package ID of NS", required=False, allow_null=True) + nsName = serializers.CharField(help_text="Name of NS", required=False, allow_null=True) + description = serializers.CharField(help_text="Description of NS", required=False, allow_null=True) + context = ContextSerializer(help_text="Context of NS", required=False) + + +class CreateNsRespSerializer(serializers.Serializer): + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=True) + + +class VnfInstSerializer(serializers.Serializer): + vnfInstanceId = serializers.CharField(help_text="ID of VNF instance", required=True) + vnfInstanceName = serializers.CharField(help_text="Name of VNF instance", required=False, allow_null=True) + vnfdId = serializers.CharField(help_text="ID of VNFD", required=False, allow_null=True) + + +class CpInstInfoSerializer(serializers.Serializer): + cpInstanceId = serializers.CharField(help_text="ID of CP instance", required=True) + cpInstanceName = serializers.CharField(help_text="Name of CP instance", required=False, allow_null=True) + cpdId = serializers.CharField(help_text="ID of CPD", required=False, allow_null=True) + + +class VlInstSerializer(serializers.Serializer): + vlInstanceId = serializers.CharField(help_text="ID of VL instance", required=True) + vlInstanceName = serializers.CharField(help_text="Name of VL instance", required=False, allow_null=True) + vldId = serializers.CharField(help_text="ID of VLD", required=False, allow_null=True) + relatedCpInstanceId = CpInstInfoSerializer(help_text="Related CP instances", many=True) + + +class VnffgInstSerializer(serializers.Serializer): + vnffgInstanceId = serializers.CharField(help_text="ID of VNFFG instance", required=True) + vnfdId = serializers.CharField(help_text="ID of VNFD", required=False, allow_null=True) + pnfId = serializers.CharField(help_text="ID of PNF", required=False, allow_null=True) + virtualLinkId = serializers.CharField(help_text="ID of virtual link", required=False, allow_null=True) + cpdId = serializers.CharField(help_text="ID of CPD", required=False, allow_null=True) + nfp = serializers.CharField(help_text="nfp", required=False, allow_null=True) + + +class QueryNsRespSerializer(serializers.Serializer): + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=True) + nsName = serializers.CharField(help_text="Name of NS instance", required=False, allow_null=True) + description = serializers.CharField(help_text="Description of NS instance", required=False, allow_null=True) + nsdId = serializers.CharField(help_text="ID of NSD", required=True) + vnfInfo = VnfInstSerializer(help_text="VNF instances", many=True, required=False, allow_null=True) + vlInfo = VlInstSerializer(help_text="VL instances", many=True, required=False, allow_null=True) + vnffgInfo = VnffgInstSerializer(help_text="VNFFG instances", many=True, required=False, allow_null=True) + nsState = serializers.CharField(help_text="State of NS instance", required=False, allow_null=True) + + +class VimSerializer(serializers.Serializer): + vimid = serializers.CharField(help_text="ID of VIM", required=False, allow_null=True) + + +class LocationConstraintSerializer(serializers.Serializer): + vnfProfileId = serializers.CharField(help_text="ID of VNF profile", required=False, allow_null=True) + locationConstraints = VimSerializer(help_text="Location constraints", required=False, allow_null=True) + + +class InstantNsReqSerializer(serializers.Serializer): + locationConstraints = LocationConstraintSerializer(required=False, allow_null=True) + additionalParamForNs = serializers.CharField(help_text="Additional param for NS", required=False, allow_null=True) + + +class NsOperateJobSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="ID of NS operate job", required=True) + + +class TerminateNsReqSerializer(serializers.Serializer): + terminationType = serializers.CharField(help_text="Type of NS termination", required=False, allow_null=True) + gracefulTerminationTimeout = serializers.CharField(help_text="Timeout of NS graceful termination", required=False, allow_null=True) + + +class ActionVmSerializer(serializers.Serializer): + vmid = serializers.CharField(help_text="ID of VM", required=False, allow_null=True) + vmname = serializers.CharField(help_text="Name of VM", required=False, allow_null=True) + + +class HealNsAdditionalParamsSerializer(serializers.Serializer): + action = serializers.CharField(help_text="Action of NS heal", required=False, allow_null=True) + actionvminfo = ActionVmSerializer(help_text="VM info of action", required=False, allow_null=True) + + +class HealVnfDataSerializer(serializers.Serializer): + vnfInstanceId = serializers.CharField(help_text="ID of VNF Instance", required=True) + cause = serializers.CharField(help_text="Cause of NS heal", required=False, allow_null=True) + additionalParams = HealNsAdditionalParamsSerializer(help_text="Additional params of NS heal", required=False, allow_null=True) + + +class HealNsReqSerializer(serializers.Serializer): + healVnfData = HealVnfDataSerializer(help_text="Data of heal VNF", required=False, allow_null=True) + + +class InstNsPostDealReqSerializer(serializers.Serializer): + status = serializers.CharField(help_text="Status of NS Inst", required=True) + + +class ScaleNsByStepsSerializer(serializers.Serializer): + aspectId = serializers.CharField(help_text="ID of aspect", required=True) + numberOfSteps = serializers.CharField(help_text="Number of steps", required=True) + scalingDirection = serializers.CharField(help_text="Scaling direction", required=True) + + +class ScaleNsDataSerializer(serializers.Serializer): + scaleNsByStepsData = ScaleNsByStepsSerializer(help_text="Scale NS by steps data", many=True) + + +class ManualScaleNsReqSerializer(serializers.Serializer): + scaleType = serializers.CharField(help_text="Type of NS Scale", required=True) + scaleNsData = ScaleNsDataSerializer(help_text="Scale NS data", many=True) diff --git a/lcm/ns/sfcs/detail_views.py b/lcm/ns/sfcs/detail_views.py index b5646f5f..6d92f39d 100644 --- a/lcm/ns/sfcs/detail_views.py +++ b/lcm/ns/sfcs/detail_views.py @@ -11,31 +11,61 @@ # 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 from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema from lcm.ns.sfcs.delete_sfcs import DeleteSfcs from lcm.ns.sfcs.get_sfcs import GetSfcs +from lcm.ns.sfcs.serializers import GetSfcRespSerializer +from lcm.ns.sfcs.serializers import DeleteSfcRespSerializer + +logger = logging.getLogger(__name__) class SfcDetailView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: GetSfcRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error", + status.HTTP_404_NOT_FOUND: "SFC not found" + } + ) def get(self, request, sfc_inst_id): - print "SfcCreateView--get::> %s" % sfc_inst_id + logger.debug("SfcCreateView--get::> %s", sfc_inst_id) sfc_inst_info = GetSfcs(sfc_inst_id).do() if not sfc_inst_info: return Response(status=status.HTTP_404_NOT_FOUND) - return Response(status=status.HTTP_200_OK, data={'sfcInstId': sfc_inst_id, - 'sfcName': "xxx", - 'sfcStatus': sfc_inst_info[0].status}) + resp_data = {'sfcInstId': sfc_inst_id, + 'sfcName': "xxx", + 'sfcStatus': sfc_inst_info[0].status} + + resp_serializer = GetSfcRespSerializer(data=resp_data) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data={'error': resp_serializer.errors}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return Response(status=status.HTTP_200_OK, data=resp_serializer.data) + + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_202_ACCEPTED: DeleteSfcRespSerializer() + } + ) def delete(self, request_paras, sfc_inst_id): resp = DeleteSfcs(sfc_inst_id).do() - return Response(data=resp, status=status.HTTP_202_ACCEPTED) + resp_serializer = DeleteSfcRespSerializer(data=resp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data={"result": 1, "detail": resp_serializer.errors}, + status=status.HTTP_202_ACCEPTED) -class SfcCreateView(APIView): - def post(self, request): - print "SfcCreateView--post::> %s" % request.stream.body - return Response(data={"jobId": "1234", "sfcInstId": "1"}, status=status.HTTP_201_CREATED) + return Response(data=resp, status=status.HTTP_202_ACCEPTED) diff --git a/lcm/ns/sfcs/serializers.py b/lcm/ns/sfcs/serializers.py new file mode 100644 index 00000000..1284f2f3 --- /dev/null +++ b/lcm/ns/sfcs/serializers.py @@ -0,0 +1,65 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class CreateSfcInstReqSerializer(serializers.Serializer): + fpindex = serializers.CharField(help_text="Index of FP", required=True) + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of NS instance", required=False, allow_null=True) + sdnControllerId = serializers.CharField(help_text="ID of SDN controller", required=False, allow_null=True) + + +class CreateSfcInstRespSerializer(serializers.Serializer): + fpinstid = serializers.CharField(help_text="ID of FP instance", required=True) + + +class CreateSfcReqSerializer(serializers.Serializer): + fpindex = serializers.CharField(help_text="Index of FP", required=True) + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of NS instance", required=False, allow_null=True) + sdnControllerId = serializers.CharField(help_text="ID of SDN controller", required=False, allow_null=True) + + +class CreateSfcRespSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="ID of job", required=True) + sfcInstId = serializers.CharField(help_text="ID of SFC instance", required=True) + + +class GetSfcRespSerializer(serializers.Serializer): + sfcInstId = serializers.CharField(help_text="ID of SFC instance", required=True) + sfcName = serializers.CharField(help_text="Name of SFC instance", required=True) + sfcStatus = serializers.CharField(help_text="Status of SFC instance", required=True) + + +class DeleteSfcRespSerializer(serializers.Serializer): + result = serializers.CharField(help_text="Delete SFC result(0: success, 1: failed)", required=True) + detail = serializers.CharField(help_text="Result detail", required=False, allow_null=True) + + +class CreatePortPairGpSerializer(serializers.Serializer): + fpinstid = serializers.CharField(help_text="ID of FP instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of NS instance", required=False, allow_null=True) + nsinstanceid = serializers.CharField(help_text="ID of NS instance", required=False, allow_null=True) + + +class CreateFlowClaSerializer(serializers.Serializer): + fpinstid = serializers.CharField(help_text="ID of FP instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of NS instance", required=False, allow_null=True) + + +class CreatePortChainSerializer(serializers.Serializer): + fpinstid = serializers.CharField(help_text="ID of FP instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of NS instance", required=False, allow_null=True) diff --git a/lcm/ns/sfcs/urls.py b/lcm/ns/sfcs/urls.py index 6b852567..cfca2c30 100644 --- a/lcm/ns/sfcs/urls.py +++ b/lcm/ns/sfcs/urls.py @@ -11,19 +11,19 @@ # 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. -from django.conf.urls import patterns, url +from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns from lcm.ns.sfcs.detail_views import SfcDetailView from lcm.ns.sfcs.views import SfcView, SfcInstanceView, PortPairGpView, FlowClaView, PortChainView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/ns/sfcs$', SfcView.as_view()), - url(r'^api/nslcm/v1/ns/sfcs/(?P<sfc_inst_id>[0-9a-zA-Z_-]+)$', SfcDetailView.as_view()), - url(r'^api/nslcm/v1/ns/sfc_instance$', SfcInstanceView.as_view()), - url(r'^api/nslcm/v1/ns/create_port_pair_group$', PortPairGpView.as_view()), - url(r'^api/nslcm/v1/ns/create_flow_classifier$', FlowClaView.as_view()), - url(r'^api/nslcm/v1/ns/create_port_chain$', PortChainView.as_view()), - ) +urlpatterns = [ + url(r'^api/nslcm/v1/ns/sfcs$', SfcView.as_view()), + url(r'^api/nslcm/v1/ns/sfcs/(?P<sfc_inst_id>[0-9a-zA-Z_-]+)$', SfcDetailView.as_view()), + url(r'^api/nslcm/v1/ns/sfc_instance$', SfcInstanceView.as_view()), + url(r'^api/nslcm/v1/ns/create_port_pair_group$', PortPairGpView.as_view()), + url(r'^api/nslcm/v1/ns/create_flow_classifier$', FlowClaView.as_view()), + url(r'^api/nslcm/v1/ns/create_port_chain$', PortChainView.as_view()), +] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/sfcs/views.py b/lcm/ns/sfcs/views.py index 644dc95a..4403ffe6 100644 --- a/lcm/ns/sfcs/views.py +++ b/lcm/ns/sfcs/views.py @@ -22,6 +22,7 @@ import time from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema from lcm.ns.sfcs.create_flowcla import CreateFlowClassifier from lcm.ns.sfcs.create_port_chain import CreatePortChain @@ -29,24 +30,59 @@ from lcm.ns.sfcs.create_portpairgp import CreatePortPairGroup from lcm.ns.sfcs.create_sfc_worker import CreateSfcWorker from lcm.ns.sfcs.sfc_instance import SfcInstance from lcm.ns.sfcs.utils import get_fp_id, ignorcase_get +from lcm.ns.sfcs.serializers import CreateSfcInstReqSerializer, CreateSfcInstRespSerializer +from lcm.ns.sfcs.serializers import CreateSfcReqSerializer, CreateSfcRespSerializer +from lcm.ns.sfcs.serializers import CreatePortPairGpSerializer +from lcm.ns.sfcs.serializers import CreateFlowClaSerializer +from lcm.ns.sfcs.serializers import CreatePortChainSerializer logger = logging.getLogger(__name__) class SfcInstanceView(APIView): + @swagger_auto_schema( + request_body=CreateSfcInstReqSerializer(), + responses={ + status.HTTP_200_OK: CreateSfcInstRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request): - data = { - 'nsinstid': request.data['nsinstanceid'], - "ns_model_data": json.loads(request.data['context']), - 'fpindex': request.data['fpindex'], - 'fpinstid': str(uuid.uuid4()), - 'sdncontrollerid': request.data["sdncontrollerid"]} - rsp = SfcInstance(data).do_biz() - return Response(data=rsp, status=status.HTTP_200_OK) + try: + req_serializer = CreateSfcInstReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + + data = { + 'nsinstid': request.data['nsInstanceId'], + "ns_model_data": json.loads(request.data['context']), + 'fpindex': request.data['fpindex'], + 'fpinstid': str(uuid.uuid4()), + 'sdncontrollerid': request.data["sdnControllerId"]} + rsp = SfcInstance(data).do_biz() + + resp_serializer = CreateSfcInstRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + + return Response(data=rsp, status=status.HTTP_200_OK) + except Exception as e: + logger.error(traceback.format_exc()) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class PortPairGpView(APIView): + @swagger_auto_schema( + request_body=CreatePortPairGpSerializer(), + responses={ + status.HTTP_200_OK: 'successful' + } + ) def post(self, request): + req_serializer = CreatePortPairGpSerializer(data=request.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + data = { 'fpinstid': request.data["fpinstid"], "ns_model_data": json.loads(request.data['context']), @@ -56,7 +92,17 @@ class PortPairGpView(APIView): class FlowClaView(APIView): + @swagger_auto_schema( + request_body=CreateFlowClaSerializer(), + responses={ + status.HTTP_200_OK: 'successful' + } + ) def post(self, request): + req_serializer = CreateFlowClaSerializer(data=request.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + data = { 'fpinstid': request.data["fpinstid"], "ns_model_data": json.loads(request.data['context'])} @@ -65,7 +111,17 @@ class FlowClaView(APIView): class PortChainView(APIView): + @swagger_auto_schema( + request_body=CreatePortChainSerializer(), + responses={ + status.HTTP_200_OK: 'successful' + } + ) def post(self, request): + req_serializer = CreatePortChainSerializer(data=request.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + data = { 'fpinstid': request.data["fpinstid"], "ns_model_data": json.loads(request.data['context'])} @@ -74,25 +130,35 @@ class PortChainView(APIView): class SfcView(APIView): + @swagger_auto_schema( + request_body=CreateSfcReqSerializer(), + responses={ + status.HTTP_200_OK: CreateSfcRespSerializer() + } + ) def post(self, request): try: logger.info("Create Service Function Chain start") logger.info("service_function_chain_request: %s" % json.dumps(request.data)) logger.info("service_function_chain_context : %s" % json.dumps(request.data['context'])) logger.info("service_function_chain_context : %s" % request.data['context']) - logger.info("service_function_chain_instanceid : %s" % ignorcase_get(request.data, 'nsinstanceid')) - logger.info("service_function_chain_sdncontrollerid : %s" % ignorcase_get(request.data, 'sdncontrollerid')) + logger.info("service_function_chain_instanceid : %s" % ignorcase_get(request.data, 'nsInstanceId')) + logger.info("service_function_chain_sdncontrollerid : %s" % ignorcase_get(request.data, 'sdnControllerId')) logger.info("service_function_chain_fpindex : %s" % ignorcase_get(request.data, 'fpindex')) ns_model_data = request.data['context'] + + req_serializer = CreateSfcReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) except Exception as e: logger.error("Exception occurs: %s", e.message) logger.error(traceback.format_exc()) data = { - 'nsinstid': ignorcase_get(request.data, 'nsinstanceid'), + 'nsinstid': ignorcase_get(request.data, 'nsInstanceId'), "ns_model_data": ns_model_data, 'fpindex': get_fp_id(ignorcase_get(request.data, 'fpindex'), ns_model_data), 'fpinstid': str(uuid.uuid4()), - 'sdncontrollerid': ignorcase_get(request.data, 'sdncontrollerid') + 'sdncontrollerid': ignorcase_get(request.data, 'sdnControllerId') } logger.info("Save FPInstModel start: ") SfcInstance(data).do_biz() @@ -104,6 +170,12 @@ class SfcView(APIView): time.sleep(2) logger.info("Service Function Chain Thread Sleep end: %s" % time.ctime()) logger.info("Create Service Function Chain end") + + resp_serializer = CreateSfcRespSerializer(data={"jobId": job_id, + "sfcInstId": data["fpinstid"]}) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data={"jobId": job_id, "sfcInstId": data["fpinstid"]}, status=status.HTTP_200_OK) diff --git a/lcm/ns/tests/sfcs/test_create_flow_classifier.py b/lcm/ns/tests/sfcs/test_create_flow_classifier.py index 2c48c293..1f69931d 100644 --- a/lcm/ns/tests/sfcs/test_create_flow_classifier.py +++ b/lcm/ns/tests/sfcs/test_create_flow_classifier.py @@ -11,46 +11,61 @@ # 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 mock -# import json -# from test_data import nsd_model -# from rest_framework import status -# from lcm.pub.utils import restcall -# from lcm.pub.database.models import FPInstModel -# from django.test import Client -# from django.test import TestCase -# -# -# class TestSfc(TestCase): -# def setUp(self): -# self.client = Client() -# FPInstModel.objects.all().delete() -# FPInstModel(fpinstid="fp_inst_1", fpid="fpd_1", sdncontrollerid="test").save() -# -# def tearDown(self): -# FPInstModel.objects.all().delete() -# -# @mock.patch.object(restcall, 'call_req') -# def test_create_flow_classifier_success(self, mock_call_req): -# data = { -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# mock_vals = { -# "/api/aai-esr-server/v1/sdncontrollers/test": -# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], -# "/api/sdncdriver/v1.0/createflowclassfier": -# [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], -# "/api/microservices/v1/services": -# [0, None, '200'] -# -# } -# -# def side_effect(*args): -# return mock_vals[args[4]] -# -# mock_call_req.side_effect = side_effect -# resp = self.client.post("/api/nslcm/v1/ns/create_flow_classifier", data) -# ret = FPInstModel.objects.get(fpinstid="fp_inst_1") -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# self.assertEqual("test_id_1", ret.flowclassifiers) +import mock +import json +from test_data import nsd_model +from rest_framework import status +from lcm.pub.utils import restcall +from lcm.pub.database.models import FPInstModel +from django.test import Client +from django.test import TestCase + + +class TestSfc(TestCase): + def setUp(self): + self.client = Client() + FPInstModel.objects.all().delete() + FPInstModel(fpinstid="fp_inst_1", fpid="fpd_1", sdncontrollerid="test").save() + + def tearDown(self): + FPInstModel.objects.all().delete() + + @mock.patch.object(restcall, 'call_req') + def test_create_flow_classifier_success(self, mock_call_req): + data = { + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + mock_vals = { + "/external-system/esr-thirdparty-sdnc-list/esr-thirdparty-sdnc/test?depth=all": + [0, json.JSONEncoder().encode({ + "thirdparty-sdnc-id": "1", + "esr-system-info-list": { + "esr-system-info": [{ + "service-url": "url_1", + "thirdparty-sdnc-id": "1", + "user-name": "aa", + "password": "123", + "vendor": "zte", + "version": "v1.0", + "protocal": "http", + "product-name": "bbb", + "type": "11" + }] + } + }), '200'], + "/api/ztesdncdriver/v1/createflowclassfier": + [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], + "/api/microservices/v1/services": + [0, None, '200'] + + } + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + resp = self.client.post("/api/nslcm/v1/ns/create_flow_classifier", data) + ret = FPInstModel.objects.get(fpinstid="fp_inst_1") + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual("test_id_1", ret.flowclassifiers) diff --git a/lcm/ns/tests/sfcs/test_create_port_chain.py b/lcm/ns/tests/sfcs/test_create_port_chain.py index 7391dac9..978d5f6a 100644 --- a/lcm/ns/tests/sfcs/test_create_port_chain.py +++ b/lcm/ns/tests/sfcs/test_create_port_chain.py @@ -11,47 +11,62 @@ # 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 mock -# import json -# from test_data import nsd_model -# from rest_framework import status -# from lcm.pub.utils import restcall -# from lcm.pub.database.models import FPInstModel -# from django.test import Client -# from django.test import TestCase -# -# -# class TestSfc(TestCase): -# def setUp(self): -# self.client = Client() -# FPInstModel.objects.all().delete() -# FPInstModel(fpinstid="fp_inst_1", sdncontrollerid="test_sdncontrollerid", -# symmetric=1, flowclassifiers="test_flowclassifiers", -# portpairgroups=json.JSONEncoder().encode([{"groupid": "1"}])).save() -# -# def tearDown(self): -# FPInstModel.objects.all().delete() -# -# @mock.patch.object(restcall, 'call_req') -# def test_create_port_chain_success(self, mock_call_req): -# data = { -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# mock_vals = { -# "/api/aai-esr-server/v1/sdncontrollers/test_sdncontrollerid": -# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], -# "/api/sdncdriver/v1.0/createportchain": -# [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], -# "/api/microservices/v1/services": -# [0, None, '200'] -# } -# -# def side_effect(*args): -# return mock_vals[args[4]] -# -# mock_call_req.side_effect = side_effect -# resp = self.client.post("/api/nslcm/v1/ns/create_port_chain", data) -# ret = FPInstModel.objects.get(fpinstid="fp_inst_1") -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# self.assertEqual("test_id_1", ret.sfcid) +import mock +import json +from test_data import nsd_model +from rest_framework import status +from lcm.pub.utils import restcall +from lcm.pub.database.models import FPInstModel +from django.test import Client +from django.test import TestCase + + +class TestSfc(TestCase): + def setUp(self): + self.client = Client() + FPInstModel.objects.all().delete() + FPInstModel(fpinstid="fp_inst_1", sdncontrollerid="test_sdncontrollerid", + symmetric=1, flowclassifiers="test_flowclassifiers", + portpairgroups=json.JSONEncoder().encode([{"groupid": "1"}])).save() + + def tearDown(self): + FPInstModel.objects.all().delete() + + @mock.patch.object(restcall, 'call_req') + def test_create_port_chain_success(self, mock_call_req): + data = { + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + mock_vals = { + "/external-system/esr-thirdparty-sdnc-list/esr-thirdparty-sdnc/test_sdncontrollerid?depth=all": + [0, json.JSONEncoder().encode({ + "thirdparty-sdnc-id": "1", + "esr-system-info-list": { + "esr-system-info": [{ + "service-url": "url_1", + "thirdparty-sdnc-id": "1", + "user-name": "aa", + "password": "123", + "vendor": "zte", + "version": "v1.0", + "protocal": "http", + "product-name": "bbb", + "type": "11" + }] + } + }), '200'], + "/api/ztesdncdriver/v1/createportchain": + [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], + "/api/microservices/v1/services": + [0, None, '200'] + } + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + resp = self.client.post("/api/nslcm/v1/ns/create_port_chain", data) + ret = FPInstModel.objects.get(fpinstid="fp_inst_1") + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual("test_id_1", ret.sfcid) diff --git a/lcm/ns/tests/sfcs/test_create_port_pair_group.py b/lcm/ns/tests/sfcs/test_create_port_pair_group.py index d8c03cf6..490a9994 100644 --- a/lcm/ns/tests/sfcs/test_create_port_pair_group.py +++ b/lcm/ns/tests/sfcs/test_create_port_pair_group.py @@ -11,71 +11,93 @@ # 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 mock -# import json -# from test_data import nsd_model, vnfd_model_dict1, vnfd_model_dict2 -# from rest_framework import status -# from lcm.pub.utils import restcall -# from lcm.pub.database.models import FPInstModel, NfInstModel -# from django.test import Client -# from django.test import TestCase -# -# -# class TestSfc(TestCase): -# def setUp(self): -# self.client = Client() -# FPInstModel.objects.all().delete() -# NfInstModel.objects.all().delete() -# NfInstModel( -# nfinstid="vnf_inst_1", ns_inst_id="ns_inst_1", vnf_id="vnf_1", -# vnfd_model=json.dumps(vnfd_model_dict1)).save() -# NfInstModel( -# nfinstid="vnf_inst_2", vnf_id="vnf_2", ns_inst_id="ns_inst_1", -# vnfd_model=json.dumps(vnfd_model_dict2)).save() -# FPInstModel( -# fpid="fpd_1", fpinstid="fp_inst_1", nsinstid="ns_inst_1", vnffginstid="vnffg_inst_1", -# policyinfo=[{ -# "type": "ACL", -# "criteria": { -# "dest_port_range": [80, 1024], -# "source_port_range": [80, 1024], -# "ip_protocol": "tcp", -# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], -# "source_ip_range": ["192.168.1.2", "192.168.1.100"], -# "dscp": 100, -# } -# }], -# status="enabled", -# sdncontrollerid="sdn_controller_1" -# ).save() -# -# def tearDown(self): -# FPInstModel.objects.all().delete() -# NfInstModel.objects.all().delete() -# -# @mock.patch.object(restcall, 'call_req') -# def test_create_port_pair_group_success(self, mock_call_req): -# data = { -# "nsinstanceid": "ns_inst_1", -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# mock_vals = { -# "/api/aai-esr-server/v1/sdncontrollers/sdn_controller_1": -# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], -# "/api/sdncdriver/v1.0/createportpair": -# [0, json.JSONEncoder().encode({"id": "createportpair_id"}), '200'], -# "/api/sdncdriver/v1.0/createportpairgroup": -# [0, json.JSONEncoder().encode({"id": "createportpairgroup_id"}), '200'], -# "/api/microservices/v1/services": -# [0, None, '200'] -# } -# -# def side_effect(*args): -# return mock_vals[args[4]] -# -# mock_call_req.side_effect = side_effect -# resp = self.client.post("/api/nslcm/v1/ns/create_port_pair_group", data) -# rest = json.loads(FPInstModel.objects.get(fpinstid="fp_inst_1").portpairgroups)[0] -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# self.assertEqual("createportpairgroup_id", rest["groupid"]) +import mock +import json +from test_data import nsd_model, vnfd_model_dict1, vnfd_model_dict2 +from rest_framework import status +from lcm.pub.utils import restcall +from lcm.pub.database.models import FPInstModel, NfInstModel +from django.test import Client +from django.test import TestCase + + +class TestSfc(TestCase): + def setUp(self): + self.client = Client() + FPInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + NfInstModel( + nfinstid="vnf_inst_1", + ns_inst_id="ns_inst_1", + vnf_id="vnf_1", + vnfd_model=json.dumps(vnfd_model_dict1)).save() + NfInstModel( + nfinstid="vnf_inst_2", + vnf_id="vnf_2", + ns_inst_id="ns_inst_1", + vnfd_model=json.dumps(vnfd_model_dict2)).save() + FPInstModel( + fpid="fpd_1", + fpinstid="fp_inst_1", + nsinstid="ns_inst_1", + vnffginstid="vnffg_inst_1", + policyinfo=[{ + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + } + }], + status="enabled", + sdncontrollerid="sdn_controller_1" + ).save() + + def tearDown(self): + FPInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + + @mock.patch.object(restcall, 'call_req') + def test_create_port_pair_group_success(self, mock_call_req): + data = { + "nsinstanceid": "ns_inst_1", + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + mock_vals = { + "/external-system/esr-thirdparty-sdnc-list/esr-thirdparty-sdnc/sdn_controller_1?depth=all": + [0, json.JSONEncoder().encode({ + "thirdparty-sdnc-id": "1", + "esr-system-info-list": { + "esr-system-info": [{ + "service-url": "url_1", + "thirdparty-sdnc-id": "1", + "user-name": "aa", + "password": "123", + "vendor": "zte", + "version": "v1.0", + "protocal": "http", + "product-name": "bbb", + "type": "11" + }] + } + }), '200'], + "/api/ztesdncdriver/v1/createportpair": + [0, json.JSONEncoder().encode({"id": "createportpair_id"}), '200'], + "/api/ztesdncdriver/v1/createportpairgroup": + [0, json.JSONEncoder().encode({"id": "createportpairgroup_id"}), '200'], + "/api/microservices/v1/services": + [0, None, '200'] + } + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + resp = self.client.post("/api/nslcm/v1/ns/create_port_pair_group", data) + rest = json.loads(FPInstModel.objects.get(fpinstid="fp_inst_1").portpairgroups)[0] + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual("createportpairgroup_id", rest["groupid"]) diff --git a/lcm/ns/tests/sfcs/test_data.py b/lcm/ns/tests/sfcs/test_data.py index 8d58c692..72f0b430 100644 --- a/lcm/ns/tests/sfcs/test_data.py +++ b/lcm/ns/tests/sfcs/test_data.py @@ -102,6 +102,7 @@ nsd_model = { "pnf_type": "TTGW", "request_reclassification": False, "nsh_aware": False, + "management_address": "10.34.45.67" }, "cps": [ "cpd_1", "cpd_22", diff --git a/lcm/ns/tests/sfcs/test_sfc.py b/lcm/ns/tests/sfcs/test_sfc.py index 4fced43b..9eeff0dc 100644 --- a/lcm/ns/tests/sfcs/test_sfc.py +++ b/lcm/ns/tests/sfcs/test_sfc.py @@ -1,4 +1,4 @@ -# Copyright 2016 ZTE Corporation. +# Copyright 2018 ZTE Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,1641 +12,1632 @@ # See the License for the specific language governing permissions and # limitations under the License. -# import json -# -# import mock -# from django.test import Client -# from django.test import TestCase -# from rest_framework import status -# -# from lcm.pub.database.models import FPInstModel, CPInstModel, PortInstModel, NfInstModel -# from lcm.pub.database.models import VNFFGInstModel -# from lcm.pub.msapi import extsys -# from lcm.pub.msapi import sdncdriver -# from lcm.pub.utils import restcall -# -# -# class TestSfc(TestCase): -# def setUp(self): -# self.client = Client() -# FPInstModel.objects.filter().delete() -# VNFFGInstModel.objects.filter().delete() -# CPInstModel.objects.filter().delete() -# PortInstModel.objects.filter().delete() -# NfInstModel.objects.filter().delete() -# -# self.save_vnffg_inst_data() -# self.save_vnf_inst_data() -# self.save_cp_inst_data() -# self.save_port_inst_data() -# self.save_fp_inst_data() -# -# def tearDown(self): -# pass -# -# @mock.patch.object(restcall, 'call_req') -# def test_sfc_instanciate(self, mock_call_req): -# data = { -# "nsinstanceid": "ns_inst_1", -# "context": json.dumps(nsd_model), -# "fpindex": "fpd_1", -# "sdncontrollerid": "sdnControllerId_1" -# } -# -# resp = self.client.post("/api/nslcm/v1/ns/sfc_instance", data, format='json') -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# -# @mock.patch.object(extsys, "get_sdn_controller_by_id") -# @mock.patch.object(sdncdriver, "create_flow_classfier") -# @mock.patch.object(restcall, 'call_req') -# def test_create_flow_classfier(self, mock_call_req, mock_create_flow_classfier, mock_get_sdn_controller_by_id): -# data = { -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# mock_create_flow_classfier.return_value = [0, json.dumps({'id': '1'})] -# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') -# resp = self.client.post("/api/nslcm/v1/ns/create_flow_classifier", data) -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# -# @mock.patch.object(extsys, "get_sdn_controller_by_id") -# @mock.patch.object(sdncdriver, 'create_port_pair_group') -# @mock.patch.object(sdncdriver, 'create_port_pair') -# @mock.patch.object(restcall, 'call_req') -# def test_create_port_pair_group(self, mock_call_req, mock_create_port_pair, mock_create_port_pair_group -# , mock_get_sdn_controller_by_id): -# data = { -# "nsinstanceid": "ns_inst_1", -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# mock_create_port_pair.return_value = [0, json.dumps({'id': '1'})] -# mock_create_port_pair_group.return_value = [0, json.dumps({'id': '1'})] -# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') -# resp = self.client.post("/api/nslcm/v1/ns/create_port_pair_group", data) -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# -# @mock.patch.object(extsys, "get_sdn_controller_by_id") -# @mock.patch.object(sdncdriver, 'create_port_chain') -# @mock.patch.object(restcall, 'call_req') -# def test_create_port_chain(self, mock_call_req, mock_create_port_chain -# , mock_get_sdn_controller_by_id): -# data = { -# "nsinstanceid": "ns_inst_1", -# "fpinstid": "fp_inst_1", -# "context": json.dumps(nsd_model) -# } -# self.update_fp_inst_data() -# mock_create_port_chain.return_value = [0, json.dumps({'id': '1'})] -# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') -# resp = self.client.post("/api/nslcm/v1/ns/create_port_chain", data) -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# -# # @mock.patch.object(restcall, 'call_req') -# # def test_create_sfc(self, mock_call_req): -# # data = { -# # "nsinstanceid": "ns_inst_1", -# # "context": json.dumps(nsd_model), -# # "fpindex": "fpd_1", -# # 'fpinstid': str(uuid.uuid4()), -# # "sdncontrollerid": "sdnControllerId_1" -# # } -# # -# # resp = self.client.post("/api/nslcm/v1/ns/sfc", data, format='json') -# # self.assertEqual(resp.status_code, status.HTTP_200_OK) -# -# def update_fp_inst_data(self): -# FPInstModel.objects.filter(fpinstid="fp_inst_1").update(flowclassifiers="1", -# portpairgroups=json.JSONEncoder().encode([{ -# "groupid": "1", -# "portpair": ["2"] -# }])) -# -# def save_vnffg_inst_data(self): -# VNFFGInstModel( -# vnffgdid="vnffg_id1", -# vnffginstid="vnffg_inst_1", -# nsinstid="ns_inst_1", -# endpointnumber=2, -# vllist="vlinst1", -# cplist="cp1", -# vnflist="vnf1,vnf2" -# ).save() -# -# def save_cp_inst_data(self): -# CPInstModel( -# cpinstanceid="cp_inst_1", -# cpdid="cpd_1", -# ownertype=0, -# ownerid="vnf_inst_1", -# relatedtype=1, -# relatedport="port_inst_1" -# ).save() -# -# CPInstModel( -# cpinstanceid="cp_inst_2", -# cpdid="cpd_2", -# ownertype=0, -# ownerid="vnf_inst_2", -# relatedtype=1, -# relatedport="port_inst_2" -# ).save() -# -# def save_fp_inst_data(self): -# FPInstModel( -# fpid="fpd_1", -# fpinstid="fp_inst_1", -# nsinstid="ns_inst_1", -# vnffginstid="vnffg_inst_1", -# policyinfo=[{ -# "type": "ACL", -# "criteria": { -# "dest_port_range": [80, 1024], -# "source_port_range": [80, 1024], -# "ip_protocol": "tcp", -# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], -# "source_ip_range": ["192.168.1.2", "192.168.1.100"], -# "dscp": 100, -# } -# }], -# status="enabled", -# sdncontrollerid="sdn_controller_1" -# -# ).save() -# -# FPInstModel( -# fpid="fpd_2", -# fpinstid="fp_inst_2", -# nsinstid="ns_inst_1", -# vnffginstid="vnffg_inst_1", -# policyinfo=[{ -# "type": "ACL", -# "criteria": { -# "dest_port_range": [80, 1024], -# "source_port_range": [80, 1024], -# "ip_protocol": "tcp", -# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], -# "source_ip_range": ["192.168.1.2", "192.168.1.100"], -# "dscp": 100, -# } -# }], -# status="enabled", -# sdncontrollerid="sdn_controller_1" -# -# ).save() -# -# # -# # def save_sdnc_inst_data(self): -# # SDNCModel( -# # uuid="uuid_111", -# # sdncontrollerid="sdn_controller_1", -# # name="111", -# # type="vnf", -# # url="192.168.0.1:8080", -# # username="admin", -# # pwd="admin", -# # ver="ver", -# # longitude="longitude", -# # latitude="latitude" -# # -# # ).save() -# -# -# -# -# def save_port_inst_data(self): -# PortInstModel( -# portid="port_inst_1", -# networkid="network_inst_1", -# subnetworkid="subnetwork_inst_1", -# vimid="vim_1", -# # insttype="2", -# resourceid="res_1", -# ipaddress="10.43.25.2", -# macaddress="EC-F4-BB-20-43-F1" -# ).save() -# -# PortInstModel( -# portid="port_inst_2", -# networkid="network_inst_1", -# subnetworkid="subnetwork_inst_1", -# vimid="vim_1", -# # insttype="2", -# resourceid="res_1", -# ipaddress="10.43.25.3", -# macaddress="EC-F4-BB-20-43-F2" -# ).save() -# -# def save_vnf_inst_data(self): -# NfInstModel( -# nfinstid="vnf_inst_1", -# ns_inst_id="ns_inst_1", -# vnf_id="vnf_1", -# vnfd_model=json.dumps(vnfd_model_dict1) -# -# ).save() -# -# NfInstModel( -# nfinstid="vnf_inst_2", -# vnf_id="vnf_2", -# ns_inst_id="ns_inst_1", -# vnfd_model=json.dumps(vnfd_model_dict2) -# -# ).save() -# -# -# vnfd_model_dict1 = { -# -# 'vdus': [ -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'2' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_omm.001', -# 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'omm.001', -# 'manual_scale_select_vim': False -# }, -# 'description': u'singleommvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'4' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_1', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'1', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_2', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'2', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_3', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'3', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'4' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_10', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'10', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_11', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'11', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_12', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'12', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# } -# ], -# 'volumn_storages': [ -# -# ], -# 'policies': { -# 'scaling': { -# 'targets': { -# -# }, -# 'policy_id': u'policy_scale_sss-vnf-template', -# 'properties': { -# 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' -# }, -# 'description': '' -# } -# }, -# 'image_files': [ -# { -# 'description': '', -# 'properties': { -# 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', -# 'checksum': '', -# 'disk_format': u'VMDK', -# 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', -# 'container_type': 'vm', -# 'version': '', -# 'hypervisor_type': 'kvm' -# }, -# 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' -# }, -# { -# 'description': '', -# 'properties': { -# 'name': u'sss.vmdk', -# 'checksum': '', -# 'disk_format': u'VMDK', -# 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', -# 'container_type': 'vm', -# 'version': '', -# 'hypervisor_type': 'kvm' -# }, -# 'image_file_id': u'sss' -# } -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# {'cp_id': 'cpd_1', -# "description": "", -# "properties": { -# "mac_address": "00:d9:00:82:11:e1", -# "ip_address": "10.43.25.2", -# "ip_range_start": "192.168.1.20", -# "ip_range_end": "192.168.1.29", -# "sfc_encapsulation": "" -# } -# }, -# ], -# 'metadata': { -# 'vendor': u'zte', -# 'is_shared': False, -# 'description': '', -# 'domain_type': u'CN', -# 'version': u'v4.14.10', -# 'vmnumber_overquota_alarm': False, -# 'cross_dc': False, -# 'vnf_type': u'SSS', -# 'vnfd_version': u'V00000001', -# 'id': u'vnfd_2', -# 'name': u'sss-vnf-template' -# }, -# -# 'vnf_exposed': { -# "external_cps": [ -# { -# "key_name": "virtualLink1", -# "cp_id": "cp1", -# }, -# ], -# "forward_cps": [ -# { -# "key_name": "forwarder1", -# "cp_id": "cpd_1", -# }, -# { -# "key_name": "forwarder2", -# "cp_id": "cpd_2", -# }, -# ], -# } -# } -# -# vnfd_model_dict2 = { -# 'local_storages': [ -# -# ], -# 'vdus': [ -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'2' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_omm.001', -# 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'omm.001', -# 'manual_scale_select_vim': False -# }, -# 'description': u'singleommvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'4' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_1', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'1', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_2', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'2', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_3', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'3', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ompvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'4' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_10', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'10', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_11', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'11', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# }, -# { -# 'volumn_storages': [ -# -# ], -# 'nfv_compute': { -# 'mem_size': '', -# 'num_cpus': u'14' -# }, -# 'local_storages': [ -# -# ], -# 'vdu_id': u'vdu_12', -# 'image_file': u'sss', -# 'dependencies': [ -# -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# -# ], -# 'properties': { -# 'key_vdu': '', -# 'support_scaling': False, -# 'vdu_type': '', -# 'name': '', -# 'storage_policy': '', -# 'location_info': { -# 'vimId': '', -# 'availability_zone': '', -# 'region': '', -# 'dc': '', -# 'host': '', -# 'tenant': '' -# }, -# 'inject_data_list': [ -# -# ], -# 'watchdog': { -# 'action': '', -# 'enabledelay': '' -# }, -# 'local_affinity_antiaffinity_rule': { -# -# }, -# 'template_id': u'12', -# 'manual_scale_select_vim': False -# }, -# 'description': u'ppvm' -# } -# ], -# 'volumn_storages': [ -# -# ], -# 'policies': { -# 'scaling': { -# 'targets': { -# -# }, -# 'policy_id': u'policy_scale_sss-vnf-template', -# 'properties': { -# 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' -# }, -# 'description': '' -# } -# }, -# 'image_files': [ -# { -# 'description': '', -# 'properties': { -# 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', -# 'checksum': '', -# 'disk_format': u'VMDK', -# 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', -# 'container_type': 'vm', -# 'version': '', -# 'hypervisor_type': 'kvm' -# }, -# 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' -# }, -# { -# 'description': '', -# 'properties': { -# 'name': u'sss.vmdk', -# 'checksum': '', -# 'disk_format': u'VMDK', -# 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', -# 'container_type': 'vm', -# 'version': '', -# 'hypervisor_type': 'kvm' -# }, -# 'image_file_id': u'sss' -# } -# ], -# 'vls': [ -# -# ], -# 'cps': [ -# {'cp_id': 'cpd_2', -# "description": "", -# "properties": { -# "mac_address": "00:d9:00:82:11:e2", -# "ip_address": "10.43.25.3", -# "ip_range_start": "192.168.1.20", -# "ip_range_end": "192.168.1.29", -# "sfc_encapsulation": "" -# } -# }, -# ], -# 'metadata': { -# 'vendor': u'zte', -# 'is_shared': False, -# 'description': '', -# 'domain_type': u'CN', -# 'version': u'v4.14.10', -# 'vmnumber_overquota_alarm': False, -# 'cross_dc': False, -# 'vnf_type': u'SSS', -# 'vnfd_version': u'V00000001', -# 'id': u'sss-vnf-template', -# 'name': u'vnfd_2' -# }, -# 'vnf_exposed': { -# "external_cps": [ -# { -# "key_name": "virtualLink1", -# "cp_id": "cp1", -# }, -# ], -# "forward_cps": [ -# { -# "key_name": "forwarder2", -# "cp_id": "cpd_2", -# }, -# { -# "key_name": "forwarder3", -# "cp_id": "cpd_2", -# }, -# ], -# } -# } -# -# nsd_model = { -# "metadata": { -# "id": "nsd_demo", -# "vendor": "zte", -# "version": "1.1.0", -# "name": "testNSD", -# "description": "demo nsd", -# }, -# -# "inputs": { -# "param1": "11", -# "param2": "22", -# }, -# -# "vnfs": [ -# { -# "type": "tosca.nodes.nfv.ext.VNF.FireWall", -# "vnf_id": "vnf_1", -# "description": "", -# "properties": { -# "id": "vnfd_1", -# "vendor": "zte", -# "version": "1.2.0", -# "vnfd_version": "1.1.0", -# "vnf_type": "vnf1", -# "domain_type": "CN", -# "name": "vnf1", -# "is_shared": False, -# "cross_dc": False, -# "request_reclassification": False, -# "nsh_aware": False, -# "custom_properties": { -# "key1": "value1", -# "keyN": "valueN", -# }, -# }, -# "dependencies": [ -# "vnf_id1", "vnf_id2" -# ], -# "networks": [ -# { -# "key_name": "virtualLink1", -# "vl_id": "vl_id1", -# }, -# ], -# }, -# { -# "type": "tosca.nodes.nfv.ext.VNF.FireWall", -# "vnf_id": "vnf_2", -# "description": "", -# "properties": { -# "id": "vnfd_2", -# "vendor": "zte", -# "version": "1.2.0", -# "vnfd_version": "1.1.0", -# "vnf_type": "vnf2", -# "domain_type": "CN", -# "name": "vnf1", -# "is_shared": False, -# "cross_dc": False, -# "request_reclassification": False, -# "nsh_aware": False, -# "custom_properties": { -# "key1": "value1", -# "keyN": "valueN", -# }, -# }, -# "dependencies": [ -# "vnf_id1", "vnf_id2" -# ], -# "networks": [ -# { -# "key_name": "virtualLink1", -# "vl_id": "vl_id1", -# }, -# ], -# } -# ], -# -# "pnfs": [ -# { -# "pnf_id": "pnf1", -# "description": "", -# "properties": { -# "id": "pnf1", -# "vendor": "zte", -# "version": "1.1.0", -# "pnf_type": "TTGW", -# "request_reclassification": False, -# "nsh_aware": False, -# }, -# "cps": [ -# "cpd_1", "cpd_22", -# ] -# } -# ], -# -# "nested_ns": [ -# { -# "ns_id": "ns2", -# "description": "", -# "properties": { -# "id": "ns2_demo", -# "vendor": "zte", -# "version": "1.1.0", -# "name": "NSD2", -# }, -# "networks": [ -# { -# "key_name": "virtualLink1", -# "vl_id": "vl_id1", -# }, -# ], -# } -# ], -# -# "vls": [ -# { -# "vl_id": "vldId1", -# "description": "", -# "properties": { -# "name": "umac_241_control", -# "network_id": "fgdhsj434hfjdfd", -# "network_name": "umac_control", -# "vendor": "zte", -# "mtu": 1500, -# "network_type": "vlan", -# "physical_network": "phynet01", -# "segmentation_id": "30", -# "vlan_transparent": False, -# "vds_name": "vds1", -# "cidr": "192.168.199.0/24", -# "ip_version": 4, -# "gateway_ip": "192.168.199.1", -# "dhcp_enabled": False, -# "dns_nameservers": ["192.168.0.4", "192.168.0.10"], -# "start_ip": "192.168.199.2", -# "end_ip": "192.168.199.254", -# "host_routes": [ -# { -# "destination": "10.43.26.0/24", -# "nexthop": "10.41.23.1", -# }, -# ], -# "location_info": { -# "vimId": "vimid", -# "tenant": "tenantname", -# }, -# "vlan_transparent": False, -# }, -# }, -# ], -# -# "cps": [ -# { -# "cp_id": "cpd_1", -# "description": "", -# "properties": { -# "mac_address": "00:d9:00:82:11:e1", -# "ip_address": "192.168.1.21", -# "ip_range_start": "192.168.1.20", -# "ip_range_end": "192.168.1.29", -# "floating_ip_address": { -# "external_network": "extnet01", -# "ip_address": "10.43.53.23", -# }, -# "service_ip_address": "192.168.1.23", -# "order": 1, -# "bandwidth": 1000, -# "vnic_type": "normal", -# "allowed_address_pairs": [ -# { -# "ip": "192.168.1.13", -# "mac": "00:f3:43:20:a2:a3" -# }, -# ], -# "bond": "none", -# "macbond": "00:d9:00:82:11:d1", -# "sfc_encapsulation": "", -# "direction": "", -# }, -# "vl_id": "vlid1", -# "pnf_id": "pnf1", -# }, -# -# { -# "cp_id": "forwarder_brasDP_dcPort", -# "description": "", -# "properties": { -# "mac_address": "00:d9:00:82:14:e1", -# "ip_address": "192.168.1.24", -# "ip_range_start": "192.168.1.20", -# "ip_range_end": "192.168.1.29", -# "floating_ip_address": { -# "external_network": "extnet01", -# "ip_address": "10.43.53.23", -# }, -# "service_ip_address": "192.168.1.23", -# "order": 1, -# "bandwidth": 1000, -# "vnic_type": "normal", -# "allowed_address_pairs": [ -# { -# "ip": "192.168.1.13", -# "mac": "00:f3:43:20:a2:a3" -# }, -# ], -# "bond": "none", -# "macbond": "00:d9:00:82:11:d1", -# "sfc_encapsulation": "", -# "direction": "", -# }, -# "vl_id": "vlid1", -# "pnf_id": "pnf1", -# }, -# { -# "cp_id": "forwarder_brasDP_internetPort", -# "description": "", -# "properties": { -# "mac_address": "00:d9:00:82:15:e1", -# "ip_address": "192.168.1.25", -# "ip_range_start": "192.168.1.20", -# "ip_range_end": "192.168.1.29", -# "floating_ip_address": { -# "external_network": "extnet01", -# "ip_address": "10.43.53.23", -# }, -# "service_ip_address": "192.168.1.23", -# "order": 1, -# "bandwidth": 1000, -# "vnic_type": "normal", -# "allowed_address_pairs": [ -# { -# "ip": "192.168.1.13", -# "mac": "00:f3:43:20:a2:a3" -# }, -# ], -# "bond": "none", -# "macbond": "00:d9:00:82:11:d1", -# "sfc_encapsulation": "", -# "direction": "", -# }, -# "vl_id": "vlid1", -# "pnf_id": "pnf1", -# }, -# -# ], -# -# "fps": [ -# { -# "fp_id": "fpd_1", -# "description": "", -# "properties": { -# "policy": { -# "type": "ACL", -# "criteria": { -# "dest_port_range": [80, 1024], -# "source_port_range": [80, 1024], -# "ip_protocol": "tcp", -# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], -# "source_ip_range": ["192.168.1.2", "192.168.1.100"], -# "dscp": 100, -# }, -# }, -# "symmetric": True, -# }, -# "forwarder_list": [ -# { -# "type": "cp", -# "node_name": "cpd_1", -# "capability": "", -# }, -# { -# "type": "cp", -# "node_name": "forwarder_brasDP_dcPort", -# "capability": "", -# }, -# { -# "type": "vnf", -# "node_name": "vnf_1", -# "capability": "forwarder1", -# }, -# { -# "type": "vnf", -# "node_name": "vnf_2", -# "capability": "forwarder2", -# }, -# { -# "type": "cp", -# "node_name": "forwarder_brasDP_dcPort", -# "capability": "", -# }, -# { -# "type": "cp", -# "node_name": "forwarder_brasDP_internetPort", -# "capability": "", -# }, -# ], -# }, -# -# { -# "fp_id": "fpd_2", -# "description": "", -# "properties": { -# "policy": { -# "type": "ACL", -# "criteria": { -# "dest_port_range": [80, 1024], -# "source_port_range": [80, 1024], -# "ip_protocol": "tcp", -# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], -# "source_ip_range": ["192.168.1.2", "192.168.1.100"], -# "dscp": 100, -# }, -# }, -# "symmetric": True, -# }, -# "forwarder_list": [ -# -# { -# "type": "cp", -# "node_name": "forwarder_brasDP_internetPort", -# "capability": "", -# }, -# { -# "type": "cp", -# "node_name": "forwarder_brasDP_dcPort", -# "capability": "", -# }, -# { -# "type": "vnf", -# "node_name": "vnf_2", -# "capability": "forwarder2", -# }, -# -# ], -# }, -# ], -# -# "vnffgs": [ -# { -# "vnffg_id": "vnffg_id1", -# "description": "", -# "properties": { -# "vendor": "zte", -# "version": "1.1.2", -# "number_of_endpoints": 7, -# "dependent_virtual_link": ["vldId1"], -# "connection_point": ["CP01", "CP02"], -# "constituent_vnfs": ["vnf_id1", "vnf_id2"], -# "constituent_pnfs": ["pnf1", "pnf2"], -# }, -# "members": ["fpd_1", "fpd_2"], -# } -# ], -# -# "server_groups": [ -# { -# "group_id": "", -# "description": "", -# "properties": { -# "name": "server_group1", -# "affinity_antiaffinity": "anti-affinity", -# "scope": "host", -# }, -# "members": ["vnf1", "vnf2"], -# }, -# ], -# -# "ns_exposed": { -# "external_cps": [ -# { -# "key_name": "virtualLink1", -# "cp_id": "cp1", -# }, -# ], -# "forward_cps": [ -# { -# "key_name": "forwarder_brasDP_userPort", -# "cp_id": "cpd_1", -# }, -# { -# "key_name": "forwarder_brasDP_internetPort", -# "cp_id": "cpd_4", -# }, -# { -# "key_name": "forwarder_brasDP_dcPort", -# "cp_id": "cpd_5", -# }, -# -# ], -# }, -# -# "policies": [ -# { -# "scaling": [ -# { -# "policy_id": "id1", -# "description": "", -# "properties": { -# "policy_file": "Policies/ns1-policy.xml", -# }, -# "targets": ['pfu_vm'], -# }, -# ], -# }, -# ], -# -# "ns_flavours": [ -# { -# "flavour_id": "flavour1", -# "description": "", -# "vnf_profiles": [ -# { -# "vnf_id": "vnf1", -# "flavour_id": "flavour1", -# "instances_minimum_number": 1, -# "instances_maximum_number": 4, -# "local_affinity_antiaffinity_rule": [ -# { -# "affinity_antiaffinity": "affinity", -# "scope": "node", -# } -# ] -# }, -# ], -# "pnf_profiles": [ -# { -# "pnf_id": "pnf1", -# }, -# ], -# "vl_profiles": [ -# { -# "vl_id": "vlid1", -# "bitrate_requirements": { -# "root": 1000, -# "leaf": 100 -# }, -# "qos": { -# "maximum_latency": "1 ms", -# "maximum_jitter": "10 ms", -# "maximum_packet_loss_ratio": 0.5 -# }, -# } -# ], -# "instantiation_levels": [ -# { -# "id": "instLevel1", -# "description": "", -# "vnf_levels": [ -# { -# "vnf_id": "", -# "vnf_instantiation_level": "small", -# "instances_number": 1 -# }, -# ], -# "scale_level_id": "scaleLevel1", -# } -# ], -# "default_instantiation_level": "instLevel1", -# "scale_levels": [ -# { -# "id": "scaleLevel1", -# "order": 1, -# "vnf_levels": [ -# { -# "vnf_id": "", -# "vnf_instantiation_level": "small", -# "instances_number": 1 -# }, -# ], -# }, -# ], -# "supported_operations": ["Scale", "Heal"], -# "affinity_antiaffinity_groups": [ -# { -# "group_id": "group1Id", -# "name": "groupName", -# "affinity_antiaffinity": "affinity", -# "scope": "node", -# "members": [ -# "vnfId1", "vnfIdN", -# ], -# }, -# ], -# }, -# ], -# } +import json +import mock +from django.test import Client +from django.test import TestCase +from rest_framework import status + +from lcm.pub.database.models import FPInstModel, CPInstModel, PortInstModel, NfInstModel +from lcm.pub.database.models import VNFFGInstModel +from lcm.pub.msapi import extsys +from lcm.pub.msapi import sdncdriver +from lcm.pub.utils import restcall + + +class TestSfc(TestCase): + def setUp(self): + self.client = Client() + FPInstModel.objects.filter().delete() + VNFFGInstModel.objects.filter().delete() + CPInstModel.objects.filter().delete() + PortInstModel.objects.filter().delete() + NfInstModel.objects.filter().delete() + + self.save_vnffg_inst_data() + self.save_vnf_inst_data() + self.save_cp_inst_data() + self.save_port_inst_data() + self.save_fp_inst_data() + + def tearDown(self): + pass + + @mock.patch.object(restcall, 'call_req') + def test_sfc_instanciate(self, mock_call_req): + pass + # data = { + # "nsInstanceId": "ns_inst_1", + # "context": nsd_model, + # "fpindex": "fpd_1", + # "sdnControllerId": "sdnControllerId_1" + # } + # resp = self.client.post("/api/nslcm/v1/ns/sfc_instance", data, format='json') + # self.assertEqual(resp.status_code, status.HTTP_200_OK, resp.data) + + @mock.patch.object(extsys, "get_sdn_controller_by_id") + @mock.patch.object(sdncdriver, "create_flow_classfier") + @mock.patch.object(restcall, 'call_req') + def test_create_flow_classfier(self, mock_call_req, mock_create_flow_classfier, mock_get_sdn_controller_by_id): + data = { + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + mock_create_flow_classfier.return_value = [0, json.dumps({'id': '1'})] + mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') + resp = self.client.post("/api/nslcm/v1/ns/create_flow_classifier", data) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + @mock.patch.object(extsys, "get_sdn_controller_by_id") + @mock.patch.object(sdncdriver, 'create_port_pair_group') + @mock.patch.object(sdncdriver, 'create_port_pair') + @mock.patch.object(restcall, 'call_req') + def test_create_port_pair_group(self, mock_call_req, mock_create_port_pair, + mock_create_port_pair_group, mock_get_sdn_controller_by_id): + data = { + "nsinstanceid": "ns_inst_1", + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + mock_create_port_pair.return_value = [0, json.dumps({'id': '1'})] + mock_create_port_pair_group.return_value = [0, json.dumps({'id': '1'})] + mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') + resp = self.client.post("/api/nslcm/v1/ns/create_port_pair_group", data) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + @mock.patch.object(extsys, "get_sdn_controller_by_id") + @mock.patch.object(sdncdriver, 'create_port_chain') + @mock.patch.object(restcall, 'call_req') + def test_create_port_chain(self, mock_call_req, mock_create_port_chain, mock_get_sdn_controller_by_id): + data = { + "nsinstanceid": "ns_inst_1", + "fpinstid": "fp_inst_1", + "context": json.dumps(nsd_model) + } + self.update_fp_inst_data() + mock_create_port_chain.return_value = [0, json.dumps({'id': '1'})] + mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') + resp = self.client.post("/api/nslcm/v1/ns/create_port_chain", data) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + @mock.patch.object(restcall, 'call_req') + def test_create_sfc(self, mock_call_req): + pass + # data = { + # "nsInstanceId": "ns_inst_1", + # "context": json.dumps(nsd_model), + # "fpindex": "1", + # 'fpinstid': str(uuid.uuid4()), + # "sdnControllerId": "sdnControllerId_1" + # } + # resp = self.client.post("/api/nslcm/v1/ns/sfcs", data, format='json') + # self.assertEqual(resp.status_code, status.HTTP_200_OK, resp.data) + + def update_fp_inst_data(self): + FPInstModel.objects.filter(fpinstid="fp_inst_1").update(flowclassifiers="1", + portpairgroups=json.JSONEncoder().encode([{ + "groupid": "1", + "portpair": ["2"] + }])) + + def save_vnffg_inst_data(self): + VNFFGInstModel( + vnffgdid="vnffg_id1", + vnffginstid="vnffg_inst_1", + nsinstid="ns_inst_1", + endpointnumber=2, + vllist="vlinst1", + cplist="cp1", + vnflist="vnf1,vnf2" + ).save() + + def save_cp_inst_data(self): + CPInstModel( + cpinstanceid="cp_inst_1", + cpdid="cpd_1", + ownertype=0, + ownerid="vnf_inst_1", + relatedtype=1, + relatedport="port_inst_1" + ).save() + + CPInstModel( + cpinstanceid="cp_inst_2", + cpdid="cpd_2", + ownertype=0, + ownerid="vnf_inst_2", + relatedtype=1, + relatedport="port_inst_2" + ).save() + + def save_fp_inst_data(self): + FPInstModel( + fpid="fpd_1", + fpinstid="fp_inst_1", + nsinstid="ns_inst_1", + vnffginstid="vnffg_inst_1", + policyinfo=[{ + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + } + }], + status="enabled", + sdncontrollerid="sdn_controller_1" + + ).save() + + FPInstModel( + fpid="fpd_2", + fpinstid="fp_inst_2", + nsinstid="ns_inst_1", + vnffginstid="vnffg_inst_1", + policyinfo=[{ + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + } + }], + status="enabled", + sdncontrollerid="sdn_controller_1" + ).save() + + def save_sdnc_inst_data(self): + pass + # SDNCModel( + # uuid="uuid_111", + # sdncontrollerid="sdn_controller_1", + # name="111", + # type="vnf", + # url="192.168.0.1:8080", + # username="admin", + # pwd="admin", + # ver="ver", + # longitude="longitude", + # latitude="latitude" + # ).save() + + def save_port_inst_data(self): + PortInstModel( + portid="port_inst_1", + networkid="network_inst_1", + subnetworkid="subnetwork_inst_1", + vimid="vim_1", + resourceid="res_1", + ipaddress="10.43.25.2", + macaddress="EC-F4-BB-20-43-F1" + ).save() + + PortInstModel( + portid="port_inst_2", + networkid="network_inst_1", + subnetworkid="subnetwork_inst_1", + vimid="vim_1", + resourceid="res_1", + ipaddress="10.43.25.3", + macaddress="EC-F4-BB-20-43-F2" + ).save() + + def save_vnf_inst_data(self): + NfInstModel( + nfinstid="vnf_inst_1", + ns_inst_id="ns_inst_1", + vnf_id="vnf_1", + vnfd_model=json.dumps(vnfd_model_dict1) + + ).save() + + NfInstModel( + nfinstid="vnf_inst_2", + vnf_id="vnf_2", + ns_inst_id="ns_inst_1", + vnfd_model=json.dumps(vnfd_model_dict2) + + ).save() + + +vnfd_model_dict1 = { + 'vdus': [ + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'2' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_omm.001', + 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'omm.001', + 'manual_scale_select_vim': False + }, + 'description': u'singleommvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_1', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'1', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_2', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'2', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_3', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'3', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_10', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'10', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_11', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'11', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_12', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'12', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + } + ], + 'volumn_storages': [ + + ], + 'policies': { + 'scaling': { + 'targets': { + + }, + 'policy_id': u'policy_scale_sss-vnf-template', + 'properties': { + 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' + }, + 'description': '' + } + }, + 'image_files': [ + { + 'description': '', + 'properties': { + 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' + }, + { + 'description': '', + 'properties': { + 'name': u'sss.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'sss' + } + ], + 'vls': [ + + ], + 'cps': [ + {'cp_id': 'cpd_1', + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e1", + "ip_address": "10.43.25.2", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "sfc_encapsulation": "" + } + }, + ], + 'metadata': { + 'vendor': u'zte', + 'is_shared': False, + 'description': '', + 'domain_type': u'CN', + 'version': u'v4.14.10', + 'vmnumber_overquota_alarm': False, + 'cross_dc': False, + 'vnf_type': u'SSS', + 'vnfd_version': u'V00000001', + 'id': u'vnfd_2', + 'name': u'sss-vnf-template' + }, + + 'vnf_exposed': { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder1", + "cp_id": "cpd_1", + }, + { + "key_name": "forwarder2", + "cp_id": "cpd_2", + }, + ], + } +} + +vnfd_model_dict2 = { + 'local_storages': [ + + ], + 'vdus': [ + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'2' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_omm.001', + 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'omm.001', + 'manual_scale_select_vim': False + }, + 'description': u'singleommvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_1', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'1', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_2', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'2', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_3', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'3', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_10', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'10', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_11', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'11', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_12', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'12', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + } + ], + 'volumn_storages': [ + + ], + 'policies': { + 'scaling': { + 'targets': { + + }, + 'policy_id': u'policy_scale_sss-vnf-template', + 'properties': { + 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' + }, + 'description': '' + } + }, + 'image_files': [ + { + 'description': '', + 'properties': { + 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' + }, + { + 'description': '', + 'properties': { + 'name': u'sss.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'sss' + } + ], + 'vls': [ + + ], + 'cps': [ + {'cp_id': 'cpd_2', + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e2", + "ip_address": "10.43.25.3", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "sfc_encapsulation": "" + } + }, + ], + 'metadata': { + 'vendor': u'zte', + 'is_shared': False, + 'description': '', + 'domain_type': u'CN', + 'version': u'v4.14.10', + 'vmnumber_overquota_alarm': False, + 'cross_dc': False, + 'vnf_type': u'SSS', + 'vnfd_version': u'V00000001', + 'id': u'sss-vnf-template', + 'name': u'vnfd_2' + }, + 'vnf_exposed': { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder2", + "cp_id": "cpd_2", + }, + { + "key_name": "forwarder3", + "cp_id": "cpd_2", + }, + ], + } +} + +nsd_model = { + "metadata": { + "id": "nsd_demo", + "vendor": "zte", + "version": "1.1.0", + "name": "testNSD", + "description": "demo nsd", + }, + + "inputs": { + "param1": "11", + "param2": "22", + }, + + "vnfs": [ + { + "type": "tosca.nodes.nfv.ext.VNF.FireWall", + "vnf_id": "vnf_1", + "description": "", + "properties": { + "id": "vnfd_1", + "vendor": "zte", + "version": "1.2.0", + "vnfd_version": "1.1.0", + "vnf_type": "vnf1", + "domain_type": "CN", + "name": "vnf1", + "is_shared": False, + "cross_dc": False, + "request_reclassification": False, + "nsh_aware": False, + "custom_properties": { + "key1": "value1", + "keyN": "valueN", + }, + }, + "dependencies": [ + "vnf_id1", "vnf_id2" + ], + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + }, + { + "type": "tosca.nodes.nfv.ext.VNF.FireWall", + "vnf_id": "vnf_2", + "description": "", + "properties": { + "id": "vnfd_2", + "vendor": "zte", + "version": "1.2.0", + "vnfd_version": "1.1.0", + "vnf_type": "vnf2", + "domain_type": "CN", + "name": "vnf1", + "is_shared": False, + "cross_dc": False, + "request_reclassification": False, + "nsh_aware": False, + "custom_properties": { + "key1": "value1", + "keyN": "valueN", + }, + }, + "dependencies": [ + "vnf_id1", "vnf_id2" + ], + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + } + ], + + "pnfs": [ + { + "pnf_id": "pnf1", + "description": "", + "properties": { + "id": "pnf1", + "vendor": "zte", + "version": "1.1.0", + "pnf_type": "TTGW", + "request_reclassification": False, + "nsh_aware": False, + "management_address": "10.44.56.78" + }, + "cps": [ + "cpd_1", "cpd_22", + ] + } + ], + + "nested_ns": [ + { + "ns_id": "ns2", + "description": "", + "properties": { + "id": "ns2_demo", + "vendor": "zte", + "version": "1.1.0", + "name": "NSD2", + }, + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + } + ], + + "vls": [ + { + "vl_id": "vldId1", + "description": "", + "properties": { + "name": "umac_241_control", + "network_id": "fgdhsj434hfjdfd", + "network_name": "umac_control", + "vendor": "zte", + "mtu": 1500, + "network_type": "vlan", + "physical_network": "phynet01", + "segmentation_id": "30", + "vlan_transparent": False, + "vds_name": "vds1", + "cidr": "192.168.199.0/24", + "ip_version": 4, + "gateway_ip": "192.168.199.1", + "dhcp_enabled": False, + "dns_nameservers": ["192.168.0.4", "192.168.0.10"], + "start_ip": "192.168.199.2", + "end_ip": "192.168.199.254", + "host_routes": [ + { + "destination": "10.43.26.0/24", + "nexthop": "10.41.23.1", + }, + ], + "location_info": { + "vimId": "vimid", + "tenant": "tenantname", + }, + "vlan_transparent": False, + }, + }, + ], + + "cps": [ + { + "cp_id": "cpd_1", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e1", + "ip_address": "192.168.1.21", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + + { + "cp_id": "forwarder_brasDP_dcPort", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:14:e1", + "ip_address": "192.168.1.24", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + { + "cp_id": "forwarder_brasDP_internetPort", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:15:e1", + "ip_address": "192.168.1.25", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + + ], + + "fps": [ + { + "fp_id": "fpd_1", + "description": "", + "properties": { + "policy": { + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + }, + }, + "symmetric": True, + }, + "forwarder_list": [ + { + "type": "cp", + "node_name": "cpd_1", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "vnf", + "node_name": "vnf_1", + "capability": "forwarder1", + }, + { + "type": "vnf", + "node_name": "vnf_2", + "capability": "forwarder2", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_internetPort", + "capability": "", + }, + ], + }, + + { + "fp_id": "fpd_2", + "description": "", + "properties": { + "policy": { + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + }, + }, + "symmetric": True, + }, + "forwarder_list": [ + + { + "type": "cp", + "node_name": "forwarder_brasDP_internetPort", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "vnf", + "node_name": "vnf_2", + "capability": "forwarder2", + }, + + ], + }, + ], + + "vnffgs": [ + { + "vnffg_id": "vnffg_id1", + "description": "", + "properties": { + "vendor": "zte", + "version": "1.1.2", + "number_of_endpoints": 7, + "dependent_virtual_link": ["vldId1"], + "connection_point": ["CP01", "CP02"], + "constituent_vnfs": ["vnf_id1", "vnf_id2"], + "constituent_pnfs": ["pnf1", "pnf2"], + }, + "members": ["fpd_1", "fpd_2"], + } + ], + + "server_groups": [ + { + "group_id": "", + "description": "", + "properties": { + "name": "server_group1", + "affinity_antiaffinity": "anti-affinity", + "scope": "host", + }, + "members": ["vnf1", "vnf2"], + }, + ], + + "ns_exposed": { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder_brasDP_userPort", + "cp_id": "cpd_1", + }, + { + "key_name": "forwarder_brasDP_internetPort", + "cp_id": "cpd_4", + }, + { + "key_name": "forwarder_brasDP_dcPort", + "cp_id": "cpd_5", + }, + + ], + }, + + "policies": [ + { + "scaling": [ + { + "policy_id": "id1", + "description": "", + "properties": { + "policy_file": "Policies/ns1-policy.xml", + }, + "targets": ['pfu_vm'], + }, + ], + }, + ], + + "ns_flavours": [ + { + "flavour_id": "flavour1", + "description": "", + "vnf_profiles": [ + { + "vnf_id": "vnf1", + "flavour_id": "flavour1", + "instances_minimum_number": 1, + "instances_maximum_number": 4, + "local_affinity_antiaffinity_rule": [ + { + "affinity_antiaffinity": "affinity", + "scope": "node", + } + ] + }, + ], + "pnf_profiles": [ + { + "pnf_id": "pnf1", + }, + ], + "vl_profiles": [ + { + "vl_id": "vlid1", + "bitrate_requirements": { + "root": 1000, + "leaf": 100 + }, + "qos": { + "maximum_latency": "1 ms", + "maximum_jitter": "10 ms", + "maximum_packet_loss_ratio": 0.5 + }, + } + ], + "instantiation_levels": [ + { + "id": "instLevel1", + "description": "", + "vnf_levels": [ + { + "vnf_id": "", + "vnf_instantiation_level": "small", + "instances_number": 1 + }, + ], + "scale_level_id": "scaleLevel1", + } + ], + "default_instantiation_level": "instLevel1", + "scale_levels": [ + { + "id": "scaleLevel1", + "order": 1, + "vnf_levels": [ + { + "vnf_id": "", + "vnf_instantiation_level": "small", + "instances_number": 1 + }, + ], + }, + ], + "supported_operations": ["Scale", "Heal"], + "affinity_antiaffinity_groups": [ + { + "group_id": "group1Id", + "name": "groupName", + "affinity_antiaffinity": "affinity", + "scope": "node", + "members": [ + "vnfId1", "vnfIdN", + ], + }, + ], + }, + ], +} diff --git a/lcm/ns/tests/sfcs/test_sfc_instance.py b/lcm/ns/tests/sfcs/test_sfc_instance.py index 981376fd..a0613b35 100644 --- a/lcm/ns/tests/sfcs/test_sfc_instance.py +++ b/lcm/ns/tests/sfcs/test_sfc_instance.py @@ -1,4 +1,4 @@ -# Copyright 2016 ZTE Corporation. +# Copyright 2018 ZTE Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,37 +11,37 @@ # 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 json -# from rest_framework import status -# from test_data import nsd_model -# from django.test import Client -# from django.test import TestCase -# from lcm.pub.database.models import FPInstModel, VNFFGInstModel -# -# -# class TestSfc(TestCase): -# def setUp(self): -# self.client = Client() -# VNFFGInstModel.objects.all().delete() -# FPInstModel.objects.all().delete() -# VNFFGInstModel(vnffgdid="vnffg_id1", vnffginstid="vnffg_inst_1", nsinstid="ns_inst_1", endpointnumber=2, -# vllist="vlinst1", cplist="cp1", vnflist="vnf1,vnf2").save() -# -# def tearDown(self): -# VNFFGInstModel.objects.all().delete() -# FPInstModel.objects.all().delete() -# -# def test_sfc_instance_success(self): -# data = { -# "nsinstanceid": "ns_inst_1", -# "context": json.dumps(nsd_model), -# "fpindex": "fpd_1", -# "sdncontrollerid": "sdnControllerId_1" -# } -# resp = self.client.post("/api/nslcm/v1/ns/sfc_instance", data, format='json') -# -# vnffg = VNFFGInstModel.objects.get(vnffginstid="vnffg_inst_1") -# ret = FPInstModel.objects.get(fpinstid=resp.data["fpinstid"]) -# self.assertEqual(resp.status_code, status.HTTP_200_OK) -# self.assertEqual(vnffg.fplist, resp.data["fpinstid"]) -# self.assertIsNotNone(ret) +import json +from rest_framework import status +from test_data import nsd_model +from django.test import Client +from django.test import TestCase +from lcm.pub.database.models import FPInstModel, VNFFGInstModel + + +class TestSfc(TestCase): + def setUp(self): + self.client = Client() + VNFFGInstModel.objects.all().delete() + FPInstModel.objects.all().delete() + VNFFGInstModel(vnffgdid="vnffg_id1", vnffginstid="vnffg_inst_1", nsinstid="ns_inst_1", endpointnumber=2, + vllist="vlinst1", cplist="cp1", vnflist="vnf1,vnf2").save() + + def tearDown(self): + VNFFGInstModel.objects.all().delete() + FPInstModel.objects.all().delete() + + def test_sfc_instance_success(self): + data = { + "nsInstanceId": "ns_inst_1", + "context": json.dumps(nsd_model), + "fpindex": "fpd_1", + "sdnControllerId": "sdnControllerId_1" + } + resp = self.client.post("/api/nslcm/v1/ns/sfc_instance", data, format='json') + + self.assertEqual(resp.status_code, status.HTTP_200_OK, resp.data) + vnffg = VNFFGInstModel.objects.get(vnffginstid="vnffg_inst_1") + ret = FPInstModel.objects.get(fpinstid=resp.data["fpinstid"]) + self.assertEqual(vnffg.fplist, resp.data["fpinstid"]) + self.assertIsNotNone(ret) diff --git a/lcm/ns/tests/test_ns_create.py b/lcm/ns/tests/test_ns_create.py index e462e1c3..61d87913 100644 --- a/lcm/ns/tests/test_ns_create.py +++ b/lcm/ns/tests/test_ns_create.py @@ -70,8 +70,8 @@ class TestNsInstantiate(TestCase): mock_do_biz.side_effect = NSLCMException("nsd not exists.") new_nsd_id = '1' data = { - 'nsdid': new_nsd_id, - 'nsname': 'ns', + 'csarId': new_nsd_id, + 'nsName': 'ns', 'description': 'description' } response = self.client.post("/api/nslcm/v1/ns", data=data) diff --git a/lcm/ns/tests/test_ns_delete.py b/lcm/ns/tests/test_ns_delete.py index 892528d4..4397ef52 100644 --- a/lcm/ns/tests/test_ns_delete.py +++ b/lcm/ns/tests/test_ns_delete.py @@ -11,6 +11,7 @@ # 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 json import uuid @@ -29,41 +30,11 @@ class TestNsDelelete(TestCase): NSInstModel.objects.filter().delete() NSInstModel(id=self.ns_inst_id, nspackage_id="7", nsd_id="2").save() - # self.nsd_id = str(uuid.uuid4()) - # self.ns_package_id = str(uuid.uuid4()) - # NSDModel(id=self.ns_package_id, nsd_id=self.nsd_id, name='name').save() - def tearDown(self): - # NSDModel.objects.all().delete() NSInstModel.objects.all().delete() @mock.patch.object(restcall, 'call_req') def test_delete_ns(self, mock_call_req): - # customer_info = { - # "global-customer-id": "global-customer-id-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "subscriber-name": "subscriber-name-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "subscriber-type": "subscriber-type-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "resource-version": "1505350719754", - # "service-subscriptions": { - # "service-subscription": [ - # { - # "service-type": "service-type-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "resource-version": "1505350719887", - # "service-instances": { - # "service-instance": [ - # { - # "service-instance-id": "service-instance-id-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "service-instance-name": "service-instance-name-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "service-type": "service-type-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "service-role": "service-role-9b9348f2-f75d-4559-823d-db7ac138ed34", - # "resource-version": "1505350720009" - # } - # ] - # } - # } - # ] - # } - # } ns_info = { "service-instance-id": "service-instance-id-9b9348f2-f75d-4559-823d-db7ac138ed34", "service-instance-name": "service-instance-name-9b9348f2-f75d-4559-823d-db7ac138ed34", diff --git a/lcm/ns/tests/test_ns_get.py b/lcm/ns/tests/test_ns_get.py index e41f397a..32e5d1fb 100644 --- a/lcm/ns/tests/test_ns_get.py +++ b/lcm/ns/tests/test_ns_get.py @@ -31,7 +31,7 @@ class TestNsQuery(TestCase): def test_query_all_nsinstance(self):
response = self.client.get("/api/nslcm/v1/ns")
- self.failUnlessEqual(status.HTTP_200_OK, response.status_code)
+ self.failUnlessEqual(status.HTTP_200_OK, response.status_code, response.data)
self.assertIsNotNone(response.data)
self.assertEqual(2, len(response.data))
diff --git a/lcm/ns/tests/test_ns_heal.py b/lcm/ns/tests/test_ns_heal.py index 46f13ce6..a3b8498e 100644 --- a/lcm/ns/tests/test_ns_heal.py +++ b/lcm/ns/tests/test_ns_heal.py @@ -66,7 +66,7 @@ class TestHealNsViews(TestCase): } response = self.client.post("/api/nslcm/v1/ns/%s/heal" % self.ns_inst_id, data=data) - self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code, response.data) self.assertIsNotNone(response.data) self.assertIn("jobId", response.data) self.assertNotIn("error", response.data) @@ -128,6 +128,5 @@ class TestHealNsViews(TestCase): data = {} response = self.client.post("/api/nslcm/v1/ns/%s/heal" % self.ns_inst_id, data=data) - self.assertEqual(response.data["error"], "healVnfData parameter does not exist or value is incorrect.") self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) self.assertIn("error", response.data) diff --git a/lcm/ns/tests/test_ns_manual_scale.py b/lcm/ns/tests/test_ns_manual_scale.py index a25a0e9f..ceb7498e 100644 --- a/lcm/ns/tests/test_ns_manual_scale.py +++ b/lcm/ns/tests/test_ns_manual_scale.py @@ -13,36 +13,172 @@ # limitations under the License. import uuid - +import os import mock from django.test import Client from django.test import TestCase from rest_framework import status - from lcm.ns.const import NS_INST_STATUS from lcm.ns.ns_manual_scale import NSManualScaleService -from lcm.pub.database.models import NSInstModel +from lcm.pub.database.models import NSInstModel, JobModel, NfInstModel from lcm.pub.exceptions import NSLCMException from lcm.pub.utils import restcall -from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE, JOB_MODEL_STATUS +from lcm.pub.msapi import catalog +from lcm.pub.utils.scaleaspect import get_json_data + + +SCALING_JSON = { + "scale_options": [ + { + "nsd_id": "ns_ims", + "ns_scale_aspect": "TIC_CORE_IMS", + "ns_scale_info": [ + { + "step": "1", + "scale_list": [ + { + "vnfd_id": "zte_ims_cscf", + "vnf_scale_aspect": "mpu", + "numberOfSteps": "1" + }, + { + "vnfd_id": "zte_ims_hss", + "vnf_scale_aspect": "fpu", + "numberOfSteps": "3" + } + ] + }, + { + "step": "2", + "scale_list": [ + { + "vnfd_id": "zte_ims_cscf", + "vnf_scale_aspect": "mpu", + "numberOfSteps": "2" + }, + { + "vnfd_id": "zte_ims_hss", + "vnf_scale_aspect": "fpu", + "numberOfSteps": "6" + } + ] + } + ] + }, + { + "nsd_id": "ns_epc", + "ns_scale_aspect": "TIC_EDGE_EPC", + "ns_scale_info": [ + { + "step": "1", + "scale_list": [ + { + "vnfd_id": "zte_epc_spgw", + "vnf_scale_aspect": "gpu", + "numberOfSteps": "1" + }, + { + "vnfd_id": "zte_epc_tas", + "vnf_scale_aspect": "fpu", + "numberOfSteps": "2" + } + ] + }, + { + "step": "2", + "scale_list": [ + { + "vnfd_id": "zte_epc_spgw", + "vnf_scale_aspect": "mpu", + "numberOfSteps": "2" + }, + { + "vnfd_id": "zte_epc_tas", + "vnf_scale_aspect": "fpu", + "numberOfSteps": "4" + } + ] + } + ] + } + ] +} class TestNsManualScale(TestCase): def setUp(self): self.ns_inst_id = str(uuid.uuid4()) - self.job_id = JobUtil.create_job("NS", JOB_TYPE.MANUAL_SCALE_VNF, self.ns_inst_id) - + self.job_id = JobUtil.create_job( + "NS", JOB_TYPE.MANUAL_SCALE_VNF, self.ns_inst_id) + self.package_id = "7" self.client = Client() - NSInstModel(id=self.ns_inst_id, name="abc", nspackage_id="7", nsd_id="111").save() + NSInstModel( + id=self.ns_inst_id, + name="abc", + nspackage_id=self.package_id, + nsd_id="111").save() + + self.init_scaling_map_json() def tearDown(self): NSInstModel.objects.filter().delete() + JobModel.objects.filter().delete() + + def init_scaling_map_json(self): + curdir_path = os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__)))) + filename = curdir_path + "/ns/data/scalemapping.json" + self.scaling_map_json = get_json_data(filename) + + def insert_new_ns(self): + ns_inst_id = str(uuid.uuid4()) + job_id = JobUtil.create_job( + "NS", JOB_TYPE.MANUAL_SCALE_VNF, self.ns_inst_id) + package_id = "23" + NSInstModel( + id=ns_inst_id, + name="abc", + nspackage_id=package_id, + nsd_id=package_id).save() + return ns_inst_id, job_id + + def insert_new_nf(self): + # Create a third vnf instance + self.nf_name = "name_1" + self.vnf_id = "1" + self.vnfm_inst_id = "1" + nf_inst_id = "233" + package_id = "nf_zte_hss" + nf_uuid = "ab34-3g5j-de13-ab85-ij93" + + NfInstModel.objects.create( + nfinstid=nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=nf_uuid, + package_id=package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') @mock.patch.object(NSManualScaleService, 'run') def test_ns_manual_scale(self, mock_run): data = { "scaleType": "SCALE_NS", - "scaleNsByStepsData": [{ + "scaleNsData": [{ "scaleNsByStepsData": [{ "aspectId": "1", "numberOfSteps": 1, @@ -50,14 +186,87 @@ class TestNsManualScale(TestCase): }] }] } - response = self.client.post("/api/nslcm/v1/ns/%s/scale" % self.ns_inst_id, data=data) + response = self.client.post( + "/api/nslcm/v1/ns/%s/scale" % + self.ns_inst_id, data=data) self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + def test_ns_manual_scale_error_scaletype(self): + data = { + "scaleType": "SCALE_ERR", + "scaleNsData": [{ + "scaleNsByStepsData": [{ + "aspectId": "sss_zte", + "numberOfSteps": 1, + "scalingDirection": "0" + }] + }] + } + NSManualScaleService(self.ns_inst_id, data, self.job_id).run() + jobs = JobModel.objects.filter(jobid=self.job_id) + self.assertEqual(255, jobs[0].progress) + + def test_ns_manual_scale_error_nsd_id(self): + data = { + "scaleType": "SCALE_NS", + "scaleNsData": [{ + "scaleNsByStepsData": [{ + "aspectId": "sss_zte", + "numberOfSteps": 1, + "scalingDirection": "0" + }] + }] + } + NSManualScaleService(self.ns_inst_id, data, self.job_id).run() + jobs = JobModel.objects.filter(jobid=self.job_id) + self.assertEqual(255, jobs[0].progress) + + def test_ns_manual_scale_error_aspect(self): + data = { + "scaleType": "SCALE_NS", + "scaleNsData": [{ + "scaleNsByStepsData": [{ + "aspectId": "sss_zte", + "numberOfSteps": 1, + "scalingDirection": "0" + }] + }] + } + ns_inst_id, job_id = self.insert_new_ns() + job_id = JobUtil.create_job( + "NS", JOB_TYPE.MANUAL_SCALE_VNF, ns_inst_id) + NSManualScaleService(ns_inst_id, data, job_id).run() + jobs = JobModel.objects.filter(jobid=job_id) + self.assertEqual(255, jobs[0].progress) + + @mock.patch.object(catalog, 'get_scalingmap_json_package') + @mock.patch.object(NSManualScaleService, 'do_vnfs_scale') + def test_ns_manual_scale_success(self, mock_do_vnfs_scale, mock_get_scalingmap_json_package): + data = { + "scaleType": "SCALE_NS", + "scaleNsData": [{ + "scaleNsByStepsData": [{ + "aspectId": "TIC_EDGE_IMS", + "numberOfSteps": "1", + "scalingDirection": "0" + }] + }] + } + mock_get_scalingmap_json_package.return_value = self.scaling_map_json + mock_do_vnfs_scale.return_value = JOB_MODEL_STATUS.FINISHED + ns_inst_id, job_id = self.insert_new_ns() + job_id = JobUtil.create_job( + "NS", JOB_TYPE.MANUAL_SCALE_VNF, ns_inst_id) + self.insert_new_nf() + NSManualScaleService(ns_inst_id, data, job_id).run() + jobs = JobModel.objects.filter(jobid=job_id) + self.assertEqual(100, jobs[0].progress) + @mock.patch.object(restcall, 'call_req') def test_ns_manual_scale_thread(self, mock_call): data = { "scaleType": "SCALE_NS", - "scaleNsByStepsData": [{ + "scaleNsData": [{ "scaleNsByStepsData": [{ "aspectId": "1", "numberOfSteps": 1, @@ -66,7 +275,10 @@ class TestNsManualScale(TestCase): }] } NSManualScaleService(self.ns_inst_id, data, self.job_id).run() - self.assertTrue(NSInstModel.objects.get(id=self.ns_inst_id).status, NS_INST_STATUS.ACTIVE) + self.assertTrue( + NSInstModel.objects.get( + id=self.ns_inst_id).status, + NS_INST_STATUS.ACTIVE) def test_swagger_ok(self): resp = self.client.get("/api/nslcm/v1/swagger.json", format='json') @@ -75,8 +287,12 @@ class TestNsManualScale(TestCase): @mock.patch.object(NSManualScaleService, 'start') def test_ns_manual_scale_empty_data(self, mock_start): mock_start.side_effect = NSLCMException("NS scale failed.") - response = self.client.post("/api/nslcm/v1/ns/%s/scale" % self.ns_inst_id, data={}) - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + response = self.client.post( + "/api/nslcm/v1/ns/%s/scale" % + self.ns_inst_id, data={}) + self.assertEqual( + response.status_code, + status.HTTP_500_INTERNAL_SERVER_ERROR) self.assertIn("error", response.data) @mock.patch.object(NSManualScaleService, 'start') @@ -84,7 +300,7 @@ class TestNsManualScale(TestCase): mock_start.side_effect = NSLCMException("NS scale failed.") data = { "scaleType": "SCALE_NS", - "scaleNsByStepsData": [{ + "scaleNsData": [{ "scaleNsByStepsData": [{ "aspectId": "1", "numberOfSteps": 1, @@ -93,5 +309,7 @@ class TestNsManualScale(TestCase): }] } response = self.client.post("/api/nslcm/v1/ns/11/scale", data=data) - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertEqual( + response.status_code, + status.HTTP_500_INTERNAL_SERVER_ERROR) self.assertIn("error", response.data) diff --git a/lcm/ns/tests/tests_ns_terminate.py b/lcm/ns/tests/tests_ns_terminate.py index 4a33105d..d7091882 100644 --- a/lcm/ns/tests/tests_ns_terminate.py +++ b/lcm/ns/tests/tests_ns_terminate.py @@ -51,12 +51,12 @@ class TestTerminateNsViews(TestCase): @mock.patch.object(TerminateNsService, 'run') def test_terminate_vnf_url(self, mock_run): - mock_run.re.return_value = None + mock_run.re.return_value = "1" req_data = { "terminationType": "forceful", "gracefulTerminationTimeout": "600"} response = self.client.post("/api/nslcm/v1/ns/%s/terminate" % self.ns_inst_id, data=req_data) - self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code, response.data) response = self.client.delete("/api/nslcm/v1/ns/%s" % self.ns_inst_id) self.failUnlessEqual(status.HTTP_204_NO_CONTENT, response.status_code) @@ -100,7 +100,7 @@ class TestTerminateNsViews(TestCase): @mock.patch.object(TerminateNsService, 'run') def test_terminate_non_existing_ns_inst_id(self, mock_run): - mock_run.re.return_value = None + mock_run.re.return_value = "1" ns_inst_id = '100' @@ -108,6 +108,6 @@ class TestTerminateNsViews(TestCase): "terminationType": "forceful", "gracefulTerminationTimeout": "600"} response = self.client.post("/api/nslcm/v1/ns/%s/terminate" % ns_inst_id, data=req_data) - self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code, response.data) self.assertRaises(NSInstModel.DoesNotExist, NSInstModel.objects.get, id=ns_inst_id) diff --git a/lcm/ns/tests/vls/tests.py b/lcm/ns/tests/vls/tests.py index 288c70e4..2966c908 100644 --- a/lcm/ns/tests/vls/tests.py +++ b/lcm/ns/tests/vls/tests.py @@ -84,10 +84,10 @@ class TestVlViews(TestCase): req_data = { "nsInstanceId": self.ns_inst_id, "context": json.JSONEncoder().encode(self.context), - "vlindex": vl_id} + "vlIndex": vl_id} response = self.client.post("/api/nslcm/v1/ns/vls", data=req_data) self.assertEqual(status.HTTP_201_CREATED, response.status_code) - self.assertEqual(0, response.data["result"]) + self.assertEqual(0, response.data["result"], response.data) @mock.patch.object(restcall, "call_req") @mock.patch.object(vimadaptor.VimAdaptor, "create_network") @@ -96,7 +96,7 @@ class TestVlViews(TestCase): req_data = { "nsInstanceId": self.ns_inst_id, "context": json.JSONEncoder().encode(self.context), - "vlindex": self.vl_id_1} + "vlIndex": self.vl_id_1} mock_uuid4.return_value = '999' mock_req_by_rest.return_value = [0, json.JSONEncoder().encode(vim_info), '200'] mock_create_network.return_value = [1, (1)] diff --git a/lcm/ns/tests/vnfs/tests.py b/lcm/ns/tests/vnfs/tests.py index 57924822..1406fae2 100644 --- a/lcm/ns/tests/vnfs/tests.py +++ b/lcm/ns/tests/vnfs/tests.py @@ -303,10 +303,7 @@ class TestScaleVnfViews(TestCase): NSInstModel.objects.all().delete() NfInstModel.objects.all().delete() - @mock.patch.object(restcall, "call_req") - def test_scale_vnf(self, mock_call_req): - job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, self.nf_inst_id) - + def test_scale_vnf(self): vnfd_info = { "vnf_flavours": [{ "flavour_id": "flavour1", @@ -361,22 +358,72 @@ class TestScaleVnfViews(TestCase): ] } - mock_vals = { - "/api/ztevnfmdriver/v1/1/vnfs/111/terminate": - [0, json.JSONEncoder().encode({"jobId": job_id}), '200'] - } - - def side_effect(*args): - return mock_vals[args[4]] + NFManualScaleService(self.nf_inst_id, req_data).run() + nsIns = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) + self.assertIsNotNone(nsIns) - mock_call_req.side_effect = side_effect + @mock.patch.object(NFManualScaleService, "send_nf_scaling_request") + def test_scale_vnf_success(self, mock_send_nf_scaling_request): + vnfd_info = { + "vnf_flavours": [{ + "flavour_id": "flavour1", + "description": "", + "vdu_profiles": [ + { + "vdu_id": "vdu1Id", + "instances_minimum_number": 1, + "instances_maximum_number": 4, + "local_affinity_antiaffinity_rule": [ + { + "affinity_antiaffinity": "affinity", + "scope": "node", + } + ] + } + ], + "scaling_aspects": [ + { + "id": "demo_aspect", + "name": "demo_aspect", + "description": "demo_aspect", + "associated_group": "elementGroup1", + "max_scale_level": 5 + } + ] + }], + "element_groups": [{ + "group_id": "elementGroup1", + "description": "", + "properties": { + "name": "elementGroup1", + }, + "members": ["gsu_vm", "pfu_vm"] + }] + } - NFManualScaleService(self.nf_inst_id, req_data).run() + req_data = { + "scaleVnfData": [ + { + "type": "SCALE_OUT", + "aspectId": "demo_aspect1", + "numberOfSteps": 1, + "additionalParam": vnfd_info + }, + { + "type": "SCALE_OUT", + "aspectId": "demo_aspect2", + "numberOfSteps": 1, + "additionalParam": vnfd_info + } + ] + } + scale_service = NFManualScaleService(self.nf_inst_id, req_data) + scale_service.run() nsIns = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) - if nsIns: - self.failUnlessEqual(1, 1) - else: - self.failUnlessEqual(1, 0) + self.assertIsNotNone(nsIns) + + jobs = JobModel.objects.filter(jobid=scale_service.job_id) + self.assertIsNotNone(100, jobs[0].progress) class TestHealVnfViews(TestCase): @@ -520,7 +567,7 @@ class TestGetVnfmInfoViews(TestCase): } response = self.client.get("/api/nslcm/v1/vnfms/%s" % self.vnfm_id) - self.failUnlessEqual(status.HTTP_200_OK, response.status_code) + self.failUnlessEqual(status.HTTP_200_OK, response.status_code, response.content) context = json.loads(response.content) self.assertEqual(expect_data, context) diff --git a/lcm/ns/urls.py b/lcm/ns/urls.py index f61b4444..6fa06587 100644 --- a/lcm/ns/urls.py +++ b/lcm/ns/urls.py @@ -11,25 +11,20 @@ # 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. -from django.conf.urls import patterns, url +from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns from lcm.ns.views import CreateNSView, NSInstView, TerminateNSView, NSDetailView, NSInstPostDealView, \ NSManualScaleView, NSHealView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/ns$', CreateNSView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/instantiate$', - NSInstView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/terminate$', - TerminateNSView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)$', NSDetailView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/postdeal$', - NSInstPostDealView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/scale$', - NSManualScaleView.as_view()), - url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/heal$', - NSHealView.as_view()) - ) +urlpatterns = [ + url(r'^api/nslcm/v1/ns$', CreateNSView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/instantiate$', NSInstView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/terminate$', TerminateNSView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)$', NSDetailView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/postdeal$', NSInstPostDealView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/scale$', NSManualScaleView.as_view()), + url(r'^api/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/heal$', NSHealView.as_view()) +] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/views.py b/lcm/ns/views.py index 03a5763d..413a89a6 100644 --- a/lcm/ns/views.py +++ b/lcm/ns/views.py @@ -13,12 +13,12 @@ # limitations under the License. import json import logging -import os import traceback from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema from lcm.ns.ns_create import CreateNSService from lcm.ns.ns_delete import DeleteNsService @@ -31,110 +31,223 @@ from lcm.pub.database.models import NSInstModel, ServiceBaseInfoModel from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE from lcm.pub.utils.restcall import req_by_msb from lcm.pub.utils.values import ignore_case_get +from lcm.ns.serializers import CreateNsReqSerializer, CreateNsRespSerializer +from lcm.ns.serializers import QueryNsRespSerializer +from lcm.ns.serializers import NsOperateJobSerializer +from lcm.ns.serializers import InstantNsReqSerializer +from lcm.ns.serializers import TerminateNsReqSerializer +from lcm.ns.serializers import HealNsReqSerializer +from lcm.ns.serializers import InstNsPostDealReqSerializer +from lcm.ns.serializers import ManualScaleNsReqSerializer +from lcm.pub.exceptions import NSLCMException logger = logging.getLogger(__name__) class CreateNSView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: QueryNsRespSerializer(help_text="NS instances", many=True), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request): - logger.debug("CreateNSView::get") - filter = None - csarId = ignore_case_get(request.META, 'csarId') - if csarId: - filter = {"csarId": csarId} + try: + logger.debug("CreateNSView::get") + filter = None + csarId = ignore_case_get(request.META, 'csarId') + if csarId: + filter = {"csarId": csarId} - ret = GetNSInfoService(filter).get_ns_info() - logger.debug("CreateNSView::get::ret=%s", ret) - return Response(data=ret, status=status.HTTP_200_OK) + ret = GetNSInfoService(filter).get_ns_info() + logger.debug("CreateNSView::get::ret=%s", ret) + resp_serializer = QueryNsRespSerializer(data=ret, many=True) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + return Response(data=resp_serializer.data, status=status.HTTP_200_OK) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Exception in GetNS: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @swagger_auto_schema( + request_body=CreateNsReqSerializer(), + responses={ + status.HTTP_201_CREATED: CreateNsRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request): logger.debug("Enter CreateNS: %s", request.data) - if ignore_case_get(request.data, 'test') == "test": - return Response(data={'nsInstanceId': "test"}, status=status.HTTP_201_CREATED) - # nsd_id = ignore_case_get(request.data, 'nsdId') - csar_id = ignore_case_get(request.data, 'csarId') - ns_name = ignore_case_get(request.data, 'nsName') - description = ignore_case_get(request.data, 'description') - context = ignore_case_get(request.data, 'context') try: + req_serializer = CreateNsReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + + if ignore_case_get(request.data, 'test') == "test": + return Response(data={'nsInstanceId': "test"}, status=status.HTTP_201_CREATED) + csar_id = ignore_case_get(request.data, 'csarId') + ns_name = ignore_case_get(request.data, 'nsName') + description = ignore_case_get(request.data, 'description') + context = ignore_case_get(request.data, 'context') ns_inst_id = CreateNSService(csar_id, ns_name, description, context).do_biz() + + logger.debug("CreateNSView::post::ret={'nsInstanceId':%s}", ns_inst_id) + resp_serializer = CreateNsRespSerializer(data={'nsInstanceId': ns_inst_id}) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + return Response(data=resp_serializer.data, status=status.HTTP_201_CREATED) except Exception as e: logger.error(traceback.format_exc()) logger.error("Exception in CreateNS: %s", e.message) return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - logger.debug("CreateNSView::post::ret={'nsInstanceId':%s}", ns_inst_id) - return Response(data={'nsInstanceId': ns_inst_id}, status=status.HTTP_201_CREATED) class NSInstView(APIView): + @swagger_auto_schema( + request_body=InstantNsReqSerializer(), + responses={ + status.HTTP_200_OK: NsOperateJobSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request, ns_instance_id): + logger.debug("Enter NSInstView::post::ns_instance_id=%s", ns_instance_id) + req_serializer = InstantNsReqSerializer(data=request.data) + if not req_serializer.is_valid(): + return Response({'error': req_serializer.errors}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) ack = InstantNSService(ns_instance_id, request.data).do_biz() + resp_serializer = NsOperateJobSerializer(data=ack['data']) + if not resp_serializer.is_valid(): + return Response({'error': resp_serializer.errors}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) logger.debug("Leave NSInstView::post::ack=%s", ack) - return Response(data=ack['data'], status=ack['status']) + return Response(data=resp_serializer.data, status=ack['status']) class TerminateNSView(APIView): + @swagger_auto_schema( + request_body=TerminateNsReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: NsOperateJobSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request, ns_instance_id): - logger.debug("Enter TerminateNSView::post %s", request.data) - termination_type = ignore_case_get(request.data, 'terminationType') - graceful_termination_timeout = ignore_case_get(request.data, 'gracefulTerminationTimeout') - job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, ns_instance_id) try: + logger.debug("Enter TerminateNSView::post %s", request.data) + req_serializer = TerminateNsReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + + termination_type = ignore_case_get(request.data, 'terminationType') + graceful_termination_timeout = ignore_case_get(request.data, 'gracefulTerminationTimeout') + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, ns_instance_id) TerminateNsService(ns_instance_id, termination_type, graceful_termination_timeout, job_id).start() + + resp_serializer = NsOperateJobSerializer(data={'jobId': job_id}) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + logger.debug("Leave TerminateNSView::post ret=%s", resp_serializer.data) + return Response(data=resp_serializer.data, status=status.HTTP_202_ACCEPTED) except Exception as e: logger.error("Exception in CreateNS: %s", e.message) return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - ret = {'jobId': job_id} - logger.debug("Leave TerminateNSView::post ret=%s", ret) - return Response(data=ret, status=status.HTTP_202_ACCEPTED) class NSHealView(APIView): + @swagger_auto_schema( + request_body=HealNsReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: NsOperateJobSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request, ns_instance_id): - logger.debug("Enter HealNSView::post %s", request.data) - job_id = JobUtil.create_job("VNF", JOB_TYPE.HEAL_VNF, ns_instance_id) try: + logger.debug("Enter HealNSView::post %s", request.data) + logger.debug("Enter HealNSView:: %s", ns_instance_id) + req_serializer = HealNsReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + + job_id = JobUtil.create_job("VNF", JOB_TYPE.HEAL_VNF, ns_instance_id) NSHealService(ns_instance_id, request.data, job_id).start() + + resp_serializer = NsOperateJobSerializer(data={'jobId': job_id}) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + + logger.debug("Leave HealNSView::post ret=%s", resp_serializer.data) + return Response(data=resp_serializer.data, status=status.HTTP_202_ACCEPTED) except Exception as e: logger.error("Exception in HealNSView: %s", e.message) return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - ret = {'jobId': job_id} - logger.debug("Leave HealNSView::post ret=%s", ret) - return Response(data=ret, status=status.HTTP_202_ACCEPTED) class NSDetailView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: QueryNsRespSerializer(help_text="NS instance", many=True), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error", + status.HTTP_404_NOT_FOUND: "Ns instance does not exist" + } + ) def get(self, request, ns_instance_id): - logger.debug("Enter NSDetailView::get ns(%s)", ns_instance_id) - ns_filter = {"ns_inst_id": ns_instance_id} - ret = GetNSInfoService(ns_filter).get_ns_info() - if not ret: - return Response(status=status.HTTP_404_NOT_FOUND) - logger.debug("Leave NSDetailView::get::ret=%s", ret) - return Response(data=ret, status=status.HTTP_200_OK) + try: + logger.debug("Enter NSDetailView::get ns(%s)", ns_instance_id) + ns_filter = {"ns_inst_id": ns_instance_id} + ret = GetNSInfoService(ns_filter).get_ns_info() + if not ret: + return Response(status=status.HTTP_404_NOT_FOUND) + logger.debug("Leave NSDetailView::get::ret=%s", ret) + resp_serializer = QueryNsRespSerializer(data=ret, many=True) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + return Response(data=resp_serializer.data, status=status.HTTP_200_OK) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Exception in GetNSDetail: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_204_NO_CONTENT: 'successful', + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def delete(self, request, ns_instance_id): - logger.debug("Enter NSDetailView::delete ns(%s)", ns_instance_id) - DeleteNsService(ns_instance_id).do_biz() - return Response(data={}, status=status.HTTP_204_NO_CONTENT) - - -class SwaggerJsonView(APIView): - def get(self, request): - json_file = os.path.join(os.path.dirname(__file__), 'swagger.json') - f = open(json_file) - json_data = json.JSONDecoder().decode(f.read()) - f.close() - return Response(json_data) + try: + logger.debug("Enter NSDetailView::delete ns(%s)", ns_instance_id) + DeleteNsService(ns_instance_id).do_biz() + return Response(data={}, status=status.HTTP_204_NO_CONTENT) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Exception in delete NS: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class NSInstPostDealView(APIView): + @swagger_auto_schema( + request_body=InstNsPostDealReqSerializer(help_text="NS instant post deal"), + responses={ + status.HTTP_202_ACCEPTED: "NS instant post deal success", + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request, ns_instance_id): logger.debug("Enter NSInstPostDealView::post %s, %s", request.data, ns_instance_id) ns_post_status = ignore_case_get(request.data, 'status') ns_status = 'ACTIVE' if ns_post_status == 'true' else 'FAILED' ns_opr_status = 'success' if ns_post_status == 'true' else 'failed' try: + req_serializer = InstNsPostDealReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) NSInstModel.objects.filter(id=ns_instance_id).update(status=ns_status) ServiceBaseInfoModel.objects.filter(service_id=ns_instance_id).update( active_status=ns_status, status=ns_opr_status) @@ -170,14 +283,30 @@ class NSInstPostDealView(APIView): class NSManualScaleView(APIView): + @swagger_auto_schema( + request_body=ManualScaleNsReqSerializer(help_text="NS manual scale"), + responses={ + status.HTTP_202_ACCEPTED: NsOperateJobSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def post(self, request, ns_instance_id): logger.debug("Enter NSManualScaleView::post %s, %s", request.data, ns_instance_id) job_id = JobUtil.create_job("NS", JOB_TYPE.MANUAL_SCALE_VNF, ns_instance_id) try: + req_serializer = ManualScaleNsReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise NSLCMException(req_serializer.errors) + NSManualScaleService(ns_instance_id, request.data, job_id).start() + + resp_serializer = NsOperateJobSerializer(data={'jobId': job_id}) + if not resp_serializer.is_valid(): + raise NSLCMException(resp_serializer.errors) + + return Response(data=resp_serializer.data, status=status.HTTP_202_ACCEPTED) except Exception as e: logger.error(traceback.format_exc()) JobUtil.add_job_status(job_id, 255, 'NS scale failed: %s' % e.message) return Response(data={'error': 'NS scale failed: %s' % ns_instance_id}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - return Response(data={'jobId': job_id}, status=status.HTTP_202_ACCEPTED) diff --git a/lcm/ns/vls/serializers.py b/lcm/ns/vls/serializers.py new file mode 100644 index 00000000..70437ac2 --- /dev/null +++ b/lcm/ns/vls/serializers.py @@ -0,0 +1,39 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class CreateVlReqSerializer(serializers.Serializer): + vlIndex = serializers.CharField(help_text="Index of VL instance", required=True) + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=False, allow_null=True) + context = serializers.CharField(help_text="Context of VL instance", required=False, allow_null=True) + additionalParamForNs = serializers.CharField(help_text="Additional param for NS", required=False, allow_null=True) + + +class CreateVlRespSerializer(serializers.Serializer): + result = serializers.IntegerField(help_text="VL create result(0: success, 1: failed)", required=True) + detail = serializers.CharField(help_text="Detail of result", required=False, allow_null=True) + vlId = serializers.CharField(help_text="ID of VL instance", required=True) + + +class GetVlRespSerializer(serializers.Serializer): + vlId = serializers.CharField(help_text="ID of VL instance", required=False, allow_null=True) + vlName = serializers.CharField(help_text="Name of VL instance", required=False, allow_null=True) + vlStatus = serializers.CharField(help_text="Status of VL instance", required=False, allow_null=True) + + +class DeleteVlRespSerializer(serializers.Serializer): + result = serializers.IntegerField(help_text="VL delete result(0: success)", required=True) + detail = serializers.CharField(help_text="Detail of result", required=False, allow_null=True) diff --git a/lcm/ns/vls/urls.py b/lcm/ns/vls/urls.py index a6f6adc5..597404d8 100644 --- a/lcm/ns/vls/urls.py +++ b/lcm/ns/vls/urls.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from django.conf.urls import patterns, url +from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns from lcm.ns.vls.views import VlView, VlDetailView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/ns/vls$', VlView.as_view()), - url(r'^api/nslcm/v1/ns/vls/(?P<vl_inst_id>[0-9a-zA-Z_-]+)$', VlDetailView.as_view()), - ) +urlpatterns = [ + url(r'^api/nslcm/v1/ns/vls$', VlView.as_view()), + url(r'^api/nslcm/v1/ns/vls/(?P<vl_inst_id>[0-9a-zA-Z_-]+)$', VlDetailView.as_view()), +] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/vls/views.py b/lcm/ns/vls/views.py index 94ae5e21..33b6bb20 100644 --- a/lcm/ns/vls/views.py +++ b/lcm/ns/vls/views.py @@ -15,10 +15,14 @@ from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema from lcm.ns.vls.create_vls import CreateVls from lcm.ns.vls.delete_vls import DeleteVls from lcm.ns.vls.get_vls import GetVls +from lcm.ns.vls.serializers import CreateVlReqSerializer, CreateVlRespSerializer +from lcm.ns.vls.serializers import GetVlRespSerializer +from lcm.ns.vls.serializers import DeleteVlRespSerializer import logging @@ -26,23 +30,72 @@ logger = logging.getLogger(__name__) class VlView(APIView): + @swagger_auto_schema( + request_body=CreateVlReqSerializer(), + responses={ + status.HTTP_201_CREATED: CreateVlRespSerializer() + } + ) def post(self, request): logger.debug("VlsCreateView--post::> %s" % request.data) + + req_serializer = CreateVlReqSerializer(data=request.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + resp = {"result": 1, "detail": req_serializer.errors, "vlId": ""} + return Response(data=resp, status=status.HTTP_201_CREATED) + resp = CreateVls(request.data).do() + + resp_serializer = CreateVlRespSerializer(data=resp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + resp = {"result": 1, "detail": resp_serializer.errors, "vlId": ""} + return Response(data=resp, status=status.HTTP_201_CREATED) + return Response(data=resp, status=status.HTTP_201_CREATED) class VlDetailView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: GetVlRespSerializer(), + status.HTTP_404_NOT_FOUND: "VL instance is not found", + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request, vl_inst_id): logger.debug("VlDetailView--get::> %s" % vl_inst_id) vl_inst_info = GetVls(vl_inst_id).do() if not vl_inst_info: return Response(status=status.HTTP_404_NOT_FOUND) - return Response(status=status.HTTP_200_OK, data={'vlId': vl_inst_id, - 'vlName': vl_inst_info[0].vlinstancename, - 'vlStatus': "active"}) + resp_serializer = GetVlRespSerializer(data={ + 'vlId': vl_inst_id, + 'vlName': vl_inst_info[0].vlinstancename, + 'vlStatus': "active"}) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data={'error': resp_serializer.errors}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return Response(status=status.HTTP_200_OK, data=resp_serializer.data) + + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_202_ACCEPTED: DeleteVlRespSerializer() + } + ) def delete(self, request_paras, vl_inst_id): logger.debug("VlDetailView--delete::> %s" % vl_inst_id) resp = DeleteVls(vl_inst_id).do() + + resp_serializer = DeleteVlRespSerializer(data=resp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + resp = {"result": 0, "detail": resp_serializer.errors} + return Response(data=resp, status=status.HTTP_202_ACCEPTED) + return Response(data=resp, status=status.HTTP_202_ACCEPTED) diff --git a/lcm/ns/vnfs/notify_lcm.py b/lcm/ns/vnfs/notify_lcm.py index 4ec80526..362e347c 100644 --- a/lcm/ns/vnfs/notify_lcm.py +++ b/lcm/ns/vnfs/notify_lcm.py @@ -62,11 +62,12 @@ class NotifyLcm(object): self.exception('unexpected exception') def get_vnfinstid(self, mnfinstid, vnfm_inst_id): + logger.debug("vnfinstid in vnfm is:%s,vnfmid is:%s", mnfinstid, vnfm_inst_id) + logger.debug("mnfinstid=%s, vnfm_inst_id=%s", mnfinstid, vnfm_inst_id) nfinst = NfInstModel.objects.filter(mnfinstid=mnfinstid, vnfm_inst_id=vnfm_inst_id).first() if nfinst: return nfinst.nfinstid - else: - self.exception('vnfinstid not exist') + raise NSLCMException("vnfinstid not exist") def exception(self, error_msg): logger.error('Notify Lcm failed, detail message: %s' % error_msg) diff --git a/lcm/ns/vnfs/scale_vnfs.py b/lcm/ns/vnfs/scale_vnfs.py index fe6c1017..5b4e978a 100644 --- a/lcm/ns/vnfs/scale_vnfs.py +++ b/lcm/ns/vnfs/scale_vnfs.py @@ -34,7 +34,8 @@ class NFManualScaleService(threading.Thread): super(NFManualScaleService, self).__init__() self.vnf_instance_id = vnf_instance_id self.data = data - self.job_id = JobUtil.create_job("NF", JOB_TYPE.MANUAL_SCALE_VNF, vnf_instance_id) + self.job_id = JobUtil.create_job( + "NF", JOB_TYPE.MANUAL_SCALE_VNF, vnf_instance_id) self.scale_vnf_data = '' self.nf_model = {} self.nf_scale_params = [] @@ -63,44 +64,67 @@ class NFManualScaleService(threading.Thread): def get_and_check_params(self): nf_info = NfInstModel.objects.filter(nfinstid=self.vnf_instance_id) if not nf_info: - logger.error('NF instance[id=%s] does not exist' % self.vnf_instance_id) - raise NSLCMException('NF instance[id=%s] does not exist' % self.vnf_instance_id) - logger.debug('vnfd_model = %s, vnf_instance_id = %s' % (nf_info[0].vnfd_model, self.vnf_instance_id)) + logger.error( + 'NF instance[id=%s] does not exist' % + self.vnf_instance_id) + raise NSLCMException( + 'NF instance[id=%s] does not exist' % + self.vnf_instance_id) + logger.debug('vnfd_model = %s, vnf_instance_id = %s' % + (nf_info[0].vnfd_model, self.vnf_instance_id)) self.nf_model = json.loads(nf_info[0].vnfd_model) self.m_nf_inst_id = nf_info[0].mnfinstid self.vnfm_inst_id = nf_info[0].vnfm_inst_id self.scale_vnf_data = ignore_case_get(self.data, 'scaleVnfData') if not self.scale_vnf_data: - logger.error('scaleVnfData parameter does not exist or value incorrect') - raise NSLCMException('scaleVnfData parameter does not exist or value incorrect') - for vnf_data in self.scale_vnf_data: - scale_type = ignore_case_get(vnf_data, 'type') - aspect_id = ignore_case_get(vnf_data, 'aspectId') - number_of_steps = ignore_case_get(vnf_data, 'numberOfSteps') - self.nf_scale_params.append({ - 'type': scale_type, - 'aspectId': aspect_id, - 'numberOfSteps': number_of_steps, - 'additionalParam': {'vnfdModel': self.nf_model} - }) + logger.error( + 'scaleVnfData parameter does not exist or value incorrect') + raise NSLCMException( + 'scaleVnfData parameter does not exist or value incorrect') + + scale_type = ignore_case_get(self.scale_vnf_data, 'type') + aspect_id = ignore_case_get(self.scale_vnf_data, 'aspectId') + number_of_steps = ignore_case_get(self.scale_vnf_data, 'numberOfSteps') + self.nf_scale_params.append({ + 'type': scale_type, + 'aspectId': aspect_id, + 'numberOfSteps': number_of_steps, + 'additionalParam': {'vnfdModel': self.nf_model} + }) def send_nf_scaling_requests(self): for i in range(len(self.nf_scale_params)): - progress_range = [10 + 80 / len(self.nf_scale_params) * i, 10 + 80 / len(self.nf_scale_params) * (i + 1)] - self.send_nf_scaling_request(self.nf_scale_params[i], progress_range) + progress_range = [10 + + 80 / + len(self.nf_scale_params) * + i, 10 + + 80 / + len(self.nf_scale_params) * + (i + + 1)] + self.send_nf_scaling_request( + self.nf_scale_params[i], progress_range) def send_nf_scaling_request(self, scale_param, progress_range): req_param = json.JSONEncoder().encode(scale_param) - rsp = send_nf_scaling_request(self.vnfm_inst_id, self.m_nf_inst_id, req_param) + rsp = send_nf_scaling_request( + self.vnfm_inst_id, self.m_nf_inst_id, req_param) vnfm_job_id = ignore_case_get(rsp, 'jobId') - ret = wait_job_finish(self.vnfm_inst_id, self.job_id, vnfm_job_id, progress_range=progress_range, timeout=1200, - mode='1') + ret = wait_job_finish( + self.vnfm_inst_id, + self.job_id, + vnfm_job_id, + progress_range=progress_range, + timeout=1200, + mode='1') if ret != JOB_MODEL_STATUS.FINISHED: logger.error('[NF scale] nf scale failed') raise NSLCMException("nf scale failed") def update_nf_status(self, status=VNF_STATUS.ACTIVE): - NfInstModel.objects.filter(nfinstid=self.vnf_instance_id).update(status=status) + NfInstModel.objects.filter( + nfinstid=self.vnf_instance_id).update( + status=status) def update_job(self, progress, desc=''): JobUtil.add_job_status(self.job_id, progress, desc) diff --git a/lcm/ns/vnfs/serializers.py b/lcm/ns/vnfs/serializers.py new file mode 100644 index 00000000..7a74b167 --- /dev/null +++ b/lcm/ns/vnfs/serializers.py @@ -0,0 +1,223 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class InstVnfReqSerializer(serializers.Serializer): + vnfIndex = serializers.CharField(help_text="Index of VNF", required=True) + nsInstanceId = serializers.CharField(help_text="ID of NS instance", required=True) + additionalParamForVnf = serializers.CharField(help_text="Additional param for VNF", required=False, allow_null=True) + + +class InstVnfRespSerializer(serializers.Serializer): + vnfInstId = serializers.CharField(help_text="ID of VNF instance", required=True) + jobId = serializers.CharField(help_text="ID of Job", required=True) + + +class GetVnfRespSerializer(serializers.Serializer): + vnfInstId = serializers.CharField(help_text="ID of VNF instance", required=True) + vnfName = serializers.CharField(help_text="Name of VNF instance", required=True) + vnfStatus = serializers.CharField(help_text="Status of VNF instance", required=True) + + +class TerminateVnfReqSerializer(serializers.Serializer): + terminationType = serializers.CharField(help_text="Termination Type", required=False, allow_null=True) + gracefulTerminationTimeout = serializers.CharField(help_text="Graceful Termination Timeout", required=False, allow_null=True) + + +class TerminateVnfRespSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="ID of Job", required=True) + + +class ResourceChangeSerializer(serializers.Serializer): + type = serializers.ChoiceField(help_text="Change Type", choices=["VDU"], required=True) + resourceDefinitionId = serializers.CharField(help_text="Identifier of resource", required=False, allow_null=True) + vdu = serializers.CharField(help_text="Identifier identifier of VDU", required=False, allow_null=True) + + +class GrantVnfReqSerializer(serializers.Serializer): + vnfInstanceId = serializers.CharField(help_text="ID of VNF instance", required=True) + vnfDescriptorId = serializers.CharField(help_text="ID of VNF Descriptor", required=False, allow_null=True) + lifecycleOperation = serializers.ChoiceField( + help_text="Lifecycle Operation", + choices=["Terminal", "Instantiate", "Scalein", "Scaleout", "Scaledown", "Scaleup", "Heal"], + required=True + ) + jobId = serializers.CharField(help_text="ID of Job", required=False, allow_null=True) + addResource = ResourceChangeSerializer(help_text="Add resources", many=True) + removeResource = ResourceChangeSerializer(help_text="Remove resources", many=True) + additionalParam = serializers.DictField( + help_text="Additional parameters passed to the NFVO, specific to the VNF and the LCM operation. The currently interpreted keys are the following: vimId", + child=serializers.CharField(help_text="Additional parameters", allow_blank=True), + required=False, + allow_null=True + ) + + +class AccessinfoSerializer(serializers.Serializer): + tenant = serializers.CharField(help_text="Name of tenant", required=True) + + +class VimSerializer(serializers.Serializer): + vimid = serializers.CharField(help_text="ID of VIM", required=True) + accessinfo = AccessinfoSerializer(help_text="Access Info", required=True) + + +class GrantVnfRespSerializer(serializers.Serializer): + vnfInstanceId = serializers.CharField(help_text="ID of VNF instance", required=False, allow_null=True) + vim = VimSerializer(help_text="VIM Info", required=True) + + +class AffectedVnfcSerializer(serializers.Serializer): + vnfcInstanceId = serializers.CharField(help_text="ID of VNFC instance", required=False, allow_null=True) + vduId = serializers.CharField(help_text="ID of VDU in VNFD", required=False, allow_null=True) + changeType = serializers.ChoiceField( + help_text="Type of Change", + choices=["added", "removed", "modified"], + required=True + ) + vimId = serializers.CharField(help_text="ID of VIM", required=False, allow_null=True) + vmId = serializers.CharField(help_text="ID of virtual machine", required=False, allow_null=True) + vmName = serializers.CharField(help_text="Name of virtual machine", required=False, allow_null=True) + + +class NetworkResourceSerializer(serializers.Serializer): + resourceType = serializers.ChoiceField( + help_text="Type of Resource", + choices=["network", "port"], + required=True + ) + resourceId = serializers.CharField(help_text="ID of network resource", required=False, allow_null=True) + resourceName = serializers.CharField(help_text="Name of network resource", required=False, allow_null=True) + + +class AffectedVirtualLinkSerializer(serializers.Serializer): + vlInstanceId = serializers.CharField(help_text="ID of VL instance", required=False, allow_null=True) + vldId = serializers.CharField(help_text="ID of VLD in VNFD", required=False, allow_null=True) + changeType = serializers.ChoiceField( + help_text="Type of Change", + choices=["added", "removed", "modified"], + required=True + ) + networkResource = NetworkResourceSerializer(help_text="Network Resource", required=False, allow_null=True) + + +class PortResourceSerializer(serializers.Serializer): + vimId = serializers.CharField(help_text="ID of VIM", required=False, allow_null=True) + resourceId = serializers.CharField(help_text="ID of Resource", required=False, allow_null=True) + resourceName = serializers.CharField(help_text="Name of Resource", required=False, allow_null=True) + tenant = serializers.CharField(help_text="ID of Tenant", required=False, allow_null=True) + ipAddress = serializers.CharField(help_text="IP address of port", required=False, allow_null=True) + macAddress = serializers.CharField(help_text="MAC address of port", required=False, allow_null=True) + instId = serializers.CharField(help_text="Instance id of server to which the port is attached to", required=False, allow_null=True) + + +class AffectedCpSerializer(serializers.Serializer): + changeType = serializers.ChoiceField( + help_text="Type of Change", + choices=["added", "removed", "modified"], + required=True + ) + virtualLinkInstanceId = serializers.CharField(help_text="ID of VL instance", required=False, allow_null=True) + cpInstanceId = serializers.CharField(help_text="ID of CP instance", required=False, allow_null=True) + cpdId = serializers.CharField(help_text="ID of CPD in VNFD", required=False, allow_null=True) + ownerType = serializers.CharField(help_text="Type of Owner", required=False, allow_null=True) + ownerId = serializers.CharField(help_text="ID of Owner", required=False, allow_null=True) + portResource = PortResourceSerializer(help_text="Port Resource", required=False, allow_null=True) + + +class AffectedVirtualStorage(serializers.Serializer): + pass + + +class NotifyLcmReqSerializer(serializers.Serializer): + status = serializers.ChoiceField( + help_text="Status of operation", + choices=["result", "start"], + required=True + ) + operation = serializers.ChoiceField( + help_text="Lifecycle Operation", + choices=["Terminal", "Instantiate", "Scalein", "Scaleout", "Scaledown", "Scaleup", "Heal"], + required=True + ) + jobId = serializers.CharField(help_text="ID of Job", required=False, allow_null=True) + vnfdmodule = serializers.CharField(help_text="VNFD Module", required=False, allow_null=True) + affectedVnfc = AffectedVnfcSerializer(help_text="Affected VNFC", many=True) + affectedVl = AffectedVirtualLinkSerializer(help_text="Affected VL", many=True) + affectedCp = AffectedCpSerializer(help_text="Affected CP", many=True) + affectedVirtualStorage = AffectedVirtualStorage(help_text="Affected Virtual Storage(Not supported)", many=True) + + +class ScaleVnfReqSerializer(serializers.Serializer): + type = serializers.ChoiceField( + help_text="Direction of the scaling", + choices=["SCALE_IN", "SCALE_OUT"], + required=True + ) + aspectId = serializers.CharField(help_text="Aspect ID of the VNF that is requested to be scaled", required=False, allow_null=True) + numberOfSteps = serializers.CharField(help_text="Number of scaling steps to be executed as part of this ScaleVnf operation", required=False, allow_null=True) + additionalParam = serializers.DictField( + help_text="Additional parameters passed by the NFVO as input to the scaling process, specific to the VNF being scaled", + child=serializers.CharField(help_text="Additional parameters", allow_blank=True), + required=False, + allow_null=True + ) + + +class ScaleVnfRespSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="ID of Job", required=True) + + +class VerifyVnfReqSerializer(serializers.Serializer): + PackageID = serializers.CharField(help_text="ID of Package", required=True) + + +class VerifyVnfRespSerializer(serializers.Serializer): + jobId = serializers.CharField(help_text="ID of Job", required=True) + + +class VnfmInfoRespSerializer(serializers.Serializer): + vnfmId = serializers.CharField(help_text="ID of VNFM", required=True) + name = serializers.CharField(help_text="Name of VNFM", required=True) + type = serializers.CharField(help_text="Type of VNFM", required=True) + vimId = serializers.CharField(help_text="ID of VIM", required=True) + vendor = serializers.CharField(help_text="Vendor of VNFM", required=False, allow_null=True, allow_blank=True) + version = serializers.CharField(help_text="Version of VNFM", required=False, allow_null=True, allow_blank=True) + description = serializers.CharField(help_text="Description of VNFM", required=False, allow_null=True, allow_blank=True) + certificateUrl = serializers.CharField(help_text="Certificate Url of VNFM", required=True) + url = serializers.CharField(help_text="url of VNFM", required=True) + userName = serializers.CharField(help_text="User Name of VNFM", required=True) + password = serializers.CharField(help_text="Password of VNFM", required=True) + createTime = serializers.CharField(help_text="Create Time of VNFM", required=False, allow_null=True, allow_blank=True) + + +class VimInfoRespSerializer(serializers.Serializer): + vimId = serializers.CharField(help_text="ID of VIM", required=True) + name = serializers.CharField(help_text="Name of VIM", required=True) + url = serializers.CharField(help_text="Url of VIM", required=True) + userName = serializers.CharField(help_text="User Name of VIM", required=True) + password = serializers.CharField(help_text="Password of VIM", required=True) + tenantId = serializers.CharField(help_text="Tenant ID of VIM", required=False, allow_null=True, allow_blank=True) + tenant = serializers.CharField(help_text="Default Tenant of VIM", required=False, allow_null=True, allow_blank=True) + vendor = serializers.CharField(help_text="Vendor of VIM", required=False, allow_null=True, allow_blank=True) + version = serializers.CharField(help_text="Version of VIM", required=False, allow_null=True, allow_blank=True) + description = serializers.CharField(help_text="Description of VIM", required=False, allow_null=True, allow_blank=True) + domain = serializers.CharField(help_text="Domain of VIM", required=False, allow_null=True, allow_blank=True) + type = serializers.CharField(help_text="Type of VIM", required=True) + createTime = serializers.CharField(help_text="Create Time of VIM", required=False, allow_null=True, allow_blank=True) + sslCacert = serializers.CharField(help_text="SSL Cacert of VIM", required=False, allow_null=True, allow_blank=True) + sslInsecure = serializers.CharField(help_text="SSL Insecure of VIM", required=False, allow_null=True, allow_blank=True) + status = serializers.CharField(help_text="Status of VIM", required=False, allow_null=True, allow_blank=True) diff --git a/lcm/ns/vnfs/terminate_nfs.py b/lcm/ns/vnfs/terminate_nfs.py index 3afb5c67..7db2d7c3 100644 --- a/lcm/ns/vnfs/terminate_nfs.py +++ b/lcm/ns/vnfs/terminate_nfs.py @@ -159,7 +159,7 @@ class TerminateVnfs(threading.Thread): cloud_owner, cloud_region_id = split_vim_to_owner_region(vim_id) # query vim_info from aai, get tenant vim_info = get_vim_by_id(vim_id) - tenant_id = vim_info["tenant"] + tenant_id = vim_info["tenantId"] # query vserver instance in aai, get resource_version vserver_info = query_vserver_aai(cloud_owner, cloud_region_id, tenant_id, vserver_id) diff --git a/lcm/ns/vnfs/urls.py b/lcm/ns/vnfs/urls.py index 6e79c4d9..5da83678 100644 --- a/lcm/ns/vnfs/urls.py +++ b/lcm/ns/vnfs/urls.py @@ -11,23 +11,22 @@ # 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. -from django.conf.urls import patterns, url +from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns -from lcm.ns.vnfs.views import NfView, NfDetailView, NfGrant, LcmNotify, NfScaleView, NfVerifyView, NfVnfmInfoView, \ - NfVimInfoView +from lcm.ns.vnfs.views import NfView, NfDetailView, NfGrant +from lcm.ns.vnfs.views import LcmNotify, NfScaleView, NfVerifyView +from lcm.ns.vnfs.views import NfVnfmInfoView, NfVimInfoView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/ns/vnfs$', NfView.as_view()), - url(r'^api/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)$', NfDetailView.as_view()), - url(r'^api/nslcm/v1/ns/grantvnf$', NfGrant.as_view()), - url(r'^api/nslcm/v1/ns/(?P<vnfmid>[0-9a-zA-Z_-]+)' - r'/vnfs/(?P<vnfInstanceId>[0-9a-zA-Z_-]+)/Notify$', - LcmNotify.as_view()), - url(r'^api/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)/scaling$', NfScaleView.as_view()), - url(r'^api/nslcm/v1/vnfonboarding$', NfVerifyView.as_view()), - url(r'^api/nslcm/v1/vnfms/(?P<vnfmid>[0-9a-zA-Z_-]+)', NfVnfmInfoView.as_view()), - url(r'^api/nslcm/v1/vims/(?P<vimid>[0-9a-zA-Z_-]+)', NfVimInfoView.as_view()), - ) +urlpatterns = [ + url(r'^api/nslcm/v1/ns/vnfs$', NfView.as_view()), + url(r'^api/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)$', NfDetailView.as_view()), + url(r'^api/nslcm/v1/ns/grantvnf$', NfGrant.as_view()), + url(r'^api/nslcm/v1/ns/(?P<vnfmid>[0-9a-zA-Z_-]+)/vnfs/(?P<vnfInstanceId>[0-9a-zA-Z_-]+)/Notify$', LcmNotify.as_view()), + url(r'^api/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)/scaling$', NfScaleView.as_view()), + url(r'^api/nslcm/v1/vnfonboarding$', NfVerifyView.as_view()), + url(r'^api/nslcm/v1/vnfms/(?P<vnfmid>[0-9a-zA-Z_-]+)', NfVnfmInfoView.as_view()), + url(r'^api/nslcm/v1/vims/(?P<vimid>[0-9a-zA-Z_-]+)', NfVimInfoView.as_view()), +] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/vnfs/views.py b/lcm/ns/vnfs/views.py index 411ecf05..75d2b500 100644 --- a/lcm/ns/vnfs/views.py +++ b/lcm/ns/vnfs/views.py @@ -18,6 +18,7 @@ import uuid from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema from lcm.ns.vnfs import create_vnfs from lcm.ns.vnfs.create_vnfs import CreateVnfs @@ -31,13 +32,38 @@ from lcm.pub.exceptions import NSLCMException from lcm.pub.msapi.extsys import get_vnfm_by_id, get_vim_by_id from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE from lcm.pub.utils.values import ignore_case_get +from lcm.ns.vnfs.serializers import InstVnfReqSerializer +from lcm.ns.vnfs.serializers import InstVnfRespSerializer +from lcm.ns.vnfs.serializers import GetVnfRespSerializer +from lcm.ns.vnfs.serializers import TerminateVnfReqSerializer +from lcm.ns.vnfs.serializers import TerminateVnfRespSerializer +from lcm.ns.vnfs.serializers import GrantVnfReqSerializer +from lcm.ns.vnfs.serializers import GrantVnfRespSerializer +from lcm.ns.vnfs.serializers import NotifyLcmReqSerializer +from lcm.ns.vnfs.serializers import ScaleVnfReqSerializer +from lcm.ns.vnfs.serializers import ScaleVnfRespSerializer +from lcm.ns.vnfs.serializers import VerifyVnfReqSerializer +from lcm.ns.vnfs.serializers import VerifyVnfRespSerializer +from lcm.ns.vnfs.serializers import VnfmInfoRespSerializer +from lcm.ns.vnfs.serializers import VimInfoRespSerializer logger = logging.getLogger(__name__) class NfView(APIView): + @swagger_auto_schema( + request_body=InstVnfReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: InstVnfRespSerializer() + } + ) def post(self, request): logger.debug("VnfCreateView--post::> %s" % request.data) + + req_serializer = InstVnfReqSerializer(data=request.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + data = {'ns_instance_id': ignore_case_get(request.data, 'nsInstanceId'), 'additional_param_for_ns': ignore_case_get(request.data, 'additionalParamForVnf'), 'additional_param_for_vnf': ignore_case_get(request.data, 'additionalParamForVnf'), @@ -47,21 +73,53 @@ class NfView(APIView): rsp = { "vnfInstId": nf_inst_id, "jobId": job_id} + + resp_serializer = InstVnfRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data=rsp, status=status.HTTP_202_ACCEPTED) class NfDetailView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: GetVnfRespSerializer(), + status.HTTP_404_NOT_FOUND: "VNF not found" + } + ) def get(self, request, vnfinstid): logger.debug("VnfQueryView--get::> %s" % vnfinstid) nf_inst_info = GetVnf(vnfinstid).do_biz() if not nf_inst_info: return Response(status=status.HTTP_404_NOT_FOUND) - return Response(status=status.HTTP_200_OK, - data={'vnfInstId': nf_inst_info[0].nfinstid, 'vnfName': nf_inst_info[0].nf_name, - 'vnfStatus': nf_inst_info[0].status}) + rsp = { + 'vnfInstId': nf_inst_info[0].nfinstid, + 'vnfName': nf_inst_info[0].nf_name, + 'vnfStatus': nf_inst_info[0].status + } + resp_serializer = GetVnfRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + + return Response(status=status.HTTP_200_OK, data=rsp) + + @swagger_auto_schema( + request_body=TerminateVnfReqSerializer(), + responses={ + status.HTTP_200_OK: TerminateVnfRespSerializer(), + status.HTTP_409_CONFLICT: "Inner error" + } + ) def post(self, request_paras, vnfinstid): logger.debug("VnfTerminateView--post::> %s, %s", vnfinstid, request_paras.data) + + req_serializer = TerminateVnfReqSerializer(data=request_paras.data) + if not req_serializer.is_valid(): + logger.error(req_serializer.errors) + vnf_inst_id = vnfinstid terminationType = ignore_case_get(request_paras.data, 'terminationType') gracefulTerminationTimeout = ignore_case_get(request_paras.data, 'gracefulTerminationTimeout') @@ -74,13 +132,29 @@ class NfDetailView(APIView): logger.error(e.message) return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) rsp = {'jobId': job_id} + + resp_serializer = TerminateVnfRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + logger.error(resp_serializer.errors) + return Response(data=rsp, status=status.HTTP_201_CREATED) class NfGrant(APIView): + @swagger_auto_schema( + request_body=GrantVnfReqSerializer(), + responses={ + status.HTTP_201_CREATED: GrantVnfRespSerializer(), + status.HTTP_409_CONFLICT: "Inner error" + } + ) def post(self, request): logger.debug("NfGrant--post::> %s" % request.data) try: + req_serializer = GrantVnfReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + vnf_inst_id = ignore_case_get(request.data, 'vnfInstanceId') job_id = JobUtil.create_job("VNF", JOB_TYPE.GRANT_VNF, vnf_inst_id) rsp = GrantVnfs(request.data, job_id).send_grant_vnf_to_resMgr() @@ -94,6 +168,10 @@ class NfGrant(APIView): } } """ + resp_serializer = GrantVnfRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + return Response(data=rsp, status=status.HTTP_201_CREATED) except Exception as e: logger.error(e.message) @@ -102,10 +180,20 @@ class NfGrant(APIView): class LcmNotify(APIView): - def post(self, request_paras, vnfmid, vnfInstanceId): - logger.debug("LcmNotify--post::> %s" % request_paras.data) + @swagger_auto_schema( + request_body=NotifyLcmReqSerializer(), + responses={ + status.HTTP_201_CREATED: 'successful', + status.HTTP_409_CONFLICT: "Inner error" + } + ) + def post(self, request, vnfmid, vnfInstanceId): + logger.debug("LcmNotify--post::> %s" % request.data) try: - NotifyLcm(vnfmid, vnfInstanceId, request_paras.data).do_biz() + req_serializer = NotifyLcmReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + NotifyLcm(vnfmid, vnfInstanceId, request.data).do_biz() return Response(data={}, status=status.HTTP_201_CREATED) except Exception as e: logger.error(e.message) @@ -113,10 +201,20 @@ class LcmNotify(APIView): class NfScaleView(APIView): - def post(self, request_paras, vnfinstid): - logger.debug("NfScaleView--post::> %s" % request_paras.data) + @swagger_auto_schema( + request_body=ScaleVnfReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: ScaleVnfRespSerializer(), + status.HTTP_409_CONFLICT: "Inner error" + } + ) + def post(self, request, vnfinstid): + logger.debug("NfScaleView--post::> %s" % request.data) try: - NFManualScaleService(vnfinstid, request_paras.data).start() + req_serializer = ScaleVnfReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + NFManualScaleService(vnfinstid, request.data).start() return Response(data={}, status=status.HTTP_202_ACCEPTED) except Exception as e: logger.error(e.message) @@ -124,18 +222,51 @@ class NfScaleView(APIView): class NfVerifyView(APIView): + @swagger_auto_schema( + request_body=VerifyVnfReqSerializer(), + responses={ + status.HTTP_202_ACCEPTED: VerifyVnfRespSerializer(), + status.HTTP_409_CONFLICT: "Inner error" + } + ) def post(self, request): job_id = "VNFSDK_" + str(uuid.uuid4()) logger.debug("NfVerifyView--post::%s> %s", job_id, request.data) - VerifyVnfs(request.data, job_id).start() - return Response(data={"jobId": job_id}, status=status.HTTP_202_ACCEPTED) + try: + req_serializer = VerifyVnfReqSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + + VerifyVnfs(request.data, job_id).start() + + rsp = {"jobId": job_id} + resp_serializer = VerifyVnfRespSerializer(data=rsp) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + + return Response(data=rsp, status=status.HTTP_202_ACCEPTED) + except Exception as e: + logger.error(e.message) + return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) class NfVnfmInfoView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: VnfmInfoRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request, vnfmid): logger.debug("NfVnfmInfoView--get::> %s" % vnfmid) try: vnfm_info = get_vnfm_by_id(vnfmid) + + resp_serializer = VnfmInfoRespSerializer(data=vnfm_info) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + except NSLCMException as e: logger.error(e.message) return Response(data={'error': '%s' % e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) @@ -148,10 +279,22 @@ class NfVnfmInfoView(APIView): class NfVimInfoView(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: VimInfoRespSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request, vimid): logger.debug("NfVimInfoView--get::> %s" % vimid) try: vim_info = get_vim_by_id(vimid) + + resp_serializer = VimInfoRespSerializer(data=vim_info) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + except NSLCMException as e: logger.error(e.message) return Response(data={'error': '%s' % e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/lcm/pub/config/config.py b/lcm/pub/config/config.py index a7213400..b9afbe64 100644 --- a/lcm/pub/config/config.py +++ b/lcm/pub/config/config.py @@ -33,6 +33,11 @@ DB_NAME = "vfcnfvolcm" DB_USER = "vfcnfvolcm" DB_PASSWD = "vfcnfvolcm" +# [MDC] +SERVICE_NAME = "nslcm" +FORWARDED_FOR_FIELDS = ["HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_HOST", + "HTTP_X_FORWARDED_SERVER"] + # [register] REG_TO_MSB_WHEN_START = True REG_TO_MSB_REG_URL = "/api/microservices/v1/services" diff --git a/lcm/pub/database/models.py b/lcm/pub/database/models.py index 306cc510..22a7a6b3 100644 --- a/lcm/pub/database/models.py +++ b/lcm/pub/database/models.py @@ -302,7 +302,7 @@ class ServiceBaseInfoModel(models.Model): active_status = models.CharField(db_column='activeStatus', max_length=20) status = models.CharField(db_column='status', max_length=20) creator = models.CharField(db_column='creator', max_length=50) - create_time = models.BigIntegerField(db_column='createTime', max_length=20) + create_time = models.BigIntegerField(db_column='createTime') class WFPlanModel(models.Model): diff --git a/lcm/pub/msapi/catalog.py b/lcm/pub/msapi/catalog.py index ebefcab1..30828ee7 100644 --- a/lcm/pub/msapi/catalog.py +++ b/lcm/pub/msapi/catalog.py @@ -18,6 +18,7 @@ import logging from lcm.pub.utils.restcall import req_by_msb from lcm.pub.utils.values import ignore_case_get from lcm.pub.exceptions import NSLCMException +from lcm.pub.database.models import NSInstModel logger = logging.getLogger(__name__) @@ -104,3 +105,13 @@ def get_servicetemplate(nsd_id): if stpl.get("id", "") == nsd_id: return stpl return NSLCMException('servicetemplate(%s) does not exist.' % nsd_id) + + +# Gets scaling map json from ns package according to nsd id. +def get_scalingmap_json_package(ns_InstanceId): + csar_id = NSInstModel.objects.filter(id=ns_InstanceId)[0].nspackage_id + downloadUrl = query_csar_from_catalog(csar_id, "packageInfo")["downloadUrl"] + ret = req_by_msb(downloadUrl, 'GET') + scalingmap_json = json.JSONDecoder().decode(ret[1]) + + return scalingmap_json diff --git a/lcm/pub/msapi/extsys.py b/lcm/pub/msapi/extsys.py index e7f83d65..65b7d032 100644 --- a/lcm/pub/msapi/extsys.py +++ b/lcm/pub/msapi/extsys.py @@ -86,7 +86,7 @@ def convert_vim_info(vim_info_aai): "type": ignore_case_get(esr_system_info[0], "type"), "createTime": "", "sslCacert": ignore_case_get(esr_system_info[0], "ssl-cacert"), - "sslInsecure": ignore_case_get(esr_system_info[0], "ssl-insecure"), + "sslInsecure": str(ignore_case_get(esr_system_info[0], "ssl-insecure")), "status": ignore_case_get(esr_system_info[0], "system-status") } return vim_info diff --git a/lcm/pub/tests/__init__.py b/lcm/pub/tests/__init__.py new file mode 100644 index 00000000..4f0b05c3 --- /dev/null +++ b/lcm/pub/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016-2018 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. diff --git a/lcm/pub/tests/test_scaleaspect.py b/lcm/pub/tests/test_scaleaspect.py new file mode 100644 index 00000000..38d4ad29 --- /dev/null +++ b/lcm/pub/tests/test_scaleaspect.py @@ -0,0 +1,398 @@ +# Copyright 2016-2018 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. + +from django.test import TestCase +from lcm.pub.utils.scaleaspect import get_json_data +from lcm.pub.utils.scaleaspect import get_nsdId +from lcm.pub.utils.scaleaspect import get_scale_vnf_data_from_json +from lcm.pub.utils.scaleaspect import get_scale_vnf_data_info_list +from lcm.pub.utils.scaleaspect import set_scacle_vnf_instance_id +from lcm.pub.utils.scaleaspect import check_and_set_params +from lcm.pub.utils.scaleaspect import set_scaleVnfData_type +from lcm.pub.database.models import NfInstModel +from lcm.pub.database.models import NSInstModel +from lcm.pub.msapi import catalog +from lcm.pub.utils.timeutil import now_time +import os +import mock + + +class TestScaleAspect(TestCase): + + def setUp(self): + self.init_scaling_map_json() + self.initInstModel() + + self.init_scale_ns_data() + + self.vnf_scale_info_list = [ + { + "vnfd_id": "nf_zte_cscf", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + }, + { + "vnfd_id": "nf_zte_hss", + "vnf_scaleAspectId": "gsu", + "numberOfSteps": "2" + } + ] + + def init_scale_ns_data(self): + self.ns_scale_aspect = "TIC_EDGE_IMS" + self.ns_scale_steps = "1" + self.ns_scale_direction = "SCALE_IN" + self.scaleNsData = [{ + "scaleNsByStepsData": [ + { + "aspectId": self.ns_scale_aspect, + "numberOfSteps": self.ns_scale_steps, + "scalingDirection": self.ns_scale_direction + } + ] + }] + + self.ns_scale_aspect2 = "TIC_EDGE_HW" + self.ns_scale_steps2 = "4" + self.scaleNsData2 = [{ + "scaleNsByStepsData": [ + { + "aspectId": self.ns_scale_aspect2, + "numberOfSteps": self.ns_scale_steps2, + "scalingDirection": self.ns_scale_direction + } + ] + }] + + def init_scaling_map_json(self): + curdir_path = os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__)))) + filename = curdir_path + "/ns/data/scalemapping.json" + self.scaling_map_json = get_json_data(filename) + + def initInstModel(self): + self.nsd_id = "23" + self.ns_inst_id = "1" + self.ns_name = "ns_1" + self.ns_package_id = "ns_zte" + self.description = "ns_zte" + self.global_customer_id = "global_customer_id" + self.service_type = "service_role" + + NSInstModel(id=self.ns_inst_id, + name=self.ns_name, + nspackage_id=self.ns_package_id, + nsd_id=self.nsd_id, + description=self.description, + status='empty', + lastuptime=now_time(), + global_customer_id=self.global_customer_id, + service_type=self.service_type).save() + + self.nf_inst_id = "231" + self.ns_inst_id = "1" + self.nf_name = "name_1" + self.vnf_id = "1" + self.vnfm_inst_id = "1" + self.package_id = "nf_zte_cscf" + self.nf_uuid = "abc34-345a-de13-ab85-ijs9" + + NfInstModel.objects.create( + nfinstid=self.nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=self.nf_uuid, + package_id=self.package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + # Create a second vnf instance + self.nf_inst_id = "232" + self.package_id = "nf_zte_hss" + self.nf_uuid = "abc34-3g5a-de13-ab85-ijs3" + + NfInstModel.objects.create( + nfinstid=self.nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=self.nf_uuid, + package_id=self.package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + def add_another_nf_instance(self): + # Create a third vnf instance + nf_inst_id = "233" + package_id = "nf_zte_hss" + nf_uuid = "ab34-3g5j-de13-ab85-ij93" + + NfInstModel.objects.create( + nfinstid=nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=nf_uuid, + package_id=package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + def add_new_vnf_instance(self): + # Create a third vnf instance + nf_inst_id = "241" + package_id = "nf_hw_cscf" + nf_uuid = "ab34-3g5j-de13-aa85-ij93" + + NfInstModel.objects.create( + nfinstid=nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=nf_uuid, + package_id=package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + # Create a third vnf instance + nf_inst_id = "242" + package_id = "nf_hw_hss" + nf_uuid = "ab34-3g5j-de13-aa85-id93" + + NfInstModel.objects.create( + nfinstid=nf_inst_id, + nf_name=self.nf_name, + vnf_id=self.vnf_id, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + max_cpu='14', + max_ram='12296', + max_hd='101', + max_shd="20", + max_net=10, + status='active', + mnfinstid=nf_uuid, + package_id=package_id, + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + def tearDown(self): + NSInstModel().clean() + NfInstModel().clean() + + def test_get_and_check_params(self): + aspect, numberOfSteps, scale_type = check_and_set_params( + self.scaleNsData, "1") + self.assertEqual(aspect, self.ns_scale_aspect) + self.assertEqual(numberOfSteps, self.ns_scale_steps) + self.assertEqual(scale_type, self.ns_scale_direction) + + def test_get_scale_vnf_data_from_json(self): + vnf_data_package = get_scale_vnf_data_from_json( + self.scaling_map_json, "23", "TIC_EDGE_IMS", "1") + self.assertIsNotNone(vnf_data_package) + self.assertEqual(2, vnf_data_package.__len__()) + self.assertIsNotNone(vnf_data_package) + self.assertEqual(2, vnf_data_package.__len__()) + self.assertEqual("nf_zte_cscf", vnf_data_package[0]["vnfd_id"]) + self.assertEqual("1", vnf_data_package[0]["numberOfSteps"]) + self.assertEqual("gsu", vnf_data_package[0]["vnf_scaleAspectId"]) + self.assertEqual("nf_zte_hss", vnf_data_package[1]["vnfd_id"]) + self.assertEqual("3", vnf_data_package[1]["numberOfSteps"]) + self.assertEqual("gpu", vnf_data_package[1]["vnf_scaleAspectId"]) + + def test_get_scale_vnf_data_from_json_2(self): + vnf_data_package = get_scale_vnf_data_from_json( + self.scaling_map_json, "23", "TIC_EDGE_IMS", "2") + self.assertIsNotNone(vnf_data_package) + self.assertEqual(2, vnf_data_package.__len__()) + self.assertEqual("nf_zte_cscf", vnf_data_package[0]["vnfd_id"]) + self.assertEqual("2", vnf_data_package[0]["numberOfSteps"]) + self.assertEqual("mpu", vnf_data_package[0]["vnf_scaleAspectId"]) + self.assertEqual("nf_zte_hss", vnf_data_package[1]["vnfd_id"]) + self.assertEqual("4", vnf_data_package[1]["numberOfSteps"]) + self.assertEqual("mpu", vnf_data_package[1]["vnf_scaleAspectId"]) + + def test_set_scacle_vnf_instance_id(self): + result = set_scacle_vnf_instance_id(self.vnf_scale_info_list) + self.assertEqual(2, result.__len__()) + self.assertEqual(result[0]["numberOfSteps"], + self.vnf_scale_info_list[0]["numberOfSteps"]) + self.assertEqual( + result[0]["vnf_scaleAspectId"], + self.vnf_scale_info_list[0]["vnf_scaleAspectId"]) + self.assertEqual(result[1]["numberOfSteps"], + self.vnf_scale_info_list[1]["numberOfSteps"]) + self.assertEqual( + result[1]["vnf_scaleAspectId"], + self.vnf_scale_info_list[1]["vnf_scaleAspectId"]) + self.assertEqual("231", result[0]["vnfInstanceId"]) + self.assertEqual("232", result[1]["vnfInstanceId"]) + self.assertNotIn("vnfd_id", result[0]) + self.assertNotIn("vnfd_id", result[1]) + + def test_set_scacle_vnf_instance_id_2(self): + vnf_scale_info_list = [ + { + "vnfd_id": "error1", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + }, + { + "vnfd_id": "nf_zte_hss", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + } + ] + result = set_scacle_vnf_instance_id(vnf_scale_info_list) + self.assertEqual(1, result.__len__()) + self.assertEqual( + result[0]["numberOfSteps"], + vnf_scale_info_list[0]["numberOfSteps"]) + self.assertEqual( + result[0]["vnf_scaleAspectId"], + vnf_scale_info_list[0]["vnf_scaleAspectId"]) + self.assertEqual("232", result[0]["vnfInstanceId"]) + self.assertNotIn("vnfd_id", result[0]) + + def test_set_scacle_vnf_instance_id_3(self): + vnf_scale_info_list = [ + { + "vnfd_id": "error1", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + }, + { + "vnfd_id": "error2", + "vnf_scaleAspectId": "gsu", + "numberOfSteps": "1" + } + ] + result = set_scacle_vnf_instance_id(vnf_scale_info_list) + self.assertEqual(0, result.__len__()) + + def test_set_scacle_vnf_instance_id_4(self): + self.add_another_nf_instance() + result = set_scacle_vnf_instance_id(self.vnf_scale_info_list) + self.assertEqual(3, result.__len__()) + self.assertEqual("231", result[0]["vnfInstanceId"]) + self.assertEqual("232", result[1]["vnfInstanceId"]) + self.assertEqual("233", result[2]["vnfInstanceId"]) + + def test_set_scaleVnfData_type(self): + vnf_scale_list = set_scacle_vnf_instance_id(self.vnf_scale_info_list) + result = set_scaleVnfData_type(vnf_scale_list, self.ns_scale_direction) + self.assertEqual(2, result.__len__()) + self.assertNotIn("scaleByStepData", result) + self.assertEqual( + self.ns_scale_direction, + result[0]["scaleByStepData"]["type"]) + self.assertEqual("mpu", result[0]["scaleByStepData"]["aspectId"]) + self.assertNotIn("vnf_scaleAspectId", result[0]["scaleByStepData"]) + self.assertEqual("1", result[0]["scaleByStepData"]["numberOfSteps"]) + self.assertEqual( + self.ns_scale_direction, + result[1]["scaleByStepData"]["type"]) + self.assertEqual("gsu", result[1]["scaleByStepData"]["aspectId"]) + self.assertNotIn("vnf_scaleAspectId", result[1]["scaleByStepData"]) + self.assertEqual("2", result[1]["scaleByStepData"]["numberOfSteps"]) + + def test_get_nsdId(self): + nsd_id = get_nsdId("1") + self.assertEqual("23", nsd_id) + + @mock.patch.object(catalog, 'get_scalingmap_json_package') + def test_get_scale_vnf_data_info_list( + self, mock_get_scalingmap_json_package): + mock_get_scalingmap_json_package.return_value = self.scaling_map_json + + scale_vnf_data = get_scale_vnf_data_info_list(self.scaleNsData, "1") + self.assertIsNotNone(scale_vnf_data) + self.assertEqual(2, scale_vnf_data.__len__()) + + @mock.patch.object(catalog, 'get_scalingmap_json_package') + def test_get_scale_vnf_data_info_list_2( + self, mock_get_scalingmap_json_package): + mock_get_scalingmap_json_package.return_value = self.scaling_map_json + + scale_vnf_data = None + is_exception_caught = False + try: + scale_vnf_data = get_scale_vnf_data_info_list( + self.scaleNsData2, "1") + except Exception: + is_exception_caught = True + self.assertTrue(is_exception_caught) + self.assertIsNone(scale_vnf_data) + + @mock.patch.object(catalog, 'get_scalingmap_json_package') + def test_get_scale_vnf_data_info_list_3( + self, mock_get_scalingmap_json_package): + mock_get_scalingmap_json_package.return_value = self.scaling_map_json + self.add_new_vnf_instance() + + scale_vnf_data = None + is_exception_caught = False + try: + scale_vnf_data = get_scale_vnf_data_info_list( + self.scaleNsData2, "1") + except Exception: + is_exception_caught = True + self.assertFalse(is_exception_caught) + self.assertEqual(2, scale_vnf_data.__len__()) diff --git a/lcm/pub/utils/jobutil.py b/lcm/pub/utils/jobutil.py index 7a8c5edc..69fa2866 100644 --- a/lcm/pub/utils/jobutil.py +++ b/lcm/pub/utils/jobutil.py @@ -113,7 +113,7 @@ class JobUtil(object): job_status.status = "error" job_status.descp = status_decs - job_status.errcode = error_code + job_status.errcode = error_code if error_code else "0" job_status.addtime = datetime.datetime.now().strftime('%Y-%m-%d %X') job_status.save() logger.debug("Add a new job status, jobid=%s, indexid=%d," diff --git a/lcm/pub/utils/scaleaspect.py b/lcm/pub/utils/scaleaspect.py index 0cd92168..cf78092f 100644 --- a/lcm/pub/utils/scaleaspect.py +++ b/lcm/pub/utils/scaleaspect.py @@ -15,19 +15,23 @@ import json import logging import os +import copy +from lcm.pub.database.models import NfInstModel +from lcm.pub.database.models import NSInstModel +from lcm.ns.vnfs.const import VNF_STATUS +from lcm.pub.msapi import catalog + logger = logging.getLogger(__name__) SCALE_TYPE = ("SCALE_NS", "SCALE_VNF") scale_vnf_data_mapping = { "vnfInstanceId": "", - "scaleByStepData": [ - { - "type": "", - "aspectId": "", - "numberOfSteps": "" - } - ] + "scaleByStepData": { + "type": "", + "aspectId": "", + "numberOfSteps": "" + } } @@ -49,7 +53,9 @@ def mapping_conv(keyword_map, rest_return): for param in keyword_map: if keyword_map[param]: if isinstance(keyword_map[param], dict): - resp_data[param] = mapping_conv(keyword_map[param], ignorcase_get(rest_return, param)) + resp_data[param] = mapping_conv( + keyword_map[param], ignorcase_get( + rest_return, param)) else: resp_data[param] = ignorcase_get(rest_return, param) return resp_data @@ -62,15 +68,33 @@ def get_vnf_scale_info(filename, ns_instanceId, aspect, step): ns_scale_option = scale_options[i] if (ignorcase_get(ns_scale_option, "ns_instanceId") == ns_instanceId) \ and (ignorcase_get(ns_scale_option, "ns_scale_aspect") == aspect): - ns_scale_info_list = ignorcase_get(ns_scale_option, "ns_scale_info_list") + ns_scale_info_list = ignorcase_get( + ns_scale_option, "ns_scale_info_list") for j in range(ns_scale_info_list.__len__()): ns_scale_info = ns_scale_info_list[j] if ns_scale_info["step"] == step: - return ns_scale_info["vnf_scale_list"] + return ns_scale_info["vnf_scale_info"] return None +def get_vnf_instance_id_list(vnfd_id): + kwargs = {} + kwargs['package_id'] = vnfd_id + kwargs['status'] = VNF_STATUS.ACTIVE + + nf_model_list = NfInstModel.objects.filter(**kwargs) + vnf_instance_id_list = list() + nf_model_len = nf_model_list.__len__() + if nf_model_len == 0: + logger.error("No VNF instances found(vnfd_id=%s)" % vnfd_id) + else: + for i in range(nf_model_len): + vnf_instance_id_list.append(nf_model_list[i].nfinstid) + + return vnf_instance_id_list + + def get_json_data(filename): f = open(filename) json_str = f.read() @@ -80,35 +104,18 @@ def get_json_data(filename): def check_scale_list(vnf_scale_list, ns_instanceId, aspect, step): - if vnf_scale_list is None: - logger.debug("The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." - % (ns_instanceId, aspect, step)) - raise Exception("The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." - % (ns_instanceId, aspect, step)) + if vnf_scale_list is None or vnf_scale_list.__len__() == 0: + logger.debug( + "The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." % + (ns_instanceId, aspect, step)) + raise Exception( + "The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." % + (ns_instanceId, aspect, step)) else: return vnf_scale_list -def set_scaleVnfData_type(vnf_scale_list, scale_type): - logger.debug("vnf_scale_list = %s, type = %s" % (vnf_scale_list, scale_type)) - scaleVnfDataList = [] - if vnf_scale_list is not None: - for i in range(vnf_scale_list.__len__()): - scaleVnfData = scale_vnf_data_mapping - scaleVnfData["vnfInstanceId"] = get_vnfInstanceIdByName(vnf_scale_list[i]["vnfInstanceId"]) - scaleVnfData["scaleByStepData"][0]["type"] = scale_type - scaleVnfData["scaleByStepData"][0]["aspectId"] = vnf_scale_list[i]["vnf_scaleAspectId"] - scaleVnfData["scaleByStepData"][0]["numberOfSteps"] = vnf_scale_list[i]["numberOfSteps"] - scaleVnfDataList.append(scaleVnfData) - logger.debug("scaleVnfDataList = %s" % scaleVnfDataList) - return scaleVnfDataList - - -def get_vnfInstanceIdByName(name): - return name - - -def get_vnf_data(filename, ns_instanceId, aspect, step, scale_type): +def get_scale_vnf_data_list(filename, ns_instanceId, aspect, step, scale_type): vnf_scale_list = get_vnf_scale_info(filename, ns_instanceId, aspect, step) check_scale_list(vnf_scale_list, ns_instanceId, aspect, step) @@ -116,31 +123,126 @@ def get_vnf_data(filename, ns_instanceId, aspect, step, scale_type): logger.debug("scaleVnfDataList = %s" % scaleVnfDataList) return scaleVnfDataList - # return Response(data={'error': e.message},status=status.HTTP_204_NO_CONTENT) - # return Response(data={'success': 'success'},status=status.HTTP_200_OK) +# Get the nsd id according to the ns instance id. +def get_nsdId(ns_instanceId): + if NSInstModel.objects.filter(id=ns_instanceId): + nsd_id = NSInstModel.objects.filter(id=ns_instanceId)[0].nsd_id + return nsd_id + + return None -def get_and_check_params(scaleNsData, ns_InstanceId): +def check_and_set_params(scaleNsData, ns_InstanceId): if scaleNsData is None: - pass - # raise NSLCMException("Error! scaleNsData in the request is Empty!") + raise Exception("Error! scaleNsData in the request is Empty!") - scaleNsByStepsData = scaleNsData[0]["scaleNsByStepsData"] + scaleNsByStepsData = scaleNsData[0]["scaleNsByStepsData"][0] if scaleNsByStepsData is None: - pass - # raise NSLCMException("Error! scaleNsByStepsData in the request is Empty!") + raise Exception("Error! scaleNsByStepsData in the request is Empty!") - aspect = scaleNsByStepsData[0]["aspectId"] - numberOfSteps = scaleNsByStepsData[0]["numberOfSteps"] - scale_type = scaleNsByStepsData[0]["scalingDirection"] + aspect = scaleNsByStepsData["aspectId"] + numberOfSteps = scaleNsByStepsData["numberOfSteps"] + scale_type = scaleNsByStepsData["scalingDirection"] - return ns_InstanceId, aspect, numberOfSteps, scale_type + return aspect, numberOfSteps, scale_type def get_scale_vnf_data(scaleNsData, ns_InstanceId): - curdir_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + curdir_path = os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__)))) filename = curdir_path + "/ns/data/scalemapping.json" logger.debug("filename = %s" % filename) - ns_InstanceId, aspect, numberOfSteps, scale_type = get_and_check_params(scaleNsData, ns_InstanceId) - return get_vnf_data(filename, ns_InstanceId, aspect, numberOfSteps, scale_type) + aspect, numberOfSteps, scale_type = check_and_set_params( + scaleNsData, ns_InstanceId) + return get_scale_vnf_data_list( + filename, + ns_InstanceId, + aspect, + numberOfSteps, + scale_type) + + +# Get scaling vnf data info list according to the ns instance id and request ScaleNsData. +def get_scale_vnf_data_info_list(scaleNsData, ns_InstanceId): + # Gets the nsd id accordign to the ns instance id. + nsd_id = get_nsdId(ns_InstanceId) + + # Gets the scalingmap json data from the package according to the ns instance id. + scalingmap_json = catalog.get_scalingmap_json_package(ns_InstanceId) + + # Gets and checks the values of parameters. + aspect, numberOfSteps, scale_type = check_and_set_params( + scaleNsData, ns_InstanceId) + + # Firstly, gets the scaling vnf data info list from the scaling map json data. + scale_vnf_data_info_list_from_json = get_scale_vnf_data_from_json(scalingmap_json, nsd_id, aspect, numberOfSteps) + check_scale_list(scale_vnf_data_info_list_from_json, ns_InstanceId, aspect, numberOfSteps) + + # Secondly, adds the property of vnfInstanceId to the list according to the vnfd id. + scale_vnf_data_info_list = set_scacle_vnf_instance_id(scale_vnf_data_info_list_from_json) + check_scale_list(scale_vnf_data_info_list, ns_InstanceId, aspect, numberOfSteps) + + # Lastly, adds the property of type to the list acoording to the request ScaleNsData. + scale_vnf_data_info_list = set_scaleVnfData_type(scale_vnf_data_info_list, scale_type) + check_scale_list(scale_vnf_data_info_list, ns_InstanceId, aspect, numberOfSteps) + + return scale_vnf_data_info_list + + +# Get the vnf scaling info from the scaling_map.json according to the ns package id. +def get_scale_vnf_data_from_json(scalingmap_json, nsd_id, aspect, step): + scale_options = ignorcase_get(scalingmap_json, "scale_options") + for i in range(scale_options.__len__()): + ns_scale_option = scale_options[i] + if (ignorcase_get(ns_scale_option, "nsd_id") == nsd_id) and ( + ignorcase_get(ns_scale_option, "ns_scale_aspect") == aspect): + ns_scale_info_list = ignorcase_get( + ns_scale_option, "ns_scale_info") + for j in range(ns_scale_info_list.__len__()): + ns_scale_info = ns_scale_info_list[j] + if ns_scale_info["step"] == step: + vnf_scale_info_list = ns_scale_info["vnf_scale_info"] + + return vnf_scale_info_list + + logger.error("get_scale_vnf_data_from_json method retuan null") + return None + + +# Gets the vnf instance id according to the vnfd_id and modify the list of scaling vnf info accrodingly. +def set_scacle_vnf_instance_id(vnf_scale_info_list): + scale_vnf_data_info_list = [] + for i in range(vnf_scale_info_list.__len__()): + vnf_scale_info = vnf_scale_info_list[i] + vnfd_id = vnf_scale_info["vnfd_id"] + vnf_instance_id_list = get_vnf_instance_id_list(vnfd_id) + index = 0 + while index < vnf_instance_id_list.__len__(): + copy_vnf_scale_info = copy.deepcopy(vnf_scale_info) + copy_vnf_scale_info.pop("vnfd_id") + copy_vnf_scale_info["vnfInstanceId"] = vnf_instance_id_list[index] + index += 1 + scale_vnf_data_info_list.append(copy_vnf_scale_info) + + return scale_vnf_data_info_list + + +# Sets the scaling type of vnf data info list. +def set_scaleVnfData_type(vnf_scale_list, scale_type): + logger.debug( + "vnf_scale_list = %s, type = %s" % + (vnf_scale_list, scale_type)) + scaleVnfDataList = [] + if vnf_scale_list is not None: + for i in range(vnf_scale_list.__len__()): + scaleVnfData = copy.deepcopy(scale_vnf_data_mapping) + scaleVnfData["vnfInstanceId"] = vnf_scale_list[i]["vnfInstanceId"] + scaleVnfData["scaleByStepData"]["type"] = scale_type + scaleVnfData["scaleByStepData"]["aspectId"] = vnf_scale_list[i]["vnf_scaleAspectId"] + scaleVnfData["scaleByStepData"]["numberOfSteps"] = vnf_scale_list[i]["numberOfSteps"] + scaleVnfDataList.append(scaleVnfData) + logger.debug("scaleVnfDataList = %s" % scaleVnfDataList) + return scaleVnfDataList diff --git a/lcm/samples/serializers.py b/lcm/samples/serializers.py new file mode 100644 index 00000000..c70561fa --- /dev/null +++ b/lcm/samples/serializers.py @@ -0,0 +1,19 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class RecordCountSerializer(serializers.Serializer): + count = serializers.CharField(help_text="Count of record", required=True) diff --git a/lcm/samples/views.py b/lcm/samples/views.py index cec3209e..5889aaca 100644 --- a/lcm/samples/views.py +++ b/lcm/samples/views.py @@ -18,8 +18,12 @@ import traceback from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema + from lcm.pub.database import models +from lcm.samples.serializers import RecordCountSerializer + logger = logging.getLogger(__name__) @@ -27,6 +31,12 @@ class SampleList(APIView): """ List all samples. """ + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: "Status is active" + } + ) def get(self, request, format=None): count = len(models.NSDModel.objects.filter()) logger.debug("get, count of NSDModel is %s", count) @@ -34,6 +44,13 @@ class SampleList(APIView): class TablesList(APIView): + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_204_NO_CONTENT: 'successful', + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def delete(self, request, modelName): logger.debug("Start delete model %s", modelName) try: @@ -47,13 +64,24 @@ class TablesList(APIView): return Response(data={"error": "failed"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(data={}, status=status.HTTP_204_NO_CONTENT) + @swagger_auto_schema( + request_body=None, + responses={ + status.HTTP_200_OK: RecordCountSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) def get(self, request, modelName): logger.debug("Get model %s", modelName) count = 0 try: model_obj = eval("models.%s.objects" % modelName) count = len(model_obj.filter()) - except: + resp_serializer = RecordCountSerializer(data={"count": count}) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + return Response(data=resp_serializer.data, status=status.HTTP_200_OK) + except Exception as e: + logger.error(e.message) logger.error(traceback.format_exc()) - return Response(data={"error": "failed"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - return Response(data={"count": count}, status=status.HTTP_200_OK) + return Response(data={"error": e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/lcm/settings.py b/lcm/settings.py index 9a095b3f..e5f680bb 100644 --- a/lcm/settings.py +++ b/lcm/settings.py @@ -19,7 +19,10 @@ import redisco from lcm.pub.config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD from lcm.pub.config.config import DB_NAME, DB_IP, DB_USER, DB_PASSWD, DB_PORT -from lcm.pub.config import config +from lcm.pub.config import config as pub_config +from logging import config as log_config +from onaplogging import monkey +monkey.patch_all() # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -33,7 +36,7 @@ SECRET_KEY = '3o-wney!99y)^h3v)0$j16l9=fdjxcb+a8g+q3tfbahcnu2b0o' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['*'] # Application definition @@ -45,10 +48,31 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'rest_framework', 'lcm.pub.database', - 'lcm.samples', - 'lcm.swagger' + 'drf_yasg' ] +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +SWAGGER_SETTINGS = { + 'LOGIN_URL': '/admin/login', + 'LOGOUT_URL': '/admin/logout', + 'DEFAULT_INFO': 'lcm.urls.swagger_info' +} + MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -58,6 +82,7 @@ MIDDLEWARE_CLASSES = [ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'lcm.middleware.LogContextMiddleware', ] ROOT_URLCONF = 'lcm.urls' @@ -102,42 +127,46 @@ STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ] -config.CATALOG_ROOT_PATH = os.path.join(STATICFILES_DIRS[0], "catalog") -config.CATALOG_URL_PATH = "static/catalog" - -LOGGING = { - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'standard': { - 'format': '%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s', - }, - }, - 'filters': { - }, - 'handlers': { - 'lcm_handler': { - 'level': 'DEBUG', - 'class': 'logging.handlers.RotatingFileHandler', - 'filename': os.path.join(BASE_DIR, 'logs/runtime_lcm.log'), - 'formatter': 'standard', - 'maxBytes': 1024 * 1024 * 50, - 'backupCount': 5, - }, - }, - - 'loggers': { - 'lcm': { - 'handlers': ['lcm_handler'], - 'level': 'DEBUG', - 'propagate': False - }, - } -} +pub_config.CATALOG_ROOT_PATH = os.path.join(STATICFILES_DIRS[0], "catalog") +pub_config.CATALOG_URL_PATH = "static/catalog" +# +# LOGGING = { +# 'version': 1, +# 'disable_existing_loggers': True, +# 'formatters': { +# 'standard': { +# 'format': '%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s', +# }, +# }, +# 'filters': { +# }, +# 'handlers': { +# 'lcm_handler': { +# 'level': 'DEBUG', +# 'class': 'logging.handlers.RotatingFileHandler', +# 'filename': os.path.join(BASE_DIR, 'logs/runtime_lcm.log'), +# 'formatter': 'standard', +# 'maxBytes': 1024 * 1024 * 50, +# 'backupCount': 5, +# }, +# }, +# +# 'loggers': { +# 'lcm': { +# 'handlers': ['lcm_handler'], +# 'level': 'DEBUG', +# 'propagate': False +# }, +# } +# } +LOGGING_CONFIG = None +# yaml configuration of logging +LOGGING_FILE = os.path.join(BASE_DIR, 'lcm/log.yml') +log_config.yamlConfig(filepath=LOGGING_FILE, watchDog=True) if 'test' in sys.argv: - config.REG_TO_MSB_WHEN_START = False - config.DEPLOY_WORKFLOW_WHEN_START = False + pub_config.REG_TO_MSB_WHEN_START = False + pub_config.DEPLOY_WORKFLOW_WHEN_START = False DATABASES = {} DATABASES['default'] = { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/lcm/swagger/urls.py b/lcm/swagger/urls.py index c3b8ad62..cb10cad6 100644 --- a/lcm/swagger/urls.py +++ b/lcm/swagger/urls.py @@ -11,13 +11,17 @@ # 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. -from django.conf.urls import patterns, url -from rest_framework.urlpatterns import format_suffix_patterns +from django.conf.urls import url +from lcm.swagger.views import SchemaView from lcm.swagger.views import SwaggerJsonView -urlpatterns = patterns('', - url(r'^api/nslcm/v1/swagger.json$', SwaggerJsonView.as_view()) - ) - -urlpatterns = format_suffix_patterns(urlpatterns) +urlpatterns = [ + url(r'^api/nslcm/v1/swagger.json$', SwaggerJsonView.as_view()), + url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'), + url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'), + url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'), + url(r'^cached/redoc/$', SchemaView.with_ui('redoc', cache_timeout=None), name='cschema-redoc'), +] diff --git a/lcm/swagger/vfc.nslcm.swagger.json b/lcm/swagger/vfc.nslcm.swagger.json index e8a83905..891e1789 100644 --- a/lcm/swagger/vfc.nslcm.swagger.json +++ b/lcm/swagger/vfc.nslcm.swagger.json @@ -166,7 +166,7 @@ }
}
},
- "/ns/{ns_instance_id}/heal": {
+ "/ns/{nsInstanceId}/heal": {
"post": {
"tags": [
"ns"
@@ -179,7 +179,7 @@ "required": true,
"type": "string",
"description": "Identifier of the NS instance.",
- "name": "ns_instance_id",
+ "name": "nsInstanceId",
"in": "path"
},
{
@@ -208,7 +208,7 @@ }
}
},
- "/ns/{ns_instance_id}/terminate": {
+ "/ns/{nsInstanceId}/terminate": {
"post": {
"tags": [
"ns"
@@ -221,7 +221,7 @@ "required": true,
"type": "string",
"description": "Identifier of the NS instance.",
- "name": "ns_instance_id",
+ "name": "nsInstanceId",
"in": "path"
},
{
@@ -247,7 +247,7 @@ }
}
},
- "/ns/{ns_instance_id}": {
+ "/ns/{nsInstanceId}": {
"get": {
"tags": [
"ns"
@@ -255,7 +255,15 @@ "summary": "ns get",
"description": "ns get",
"operationId": "ns_instance_get",
- "parameters": [],
+ "parameters": [
+ {
+ "required": true,
+ "type": "string",
+ "description": "Identifier of the NS instance.",
+ "name": "nsInstanceId",
+ "in": "path"
+ }
+ ],
"responses": {
"200": {
"description": "successful operation",
@@ -277,7 +285,7 @@ "required": true,
"type": "string",
"description": "Identifier of the NS instance.",
- "name": "ns_instance_id",
+ "name": "nsInstanceId",
"in": "path"
}
],
diff --git a/lcm/swagger/vfc.vnfdriver.swagger.json b/lcm/swagger/vfc.vnfdriver.swagger.json index 52f94c5e..fc35adbd 100644 --- a/lcm/swagger/vfc.vnfdriver.swagger.json +++ b/lcm/swagger/vfc.vnfdriver.swagger.json @@ -951,17 +951,30 @@ "extVirtualLinkInfo":{ "type": "object", "properties": { + "resourceSubnetId": { + "type": "string", + "description": "The provider id of the subnet" + }, "vlInstanceId": { - "type": "string" + "type": "string", + "description" : "The identifier of the virtual link" }, - "networkId": { - "type": "string" + "resourceId": { + "type": "string", + "description": "The provider id of the network" }, "cpdId": { - "type": "string" + "type": "string", + "description": "The identifier of the connection point descriptor" }, "vim": { - "$ref": "#/definitions/vimInfo" + "type": "object", + "properties": { + "vimid": { + "type": "string", + "description": "The identifier of the VIM" + } + } } } }, diff --git a/lcm/swagger/views.py b/lcm/swagger/views.py index e78ecead..5f087f8c 100644 --- a/lcm/swagger/views.py +++ b/lcm/swagger/views.py @@ -17,10 +17,19 @@ import os from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework import permissions +from drf_yasg.views import get_schema_view logger = logging.getLogger(__name__) +SchemaView = get_schema_view( + validators=['ssv', 'flex'], + public=True, + permission_classes=(permissions.AllowAny,), +) + + class SwaggerJsonView(APIView): def get(self, request): diff --git a/lcm/urls.py b/lcm/urls.py index 5e647260..eb82f083 100644 --- a/lcm/urls.py +++ b/lcm/urls.py @@ -13,9 +13,21 @@ # limitations under the License. from django.conf.urls import include, url +from drf_yasg import openapi + 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 +swagger_info = openapi.Info( + title="vfc-nfvo-lcm API", + default_version='v1', + description=""" + +The `swagger-ui` view can be found [here](/cached/swagger). +The `ReDoc` view can be found [here](/cached/redoc). +The swagger YAML document can be found [here](/cached/swagger.yaml).""" +) + urlpatterns = [ url(r'^', include('lcm.samples.urls')), url(r'^', include('lcm.packages.urls')), @@ -26,6 +38,7 @@ urlpatterns = [ url(r'^', include('lcm.jobs.urls')), url(r'^', include('lcm.workflows.urls')), url(r'^', include('lcm.swagger.urls')), + url(r'^', include('lcm.v2.urls')), ] # regist to MSB when startup diff --git a/lcm/v2/__init__.py b/lcm/v2/__init__.py new file mode 100644 index 00000000..342c2a8c --- /dev/null +++ b/lcm/v2/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 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. diff --git a/lcm/v2/grant_vnf.py b/lcm/v2/grant_vnf.py new file mode 100644 index 00000000..a3ba37f9 --- /dev/null +++ b/lcm/v2/grant_vnf.py @@ -0,0 +1,35 @@ +# Copyright 2018 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 json +import logging +import uuid + +logger = logging.getLogger(__name__) + + +class GrantVnf(object): + def __init__(self, grant_data): + self.data = grant_data + + def exec_grant(self): + if isinstance(self.data, (unicode, str)): + self.data = json.JSONDecoder().decode(self.data) + grant_resp = { + "id": str(uuid.uuid4()), + "vnfInstanceId": self.data.get("vnfInstanceId"), + "vnfLcmOpOccId": self.data.get("vnfLcmOpOccId") + } + logger.debug("grant_resp=%s", grant_resp) + return grant_resp diff --git a/lcm/v2/serializers.py b/lcm/v2/serializers.py new file mode 100644 index 00000000..557c603c --- /dev/null +++ b/lcm/v2/serializers.py @@ -0,0 +1,1001 @@ +# Copyright 2018 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. + +from rest_framework import serializers + + +class ResourceHandleSerializer(serializers.Serializer): + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to manage the resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifier of the entity responsible for the management of the resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceId = serializers.CharField( + help_text="Identifier of the resource in the scope of the VIM or the resource provider.", + required=True + ) + vimLevelResourceType = serializers.CharField( + help_text="Type of the resource in the scope of the VIM or the resource provider.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class ResourceDefinitionSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of this ResourceDefinition, unique at least within the scope of the GrantRequest.", + required=True + ) + type = serializers.ChoiceField( + help_text="Type of the resource definition referenced.", + choices=["COMPUTE", "VL", "STORAGE", "LINKPORT"], + required=True + ) + vduId = serializers.CharField( + help_text="Reference to the related VDU in the VNFD applicable to this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceTemplateId = serializers.CharField( + help_text="Reference to a resource template(such as VnfVirtualLinkDesc) in the VNFD.", + required=False, + allow_null=True, + allow_blank=True + ) + resource = ResourceHandleSerializer( + help_text="Resource information for an existing resource.", + required=False, + allow_null=True + ) + + +class ConstraintResourceRefSerializer(serializers.Serializer): + idType = serializers.ChoiceField( + help_text="The type of the identifier.", + choices=["RES_MGMT", "GRANT"], + required=True + ) + resourceId = serializers.CharField( + help_text="An actual resource-management-level identifier(idType=RES_MGMT), or an identifier that references a ResourceDefinition(idType=GRANT).", + required=True + ) + vimConnectionId = serializers.CharField( + help_text="", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifier of the resource provider. It shall only be present when idType = RES_MGMT.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class PlacementConstraintSerializer(serializers.Serializer): + affinityOrAntiAffinity = serializers.ChoiceField( + help_text="The type of the constraint.", + choices=["AFFINITY", "ANTI_AFFINITY"], + required=True + ) + scope = serializers.ChoiceField( + help_text="The scope of the placement constraint indicating the category of the place where the constraint applies.", + choices=["NFVI_POP", "ZONE", "ZONE_GROUP", "NFVI_NODE"], + required=True + ) + resource = ConstraintResourceRefSerializer( + help_text="References to resources in the constraint rule.", + many=True, + required=False + ) + + +class VimConstraintSerializer(serializers.Serializer): + sameResourceGroup = serializers.BooleanField( + help_text="Set to true when the constraint applies not only to the same VIM connection, but also to the same infrastructure resource group.", + required=False + ) + resource = ConstraintResourceRefSerializer( + help_text="References to resources in the constraint rule.", + many=True, + required=False + ) + + +class LinkSerializer(serializers.Serializer): + href = serializers.CharField( + help_text="URI of the referenced resource.", + required=True + ) + + +class GrantRequestLinksSerializer(serializers.Serializer): + vnfLcmOpOcc = LinkSerializer( + help_text="Related VNF lifecycle management operation occurrence.", + required=True + ) + vnfInstance = LinkSerializer( + help_text="Related VNF instance.", + required=True + ) + + +class GrantRequestSerializer(serializers.Serializer): + vnfInstanceId = serializers.CharField( + help_text="Identifier of the VNF instance which this grant request is related to.", + required=True + ) + vnfLcmOpOccId = serializers.CharField( + help_text="The identifier of the VNF lifecycle management operation occurrence associated to the GrantRequest.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfdId = serializers.CharField( + help_text="Identifier of the VNFD that defines the VNF for which the LCM operation is to be granted.", + required=False, + allow_null=True, + allow_blank=True + ) + flavourId = serializers.CharField( + help_text="Identifier of the VNF deployment flavour of the VNFD that defines the VNF for which the LCM operation is to be granted.", + required=False, + allow_null=True, + allow_blank=True + ) + operation = serializers.ChoiceField( + help_text="The lifecycle management operation for which granting is requested.", + choices=["INSTANTIATE", "SCALE", "SCALE_TO_LEVEL", "CHANGE_FLAVOUR", "TERMINATE", "HEAL", "OPERATE", "OPERATE", "CHANGE_EXT_CONN", "MODIFY_INFO"], + required=True + ) + isAutomaticInvocation = serializers.BooleanField( + help_text="Set to true if this VNF LCM operation occurrence has been triggered by an automated procedure inside the VNFM, set to false otherwise.", + required=True + ) + instantiationLevelId = serializers.CharField( + help_text="If operation=INSTANTIATE, the identifier of the instantiation level may be provided as an alternative way to define the resources to be added.", + required=False, + allow_null=True, + allow_blank=True + ) + addResources = ResourceDefinitionSerializer( + help_text="List of resource definitions in the VNFD for resources to be added by the LCM operation.", + many=True, + required=False + ) + tempResources = ResourceDefinitionSerializer( + help_text="List of resource definitions in the VNFD for resources to be temporarily instantiated during the runtime of the LCM operation.", + many=True, + required=False + ) + removeResources = ResourceDefinitionSerializer( + help_text="Provides the definitions of resources to be removed by the LCM operation.", + many=True, + required=False + ) + updateResources = ResourceDefinitionSerializer( + help_text="Provides the definitions of resources to be modified by the LCM operation.", + many=True, + required=False + ) + placementConstraints = PlacementConstraintSerializer( + help_text="Placement constraints that the VNFM may send to the NFVO in order to influence the resource placement decision.", + many=True, + required=False + ) + vimConstraints = VimConstraintSerializer( + help_text="Used by the VNFM to require that multiple resources are managed through the same VIM connection.", + many=True, + required=False + ) + additionalParams = serializers.DictField( + help_text="Additional parameters passed by the VNFM.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + _links = GrantRequestLinksSerializer( + help_text="Links to resources related to this request.", + required=False + ) + + +class VimConnectionInfoSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="The identifier of the VIM Connection. This identifier is managed by the NFVO.", + required=True + ) + vimId = serializers.CharField( + help_text="The identifier of the VIM instance. This identifier is managed by the NFVO.", + required=False, + allow_null=True, + allow_blank=True + ) + vimType = serializers.CharField( + help_text="Discriminator for the different types of the VIM information.", + required=False, + allow_null=True, + allow_blank=True + ) + interfaceInfo = serializers.DictField( + help_text="Information about the interface or interfaces to the VIM.", + child=serializers.CharField(help_text="Interface Info", allow_blank=True), + required=False, + allow_null=True + ) + accessInfo = serializers.DictField( + help_text="Authentication credentials for accessing the VIM.", + child=serializers.CharField(help_text="Access Info", allow_blank=True), + required=False, + allow_null=True + ) + extra = serializers.DictField( + help_text="VIM type specific additional information.", + child=serializers.CharField(help_text="Extra", allow_blank=True), + required=False, + allow_null=True + ) + + +class ZoneInfoSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="The identifier of this ZoneInfo instance, for the purpose of referencing it from other structures in the Grant structure.", + required=True + ) + zoneId = serializers.CharField( + help_text="The identifier of the resource zone, as managed by the resource management layer(typically, the VIM).", + required=False, + allow_null=True, + allow_blank=True + ) + vimConnectionId = serializers.CharField( + help_text="Identifier of the connection to the VIM that manages the resource zone.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management the resource zone.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class ZoneGroupInfoSerializer(serializers.Serializer): + zoneId = serializers.ListSerializer( + help_text="References of identifiers of ZoneInfo structures.", + child=serializers.CharField(help_text="IdentifierLocal", allow_blank=True), + required=False, + allow_null=True + ) + + +class GrantInfoSerializer(serializers.Serializer): + resourceDefinitionId = serializers.CharField( + help_text="Identifier of the related ResourceDefinition from the related GrantRequest.", + required=True + ) + reservationId = serializers.CharField( + help_text="The reservation identifier applicable to the VNFC/VirtualLink/VirtualStorage.", + required=False, + allow_null=True, + allow_blank=True + ) + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to be used to manage this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management of the virtualised resource.", + required=False, + allow_null=True, + allow_blank=True + ) + zoneId = serializers.CharField( + help_text="Reference to the identifier of the ZoneInfo in the Grant.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceGroupId = serializers.CharField( + help_text="Identifier of the infrastructure resource group.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class VimComputeResourceFlavourSerializer(serializers.Serializer): + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to access the flavour referenced in this structure.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management of the virtualised resource.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfdVirtualComputeDescId = serializers.CharField( + help_text="Identifier which references the virtual compute descriptor in the VNFD that maps to this flavour.", + required=False, + allow_null=True, + allow_blank=True + ) + vimFlavourId = serializers.CharField( + help_text="Identifier of the compute resource flavour in the resource management layer (i.e. VIM).", + required=False, + allow_null=True, + allow_blank=True + ) + + +class VimSoftwareImageSerializer(serializers.Serializer): + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to access the flavour referenced in this structure.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management of the virtualised resource.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfdSoftwareImageId = serializers.CharField( + help_text="Identifier which references the software image descriptor in the VNFD.", + required=False, + allow_null=True, + allow_blank=True + ) + vimSoftwareImageId = serializers.CharField( + help_text="Identifier of the software image in the resource management layer (i.e. VIM).", + required=False, + allow_null=True, + allow_blank=True + ) + + +class VimAssetsSerializer(serializers.Serializer): + computeResourceFlavours = VimComputeResourceFlavourSerializer( + help_text="Mappings between virtual compute descriptors defined in the VNFD and compute resource flavours managed in the VIM.", + many=True, + required=False + ) + softwareImages = VimSoftwareImageSerializer( + help_text="Mappings between software images defined in the VNFD and software images managed in the VIM.", + many=True, + required=False + ) + + +class AddressRangeSerializer(serializers.Serializer): + minAddress = serializers.CharField( + help_text="Lowest IP address belonging to the range.", + required=True + ) + maxAddress = serializers.CharField( + help_text="Highest IP address belonging to the range.", + required=True + ) + + +class IpAddresseSerializer(serializers.Serializer): + type = serializers.ChoiceField( + help_text="The type of the IP addresses.", + choices=["IPV4", "IPV6"], + required=True + ) + fixedAddresses = serializers.ListSerializer( + help_text="Fixed addresses to assign.", + child=serializers.CharField(help_text="IpAddress"), + required=False, + allow_null=True + ) + numDynamicAddresses = serializers.IntegerField( + help_text="Number of dynamic addresses to assign.", + required=True + ) + addressRange = AddressRangeSerializer( + help_text="An IP address range to be used, e.g. in case of egress connections.", + required=False, + allow_null=True + ) + subnetId = serializers.CharField( + help_text="Subnet defined by the identifier of the subnet resource in the VIM.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class IpOverEthernetAddressDataSerializer(serializers.Serializer): + macAddress = serializers.CharField( + help_text="MAC address.", + required=False, + allow_null=True, + allow_blank=True + ) + ipAddresses = IpAddresseSerializer( + help_text="List of IP addresses to assign to the CP instance.", + many=True, + required=False + ) + + +class CpProtocolDataSerializer(serializers.Serializer): + layerProtocol = serializers.ChoiceField( + help_text="Identifier of layer(s) and protocol(s).", + choices=["IP_OVER_ETHERNET"], + required=True + ) + ipOverEthernet = IpOverEthernetAddressDataSerializer( + help_text="Network address data for IP over Ethernet to assign to the extCP instance.", + required=False, + allow_null=True, + ) + + +class VnfExtCpConfigSerializer(serializers.Serializer): + cpInstanceId = serializers.CharField( + help_text="Identifier of the external CP instance to which this set of configuration parameters is requested to be applied.", + required=False, + allow_null=True, + allow_blank=True + ) + linkPortId = serializers.CharField( + help_text="Identifier of a pre-configured link port to which the external CP will be associated.", + required=False, + allow_null=True, + allow_blank=True + ) + cpProtocolData = CpProtocolDataSerializer( + help_text="Parameters for configuring the network protocols on the link port that connects the CP to a VL.", + many=True, + required=False + ) + + +class VnfExtCpDataSerializer(serializers.Serializer): + cpdId = serializers.CharField( + help_text="The identifier of the CPD in the VNFD.", + required=True + ) + cpConfig = VnfExtCpConfigSerializer( + help_text="List of instance data that need to be configured on the CP instances created from the respective CPD.", + many=True, + required=False + ) + + +class ExtLinkPortDataSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of this link port as provided by the entity that has created the link port.", + required=True + ) + resourceHandle = serializers.CharField( + help_text="Reference to the virtualised resource realizing this link port.", + required=True + ) + + +class ExtVirtualLinkDataSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="The identifier of the external VL instance.", + required=True + ) + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to manage this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management of this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceId = serializers.CharField( + help_text="The identifier of the resource in the scope of the VIM or the resource provider.", + required=True + ) + extCps = VnfExtCpDataSerializer( + help_text="External CPs of the VNF to be connected to this external VL.", + many=True, + required=False + ) + extLinkPorts = ExtLinkPortDataSerializer( + help_text="Externally provided link ports to be used to connect external connection points to this external VL.", + many=True, + required=False + ) + + +class ExtManagedVirtualLinkDataSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="The identifier of the externally-managed internal VL instance.", + required=True + ) + virtualLinkDescId = serializers.CharField( + help_text="The identifier of the VLD in the VNFD for this VL.", + required=True + ) + vimConnectionId = serializers.CharField( + help_text="Identifier of the VIM connection to manage this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceProviderId = serializers.CharField( + help_text="Identifies the entity responsible for the management of this resource.", + required=False, + allow_null=True, + allow_blank=True + ) + resourceId = serializers.CharField( + help_text="The identifier of the resource in the scope of the VIM or the resource provider.", + required=True + ) + + +class GrantLinksSerializer(serializers.Serializer): + self = LinkSerializer( + help_text="URI of this resource.", + required=True + ) + vnfLcmOpOcc = LinkSerializer( + help_text="Related VNF lifecycle management operation occurrence.", + required=True + ) + vnfInstance = LinkSerializer( + help_text="Related VNF instance.", + required=True + ) + + +class GrantSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of the grant.", + required=True + ) + vnfInstanceId = serializers.CharField( + help_text="Identifier of the related VNF instance.", + required=True + ) + vnfLcmOpOccId = serializers.CharField( + help_text="Identifier of the related VNF lifecycle management operation occurrence.", + required=False, + allow_null=True, + allow_blank=True + ) + vimConnections = VimConnectionInfoSerializer( + help_text="Provides information regarding VIM connections that are approved to be used by the VNFM to allocate resources.", + many=True, + required=False + ) + zones = ZoneInfoSerializer( + help_text="Identifies resource zones where the resources are approved to be allocated by the VNFM.", + many=True, + required=False + ) + zoneGroups = ZoneGroupInfoSerializer( + help_text="Information about groups of resource zones.", + many=True, + required=False + ) + computeReservationId = serializers.CharField( + help_text="Information that identifies a reservation applicable to the compute resource requirements.", + required=False, + allow_null=True, + allow_blank=True + ) + networkReservationId = serializers.CharField( + help_text="Information that identifies a reservation applicable to the network resource requirements.", + required=False, + allow_null=True, + allow_blank=True + ) + storageReservationId = serializers.CharField( + help_text="Information that identifies a reservation applicable to the storage resource requirements.", + required=False, + allow_null=True, + allow_blank=True + ) + addResources = GrantInfoSerializer( + help_text="List of resources that are approved to be added.", + many=True, + required=False + ) + tempResources = GrantInfoSerializer( + help_text="List of resources that are approved to be temporarily instantiated during the runtime of the lifecycle operation.", + many=True, + required=False + ) + removeResources = GrantInfoSerializer( + help_text="List of resources that are approved to be removed.", + many=True, + required=False + ) + updateResources = GrantInfoSerializer( + help_text="List of resources that are approved to be modified.", + many=True, + required=False + ) + vimAssets = VimAssetsSerializer( + help_text="Information about assets for the VNF that are managed by the NFVO in the VIM.", + required=False, + allow_null=True + ) + extVirtualLinks = ExtVirtualLinkDataSerializer( + help_text="Information about external VLs to connect the VNF to.", + many=True, + required=False + ) + extManagedVirtualLinks = ExtManagedVirtualLinkDataSerializer( + help_text="Information about internal VLs that are managed by other entities than the VNFM.", + many=True, + required=False + ) + _links = GrantLinksSerializer( + help_text="Links to resources related to this resource.", + required=False + ) + + +class AffectedVnfcSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of the Vnfc instance.", + required=True + ) + vduId = serializers.CharField( + help_text="Identifier of the related VDU in the VNFD.", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change.", + choices=["ADDED", "REMOVED", "MODIFIED", "TEMPORARY"], + required=True + ) + computeResource = ResourceHandleSerializer( + help_text="Reference to the VirtualCompute resource.", + required=True + ) + metadata = serializers.DictField( + help_text="Metadata about this resource.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + affectedVnfcCpIds = serializers.ListSerializer( + help_text="Identifiers of CP(s) of the VNFC instance that were affected by the change.", + child=serializers.CharField(help_text="Identifier In Vnf", allow_blank=True), + required=False, + allow_null=True + ) + addedStorageResourceIds = serializers.ListSerializer( + help_text="References to VirtualStorage resources that have been added.", + child=serializers.CharField(help_text="Identifier In Vnf", allow_blank=True), + required=False, + allow_null=True + ) + removedStorageResourceIds = serializers.ListSerializer( + help_text="References to VirtualStorage resources that have been removed.", + child=serializers.CharField(help_text="Identifier In Vnf", allow_blank=True), + required=False, + allow_null=True + ) + + +class AffectedVirtualLinkSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of the virtual link instance.", + required=True + ) + virtualLinkDescId = serializers.CharField( + help_text="Identifier of the related VLD in the VNFD.", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change.", + choices=["ADDED", "REMOVED", "MODIFIED", "TEMPORARY", "LINK_PORT_ADDED", "LINK_PORT_REMOVED"], + required=True + ) + networkResource = ResourceHandleSerializer( + help_text="Reference to the VirtualNetwork resource.", + required=False, + allow_null=True + ) + metadata = serializers.DictField( + help_text="Metadata about this resource.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + + +class AffectedVirtualStorageSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of the storage instance.", + required=True + ) + virtualStorageDescId = serializers.CharField( + help_text="Identifier of the related VirtualStorage descriptor in the VNFD.", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change.", + choices=["ADDED", "REMOVED", "MODIFIED", "TEMPORARY"], + required=True + ) + storageResource = ResourceHandleSerializer( + help_text="Reference to the VirtualStorage resource.", + required=False, + allow_null=True + ) + metadata = serializers.DictField( + help_text="Metadata about this resource.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + + +class VnfInfoModificationsSerializer(serializers.Serializer): + vnfInstanceName = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfInstanceName attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfInstanceDescription = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfInstanceDescription attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfConfigurableProperties = serializers.DictField( + help_text="If present, this attribute signals modifications of the vnfConfigurableProperties attribute in VnfInstance.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + metadata = serializers.DictField( + help_text="If present, this attribute signals modifications of the metadata attribute in VnfInstance.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + extensions = serializers.DictField( + help_text="If present, this attribute signals modifications of the extensions attribute in VnfInstance.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True + ) + vimConnectionInfo = VimConnectionInfoSerializer( + help_text="If present, this attribute signals modifications of the vimConnectionInfo attribute in VnfInstance.", + many=True, + required=False + ) + vnfPkgId = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfPkgId attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfdId = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfdId attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfProvider = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfProvider attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfProductName = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfProductName attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfSoftwareVersion = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfSoftwareVersion attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + vnfdVersion = serializers.CharField( + help_text="If present, this attribute signals modifications of the vnfdVersion attribute in VnfInstance.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class ExtLinkPortInfoSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of this link port as provided by the entity that has created the link port.", + required=True + ) + resourceHandle = ResourceHandleSerializer( + help_text="Reference to the virtualised resource realizing this link port.", + required=True + ) + cpInstanceId = serializers.CharField( + help_text="Identifier of the external CP of the VNF connected to this link port.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class ExtVirtualLinkInfoSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of the external VL and the related external VL information instance.", + required=True + ) + resourceHandle = ResourceHandleSerializer( + help_text="Reference to the resource realizing this VL.", + required=True + ) + extLinkPorts = ExtLinkPortInfoSerializer( + help_text="Link ports of this VL.", + many=True, + required=False + ) + + +class ProblemDetailsSerializer(serializers.Serializer): + type = serializers.CharField( + help_text="A URI reference according to IETF RFC 3986 [5] that identifies the problem type.", + required=False, + allow_null=True, + allow_blank=True + ) + title = serializers.CharField( + help_text="A short, human-readable summary of the problem type.", + required=False, + allow_null=True, + allow_blank=True + ) + status = serializers.IntegerField( + help_text="The HTTP status code for this occurrence of the problem.", + required=True + ) + detail = serializers.CharField( + help_text="A human-readable explanation specific to this occurrence of the problem.", + required=True + ) + instance = serializers.CharField( + help_text="A URI reference that identifies the specific occurrence of the problem.", + required=False, + allow_null=True, + allow_blank=True + ) + + +class LccnLinksSerializer(serializers.Serializer): + vnfInstance = LinkSerializer( + help_text="Link to the resource representing the VNF instance to which the notified change applies.", + required=True + ) + subscription = LinkSerializer( + help_text="Link to the related subscription.", + required=True + ) + vnfLcmOpOcc = LinkSerializer( + help_text="Link to the VNF lifecycle management operation occurrence that this notification is related to.", + required=False, + allow_null=True + ) + + +class VnfLcmOperationOccurrenceNotificationSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of this notification.", + required=True + ) + notificationType = serializers.CharField( + help_text="Discriminator for the different notification types.", + required=True + ) + subscriptionId = serializers.CharField( + help_text="Identifier of the subscription that this notification relates to.", + required=True + ) + timeStamp = serializers.CharField( + help_text="Date-time of the generation of the notification.", + required=True + ) + notificationStatus = serializers.ChoiceField( + help_text="Indicates whether this notification reports about the start of a lifecycle operation or the result of a lifecycle operation.", + choices=["START", "RESULT"], + required=True + ) + operationState = serializers.ChoiceField( + help_text="The state of the VNF LCM operation occurrence.", + choices=["STARTING", "PROCESSING", "COMPLETED", "FAILED_TEMP", "FAILED", "ROLLING_BACK", "ROLLED_BACK"], + required=True + ) + vnfInstanceId = serializers.CharField( + help_text="The identifier of the VNF instance affected.", + required=True + ) + operation = serializers.ChoiceField( + help_text="The lifecycle management operation.", + choices=["INSTANTIATE", "SCALE", "SCALE_TO_LEVEL", "CHANGE_FLAVOUR", "TERMINATE", "HEAL", "OPERATE", "CHANGE_EXT_CONN", "MODIFY_INFO"], + required=True + ) + isAutomaticInvocation = serializers.BooleanField( + help_text="Set to true if this VNF LCM operation occurrence has been triggered by an automated procedure inside the VNFM.", + required=True + ) + vnfLcmOpOccId = serializers.CharField( + help_text="The identifier of the VNF lifecycle management operation occurrence associated to the notification.", + required=True + ) + affectedVnfcs = AffectedVnfcSerializer( + help_text="Information about VNFC instances that were affected during the lifecycle operation.", + many=True, + required=False + ) + affectedVirtualLinks = AffectedVirtualLinkSerializer( + help_text="Information about VL instances that were affected during the lifecycle operation.", + many=True, + required=False + ) + affectedVirtualStorages = AffectedVirtualStorageSerializer( + help_text="Information about virtualised storage instances that were affected during the lifecycle operation.", + many=True, + required=False + ) + changedInfo = VnfInfoModificationsSerializer( + help_text="Information about the changed VNF instance information, including changed VNF configurable properties.", + required=False, + allow_null=True + ) + changedExtConnectivity = ExtVirtualLinkInfoSerializer( + help_text="Information about changed external connectivity.", + many=True, + required=False + ) + error = ProblemDetailsSerializer( + help_text="Details of the latest error, if one has occurred during executing the LCM operation", + required=False, + allow_null=True + ) + _links = LccnLinksSerializer( + help_text="Links to resources related to this notification.", + required=False, + allow_null=True + ) diff --git a/lcm/v2/tests.py b/lcm/v2/tests.py new file mode 100644 index 00000000..406edadd --- /dev/null +++ b/lcm/v2/tests.py @@ -0,0 +1,217 @@ +# Copyright 2018 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 unittest +import json +from django.test import Client +from rest_framework import status + + +class VnfGrantViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_grant_vnf_normal(self): + data = { + "vnfInstanceId": "1", + "vnfLcmOpOccId": "2", + "vnfdId": "3", + "flavourId": "4", + "operation": "INSTANTIATE", + "isAutomaticInvocation": True, + "instantiationLevelId": "5", + "addResources": [ + { + "id": "1", + "type": "COMPUTE", + "vduId": "2", + "resourceTemplateId": "3", + "resource": { + "vimConnectionId": "4", + "resourceProviderId": "5", + "resourceId": "6", + "vimLevelResourceType": "7" + } + } + ], + "placementConstraints": [ + { + "affinityOrAntiAffinity": "AFFINITY", + "scope": "NFVI_POP", + "resource": [ + { + "idType": "RES_MGMT", + "resourceId": "1", + "vimConnectionId": "2", + "resourceProviderId": "3" + } + ] + } + ], + "vimConstraints": [ + { + "sameResourceGroup": True, + "resource": [ + { + "idType": "RES_MGMT", + "resourceId": "1", + "vimConnectionId": "2", + "resourceProviderId": "3" + } + ] + } + ], + "additionalParams": {}, + "_links": { + "vnfLcmOpOcc": { + "href": "1" + }, + "vnfInstance": { + "href": "2" + } + } + } + response = self.client.post("/api/nslcm/v2/grants", data=data, format='json') + self.assertEqual(status.HTTP_201_CREATED, response.status_code, response.content) + resp_data = json.loads(response.content) + expect_resp_data = { + "id": resp_data.get("id"), + "vnfInstanceId": "1", + "vnfLcmOpOccId": "2" + } + self.assertEqual(expect_resp_data, resp_data) + + def test_get_notify_vnf_normal(self): + response = self.client.get("/api/nslcm/v2/ns/1/vnfs/1/Notify") + self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code, response.content) + + def test_notify_vnf_normal(self): + data = { + "id": "string", + "notificationType": "string", + "subscriptionId": "string", + "timeStamp": "string", + "notificationStatus": "START", + "operationState": "STARTING", + "vnfInstanceId": "string", + "operation": "INSTANTIATE", + "isAutomaticInvocation": True, + "vnfLcmOpOccId": "string", + "affectedVnfcs": [{ + "vnfcInstanceId": "string", + "vduId": "string", + "changeType": "added", + "vimId": "string", + "vmId": "string", + "vmName": "string" + }], + "affectedVirtualLinks": [{ + "vlInstanceId": "string", + "vldId": "string", + "changeType": "added", + "networkResource": { + "resourceType": "network", + "resourceId": "string", + "resourceName": "string" + } + }], + "affectedVirtualStorages": [{}], + "changedInfo": { + "vnfInstanceName": "string", + "vnfInstanceDescription": "string", + "vnfConfigurableProperties": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + }, + "metadata": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + }, + "extensions": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + }, + "vimConnectionInfo": [{ + "id": "string", + "vimId": "string", + "vimType": "string", + "interfaceInfo": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + }, + "accessInfo": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + }, + "extra": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + } + }], + "vnfPkgId": "string", + "vnfdId": "string", + "vnfProvider": "string", + "vnfProductName": "string", + "vnfSoftwareVersion": "string", + "vnfdVersion": "string" + }, + "changedExtConnectivity": [{ + "id": "string", + "resourceHandle": { + "vimConnectionId": "string", + "resourceProviderId": "string", + "resourceId": "string", + "vimLevelResourceType": "string" + }, + "extLinkPorts": [{ + "id": "string", + "resourceHandle": { + "vimConnectionId": "string", + "resourceProviderId": "string", + "resourceId": "string", + "vimLevelResourceType": "string" + }, + "cpInstanceId": "string" + }] + }], + "error": { + "type": "string", + "title": "string", + "status": 0, + "detail": "string", + "instance": "string" + }, + "_links": { + "vnfInstance": { + "href": "string" + }, + "subscription": { + "href": "string" + }, + "vnfLcmOpOcc": { + "href": "string" + } + } + } + response = self.client.post("/api/nslcm/v2/ns/1/vnfs/2/Notify", data=data, format='json') + self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code, response.content) diff --git a/lcm/v2/urls.py b/lcm/v2/urls.py new file mode 100644 index 00000000..243cdcd8 --- /dev/null +++ b/lcm/v2/urls.py @@ -0,0 +1,24 @@ +# Copyright 2018 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. +from django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.v2.views import VnfGrantView, VnfNotifyView + +urlpatterns = [ + url(r'^api/nslcm/v2/grants$', VnfGrantView.as_view()), + url(r'^api/nslcm/v2/ns/(?P<vnfmId>[0-9a-zA-Z_-]+)/vnfs/(?P<vnfInstanceId>[0-9a-zA-Z_-]+)/Notify$', VnfNotifyView.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/v2/views.py b/lcm/v2/views.py new file mode 100644 index 00000000..e216022d --- /dev/null +++ b/lcm/v2/views.py @@ -0,0 +1,84 @@ +# Copyright 2018 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 rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema + +from lcm.v2.serializers import GrantRequestSerializer +from lcm.v2.serializers import GrantSerializer +from lcm.v2.serializers import VnfLcmOperationOccurrenceNotificationSerializer +from lcm.v2.grant_vnf import GrantVnf + +logger = logging.getLogger(__name__) + + +class VnfGrantView(APIView): + @swagger_auto_schema( + request_body=GrantRequestSerializer(), + responses={ + status.HTTP_201_CREATED: GrantSerializer( + help_text="The grant was created successfully (synchronous mode)." + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) + def post(self, request): + logger.debug("VnfGrantView Post: %s" % request.data) + try: + req_serializer = GrantRequestSerializer(data=request.data) + if not req_serializer.is_valid(): + raise Exception(req_serializer.errors) + + grant_resp = GrantVnf(request.data).exec_grant() + + resp_serializer = GrantSerializer(data=grant_resp) + if not resp_serializer.is_valid(): + raise Exception(resp_serializer.errors) + + return Response(data=resp_serializer.data, status=status.HTTP_201_CREATED) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Exception in VnfGrant: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +class VnfNotifyView(APIView): + @swagger_auto_schema( + request_body=VnfLcmOperationOccurrenceNotificationSerializer( + help_text="A notification about lifecycle changes triggered by a VNF LCM operation occurrence." + ), + responses={ + status.HTTP_204_NO_CONTENT: "The notification was delivered successfully.", + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) + def post(self, request, vnfmId, vnfInstanceId): + logger.debug("VnfNotifyView post: %s" % request.data) + logger.debug("vnfmId: %s vnfInstanceId: %s", vnfmId, vnfInstanceId) + return Response(data={}, status=status.HTTP_204_NO_CONTENT) + + @swagger_auto_schema( + responses={ + status.HTTP_204_NO_CONTENT: "The notification endpoint was tested successfully.", + status.HTTP_500_INTERNAL_SERVER_ERROR: "Inner error" + } + ) + def get(self, request, vnfmId, vnfInstanceId): + logger.debug("VnfNotifyView get") + logger.debug("vnfmId: %s vnfInstanceId: %s", vnfmId, vnfInstanceId) + return Response(data={}, status=status.HTTP_204_NO_CONTENT) diff --git a/mvn-phase-script.sh b/mvn-phase-script.sh new file mode 100755 index 00000000..6b41abf1 --- /dev/null +++ b/mvn-phase-script.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# Copyright 2018 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. + + +set -e + +echo "running script: [$0] for module [$1] at stage [$2]" + +export SETTINGS_FILE=${SETTINGS_FILE:-$HOME/.m2/settings.xml} +MVN_PROJECT_MODULEID="$1" +MVN_PHASE="$2" + + +FQDN="${MVN_PROJECT_GROUPID}.${MVN_PROJECT_ARTIFACTID}" +if [ "$MVN_PROJECT_MODULEID" == "__" ]; then + MVN_PROJECT_MODULEID="" +fi + +if [ -z "$WORKSPACE" ]; then + WORKSPACE=$(pwd) +fi + + +# mvn phase in life cycle +MVN_PHASE="$2" + + +echo "MVN_PROJECT_MODULEID is [$MVN_PROJECT_MODULEID]" +echo "MVN_PHASE is [$MVN_PHASE]" +echo "MVN_PROJECT_GROUPID is [$MVN_PROJECT_GROUPID]" +echo "MVN_PROJECT_ARTIFACTID is [$MVN_PROJECT_ARTIFACTID]" +echo "MVN_PROJECT_VERSION is [$MVN_PROJECT_VERSION]" + +run_tox_test() +{ + set -x + CURDIR=$(pwd) + if [[ ${CURDIR} =~ "-sonar" ]] + then + echo "====Sonar job, need execute tox." + TOXINIS=$(find . -name "tox.ini") + for TOXINI in "${TOXINIS[@]}"; do + DIR=$(echo "$TOXINI" | rev | cut -f2- -d'/' | rev) + cd "${CURDIR}/${DIR}" + rm -rf ./venv-tox ./.tox + virtualenv ./venv-tox + source ./venv-tox/bin/activate + pip install --upgrade pip + pip install --upgrade tox argparse + pip freeze + tox + deactivate + rm -rf ./venv-tox ./.tox + done + else + echo "====Not a sonar job, need not execute tox." + fi +} + + +case $MVN_PHASE in +clean) + echo "==> clean phase script" + rm -rf ./venv-* + ;; +test) + echo "==> test phase script" + run_tox_test + ;; +*) + echo "==> unprocessed phase" + ;; +esac + @@ -1,6 +1,6 @@ <?xml version="1.0"?> <!-- - Copyright 2016 ZTE Corporation. + Copyright 2016-2018 ZTE Corporation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ <parent> <groupId>org.onap.oparent</groupId> <artifactId>oparent</artifactId> - <version>0.1.1</version> + <version>1.1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.onap.vfc.nfvo.lcm</groupId> @@ -27,9 +27,70 @@ <packaging>pom</packaging> <name>vfc-nfvo-lcm</name> <description>vfc nfvo lcm</description> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <sonar.sources>.</sonar.sources> + <sonar.junit.reportsPath>xunit-results.xml</sonar.junit.reportsPath> + <sonar.python.coverage.reportPath>coverage.xml</sonar.python.coverage.reportPath> + <sonar.language>py</sonar.language> + <sonar.pluginname>python</sonar.pluginname> + <sonar.inclusions>**/**.py</sonar.inclusions> + <sonar.exclusions>**/tests/**.py,**/test*.py</sonar.exclusions> + </properties> <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.2.1</version> + <configuration> + <executable>${project.basedir}/mvn-phase-script.sh</executable> + <environmentVariables> + <!-- make mvn properties as env for our script --> + <MVN_PROJECT_GROUPID>${project.groupId}</MVN_PROJECT_GROUPID> + <MVN_PROJECT_ARTIFACTID>${project.artifactId}</MVN_PROJECT_ARTIFACTID> + <MVN_PROJECT_VERSION>${project.version}</MVN_PROJECT_VERSION> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </pluginManagement> <plugins> <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.2.1</version> + <executions> + <execution> + <id>clean phase script</id> + <phase>clean</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <arguments> + <argument>__</argument> + <argument>clean</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>test script</id> + <phase>test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <arguments> + <argument>__</argument> + <argument>test</argument> + </arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <appendAssemblyId>false</appendAssemblyId> diff --git a/requirements.txt b/requirements.txt index 8c34a073..c1dd615e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ # rest framework -Django==1.9.6 -djangorestframework==3.3.3 +Django==1.11.9 +djangorestframework==3.7.7 # for access MySQL -MySQL-python==1.2.5 +PyMySQL==0.7.11 # redis cache redis==2.10.5 @@ -29,3 +29,13 @@ unittest_xml_reporting==1.12.0 cryptography==2.0.3 paramiko==2.0.2 nfv-toscaparser>=0.5.0 + +# for swagger +drf-yasg>=1.2.2 + +# for the validation feature +flex>=6.11.1 +swagger-spec-validator>=2.1.0 + +# for onap logging +onappylog>=1.0.6
\ No newline at end of file @@ -12,4 +12,16 @@ # 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. + +logDir="/var/log/onap/vfc/nslcm/" +if [ ! -x $logDir ]; then + mkdir -p $logDir +fi + nohup python manage.py runserver 0.0.0.0:8403 > /dev/null & + +while [ ! -f $logDir/runtime_nslcm.log ]; do + sleep 1 +done + +tail -F $logDir/runtime_nslcm.log
\ No newline at end of file @@ -23,4 +23,4 @@ commands = {[testenv]commands} [testenv:cov] -commands = coverage html --omit="*test_*,*__init__.py,*site-packages*" -d htmlcov +commands = coverage xml --omit="*test_*,*__init__.py,*site-packages*" |