summaryrefslogtreecommitdiff
path: root/nova/objects/instance_pci_requests.py
blob: 3500c9a68dc3e97aa6090469f7e5d8c9efd9e22d (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#    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 oslo_serialization import jsonutils
from oslo_utils import versionutils

from nova.db.main import api as db
from nova.objects import base
from nova.objects import fields


@base.NovaObjectRegistry.register
class InstancePCIRequest(base.NovaObject):
    # Version 1.0: Initial version
    # Version 1.1: Added request_id field
    # Version 1.2: Added numa_policy field
    # Version 1.3: Added requester_id field
    # Version 1.4: Added 'socket' to numa_policy field
    VERSION = '1.4'

    # Possible sources for a PCI request:
    # FLAVOR_ALIAS : Request originated from a flavor alias.
    # NEUTRON_PORT : Request originated from a neutron port.
    FLAVOR_ALIAS = 0
    NEUTRON_PORT = 1

    fields = {
        'count': fields.IntegerField(),
        'spec': fields.ListOfDictOfNullableStringsField(),
        'alias_name': fields.StringField(nullable=True),
        # Note(moshele): is_new is deprecated and should be removed
        # on major version bump
        'is_new': fields.BooleanField(default=False),
        'request_id': fields.UUIDField(nullable=True),
        'requester_id': fields.StringField(nullable=True),
        'numa_policy': fields.PCINUMAAffinityPolicyField(nullable=True),
    }

    @property
    def source(self):
        # PCI requests originate from two sources: instance flavor alias and
        # neutron SR-IOV ports.
        # SR-IOV ports pci_request don't have an alias_name.
        return (InstancePCIRequest.NEUTRON_PORT if self.alias_name is None
                else InstancePCIRequest.FLAVOR_ALIAS)

    def obj_load_attr(self, attr):
        setattr(self, attr, None)

    def obj_make_compatible(self, primitive, target_version):
        super(InstancePCIRequest, self).obj_make_compatible(primitive,
                                                            target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 3) and 'requester_id' in primitive:
            del primitive['requester_id']
        if target_version < (1, 2) and 'numa_policy' in primitive:
            del primitive['numa_policy']
        if target_version < (1, 1) and 'request_id' in primitive:
            del primitive['request_id']


@base.NovaObjectRegistry.register
class InstancePCIRequests(base.NovaObject):
    # Version 1.0: Initial version
    # Version 1.1: InstancePCIRequest 1.1
    VERSION = '1.1'

    fields = {
        'instance_uuid': fields.UUIDField(),
        'requests': fields.ListOfObjectsField('InstancePCIRequest'),
    }

    @classmethod
    def obj_from_db(cls, context, instance_uuid, db_requests):
        self = cls(context=context, requests=[],
                   instance_uuid=instance_uuid)
        if db_requests is not None:
            requests = jsonutils.loads(db_requests)
        else:
            requests = []
        for request in requests:
            # Note(moshele): is_new is deprecated and therefore we load it
            # with default value of False
            request_obj = InstancePCIRequest(
                count=request['count'], spec=request['spec'],
                alias_name=request['alias_name'], is_new=False,
                numa_policy=request.get('numa_policy',
                                        fields.PCINUMAAffinityPolicy.LEGACY),
                request_id=request['request_id'],
                requester_id=request.get('requester_id'))
            request_obj.obj_reset_changes()
            self.requests.append(request_obj)
        self.obj_reset_changes()
        return self

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_pci_requests = db.instance_extra_get_by_instance_uuid(
                context, instance_uuid, columns=['pci_requests'])
        if db_pci_requests is not None:
            db_pci_requests = db_pci_requests['pci_requests']
        return cls.obj_from_db(context, instance_uuid, db_pci_requests)

    @staticmethod
    def _load_legacy_requests(sysmeta_value, is_new=False):
        if sysmeta_value is None:
            return []
        requests = []
        db_requests = jsonutils.loads(sysmeta_value)
        for db_request in db_requests:
            request = InstancePCIRequest(
                count=db_request['count'], spec=db_request['spec'],
                alias_name=db_request['alias_name'], is_new=is_new)
            request.obj_reset_changes()
            requests.append(request)
        return requests

    @classmethod
    def get_by_instance(cls, context, instance):
        # NOTE (baoli): not all callers are passing instance as object yet.
        # Therefore, use the dict syntax in this routine
        if 'pci_requests' in instance['system_metadata']:
            # NOTE(danms): This instance hasn't been converted to use
            # instance_extra yet, so extract the data from sysmeta
            sysmeta = instance['system_metadata']
            _requests = (
                cls._load_legacy_requests(sysmeta['pci_requests']) +
                cls._load_legacy_requests(sysmeta.get('new_pci_requests'),
                                          is_new=True))
            requests = cls(instance_uuid=instance['uuid'], requests=_requests)
            requests.obj_reset_changes()
            return requests
        else:
            return cls.get_by_instance_uuid(context, instance['uuid'])

    def to_json(self):
        blob = [{'count': x.count,
                 'spec': x.spec,
                 'alias_name': x.alias_name,
                 'is_new': x.is_new,
                 'numa_policy': x.numa_policy,
                 'request_id': x.request_id,
                 'requester_id': x.requester_id} for x in self.requests]
        return jsonutils.dumps(blob)