Merge "Separate the PCI device object handling code"
This commit is contained in:
commit
ee051ea6bc
121
nova/pci/pci_device.py
Normal file
121
nova/pci/pci_device.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
# Copyright 2014 Intel Corporation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 copy
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from nova import exception
|
||||||
|
|
||||||
|
|
||||||
|
def check_device_status(dev_status=None):
|
||||||
|
"""Decorator to check device status before changing it."""
|
||||||
|
|
||||||
|
if dev_status is not None and not isinstance(dev_status, set):
|
||||||
|
dev_status = set(dev_status)
|
||||||
|
|
||||||
|
def outer(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(devobj, instance=None):
|
||||||
|
if devobj['status'] not in dev_status:
|
||||||
|
raise exception.PciDeviceInvalidStatus(
|
||||||
|
compute_node_id=devobj.compute_node_id,
|
||||||
|
address=devobj.address, status=devobj.status,
|
||||||
|
hopestatus=dev_status)
|
||||||
|
if instance:
|
||||||
|
return f(devobj, instance)
|
||||||
|
else:
|
||||||
|
return f(devobj)
|
||||||
|
return inner
|
||||||
|
return outer
|
||||||
|
|
||||||
|
|
||||||
|
@check_device_status(dev_status=['available'])
|
||||||
|
def claim(devobj, instance):
|
||||||
|
devobj.status = 'claimed'
|
||||||
|
devobj.instance_uuid = instance['uuid']
|
||||||
|
|
||||||
|
|
||||||
|
@check_device_status(dev_status=['available', 'claimed'])
|
||||||
|
def allocate(devobj, instance):
|
||||||
|
if devobj.status == 'claimed' and devobj.instance_uuid != instance['uuid']:
|
||||||
|
raise exception.PciDeviceInvalidOwner(
|
||||||
|
compute_node_id=devobj.compute_node_id,
|
||||||
|
address=devobj.address, owner=devobj.instance_uuid,
|
||||||
|
hopeowner=instance['uuid'])
|
||||||
|
|
||||||
|
devobj.status = 'allocated'
|
||||||
|
devobj.instance_uuid = instance['uuid']
|
||||||
|
|
||||||
|
# Notes(yjiang5): remove this check when instance object for
|
||||||
|
# compute manager is finished
|
||||||
|
if isinstance(instance, dict):
|
||||||
|
if 'pci_devices' not in instance:
|
||||||
|
instance['pci_devices'] = []
|
||||||
|
instance['pci_devices'].append(copy.copy(devobj))
|
||||||
|
else:
|
||||||
|
instance.pci_devices.objects.append(copy.copy(devobj))
|
||||||
|
|
||||||
|
|
||||||
|
@check_device_status(dev_status=['available'])
|
||||||
|
def remove(devobj):
|
||||||
|
devobj.status = 'removed'
|
||||||
|
devobj.instance_uuid = None
|
||||||
|
|
||||||
|
|
||||||
|
@check_device_status(dev_status=['claimed', 'allocated'])
|
||||||
|
def free(devobj, instance=None):
|
||||||
|
if instance and devobj.instance_uuid != instance['uuid']:
|
||||||
|
raise exception.PciDeviceInvalidOwner(
|
||||||
|
compute_node_id=devobj.compute_node_id,
|
||||||
|
address=devobj.address, owner=devobj.instance_uuid,
|
||||||
|
hopeowner=instance['uuid'])
|
||||||
|
old_status = devobj.status
|
||||||
|
devobj.status = 'available'
|
||||||
|
devobj.instance_uuid = None
|
||||||
|
if old_status == 'allocated' and instance:
|
||||||
|
# Notes(yjiang5): remove this check when instance object for
|
||||||
|
# compute manager is finished
|
||||||
|
existed = next((dev for dev in instance['pci_devices']
|
||||||
|
if dev.id == devobj.id))
|
||||||
|
if isinstance(instance, dict):
|
||||||
|
instance['pci_devices'].remove(existed)
|
||||||
|
else:
|
||||||
|
instance.pci_devices.objects.remove(existed)
|
||||||
|
|
||||||
|
|
||||||
|
def update_device(devobj, dev_dict):
|
||||||
|
"""Sync the content from device dictionary to device object.
|
||||||
|
|
||||||
|
The resource tracker updates the available devices periodically.
|
||||||
|
To avoid meaningless syncs with the database, we update the device
|
||||||
|
object only if a value changed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Note(yjiang5): status/instance_uuid should only be updated by
|
||||||
|
# functions like claim/allocate etc. The id is allocated by
|
||||||
|
# database. The extra_info is created by the object.
|
||||||
|
no_changes = ('status', 'instance_uuid', 'id', 'extra_info')
|
||||||
|
map(lambda x: dev_dict.pop(x, None),
|
||||||
|
[key for key in no_changes])
|
||||||
|
|
||||||
|
for k, v in dev_dict.items():
|
||||||
|
if k in devobj.fields.keys():
|
||||||
|
devobj[k] = v
|
||||||
|
else:
|
||||||
|
# Note (yjiang5) extra_info.update does not update
|
||||||
|
# obj_what_changed, set it explicitely
|
||||||
|
extra_info = devobj.extra_info
|
||||||
|
extra_info.update({k: v})
|
||||||
|
devobj.extra_info = extra_info
|
119
nova/tests/pci/test_pci_device.py
Normal file
119
nova/tests/pci/test_pci_device.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# Copyright 2014 Intel Corporation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 nova import context
|
||||||
|
from nova import exception
|
||||||
|
from nova.objects import instance
|
||||||
|
from nova.objects import pci_device as pci_device_obj
|
||||||
|
from nova.pci import pci_device
|
||||||
|
from nova import test
|
||||||
|
|
||||||
|
|
||||||
|
dev_dict = {
|
||||||
|
'created_at': None,
|
||||||
|
'updated_at': None,
|
||||||
|
'deleted_at': None,
|
||||||
|
'deleted': None,
|
||||||
|
'id': 1,
|
||||||
|
'compute_node_id': 1,
|
||||||
|
'address': 'a',
|
||||||
|
'vendor_id': 'v',
|
||||||
|
'product_id': 'p',
|
||||||
|
'dev_type': 't',
|
||||||
|
'status': 'available',
|
||||||
|
'dev_id': 'i',
|
||||||
|
'label': 'l',
|
||||||
|
'instance_uuid': None,
|
||||||
|
'extra_info': '{}',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PciDeviceTestCase(test.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(PciDeviceTestCase, self).setUp()
|
||||||
|
self.ctxt = context.get_admin_context()
|
||||||
|
self.inst = instance.Instance()
|
||||||
|
self.inst.uuid = 'fake-inst-uuid'
|
||||||
|
self.inst.pci_devices = pci_device_obj.PciDeviceList()
|
||||||
|
self.devobj = pci_device_obj.PciDevice._from_db_object(
|
||||||
|
self.ctxt,
|
||||||
|
pci_device_obj.PciDevice(),
|
||||||
|
dev_dict)
|
||||||
|
|
||||||
|
def test_claim_device(self):
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
self.assertEqual(self.devobj.status, 'claimed')
|
||||||
|
self.assertEqual(self.devobj.instance_uuid,
|
||||||
|
self.inst.uuid)
|
||||||
|
self.assertEqual(len(self.inst.pci_devices), 0)
|
||||||
|
|
||||||
|
def test_claim_device_fail(self):
|
||||||
|
self.devobj.status = 'allocated'
|
||||||
|
self.assertRaises(exception.PciDeviceInvalidStatus,
|
||||||
|
pci_device.claim, self.devobj, self.inst)
|
||||||
|
|
||||||
|
def test_allocate_device(self):
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
pci_device.allocate(self.devobj, self.inst)
|
||||||
|
self.assertEqual(self.devobj.status, 'allocated')
|
||||||
|
self.assertEqual(self.devobj.instance_uuid, 'fake-inst-uuid')
|
||||||
|
self.assertEqual(len(self.inst.pci_devices), 1)
|
||||||
|
self.assertEqual(self.inst.pci_devices[0]['vendor_id'], 'v')
|
||||||
|
self.assertEqual(self.inst.pci_devices[0]['status'], 'allocated')
|
||||||
|
|
||||||
|
def test_allocacte_device_fail_status(self):
|
||||||
|
self.devobj.status = 'removed'
|
||||||
|
self.assertRaises(exception.PciDeviceInvalidStatus,
|
||||||
|
pci_device.allocate,
|
||||||
|
self.devobj,
|
||||||
|
self.inst)
|
||||||
|
|
||||||
|
def test_allocacte_device_fail_owner(self):
|
||||||
|
inst_2 = instance.Instance()
|
||||||
|
inst_2.uuid = 'fake-inst-uuid-2'
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
self.assertRaises(exception.PciDeviceInvalidOwner,
|
||||||
|
pci_device.allocate,
|
||||||
|
self.devobj, inst_2)
|
||||||
|
|
||||||
|
def test_free_claimed_device(self):
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
pci_device.free(self.devobj, self.inst)
|
||||||
|
self.assertEqual(self.devobj.status, 'available')
|
||||||
|
self.assertIsNone(self.devobj.instance_uuid)
|
||||||
|
|
||||||
|
def test_free_allocated_device(self):
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
pci_device.allocate(self.devobj, self.inst)
|
||||||
|
self.assertEqual(len(self.inst.pci_devices), 1)
|
||||||
|
pci_device.free(self.devobj, self.inst)
|
||||||
|
self.assertEqual(len(self.inst.pci_devices), 0)
|
||||||
|
self.assertEqual(self.devobj.status, 'available')
|
||||||
|
self.assertIsNone(self.devobj.instance_uuid)
|
||||||
|
|
||||||
|
def test_free_device_fail(self):
|
||||||
|
self.devobj.status = 'removed'
|
||||||
|
self.assertRaises(exception.PciDeviceInvalidStatus,
|
||||||
|
pci_device.free, self.devobj)
|
||||||
|
|
||||||
|
def test_remove_device(self):
|
||||||
|
pci_device.remove(self.devobj)
|
||||||
|
self.assertEqual(self.devobj.status, 'removed')
|
||||||
|
self.assertIsNone(self.devobj.instance_uuid)
|
||||||
|
|
||||||
|
def test_remove_device_fail(self):
|
||||||
|
pci_device.claim(self.devobj, self.inst)
|
||||||
|
self.assertRaises(exception.PciDeviceInvalidStatus,
|
||||||
|
pci_device.remove, self.devobj)
|
Loading…
Reference in New Issue
Block a user