aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lcm/ns/biz/ns_create.py2
-rw-r--r--lcm/ns/biz/ns_get.py1
-rw-r--r--lcm/ns_vnfs/biz/create_vnfs.py87
-rw-r--r--lcm/ns_vnfs/biz/place_vnfs.py30
-rw-r--r--lcm/ns_vnfs/tests/tests.py362
-rw-r--r--lcm/pub/config/config.py5
-rw-r--r--lcm/pub/database/models.py1
7 files changed, 475 insertions, 13 deletions
diff --git a/lcm/ns/biz/ns_create.py b/lcm/ns/biz/ns_create.py
index 1f99f22b..6bcc5a05 100644
--- a/lcm/ns/biz/ns_create.py
+++ b/lcm/ns/biz/ns_create.py
@@ -54,6 +54,7 @@ class CreateNSService(object):
packageInfo = ns_package_info["packageInfo"]
self.ns_package_id = ignore_case_get(packageInfo, "nsPackageId")
self.nsd_id = ignore_case_get(packageInfo, "nsdId")
+ self.nsd_invariant_id = ignore_case_get(packageInfo, "nsdInvariantId")
logger.debug("CreateNSService::check_nsd_valid::ns_package_id=%s,nsd_id=%s", self.ns_package_id, self.nsd_id)
def check_ns_inst_name_exist(self):
@@ -69,6 +70,7 @@ class CreateNSService(object):
name=self.ns_name,
nspackage_id=self.ns_package_id,
nsd_id=self.nsd_id,
+ nsd_invariant_id=self.nsd_invariant_id,
description=self.description,
status='empty',
lastuptime=now_time(),
diff --git a/lcm/ns/biz/ns_get.py b/lcm/ns/biz/ns_get.py
index 4193bfae..31d26866 100644
--- a/lcm/ns/biz/ns_get.py
+++ b/lcm/ns/biz/ns_get.py
@@ -40,6 +40,7 @@ class GetNSInfoService(object):
'nsName': ns_inst.name,
'description': ns_inst.description,
'nsdId': ns_inst.nsd_id,
+ 'nsdInvariantId': ns_inst.nsd_invariant_id,
'vnfInfoId': self.get_vnf_infos(ns_inst.id),
'vlInfo': self.get_vl_infos(ns_inst.id),
'vnffgInfo': self.get_vnffg_infos(ns_inst.id, ns_inst.nsd_model),
diff --git a/lcm/ns_vnfs/biz/create_vnfs.py b/lcm/ns_vnfs/biz/create_vnfs.py
index ac1847ec..9952fd6e 100644
--- a/lcm/ns_vnfs/biz/create_vnfs.py
+++ b/lcm/ns_vnfs/biz/create_vnfs.py
@@ -19,7 +19,7 @@ from threading import Thread
from lcm.ns.const import OWNER_TYPE
from lcm.pub.config.config import REPORT_TO_AAI
-from lcm.pub.database.models import NfInstModel, NSInstModel, VmInstModel, VNFFGInstModel, VLInstModel
+from lcm.pub.database.models import NfInstModel, NSInstModel, VmInstModel, VNFFGInstModel, VLInstModel, OOFDataModel
from lcm.pub.exceptions import NSLCMException
from lcm.pub.msapi.aai import create_vnf_aai
from lcm.pub.msapi.extsys import get_vnfm_by_id
@@ -30,8 +30,10 @@ from lcm.pub.utils.jobutil import JOB_MODEL_STATUS, JobUtil, JOB_TYPE
from lcm.pub.utils.share_lock import do_biz_with_share_lock
from lcm.pub.utils.timeutil import now_time
from lcm.pub.utils.values import ignore_case_get
+from lcm.pub.utils import restcall
from lcm.ns_vnfs.const import VNF_STATUS, NFVO_VNF_INST_TIMEOUT_SECOND, INST_TYPE, INST_TYPE_NAME
from lcm.ns_vnfs.biz.wait_job import wait_job_finish
+from lcm.pub.config.config import REG_TO_MSB_REG_PARAM, OOF_BASE_URL, OOF_PASSWD, OOF_USER
logger = logging.getLogger(__name__)
@@ -75,6 +77,7 @@ class CreateVnfs(Thread):
self.create_vnf_in_aai()
self.check_nf_package_valid()
self.send_nf_init_request_to_vnfm()
+ self.send_homing_request_to_OOF()
self.send_get_vnfm_request_to_extsys()
self.send_create_vnf_request_to_resmgr()
self.wait_vnfm_job_finish()
@@ -208,6 +211,88 @@ class CreateVnfs(Thread):
input_params=json.JSONEncoder().encode(self.inputs),
lastuptime=now_time())
+ def build_homing_request(self):
+ id = str(uuid.uuid4())
+ callback_uri = " {vfcBaseUrl}/api/nslcm/v1/ns/placevnf"
+ IP = REG_TO_MSB_REG_PARAM["nodes"][0]["ip"]
+ PORT = REG_TO_MSB_REG_PARAM["nodes"][0]["port"]
+ vfcBaseUrl = IP + ':' + PORT
+ callback_uri = callback_uri.format(vfcBaseUrl=vfcBaseUrl)
+ modelInvariantId = "no-resourceModelInvariantId"
+ modelVersionId = "no-resourceModelVersionId"
+ nsInfo = NSInstModel.objects.filter(id=self.ns_inst_id)
+ placementDemand = {
+ "resourceModuleName": self.vnf_inst_name,
+ "serviceResourceId": self.vnfm_nf_inst_id,
+ "resourceModelInfo": {
+ "modelInvariantId": modelInvariantId,
+ "modelVersionId": modelVersionId
+ }
+ }
+ req_body = {
+ "requestInfo": {
+ "transactionId": id,
+ "requestId": id,
+ "callbackUrl": callback_uri,
+ "sourceId": "vfc",
+ "requestType": "create",
+ "numSolutions": 1,
+ "optimizers": ["placement"],
+ "timeout": 600
+ },
+ "placementInfo": {
+ "placementDemands": []
+ },
+ "serviceInfo": {
+ "serviceInstanceId": self.ns_inst_id,
+ "serviceName": self.ns_inst_name,
+ "modelInfo": {
+ "modelInvariantId": nsInfo[0].nsd_invariant_id,
+ "modelVersionId": nsInfo[0].nsd_id
+ }
+ }
+ }
+ req_body["placementInfo"]["placementDemands"].append(placementDemand)
+ # Stored the init request info inside DB
+ OOFDataModel.objects.create(
+ request_id=id,
+ transaction_id=id,
+ request_status="init",
+ request_module_name=self.vnfm_nf_inst_id,
+ service_resource_id=self.vnf_inst_name,
+ vim_id="",
+ cloud_owner="",
+ cloud_region_id="",
+ vdu_info="",
+ )
+ return req_body
+
+ def send_homing_request_to_OOF(self):
+ req_body = self.build_homing_request()
+ base_url = OOF_BASE_URL
+ resources = "/api/oof/v1/placement"
+ resp = restcall.call_req(base_url=base_url, user=OOF_USER, passwd=OOF_PASSWD,
+ auth_type=restcall.rest_oneway_auth, resource=resources,
+ method="POST", content=req_body, additional_headers="")
+ resp_body = resp[-2]
+ resp_status = resp[-1]
+ if resp_body:
+ logger.debug("Got OOF sync response")
+ else:
+ logger.warn("Missing OOF sync response")
+ logger.debug(("OOF sync response code is %s") % resp_status)
+ if str(resp_status) != '202' or resp[0] != 0:
+ OOFDataModel.objects.filter(request_id=req_body["requestInfo"]["requestId"],
+ transaction_id=req_body["requestInfo"]["transactionId"]).update(
+ request_status="failed",
+ vim_id="none",
+ cloud_owner="none",
+ cloud_region_id="none",
+ vdu_info="none"
+ )
+ raise Exception("Received a Bad Sync from OOF with response code %s" % resp_status)
+ logger.info("Completed Homing request to OOF")
+
def send_get_vnfm_request_to_extsys(self):
resp_body = get_vnfm_by_id(self.vnfm_inst_id)
self.vnfm_inst_name = ignore_case_get(resp_body, 'name')
diff --git a/lcm/ns_vnfs/biz/place_vnfs.py b/lcm/ns_vnfs/biz/place_vnfs.py
index 84577b26..18a529e8 100644
--- a/lcm/ns_vnfs/biz/place_vnfs.py
+++ b/lcm/ns_vnfs/biz/place_vnfs.py
@@ -30,7 +30,7 @@ class PlaceVnfs(object):
logger.error("Error occurred in Homing: OOF Async Callback Response is empty")
return False
if self.data.get('requestStatus') == "completed":
- if self.data.get("solutions").get("placementSolutions"):
+ if self.data.get("solutions").get("placementSolutions") is not None:
self.placements = self.data.get("solutions").get("placementSolutions")
logger.debug("Got placement solutions in OOF Async Callback response")
return True
@@ -39,10 +39,14 @@ class PlaceVnfs(object):
"does not contain placement solution")
return False
else:
- logger.error(
- "Error occurred in Homing: Request has not been completed, the request status is %s, "
- "the status message is %s" % self.data.get('requestStatus'),
- self.data.get("statusMessage"))
+ if self.data.get("statusMessage"):
+ logger.error(
+ "Error occurred in Homing: Request has not been completed, the request status is %s, "
+ "the status message is %s" % (self.data.get('requestStatus'), self.data.get("statusMessage")))
+ else:
+ logger.error(
+ "Error occurred in Homing: Request has not been completed, the request status is %s, "
+ % self.data.get('requestStatus'))
return False
def extract(self):
@@ -53,9 +57,14 @@ class PlaceVnfs(object):
self.update_response_to_db(self.data.get("requestId"), self.data.get("transactionId"),
self.data.get("requestStatus"), "none", "none", "none", "none")
return
+ if self.placements == [] or self.placements == [[]]:
+ logger.debug("No solution found for request %s " % self.data.get("requestId"))
+ self.update_response_to_db(self.data.get("requestId"), self.data.get("transactionId"),
+ self.data.get("requestStatus"), "no-solution", "no-solution",
+ "no-solution", "no-solution")
+ return
for item in self.placements:
- if not item:
- logger.debug("No solution found for request %s " % self.data.get("requestId"))
+ if not isinstance(item, list):
self.update_response_to_db(self.data.get("requestId"), self.data.get("transactionId"),
self.data.get("requestStatus"), "no-solution", "no-solution",
"no-solution", "no-solution")
@@ -94,8 +103,7 @@ class PlaceVnfs(object):
transactionId=self.data.get("transactionId"),
requestStatus=self.data.get("requestStatus"),
vimId=vim_info['vimId'],
- cloudOwner=placement.get("solution").get(
- "cloudOwner"),
+ cloudOwner=placement.get("solution").get("cloudOwner"),
cloudRegionId=vim_info['locationId'],
vduInfo=vduinfo
)
@@ -106,7 +114,7 @@ class PlaceVnfs(object):
def get_info_from_directives(self, directives):
vduinfo = []
- for directive in directives:
+ for directive in directives.get("directives"):
if directive.get("type") == "tocsa.nodes.nfv.Vdu.Compute":
vdu = {"vduName": directive.get("id")}
other_directives = []
@@ -137,5 +145,5 @@ class PlaceVnfs(object):
vim_id=vimId,
cloud_owner=cloudOwner,
cloud_region_id=cloudRegionId,
- vduinfo=vduInfo
+ vdu_info=vduInfo
)
diff --git a/lcm/ns_vnfs/tests/tests.py b/lcm/ns_vnfs/tests/tests.py
index b70d00e3..ba92e194 100644
--- a/lcm/ns_vnfs/tests/tests.py
+++ b/lcm/ns_vnfs/tests/tests.py
@@ -18,7 +18,7 @@ import mock
from django.test import TestCase, Client
from rest_framework import status
-from lcm.pub.database.models import NfInstModel, JobModel, NSInstModel, VmInstModel
+from lcm.pub.database.models import NfInstModel, JobModel, NSInstModel, VmInstModel, OOFDataModel
from lcm.pub.exceptions import NSLCMException
from lcm.pub.utils import restcall
from lcm.pub.utils.jobutil import JOB_MODEL_STATUS
@@ -31,6 +31,7 @@ from lcm.ns_vnfs.biz.scale_vnfs import NFManualScaleService
from lcm.ns_vnfs.biz.terminate_nfs import TerminateVnfs
from lcm.ns_vnfs.const import VNF_STATUS, INST_TYPE
from lcm.ns_vnfs.biz import create_vnfs
+from lcm.ns_vnfs.biz.place_vnfs import PlaceVnfs
class TestGetVnfViews(TestCase):
@@ -138,6 +139,59 @@ class TestCreateVnfViews(TestCase):
CreateVnfs(data, nf_inst_id, job_id).run()
self.assertTrue(NfInstModel.objects.get(nfinstid=nf_inst_id).status, VNF_STATUS.ACTIVE)
+ @mock.patch.object(restcall, 'call_req')
+ @mock.patch.object(CreateVnfs, 'build_homing_request')
+ def test_send_homing_request(self, mock_build_req, mock_call_req):
+ nf_inst_id, job_id = create_vnfs.prepare_create_params()
+ OOFDataModel.objects.all().delete()
+ resp = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "requestStatus": "accepted"
+ }
+ mock_build_req.return_value = {
+ "requestInfo": {
+ "transactionId": "1234",
+ "requestId": "1234",
+ "callbackUrl": "xx",
+ "sourceId": "vfc",
+ "requestType": "create",
+ "numSolutions": 1,
+ "optimizers": ["placement"],
+ "timeout": 600
+ },
+ "placementInfo": {
+ "placementDemands": [
+ {
+ "resourceModuleName": "vG",
+ "serviceResourceId": "1234",
+ "resourceModelInfo": {
+ "modelInvariantId": "1234",
+ "modelVersionId": "1234"
+ }
+ }
+ ]
+ },
+ "serviceInfo": {
+ "serviceInstanceId": "1234",
+ "serviceName": "1234",
+ "modelInfo": {
+ "modelInvariantId": "5678",
+ "modelVersionId": "7890"
+ }
+ }
+ }
+ mock_call_req.return_value = [0, json.JSONEncoder().encode(resp), '202']
+ data = {
+ 'ns_instance_id': ignore_case_get(self.data, 'nsInstanceId'),
+ 'additional_param_for_ns': ignore_case_get(self.data, 'additionalParamForNs'),
+ 'additional_param_for_vnf': ignore_case_get(self.data, 'additionalParamForVnf'),
+ 'vnf_index': ignore_case_get(self.data, 'vnfIndex')
+ }
+ CreateVnfs(data, nf_inst_id, job_id).send_homing_request_to_OOF()
+ ret = OOFDataModel.objects.filter(request_id="1234", transaction_id="1234")
+ self.assertIsNotNone(ret)
+
class TestTerminateVnfViews(TestCase):
def setUp(self):
@@ -630,6 +684,218 @@ class TestGetVimInfoViews(TestCase):
self.assertEqual(expect_data["url"], context["url"])
+class TestPlaceVnfViews(TestCase):
+ def setUp(self):
+ self.vnf_inst_id = "1234"
+ self.vnf_inst_name = "vG"
+ self.client = Client()
+ OOFDataModel.objects.all().delete()
+ OOFDataModel.objects.create(
+ request_id="1234",
+ transaction_id="1234",
+ request_status="init",
+ request_module_name=self.vnf_inst_name,
+ service_resource_id=self.vnf_inst_id,
+ vim_id="",
+ cloud_owner="",
+ cloud_region_id="",
+ vdu_info="",
+ )
+
+ def tearDown(self):
+ OOFDataModel.objects.all().delete()
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_place_vnf(self, mock_call_req):
+ vdu_info_json = [{
+ "vduName": "vG_0",
+ "flavorName": "HPA.flavor.1",
+ "directive": []
+ }]
+ PlaceVnfs(vnf_place_request).extract()
+ db_info = OOFDataModel.objects.filter(request_id=vnf_place_request.get("requestId"), transaction_id=vnf_place_request.get("transactionId"))
+ self.assertEqual(db_info[0].request_status, "completed")
+ self.assertEqual(db_info[0].vim_id, "CloudOwner1_DLLSTX1A")
+ self.assertEqual(db_info[0].cloud_owner, "CloudOwner1")
+ self.assertEqual(db_info[0].cloud_region_id, "DLLSTX1A")
+ self.assertEqual(db_info[0].vdu_info, json.dumps(vdu_info_json))
+
+ def test_place_vnf_with_invalid_response(self):
+ resp = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "statusMessage": "xx",
+ "requestStatus": "pending",
+ "solutions": {
+ "placementSolutions": [
+ [
+ {
+ "resourceModuleName": self.vnf_inst_name,
+ "serviceResourceId": self.vnf_inst_id,
+ "solution": {
+ "identifierType": "serviceInstanceId",
+ "identifiers": [
+ "xx"
+ ],
+ "cloudOwner": "CloudOwner1 "
+ },
+ "assignmentInfo": []
+ }
+ ]
+ ],
+ "licenseSolutions": [
+ {
+ "resourceModuleName": "string",
+ "serviceResourceId": "string",
+ "entitlementPoolUUID": [
+ "string"
+ ],
+ "licenseKeyGroupUUID": [
+ "string"
+ ],
+ "entitlementPoolInvariantUUID": [
+ "string"
+ ],
+ "licenseKeyGroupInvariantUUID": [
+ "string"
+ ]
+ }
+ ]
+ }
+ }
+ PlaceVnfs(resp).extract()
+ db_info = OOFDataModel.objects.filter(request_id=resp.get("requestId"), transaction_id=resp.get("transactionId"))
+ self.assertEqual(db_info[0].request_status, "pending")
+ self.assertEqual(db_info[0].vim_id, "none")
+ self.assertEqual(db_info[0].cloud_owner, "none")
+ self.assertEqual(db_info[0].cloud_region_id, "none")
+ self.assertEqual(db_info[0].vdu_info, "none")
+
+ def test_place_vnf_with_no_assignment_info(self):
+ resp = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "statusMessage": "xx",
+ "requestStatus": "completed",
+ "solutions": {
+ "placementSolutions": [
+ [
+ {
+ "resourceModuleName": self.vnf_inst_name,
+ "serviceResourceId": self.vnf_inst_id,
+ "solution": {
+ "identifierType": "serviceInstanceId",
+ "identifiers": [
+ "xx"
+ ],
+ "cloudOwner": "CloudOwner1 "
+ }
+ }
+ ]
+ ],
+ "licenseSolutions": [
+ {
+ "resourceModuleName": "string",
+ "serviceResourceId": "string",
+ "entitlementPoolUUID": [
+ "string"
+ ],
+ "licenseKeyGroupUUID": [
+ "string"
+ ],
+ "entitlementPoolInvariantUUID": [
+ "string"
+ ],
+ "licenseKeyGroupInvariantUUID": [
+ "string"
+ ]
+ }
+ ]
+ }
+ }
+ PlaceVnfs(resp).extract()
+ db_info = OOFDataModel.objects.filter(request_id=resp.get("requestId"), transaction_id=resp.get("transactionId"))
+ self.assertEqual(db_info[0].request_status, "completed")
+ self.assertEqual(db_info[0].vim_id, "none")
+ self.assertEqual(db_info[0].cloud_owner, "none")
+ self.assertEqual(db_info[0].cloud_region_id, "none")
+ self.assertEqual(db_info[0].vdu_info, "none")
+
+ def test_place_vnf_no_directives(self):
+ resp = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "statusMessage": "xx",
+ "requestStatus": "completed",
+ "solutions": {
+ "placementSolutions": [
+ [
+ {
+ "resourceModuleName": self.vnf_inst_name,
+ "serviceResourceId": self.vnf_inst_id,
+ "solution": {
+ "identifierType": "serviceInstanceId",
+ "identifiers": [
+ "xx"
+ ],
+ "cloudOwner": "CloudOwner1 "
+ },
+ "assignmentInfo": [
+ {"key": "locationId",
+ "value": "DLLSTX1A"
+ }
+ ]
+ }
+ ]
+ ],
+ "licenseSoutions": [
+ {
+ "resourceModuleName": "string",
+ "serviceResourceId": "string",
+ "entitlementPoolUUID": [
+ "string"
+ ],
+ "licenseKeyGroupUUID": [
+ "string"
+ ],
+ "entitlementPoolInvariantUUID": [
+ "string"
+ ],
+ "licenseKeyGroupInvariantUUID": [
+ "string"
+ ]
+ }
+ ]
+ }
+ }
+ PlaceVnfs(resp).extract()
+ db_info = OOFDataModel.objects.filter(request_id=resp.get("requestId"), transaction_id=resp.get("transactionId"))
+ self.assertEqual(db_info[0].request_status, "completed")
+ self.assertEqual(db_info[0].vim_id, "none")
+ self.assertEqual(db_info[0].cloud_owner, "none")
+ self.assertEqual(db_info[0].cloud_region_id, "none")
+ self.assertEqual(db_info[0].vdu_info, "none")
+
+ def test_place_vnf_with_no_solution(self):
+ resp = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "statusMessage": "xx",
+ "requestStatus": "completed",
+ "solutions": {
+ "placementSolutions": [],
+ "licenseSoutions": []
+ }
+ }
+ PlaceVnfs(resp).extract()
+ db_info = OOFDataModel.objects.filter(request_id=resp.get("requestId"), transaction_id=resp.get("transactionId"))
+ self.assertEqual(db_info[0].request_status, "completed")
+ self.assertEqual(db_info[0].vim_id, "no-solution")
+ self.assertEqual(db_info[0].cloud_owner, "no-solution")
+ self.assertEqual(db_info[0].cloud_region_id, "no-solution")
+ self.assertEqual(db_info[0].vdu_info, "no-solution")
+
+
vnfd_model_dict = {
'local_storages': [],
'vdus': [
@@ -1377,3 +1643,97 @@ nf_package_info = {
},
"imageInfo": []
}
+
+vnf_place_request = {
+ "requestId": "1234",
+ "transactionId": "1234",
+ "statusMessage": "xx",
+ "requestStatus": "completed",
+ "solutions": {
+ "placementSolutions": [
+ [
+ {
+ "resourceModuleName": "vG",
+ "serviceResourceId": "1234",
+ "solution": {
+ "identifierType": "serviceInstanceId",
+ "identifiers": [
+ "xx"
+ ],
+ "cloudOwner": "CloudOwner1"
+ },
+ "assignmentInfo": [
+ {"key": "isRehome",
+ "value": "false"
+ },
+ {"key": "locationId",
+ "value": "DLLSTX1A"
+ },
+ {"key": "locationType",
+ "value": "openstack-cloud"
+ },
+ {"key": "vimId",
+ "value": "CloudOwner1_DLLSTX1A"
+ },
+ {"key": "physicalLocationId",
+ "value": "DLLSTX1223"
+ },
+ {"key": "oofDirectives",
+ "value": {
+ "directives": [
+ {
+ "id": "vG_0",
+ "type": "tocsa.nodes.nfv.Vdu.Compute",
+ "directives": [
+ {
+ "type": "flavor_directive",
+ "attributes": [
+ {
+ "attribute_name": "flavor_name",
+ "attribute_value": "HPA.flavor.1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "",
+ "type": "vnf",
+ "directives": [
+ {"type": " ",
+ "attributes": [
+ {
+ "attribute_name": " ",
+ "attribute_value": " "
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ ],
+ "licenseSolutions": [
+ {
+ "resourceModuleName": "string",
+ "serviceResourceId": "string",
+ "entitlementPoolUUID": [
+ "string"
+ ],
+ "licenseKeyGroupUUID": [
+ "string"
+ ],
+ "entitlementPoolInvariantUUID": [
+ "string"
+ ],
+ "licenseKeyGroupInvariantUUID": [
+ "string"
+ ]
+ }
+ ]
+ }
+}
diff --git a/lcm/pub/config/config.py b/lcm/pub/config/config.py
index ebffddca..9d63b254 100644
--- a/lcm/pub/config/config.py
+++ b/lcm/pub/config/config.py
@@ -68,3 +68,8 @@ MR_PORT = '3904'
DEPLOY_WORKFLOW_WHEN_START = False
# Support option: activiti/wso2/buildin
WORKFLOW_OPTION = "buildin"
+
+# [OOF config]
+OOF_BASE_URL = "http://oof.api.simpledemo.onap.org:8698"
+OOF_USER = "vfc_test"
+OOF_PASSWD = "vfc_testpwd"
diff --git a/lcm/pub/database/models.py b/lcm/pub/database/models.py
index a078c6a8..7c6fee73 100644
--- a/lcm/pub/database/models.py
+++ b/lcm/pub/database/models.py
@@ -36,6 +36,7 @@ class NSInstModel(models.Model):
name = models.CharField(db_column='NAME', max_length=200)
nspackage_id = models.CharField(db_column='NSPACKAGEID', max_length=200, null=True, blank=True)
nsd_id = models.CharField(db_column='NSDID', max_length=200)
+ nsd_invariant_id = models.CharField(db_column='NSDINVARIANTID', max_length=200)
description = models.CharField(db_column='DESCRIPTION', max_length=255, null=True, blank=True)
sdncontroller_id = models.CharField(db_column='SDNCONTROLLERID', max_length=200, null=True, blank=True)
flavour_id = models.CharField(db_column='FLAVOURID', max_length=200, null=True, blank=True)