Add V3 api for pci support

All pci passthrough code is merged into nova, but the APIs are not supported.
This patch provides a resource: endpoint to show PCI information. This is a
part of PCI APIs, for more information see the blueprint.

Partially implement blueprint pci-api-support

Change-Id: I05c502cb7564b8b71f12d8f06249124b3d2b370f
This commit is contained in:
Shuangtai Tian
2013-10-17 16:00:50 +08:00
parent a1fe8dfd58
commit 4d8c9a9040
11 changed files with 375 additions and 1 deletions

View File

@@ -0,0 +1,36 @@
{
"pci_devices": [
{
"address": "0000:04:10.0",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_0",
"dev_type": "type-VF",
"extra_info": {
"key1": "value1",
"key2": "value2"
},
"id": 1,
"server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
},
{
"address": "0000:04:10.1",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_1",
"dev_type": "type-VF",
"extra_info": {
"key3": "value3",
"key4": "value4"
},
"id": 2,
"server_uuid": "d5b446a6-a1b4-4d01-b4f0-eac37b3a62fc",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"pci_devices": [
{
"address": "0000:04:10.0",
"compute_node_id": 1,
"id": 1,
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
},
{
"address": "0000:04:10.1",
"compute_node_id": 1,
"id": 2,
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
]
}

View File

@@ -0,0 +1,18 @@
{
"pci_device": {
"address": "0000:04:10.0",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_0",
"dev_type": "type-VF",
"extra_info": {
"key1": "value1",
"key2": "value2"
},
"id": 1,
"server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
}

View File

@@ -189,6 +189,9 @@
"compute_extension:v3:os-pause-server:unpause": "rule:admin_or_owner",
"compute_extension:v3:os-pci:pci_servers": "",
"compute_extension:v3:os-pci:discoverable": "",
"compute_extension:v3:os-pci:index": "rule:admin_api",
"compute_extension:v3:os-pci:detail": "rule:admin_api",
"compute_extension:v3:os-pci:show": "rule:admin_api",
"compute_extension:quotas:show": "",
"compute_extension:quotas:update": "rule:admin_api",
"compute_extension:quotas:delete": "rule:admin_api",

View File

@@ -14,8 +14,13 @@
# under the License.
import webob.exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova import compute
from nova import exception
from nova.objects import pci_device
from nova.openstack.common import jsonutils
@@ -23,6 +28,13 @@ ALIAS = 'os-pci'
instance_authorize = extensions.soft_extension_authorizer(
'compute', 'v3:' + ALIAS + ':pci_servers')
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
PCI_ADMIN_KEYS = ['id', 'address', 'vendor_id', 'product_id', 'status',
'compute_node_id']
PCI_DETAIL_KEYS = ['dev_type', 'label', 'instance_uuid', 'dev_id',
'extra_info']
class PciServerController(wsgi.Controller):
def _extend_server(self, server, instance):
@@ -68,6 +80,57 @@ class PciHypervisorController(wsgi.Controller):
self._extend_hypervisor(hypervisor, compute_node)
class PciController(object):
def __init__(self):
self.host_api = compute.HostAPI()
def _view_pcidevice(self, device, detail=False):
dev_dict = {}
for key in PCI_ADMIN_KEYS:
dev_dict[key] = device[key]
if detail:
for field in PCI_DETAIL_KEYS:
if field == 'instance_uuid':
dev_dict['server_uuid'] = device[field]
else:
dev_dict[field] = device[field]
return dev_dict
def _get_all_nodes_pci_devices(self, req, detail, action):
context = req.environ['nova.context']
authorize(context, action=action)
compute_nodes = self.host_api.compute_node_get_all(context)
results = []
for node in compute_nodes:
pci_devs = pci_device.PciDeviceList.get_by_compute_node(
context, node['id'])
results.extend([self._view_pcidevice(dev, detail)
for dev in pci_devs])
return results
@extensions.expected_errors(())
def detail(self, req):
results = self._get_all_nodes_pci_devices(req, True, 'detail')
return dict(pci_devices=results)
@extensions.expected_errors(404)
def show(self, req, id):
context = req.environ['nova.context']
authorize(context, action='show')
try:
pci_dev = pci_device.PciDevice.get_by_dev_id(context, id)
except exception.PciDeviceNotFoundById as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
result = self._view_pcidevice(pci_dev, True)
return dict(pci_device=result)
@extensions.expected_errors(())
def index(self, req):
results = self._get_all_nodes_pci_devices(req, False, 'index')
return dict(pci_devices=results)
class Pci(extensions.V3APIExtensionBase):
"""Pci access support."""
name = "PCIAccess"
@@ -75,7 +138,10 @@ class Pci(extensions.V3APIExtensionBase):
version = 1
def get_resources(self):
return []
resources = [extensions.ResourceExtension(ALIAS,
PciController(),
collection_actions={'detail': 'GET'})]
return resources
def get_controller_extensions(self):
server_extension = extensions.ControllerExtension(

View File

@@ -13,10 +13,13 @@
# under the License.
from webob import exc
from nova.api.openstack.compute.plugins.v3 import pci
from nova.api.openstack import wsgi
from nova import context
from nova import db
from nova import exception
from nova.objects import instance
from nova.objects import pci_device
from nova.openstack.common import jsonutils
@@ -148,3 +151,86 @@ class PciHypervisorControllerTest(test.NoDBTestCase):
self.assertIn('os-pci:pci_stats', resp.obj['hypervisors'][0])
self.assertEqual(fake_compute_node['pci_stats'][0],
resp.obj['hypervisors'][0]['os-pci:pci_stats'][0])
class PciControlletest(test.NoDBTestCase):
def setUp(self):
super(PciControlletest, self).setUp()
self.controller = pci.PciController()
def test_show(self):
def fake_pci_device_get_by_id(context, id):
return test_pci_device.fake_db_dev
self.stubs.Set(db, 'pci_device_get_by_id', fake_pci_device_get_by_id)
req = fakes.HTTPRequestV3.blank('/os-pci/1', use_admin_context=True)
result = self.controller.show(req, '1')
dist = {'pci_device': {'address': 'a',
'compute_node_id': 1,
'dev_id': 'i',
'extra_info': {},
'dev_type': 't',
'id': 1,
'server_uuid': None,
'label': 'l',
'product_id': 'p',
'status': 'available',
'vendor_id': 'v'}}
self.assertEqual(dist, result)
def test_show_error_id(self):
def fake_pci_device_get_by_id(context, id):
raise exception.PciDeviceNotFoundById(id=id)
self.stubs.Set(db, 'pci_device_get_by_id', fake_pci_device_get_by_id)
req = fakes.HTTPRequestV3.blank('/os-pci/0', use_admin_context=True)
self.assertRaises(exc.HTTPNotFound, self.controller.show, req, '0')
def _fake_compute_node_get_all(self, context):
return [dict(id=1,
service_id=1,
cpu_info='cpu_info',
disk_available_least=100)]
def _fake_pci_device_get_all_by_node(self, context, node):
return [test_pci_device.fake_db_dev, test_pci_device.fake_db_dev_1]
def test_index(self):
self.stubs.Set(db, 'compute_node_get_all',
self._fake_compute_node_get_all)
self.stubs.Set(db, 'pci_device_get_all_by_node',
self._fake_pci_device_get_all_by_node)
req = fakes.HTTPRequestV3.blank('/os-pci', use_admin_context=True)
result = self.controller.index(req)
dist = {'pci_devices': [test_pci_device.fake_db_dev,
test_pci_device.fake_db_dev_1]}
for i in range(len(result['pci_devices'])):
self.assertEqual(dist['pci_devices'][i]['vendor_id'],
result['pci_devices'][i]['vendor_id'])
self.assertEqual(dist['pci_devices'][i]['id'],
result['pci_devices'][i]['id'])
self.assertEqual(dist['pci_devices'][i]['status'],
result['pci_devices'][i]['status'])
self.assertEqual(dist['pci_devices'][i]['address'],
result['pci_devices'][i]['address'])
def test_detail(self):
self.stubs.Set(db, 'compute_node_get_all',
self._fake_compute_node_get_all)
self.stubs.Set(db, 'pci_device_get_all_by_node',
self._fake_pci_device_get_all_by_node)
req = fakes.HTTPRequestV3.blank('/os-pci/detail',
use_admin_context=True)
result = self.controller.detail(req)
dist = {'pci_devices': [test_pci_device.fake_db_dev,
test_pci_device.fake_db_dev_1]}
for i in range(len(result['pci_devices'])):
self.assertEqual(dist['pci_devices'][i]['vendor_id'],
result['pci_devices'][i]['vendor_id'])
self.assertEqual(dist['pci_devices'][i]['id'],
result['pci_devices'][i]['id'])
self.assertEqual(dist['pci_devices'][i]['label'],
result['pci_devices'][i]['label'])
self.assertEqual(dist['pci_devices'][i]['dev_id'],
result['pci_devices'][i]['dev_id'])

View File

@@ -241,6 +241,9 @@ policy_data = """
"compute_extension:v3:os-pause-server:pause": "",
"compute_extension:v3:os-pause-server:unpause": "",
"compute_extension:v3:os-pci:pci_servers": "",
"compute_extension:v3:os-pci:index": "",
"compute_extension:v3:os-pci:detail": "",
"compute_extension:v3:os-pci:show": "",
"compute_extension:quotas:show": "",
"compute_extension:quotas:update": "",
"compute_extension:quotas:delete": "",

View File

@@ -0,0 +1,36 @@
{
"pci_devices": [
{
"address": "0000:04:10.0",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_0",
"dev_type": "type-VF",
"extra_info": {
"key1": "value1",
"key2": "value2"
},
"id": 1,
"server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
},
{
"address": "0000:04:10.1",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_1",
"dev_type": "type-VF",
"extra_info": {
"key3": "value3",
"key4": "value4"
},
"id": 2,
"server_uuid": "d5b446a6-a1b4-4d01-b4f0-eac37b3a62fc",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"pci_devices": [
{
"address": "0000:04:10.0",
"compute_node_id": 1,
"id": 1,
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
},
{
"address": "0000:04:10.1",
"compute_node_id": 1,
"id": 2,
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
]
}

View File

@@ -0,0 +1,18 @@
{
"pci_device": {
"address": "0000:04:10.0",
"compute_node_id": 1,
"dev_id": "pci_0000_04_10_0",
"dev_type": "type-VF",
"extra_info": {
"key1": "value1",
"key2": "value2"
},
"id": 1,
"server_uuid": "69ba1044-0766-4ec0-b60d-09595de034a1",
"label": "label_8086_1520",
"product_id": "1520",
"status": "available",
"vendor_id": "8086"
}
}

View File

@@ -18,6 +18,43 @@ from nova.tests.integrated.v3 import api_sample_base
from nova.tests.integrated.v3 import test_servers
fake_db_dev_1 = {
'created_at': None,
'updated_at': None,
'deleted_at': None,
'deleted': None,
'id': 1,
'compute_node_id': 1,
'address': '0000:04:10.0',
'vendor_id': '8086',
'product_id': '1520',
'dev_type': 'type-VF',
'status': 'available',
'dev_id': 'pci_0000_04_10_0',
'label': 'label_8086_1520',
'instance_uuid': '69ba1044-0766-4ec0-b60d-09595de034a1',
'extra_info': '{"key1": "value1", "key2": "value2"}'
}
fake_db_dev_2 = {
'created_at': None,
'updated_at': None,
'deleted_at': None,
'deleted': None,
'id': 2,
'compute_node_id': 1,
'address': '0000:04:10.1',
'vendor_id': '8086',
'product_id': '1520',
'dev_type': 'type-VF',
'status': 'available',
'dev_id': 'pci_0000_04_10_1',
'label': 'label_8086_1520',
'instance_uuid': 'd5b446a6-a1b4-4d01-b4f0-eac37b3a62fc',
'extra_info': '{"key3": "value3", "key4": "value4"}'
}
class ExtendedServerPciSampleJsonTest(test_servers.ServersSampleBase):
extension_name = "os-pci"
@@ -104,3 +141,34 @@ class ExtendedHyervisorPciSampleJsonTest(api_sample_base.ApiSampleTestBaseV3):
subs.update(self._get_regexes())
self._verify_response('hypervisors-pci-detail-resp',
subs, response, 200)
class PciSampleJsonTest(api_sample_base.ApiSampleTestBaseV3):
extension_name = "os-pci"
def _fake_pci_device_get_by_id(self, context, id):
return fake_db_dev_1
def _fake_pci_device_get_all_by_node(self, context, id):
return [fake_db_dev_1, fake_db_dev_2]
def test_pci_show(self):
self.stubs.Set(db, 'pci_device_get_by_id',
self._fake_pci_device_get_by_id)
response = self._do_get('os-pci/1')
subs = self._get_regexes()
self._verify_response('pci-show-resp', subs, response, 200)
def test_pci_index(self):
self.stubs.Set(db, 'pci_device_get_all_by_node',
self._fake_pci_device_get_all_by_node)
response = self._do_get('os-pci')
subs = self._get_regexes()
self._verify_response('pci-index-resp', subs, response, 200)
def test_pci_detail(self):
self.stubs.Set(db, 'pci_device_get_all_by_node',
self._fake_pci_device_get_all_by_node)
response = self._do_get('os-pci/detail')
subs = self._get_regexes()
self._verify_response('pci-detail-resp', subs, response, 200)