From ed1c684f7d557ed6a3fab515adcd893d27b02de9 Mon Sep 17 00:00:00 2001 From: ShunliZhou Date: Thu, 30 Nov 2017 10:42:09 +0800 Subject: [PATCH] Add pci device etcd db api Add pci device etcd db api Change-Id: Ifb22fcb9e6e301a8b0553812d945caba8979eeb8 --- zun/db/etcd/api.py | 125 +++++++++++++++++++++++++++ zun/db/etcd/models.py | 22 +++++ zun/tests/unit/db/test_pci_device.py | 87 ++++++++++++++++++- 3 files changed, 232 insertions(+), 2 deletions(-) diff --git a/zun/db/etcd/api.py b/zun/db/etcd/api.py index c5f5250d7..b22d60f5f 100644 --- a/zun/db/etcd/api.py +++ b/zun/db/etcd/api.py @@ -81,6 +81,8 @@ def translate_etcd_result(etcd_result, model_type): ret = models.ComputeNode(data) elif model_type == 'capsule': ret = models.Capsule(data) + elif model_type == 'pcidevice': + ret = models.PciDevice(data) else: raise exception.InvalidParameterValue( _('The model_type value: %s is invalid.'), model_type) @@ -744,3 +746,126 @@ class EtcdAPI(object): raise return translate_etcd_result(target, 'capsule') + + def get_pci_device_by_addr(self, node_id, dev_addr): + try: + filters = {'compute_node_uuid': node_id, + 'address': dev_addr} + pcis = self.list_pcidevices(filters=filters) + except etcd.EtcdKeyNotFound: + raise exception.PciDeviceNotFound(node_id=node_id, address=None) + except Exception as e: + LOG.error('Error occurred while retrieving pci device: %s', + six.text_type(e)) + raise + + if len(pcis) == 0: + raise exception.PciDeviceNotFound(node_id=node_id, address=None) + return pcis + + def get_pci_device_by_id(self, id): + try: + filters = {'id': id} + pcis = self.list_pcidevices(filters=filters) + except etcd.EtcdKeyNotFound: + raise exception.PciDeviceNotFoundById(id=id) + except Exception as e: + LOG.error('Error occurred while retrieving pci device: %s', + six.text_type(e)) + raise + + if len(pcis) == 0: + raise exception.PciDeviceNotFoundById(id=id) + return pcis + + def list_pcidevices(self, filters=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + try: + pcis = getattr(self.client.read('/pcidevices'), 'children', None) + except etcd.EtcdKeyNotFound: + return [] + except Exception as e: + LOG.error( + "Error occurred while reading from etcd server: %s", + six.text_type(e)) + raise + + pcis = [] + for p in pcis: + if p.value is not None: + pcis.append(translate_etcd_result(p, 'pcidevice')) + filtered_pcis = self._filter_resources(pcis, filters) + return self._process_list_result(filtered_pcis, limit=limit, + sort_key=sort_key) + + def get_all_pci_device_by_node(self, node_id): + try: + filters = {'compute_node_uuid': node_id} + pcis = self.list_pcidevices(filters=filters) + except etcd.EtcdKeyNotFound: + raise exception.PciDeviceNotFound(node_id=node_id, address=None) + except Exception as e: + LOG.error('Error occurred while retrieving pci device: %s', + six.text_type(e)) + raise + + if len(pcis) == 0: + raise exception.PciDeviceNotFound(node_id=node_id, address=None) + return pcis + + def get_all_pci_device_by_parent_addr(self, node_id, parent_addr): + pcis = [] + try: + filters = {'compute_node_uuid': node_id, + 'parent_addr': parent_addr} + pcis = self.list_pcidevices(filters=filters) + except Exception as e: + LOG.error('Error occurred while retrieving pci device: %s', + six.text_type(e)) + raise + + return pcis + + def get_all_pci_device_by_container_uuid(self, container_uuid): + pcis = [] + try: + filters = {'container_uuid': container_uuid} + pcis = self.list_pcidevices(filters=filters) + except Exception as e: + LOG.error('Error occurred while retrieving pci device: %s', + six.text_type(e)) + raise + + return pcis + + @lockutils.synchronized('etcd_pcidevice') + def destroy_pci_device(self, node_id, address): + pci_device = self.get_pci_device_by_addr(node_id, address) + self.client.delete('/pcidevices/' + pci_device.uuid) + + @lockutils.synchronized('etcd_pcidevice') + def update_pci_device(self, node_id, address, values): + target = None + try: + target = self.get_pci_device_by_addr(node_id, address) + except Exception: + # If cannot get one, we write one to etcd later + pass + + try: + if not target: + values.update({'node_id': node_id, 'address': address}) + pci_device = models.PciDevice(values) + pci_device.save() + return pci_device + target_value = json.loads(target.value) + target_value.update(values) + target.value = json.dump_as_bytes(target_value) + self.client.update(target) + except etcd.EtcdKeyNotFound: + raise exception.PciDeviceNotFound(node_id=node_id, address=address) + except Exception as e: + LOG.error('Error occurred while updating pci device: %s', + six.text_type(e)) + raise + return translate_etcd_result(target, 'pcidevice') diff --git a/zun/db/etcd/models.py b/zun/db/etcd/models.py index 8c2929345..17d11b555 100644 --- a/zun/db/etcd/models.py +++ b/zun/db/etcd/models.py @@ -256,3 +256,25 @@ class ComputeNode(Base): client.write(path, json.dump_as_bytes(self.as_dict())) return + + +class PciDevice(Base): + """Represents a PciDevice. """ + _path = '/pcidevices' + + _fields = objects.PciDevice.fields.keys() + + def __init__(self, pci_data): + self.path = PciDevice.path() + for f in PciDevice.fields(): + setattr(self, f, None) + self.id = 1 + self.update(pci_data) + + @classmethod + def path(cls): + return cls._path + + @classmethod + def fields(cls): + return cls._fields diff --git a/zun/tests/unit/db/test_pci_device.py b/zun/tests/unit/db/test_pci_device.py index eb5a744ba..37dfc48dc 100644 --- a/zun/tests/unit/db/test_pci_device.py +++ b/zun/tests/unit/db/test_pci_device.py @@ -11,12 +11,18 @@ # under the License. """Tests for manipulating pci device via the DB API""" +import mock + +import etcd +from etcd import Client as etcd_client + from oslo_config import cfg from zun.common import context from zun.common import exception import zun.conf from zun.db import api as dbapi +from zun.db.etcd import models from zun.objects import fields as z_fields from zun.tests.unit.db import base from zun.tests import uuidsentinel @@ -197,8 +203,85 @@ class DbPciDeviceTestCase(base.DbTestCase, base.ModelsObjectComparatorMixin): v1['address']) +fake_values = {'id': 1, + 'uuid': 'bd45ca46351e64f91d5c32', + 'compute_node_uuid': 'ef5ef3492b57c8b9bc0556a', + 'address': 'fake_addr', + 'vendor_id': 'fake_vendor', + 'product_id': 'fake_product', + 'dev_type': 'VF', + 'dev_id': 'fake_dev_id', + 'lable': 'fake_label', + 'status': 'in-use', + 'request_id': 'fake_request_id', + 'extra_info': '', + 'parent_addr': '11:22', + 'container_uuid': 'Id64c317ff78e95af2fc046e9630d7ae016df9f2'} + + class EtcdDbPciDeviceTestCase(base.DbTestCase): def setUp(self): - # Placeholder for etcd test case. - pass + cfg.CONF.set_override('db_type', 'etcd') + super(EtcdDbPciDeviceTestCase, self).setUp() + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_update_pci_device(self, mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + values = fake_values + pcidevice = models.PciDevice(values) + updated = self.dbapi.update_pci_device(values['compute_node_uuid'], + values['address'], values) + self.assertEqual(pcidevice.as_dict(), updated.as_dict()) + + @mock.patch.object(etcd_client, 'read') + def test_list_pci_device(self, mock_read): + res = self.dbapi.list_pcidevices() + mock_read.assert_called_with('/pcidevices') + self.assertEqual([], res) + + @mock.patch('zun.db.etcd.api.EtcdAPI.get_pci_device_by_addr') + @mock.patch.object(etcd_client, 'delete') + def test_destroy_pci_device(self, mock_delete, mock_get): + self.dbapi.destroy_pci_device('1', 'fake_address') + mock_delete.assert_called() + + @mock.patch('zun.db.etcd.api.EtcdAPI.list_pcidevices') + def test_get_all_pci_device_by_container_uuid(self, mock_list): + filters = {'container_uuid': 'Id64c317ff78e95af2fc'} + mock_list.return_value = [models.PciDevice(fake_values)] + self.dbapi.get_all_pci_device_by_container_uuid('Id64c317ff78e95af2fc') + mock_list.assert_called_with(filters=filters) + + @mock.patch('zun.db.etcd.api.EtcdAPI.list_pcidevices') + def test_get_all_pci_device_by_parent_addr(self, mock_list): + filters = {'compute_node_uuid': 'ef5ef3492b57c8b9bc0556a', + 'parent_addr': '11:22'} + mock_list.return_value = [models.PciDevice(fake_values)] + self.dbapi.get_all_pci_device_by_parent_addr('ef5ef3492b57c8b9bc0556a', + '11:22') + mock_list.assert_called_with(filters=filters) + + @mock.patch('zun.db.etcd.api.EtcdAPI.list_pcidevices') + def test_get_all_pci_device_by_node(self, mock_list): + filters = {'compute_node_uuid': 'ef5ef3492b57c8b9bc0556a'} + mock_list.return_value = [models.PciDevice(fake_values)] + self.dbapi.get_all_pci_device_by_node('ef5ef3492b57c8b9bc0556a') + mock_list.assert_called_with(filters=filters) + + @mock.patch('zun.db.etcd.api.EtcdAPI.list_pcidevices') + def test_get_pci_device_by_id(self, mock_list): + filters = {'id': '1'} + mock_list.return_value = [models.PciDevice(fake_values)] + self.dbapi.get_pci_device_by_id('1') + mock_list.assert_called_with(filters=filters) + + @mock.patch('zun.db.etcd.api.EtcdAPI.list_pcidevices') + def test_get_pci_device_by_addr(self, mock_list): + filters = {'compute_node_uuid': 'ef5ef3492b57c8b9bc0556a', + 'address': 'fake_addr'} + mock_list.return_value = [models.PciDevice(fake_values)] + self.dbapi.get_pci_device_by_addr('ef5ef3492b57c8b9bc0556a', + 'fake_addr') + mock_list.assert_called_with(filters=filters)