summaryrefslogtreecommitdiff
path: root/ironic/tests/unit/objects/utils.py
blob: 26c3a22e78ddcece82f28c3b3e94291212aaeef1 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# Copyright 2014 Rackspace Hosting
# 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.
"""Ironic object test utilities."""

import functools
import inspect

from ironic.common import exception
from ironic.common.i18n import _
from ironic import objects
from ironic.objects import notification
from ironic.tests.unit.db import utils as db_utils


def check_keyword_arguments(func):
    @functools.wraps(func)
    def wrapper(**kw):
        obj_type = kw.pop('object_type')
        result = func(**kw)

        extra_args = set(kw) - set(result)
        if extra_args:
            raise exception.InvalidParameterValue(
                _("Unknown keyword arguments (%(extra)s) were passed "
                  "while creating a test %(object_type)s object.") %
                {"extra": ", ".join(extra_args),
                 "object_type": obj_type})

        return result

    return wrapper


def get_test_node(ctxt, **kw):
    """Return a Node object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'node'
    get_db_node_checked = check_keyword_arguments(db_utils.get_test_node)
    db_node = get_db_node_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_node['id']
    node = objects.Node(ctxt)
    for key in db_node:
        if key == 'traits':
            # convert list of strings to object
            raw_traits = db_node['traits']
            trait_list = []
            for raw_trait in raw_traits:
                trait = objects.Trait(ctxt, trait=raw_trait)
                trait_list.append(trait)
            node.traits = objects.TraitList(ctxt, objects=trait_list)
            node.traits.obj_reset_changes()
        else:
            setattr(node, key, db_node[key])
    return node


def create_test_node(ctxt, **kw):
    """Create and return a test node object.

    Create a node in the DB and return a Node object with appropriate
    attributes.
    """
    node = get_test_node(ctxt, **kw)
    node.create()
    return node


def get_test_port(ctxt, **kw):
    """Return a Port object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'port'
    get_db_port_checked = check_keyword_arguments(
        db_utils.get_test_port)
    db_port = get_db_port_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_port['id']
    port = objects.Port(ctxt)
    for key in db_port:
        setattr(port, key, db_port[key])
    return port


def create_test_port(ctxt, **kw):
    """Create and return a test port object.

    Create a port in the DB and return a Port object with appropriate
    attributes.
    """
    port = get_test_port(ctxt, **kw)
    port.create()
    return port


def get_test_chassis(ctxt, **kw):
    """Return a Chassis object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'chassis'
    get_db_chassis_checked = check_keyword_arguments(
        db_utils.get_test_chassis)
    db_chassis = get_db_chassis_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_chassis['id']
    chassis = objects.Chassis(ctxt)
    for key in db_chassis:
        setattr(chassis, key, db_chassis[key])
    return chassis


def create_test_chassis(ctxt, **kw):
    """Create and return a test chassis object.

    Create a chassis in the DB and return a Chassis object with appropriate
    attributes.
    """
    chassis = get_test_chassis(ctxt, **kw)
    chassis.create()
    return chassis


def get_test_portgroup(ctxt, **kw):
    """Return a Portgroup object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'portgroup'
    get_db_port_group_checked = check_keyword_arguments(
        db_utils.get_test_portgroup)
    db_portgroup = get_db_port_group_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_portgroup['id']
    portgroup = objects.Portgroup(ctxt)
    for key in db_portgroup:
        setattr(portgroup, key, db_portgroup[key])
    return portgroup


def create_test_portgroup(ctxt, **kw):
    """Create and return a test portgroup object.

    Create a portgroup in the DB and return a Portgroup object with appropriate
    attributes.
    """
    portgroup = get_test_portgroup(ctxt, **kw)
    portgroup.create()
    return portgroup


