Add VNF resource details to get vnf API
Adds physical id and type of VDUs, CPs and VLs of a VNF to get vnf API. APIImpact Change-Id: I469b91c1a000e7a47ae1d4313ed83de26e1391b2 Partial-Bug: #1602112
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added API to fetch VNF components details.
|
||||||
@@ -140,6 +140,15 @@ class FilePathMissing(exceptions.InvalidInput):
|
|||||||
"tosca.artifacts.Deployment.Image.VM artifact type")
|
"tosca.artifacts.Deployment.Image.VM artifact type")
|
||||||
|
|
||||||
|
|
||||||
|
class InfraDriverUnreachable(exceptions.ServiceUnavailable):
|
||||||
|
message = _("Could not retrieve VNF resource IDs and"
|
||||||
|
" types. Please check %(service)s status.")
|
||||||
|
|
||||||
|
|
||||||
|
class VNFInactive(exceptions.InvalidInput):
|
||||||
|
message = _("VNF %(vnf_id)s is not in Active state %(message)s")
|
||||||
|
|
||||||
|
|
||||||
def _validate_service_type_list(data, valid_values=None):
|
def _validate_service_type_list(data, valid_values=None):
|
||||||
if not isinstance(data, list):
|
if not isinstance(data, list):
|
||||||
msg = _("invalid data format for service list: '%s'") % data
|
msg = _("invalid data format for service list: '%s'") % data
|
||||||
@@ -358,6 +367,33 @@ SUB_RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'resources': {
|
||||||
|
'parent': {
|
||||||
|
'collection_name': 'vnfs',
|
||||||
|
'member_name': 'vnf'
|
||||||
|
},
|
||||||
|
'members': {
|
||||||
|
'resource': {
|
||||||
|
'parameters': {
|
||||||
|
'name': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'type': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,6 +500,10 @@ class VNFMPluginBase(service_base.NFVPluginBase):
|
|||||||
def get_vnf(self, context, vnf_id, fields=None):
|
def get_vnf(self, context, vnf_id, fields=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vnf_resources(self, context, vnf_id, fields=None, filters=None):
|
||||||
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_vnf(self, context, vnf):
|
def create_vnf(self, context, vnf):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ class VnfTestCreate(base.BaseTackerTest):
|
|||||||
if vim_id:
|
if vim_id:
|
||||||
self.assertEqual(vim_id, vnf_instance['vnf']['vim_id'])
|
self.assertEqual(vim_id, vnf_instance['vnf']['vim_id'])
|
||||||
|
|
||||||
|
# Get vnf details when vnf is in active state
|
||||||
|
vnf_details = self.client.list_vnf_resources(vnf_id)['resources'][0]
|
||||||
|
self.assertIn('name', vnf_details)
|
||||||
|
self.assertIn('id', vnf_details)
|
||||||
|
self.assertIn('type', vnf_details)
|
||||||
|
|
||||||
# Delete vnf_instance with vnf_id
|
# Delete vnf_instance with vnf_id
|
||||||
try:
|
try:
|
||||||
self.client.delete_vnf(vnf_id)
|
self.client.delete_vnf(vnf_id)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import os
|
|||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from tacker import context
|
from tacker import context
|
||||||
|
from tacker.extensions import vnfm
|
||||||
from tacker.tests.unit import base
|
from tacker.tests.unit import base
|
||||||
from tacker.tests.unit.db import utils
|
from tacker.tests.unit.db import utils
|
||||||
from tacker.vnfm.infra_drivers.heat import heat
|
from tacker.vnfm.infra_drivers.heat import heat
|
||||||
@@ -143,6 +144,28 @@ class TestDeviceHeat(base.TestCase):
|
|||||||
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123', 'description':
|
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123', 'description':
|
||||||
u'OpenWRT with services'}
|
u'OpenWRT with services'}
|
||||||
|
|
||||||
|
def _get_expected_active_vnf(self):
|
||||||
|
return {'status': 'ACTIVE',
|
||||||
|
'instance_id': None,
|
||||||
|
'name': u'test_openwrt',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'vnfd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
|
'vnfd': {
|
||||||
|
'service_types': [{
|
||||||
|
'service_type': u'vnfd',
|
||||||
|
'id': u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}],
|
||||||
|
'description': u'OpenWRT with services',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'mgmt_driver': u'openwrt',
|
||||||
|
'infra_driver': u'heat',
|
||||||
|
'attributes': {u'vnfd': self.vnfd_openwrt},
|
||||||
|
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec',
|
||||||
|
'name': u'openwrt_services'},
|
||||||
|
'mgmt_url': '{"vdu1": "192.168.120.31"}',
|
||||||
|
'service_context': [],
|
||||||
|
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123',
|
||||||
|
'description': u'OpenWRT with services'}
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
vnf_obj = utils.get_dummy_device_obj()
|
vnf_obj = utils.get_dummy_device_obj()
|
||||||
expected_result = '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'
|
expected_result = '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'
|
||||||
@@ -408,3 +431,12 @@ class TestDeviceHeat(base.TestCase):
|
|||||||
files={'scaling.yaml': 'hot_scale_custom.yaml'},
|
files={'scaling.yaml': 'hot_scale_custom.yaml'},
|
||||||
is_monitor=False
|
is_monitor=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_get_resource_info(self):
|
||||||
|
vnf_obj = self._get_expected_active_vnf()
|
||||||
|
print(vnf_obj)
|
||||||
|
self.assertRaises(vnfm.InfraDriverUnreachable,
|
||||||
|
self.heat_driver.get_resource_info,
|
||||||
|
plugin=None, context=self.context, vnf_info=vnf_obj,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
region_name=None)
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class FakeDriverManager(mock.Mock):
|
|||||||
def invoke(self, *args, **kwargs):
|
def invoke(self, *args, **kwargs):
|
||||||
if 'create' in args:
|
if 'create' in args:
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
if 'get_resource_info' in args:
|
||||||
|
return {'resources': {'name': 'dummy_vnf',
|
||||||
|
'type': 'dummy',
|
||||||
|
'id': str(uuid.uuid4())}}
|
||||||
|
|
||||||
|
|
||||||
class FakeVNFMonitor(mock.Mock):
|
class FakeVNFMonitor(mock.Mock):
|
||||||
@@ -208,6 +212,22 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
|||||||
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
||||||
tstamp=mock.ANY, details=mock.ANY)
|
tstamp=mock.ANY, details=mock.ANY)
|
||||||
|
|
||||||
|
def test_show_vnf_details_vnf_inactive(self):
|
||||||
|
self._insert_dummy_device_template()
|
||||||
|
vnf_obj = utils.get_dummy_vnf_obj()
|
||||||
|
result = self.vnfm_plugin.create_vnf(self.context, vnf_obj)
|
||||||
|
self.assertRaises(vnfm.VNFInactive, self.vnfm_plugin.get_vnf_resources,
|
||||||
|
self.context, result['id'])
|
||||||
|
|
||||||
|
def test_show_vnf_details_vnf_active(self):
|
||||||
|
self._insert_dummy_device_template()
|
||||||
|
active_vnf = self._insert_dummy_device()
|
||||||
|
resources = self.vnfm_plugin.get_vnf_resources(self.context,
|
||||||
|
active_vnf['id'])[0]
|
||||||
|
self.assertIn('name', resources)
|
||||||
|
self.assertIn('type', resources)
|
||||||
|
self.assertIn('id', resources)
|
||||||
|
|
||||||
def test_delete_vnf(self):
|
def test_delete_vnf(self):
|
||||||
self._insert_dummy_device_template()
|
self._insert_dummy_device_template()
|
||||||
dummy_device_obj = self._insert_dummy_device()
|
dummy_device_obj = self._insert_dummy_device()
|
||||||
|
|||||||
@@ -629,3 +629,24 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
self._handle_vnf_scaling(context, policy_)
|
self._handle_vnf_scaling(context, policy_)
|
||||||
|
|
||||||
return scale['scale']
|
return scale['scale']
|
||||||
|
|
||||||
|
def get_vnf_resources(self, context, vnf_id, fields=None, filters=None):
|
||||||
|
vnf_info = self.get_vnf(context, vnf_id)
|
||||||
|
infra_driver = vnf_info['vnfd']['infra_driver']
|
||||||
|
auth = self.get_vim(context, vnf_info)
|
||||||
|
if vnf_info['status'] == constants.ACTIVE:
|
||||||
|
vnf_details = self._vnf_manager.invoke(infra_driver,
|
||||||
|
'get_resource_info',
|
||||||
|
plugin=self,
|
||||||
|
context=context,
|
||||||
|
vnf_info=vnf_info,
|
||||||
|
auth_attr=auth)
|
||||||
|
resources = [{'name': name,
|
||||||
|
'type': info.get('type'),
|
||||||
|
'id': info.get('id')}
|
||||||
|
for name, info in vnf_details.items()]
|
||||||
|
return resources
|
||||||
|
# Raise exception when VNF.status != ACTIVE
|
||||||
|
else:
|
||||||
|
raise vnfm.VNFInactive(vnf_id=vnf_id,
|
||||||
|
message=_(' Cannot fetch details'))
|
||||||
|
|||||||
@@ -68,3 +68,9 @@ class DeviceAbstractDriver(extensions.PluginInterface):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def delete_wait(self, plugin, context, vnf_id):
|
def delete_wait(self, plugin, context, vnf_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_resource_info(self, plugin, context, vnf_info, auth_attr,
|
||||||
|
region_name=None):
|
||||||
|
'''Fetches optional details of a VNF'''
|
||||||
|
pass
|
||||||
|
|||||||
@@ -854,6 +854,21 @@ class DeviceHeat(abstract_driver.DeviceAbstractDriver,
|
|||||||
|
|
||||||
return jsonutils.dumps(mgmt_ips)
|
return jsonutils.dumps(mgmt_ips)
|
||||||
|
|
||||||
|
def get_resource_info(self, plugin, context, vnf_info, auth_attr,
|
||||||
|
region_name=None):
|
||||||
|
stack_id = vnf_info['instance_id']
|
||||||
|
heatclient_ = HeatClient(auth_attr, region_name)
|
||||||
|
try:
|
||||||
|
resources_ids = heatclient_.resource_get_list(stack_id)
|
||||||
|
details_dict = {resource.resource_name:
|
||||||
|
{"id": resource.physical_resource_id,
|
||||||
|
"type": resource.resource_type}
|
||||||
|
for resource in resources_ids}
|
||||||
|
return details_dict
|
||||||
|
# Raise exception when Heat API service is not available
|
||||||
|
except Exception:
|
||||||
|
raise vnfm.InfraDriverUnreachable(service="Heat API service")
|
||||||
|
|
||||||
|
|
||||||
class HeatClient(object):
|
class HeatClient(object):
|
||||||
def __init__(self, auth_attr, region_name=None):
|
def __init__(self, auth_attr, region_name=None):
|
||||||
@@ -861,6 +876,7 @@ class HeatClient(object):
|
|||||||
self.heat = clients.OpenstackClients(auth_attr, region_name).heat
|
self.heat = clients.OpenstackClients(auth_attr, region_name).heat
|
||||||
self.stacks = self.heat.stacks
|
self.stacks = self.heat.stacks
|
||||||
self.resource_types = self.heat.resource_types
|
self.resource_types = self.heat.resource_types
|
||||||
|
self.resources = self.heat.resources
|
||||||
|
|
||||||
def create(self, fields):
|
def create(self, fields):
|
||||||
fields = fields.copy()
|
fields = fields.copy()
|
||||||
|
|||||||
Reference in New Issue
Block a user