summaryrefslogtreecommitdiff
path: root/nova/pci/whitelist.py
blob: 8862a0ef4f45d361310e9bf54d959ae0524e93f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# Copyright (c) 2013 Intel, Inc.
# Copyright (c) 2013 OpenStack Foundation
# 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 typing as ty

from oslo_serialization import jsonutils

from nova import exception
from nova.i18n import _
from nova import objects
from nova.pci import devspec


class Whitelist(object):
    """White list class to represent assignable pci devices.

    Not all devices on a compute node can be assigned to a guest. The cloud
    administrator decides which devices can be assigned based on ``vendor_id``
    or ``product_id``, etc. If no white list is specified, no devices will be
    assignable.
    """

    def __init__(self, whitelist_spec: str = None) -> None:
        """White list constructor

        For example, the following json string specifies that devices whose
        vendor_id is '8086' and product_id is '1520' can be assigned
        to guests. ::

            '[{"product_id":"1520", "vendor_id":"8086"}]'

        :param whitelist_spec: A JSON string for a dictionary or list thereof.
            Each dictionary specifies the pci device properties requirement.
            See the definition of ``device_spec`` in
            ``nova.conf.pci`` for details and examples.
        """
        if whitelist_spec:
            self.specs = self._parse_white_list_from_config(whitelist_spec)
        else:
            self.specs = []

    @staticmethod
    def _parse_white_list_from_config(
        whitelists: str,
    ) -> ty.List[devspec.PciDeviceSpec]:
        """Parse and validate the pci whitelist from the nova config."""
        specs = []
        for jsonspec in whitelists:
            try:
                dev_spec = jsonutils.loads(jsonspec)
            except ValueError:
                raise exception.PciConfigInvalidSpec(
                          reason=_("Invalid entry: '%s'") % jsonspec)
            if isinstance(dev_spec, dict):
                dev_spec = [dev_spec]
            elif not isinstance(dev_spec, list):
                raise exception.PciConfigInvalidSpec(
                          reason=_("Invalid entry: '%s'; "
                                   "Expecting list or dict") % jsonspec)

            for ds in dev_spec:
                if not isinstance(ds, dict):
                    raise exception.PciConfigInvalidSpec(
                              reason=_("Invalid entry: '%s'; "
                                       "Expecting dict") % ds)

                spec = devspec.PciDeviceSpec(ds)
                specs.append(spec)

        return specs

    def device_assignable(self, dev: ty.Dict[str, ty.Any]) -> bool:
        """Check if a device can be assigned to a guest.

        :param dev: A dictionary describing the device properties
        """
        for spec in self.specs:
            if spec.match(dev):
                return True
        return False

    def get_devspec(
        self, pci_dev: 'objects.PciDevice',
    ) -> ty.Optional[devspec.PciDeviceSpec]:
        for spec in self.specs:
            if spec.match_pci_obj(pci_dev):
                return spec

        return None