def get_test_volume_connector(ctxt, **kw):
    """Return a VolumeConnector object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    db_volume_connector = db_utils.get_test_volume_connector(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_volume_connector['id']
    volume_connector = objects.VolumeConnector(ctxt)
    for key in db_volume_connector:
        setattr(volume_connector, key, db_volume_connector[key])
    return volume_connector


def create_test_volume_connector(ctxt, **kw):
    """Create and return a test volume connector object.

    Create a volume connector in the DB and return a VolumeConnector object
    with appropriate attributes.
    """
    volume_connector = get_test_volume_connector(ctxt, **kw)
    volume_connector.create()
    return volume_connector


def get_test_volume_target(ctxt, **kw):
    """Return a VolumeTarget object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    db_volume_target = db_utils.get_test_volume_target(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_volume_target['id']
    volume_target = objects.VolumeTarget(ctxt)
    for key in db_volume_target:
        setattr(volume_target, key, db_volume_target[key])
    return volume_target


def create_test_volume_target(ctxt, **kw):
    """Create and return a test volume target object.

    Create a volume target in the DB and return a VolumeTarget object with
    appropriate attributes.
    """
    volume_target = get_test_volume_target(ctxt, **kw)
    volume_target.create()
    return volume_target


def get_test_bios_setting(ctxt, **kw):
    """Return a BiosSettingList object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'bios'
    db_bios_setting = db_utils.get_test_bios_setting(**kw)
    bios_setting = objects.BIOSSetting(ctxt)
    for key in db_bios_setting:
        setattr(bios_setting, key, db_bios_setting[key])
    return bios_setting


def create_test_bios_setting(ctxt, **kw):
    """Create and return a test bios setting list object.

    Create a BIOS setting list in the DB and return a BIOSSettingList
    object with appropriate attributes.
    """
    bios_setting = get_test_bios_setting(ctxt, **kw)
    bios_setting.create()
    return bios_setting


def create_test_conductor(ctxt, **kw):
    """Register and return a test conductor object."""
    args = db_utils.get_test_conductor(**kw)
    conductor = objects.Conductor.register(ctxt, args['hostname'],
                                           args['drivers'],
                                           args['conductor_group'],
                                           update_existing=True)
    return conductor


def get_test_allocation(ctxt, **kw):
    """Return an Allocation object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'allocation'
    get_db_allocation_checked = check_keyword_arguments(
        db_utils.get_test_allocation)
    db_allocation = get_db_allocation_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_allocation['id']
    allocation = objects.Allocation(ctxt)
    for key in db_allocation:
        setattr(allocation, key, db_allocation[key])
    return allocation


def create_test_allocation(ctxt, **kw):
    """Create and return a test allocation object.

    Create an allocation in the DB and return an Allocation object with
    appropriate attributes.
    """
    allocation = get_test_allocation(ctxt, **kw)
    allocation.create()
    return allocation


def get_test_deploy_template(ctxt, **kw):
    """Return a DeployTemplate object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    db_template = db_utils.get_test_deploy_template(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_template['id']
    if 'steps' not in kw:
        for step in db_template['steps']:
            del step['id']
            del step['deploy_template_id']
    else:
        for kw_step, template_step in zip(kw['steps'], db_template['steps']):
            if 'id' not in kw_step and 'id' in template_step:
                del template_step['id']
    template = objects.DeployTemplate(ctxt)
    for key in db_template:
        setattr(template, key, db_template[key])
    return template


def create_test_deploy_template(ctxt, **kw):
    """Create and return a test deploy template object.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    template = get_test_deploy_template(ctxt, **kw)
    template.create()
    return template


def get_payloads_with_schemas(from_module):
    """Get the Payload classes with SCHEMAs defined.

    :param from_module: module from which to get the classes.
    :returns: list of Payload classes that have SCHEMAs defined.

    """
    payloads = []
    for name, payload in inspect.getmembers(from_module, inspect.isclass):
        # Assume that Payload class names end in 'Payload'.
        if name.endswith("Payload"):
            base_classes = inspect.getmro(payload)
            if notification.NotificationPayloadBase not in base_classes:
                # The class may have the desired name but it isn't a REAL
                # Payload class; skip it.
                continue

            # First class is this payload class, parent class is the 2nd
            # one in the tuple
            parent = base_classes[1]
            if (not hasattr(parent, 'SCHEMA')
                or parent.SCHEMA != payload.SCHEMA):
                payloads.append(payload)

    return payloads


class SchemasTestMixIn(object):
    def _check_payload_schemas(self, from_module, fields):
        """Assert that the Payload SCHEMAs have the expected properties.

           A payload's SCHEMA should:

           1. Have each of its keys in the payload's fields
           2. Have each member of the schema match with a corresponding field
           in the object
        """
        resource = from_module.__name__.split('.')[-1]
        payloads = get_payloads_with_schemas(from_module)
        for payload in payloads:
            for schema_key in payload.SCHEMA:
                self.assertIn(schema_key, payload.fields,
                              "for %s, schema key %s is not in fields"
                              % (payload, schema_key))
                key = payload.SCHEMA[schema_key][1]
                self.assertIn(key, fields,
                              "for %s, schema key %s has invalid %s "
                              "field %s" % (payload, schema_key, resource,
                                            key))