# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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 test utilities."""


from oslo_utils import timeutils
from oslo_utils import uuidutils

from ironic.common import states
from ironic.db import api as db_api
from ironic.drivers import base as drivers_base
from ironic.objects import allocation
from ironic.objects import bios
from ironic.objects import chassis
from ironic.objects import conductor
from ironic.objects import deploy_template
from ironic.objects import firmware
from ironic.objects import inspection_rule as rule
from ironic.objects import node
from ironic.objects import node_history
from ironic.objects import node_inventory
from ironic.objects import port
from ironic.objects import portgroup
from ironic.objects import runbook
from ironic.objects import trait
from ironic.objects import volume_connector
from ironic.objects import volume_target


def get_test_ipmi_info():
    return {
        "ipmi_address": "1.2.3.4",
        "ipmi_username": "admin",
        "ipmi_password": "fake"
    }


def get_test_ipmi_bridging_parameters():
    return {
        "ipmi_bridging": "dual",
        "ipmi_local_address": "0x20",
        "ipmi_transit_channel": "0",
        "ipmi_transit_address": "0x82",
        "ipmi_target_channel": "7",
        "ipmi_target_address": "0x72"
    }


def get_test_pxe_driver_info():
    return {
        "deploy_kernel": "glance://deploy_kernel_uuid",
        "deploy_ramdisk": "glance://deploy_ramdisk_uuid",
        "rescue_kernel": "glance://rescue_kernel_uuid",
        "rescue_ramdisk": "glance://rescue_ramdisk_uuid"
    }


def get_test_pxe_driver_internal_info():
    return {
        "is_whole_disk_image": False,
    }


def get_test_pxe_instance_info():
    return {
        "image_source": "glance://image_uuid",
        "root_gb": 100,
        "rescue_password": "password"
    }


def get_test_ilo_info():
    return {
        "ilo_address": "1.2.3.4",
        "ilo_username": "admin",
        "ilo_password": "fake",
    }


def get_test_drac_info():
    return {
        "redfish_address": "1.2.3.4",
        "redfish_system_id": "/redfish/v1/Systems/System.Embedded.1",
        "redfish_username": "admin",
        "redfish_password": "fake"
    }


def get_test_irmc_info():
    return {
        "irmc_address": "1.2.3.4",
        "irmc_username": "admin0",
        "irmc_password": "fake0",
        "irmc_port": "80",
        "irmc_auth_method": "digest",
    }


def get_test_agent_instance_info():
    return {
        'image_source': 'fake-image',
        'image_url': 'http://image',
        'image_checksum': 'checksum',
        'image_disk_format': 'qcow2',
        'image_container_format': 'bare',
    }


def get_test_agent_driver_info():
    return {
        'deploy_kernel': 'glance://deploy_kernel_uuid',
        'deploy_ramdisk': 'glance://deploy_ramdisk_uuid',
        'ipmi_password': 'foo',
    }


def get_test_agent_driver_internal_info():
    return {
        'agent_url': 'http://127.0.0.1/foo',
        'is_whole_disk_image': True,
    }


def get_test_snmp_info(**kw):
    result = {
        "snmp_driver": kw.get("snmp_driver", "teltronix"),
        "snmp_address": kw.get("snmp_address", "1.2.3.4"),
        "snmp_port": kw.get("snmp_port", "161"),
        "snmp_outlet": kw.get("snmp_outlet", "1"),
        "snmp_version": kw.get("snmp_version", "1")
    }
    if result["snmp_version"] in ("1", "2c"):
        result["snmp_community"] = kw.get("snmp_community", "public")
        if "snmp_community_read" in kw:
            result["snmp_community_read"] = kw["snmp_community_read"]
        if "snmp_community_write" in kw:
            result["snmp_community_write"] = kw["snmp_community_write"]
    elif result["snmp_version"] == "3":
        result["snmp_user"] = kw.get(
            "snmp_user", kw.get("snmp_security", "snmpuser")
        )
        for option in ('snmp_auth_protocol', 'snmp_auth_key',
                       'snmp_priv_protocol', 'snmp_priv_key',
                       'snmp_context_engine_id', 'snmp_context_name'):
            if option in kw:
                result[option] = kw[option]
    return result


def get_test_node(**kw):
    properties = {
        "cpu_arch": "x86_64",
        "local_gb": 10,
        "memory_mb": '4096',
    }
    # NOTE(tenbrae): API unit tests confirm that sensitive fields in
    #                instance_info and driver_info will get scrubbed
    #                from the API response but other fields
    #                (eg, 'foo') do not.
    fake_instance_info = {
        "configdrive": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=",
        "image_url": "http://example.com/test_image_url",
        "foo": "bar",
    }
    fake_driver_info = {
        "foo": "bar",
        "fake_password": "fakepass",
    }
    fake_internal_info = {
        "private_state": "secret value"
    }
    result = {
        'version': kw.get('version', node.Node.VERSION),
        'id': kw.get('id', 123),
        'name': kw.get('name', None),
        'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'),
        'chassis_id': kw.get('chassis_id', None),
        'conductor_affinity': kw.get('conductor_affinity', None),
        'conductor_group': kw.get('conductor_group', ''),
        'power_state': kw.get('power_state', states.NOSTATE),
        'target_power_state': kw.get('target_power_state', states.NOSTATE),
        'provision_state': kw.get('provision_state', states.AVAILABLE),
        'target_provision_state': kw.get('target_provision_state',
                                         states.NOSTATE),
        'provision_updated_at': kw.get('provision_updated_at'),
        'last_error': kw.get('last_error'),
        'instance_uuid': kw.get('instance_uuid'),
        'instance_name': kw.get('instance_name'),
        'instance_info': kw.get('instance_info', fake_instance_info),
        'driver': kw.get('driver', 'fake-hardware'),
        'driver_info': kw.get('driver_info', fake_driver_info),
        'driver_internal_info': kw.get('driver_internal_info',
                                       fake_internal_info),
        'clean_step': kw.get('clean_step'),
        'deploy_step': kw.get('deploy_step'),
        'properties': kw.get('properties', properties),
        'reservation': kw.get('reservation'),
        'maintenance': kw.get('maintenance', False),
        'maintenance_reason': kw.get('maintenance_reason'),
        'fault': kw.get('fault'),
        'console_enabled': kw.get('console_enabled', False),
        'extra': kw.get('extra', {}),
        'updated_at': kw.get('updated_at'),
        'created_at': kw.get('created_at'),
        'inspection_finished_at': kw.get('inspection_finished_at'),
        'inspection_started_at': kw.get('inspection_started_at'),
        'raid_config': kw.get('raid_config'),
        'target_raid_config': kw.get('target_raid_config'),
        'tags': kw.get('tags', []),
        'resource_class': kw.get('resource_class'),
        'traits': kw.get('traits', []),
        'automated_clean': kw.get('automated_clean', None),
        'protected': kw.get('protected', False),
        'protected_reason': kw.get('protected_reason', None),
        'conductor': kw.get('conductor'),
        'owner': kw.get('owner', None),
        'allocation_id': kw.get('allocation_id'),
        'description': kw.get('description'),
        'retired': kw.get('retired', False),
        'retired_reason': kw.get('retired_reason', None),
        'lessee': kw.get('lessee', None),
        'network_data': kw.get('network_data'),
        'boot_mode': kw.get('boot_mode', None),
        'secure_boot': kw.get('secure_boot', None),
        'shard': kw.get('shard', None),
        'parent_node': kw.get('parent_node', None),
        'service_step': kw.get('service_step'),
        'disable_power_off': kw.get('disable_power_off', False),
        'health': kw.get('health', None),
    }

    for iface in drivers_base.ALL_INTERFACES:
        name = '%s_interface' % iface
        result[name] = kw.get(name)

    return result


def create_test_node(**kw):
    """Create test node entry in DB and return Node DB object.

    Function to be used to create test Node objects in the database.

    :param kw: kwargs with overriding values for node's attributes.
    :returns: Test Node DB object.

    """
    node = get_test_node(**kw)
    # Let DB generate an ID if one isn't specified explicitly.
    # Creating a node with tags or traits will raise an exception. If tags or
    # traits are not specified explicitly just delete them.
    for field in {'id', 'uuid', 'tags', 'traits'}:
        if field not in kw:
            del node[field]
    dbapi = db_api.get_instance()
    return dbapi.create_node(node)


def get_test_port(**kw):
    return {
        'id': kw.get('id', 987),
        'version': kw.get('version', port.Port.VERSION),
        'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c781'),
        'node_id': kw.get('node_id', 123),
        'node_uuid': kw.get('node_uuid',
                            '59d102f7-5840-4299-8ec8-80c0ebae9de1'),
        'address': kw.get('address', '52:54:00:cf:2d:31'),
        'extra': kw.get('extra', {}),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'local_link_connection': kw.get('local_link_connection',
                                        {'switch_id': '0a:1b:2c:3d:4e:5f',
                                         'port_id': 'Ethernet3/1',
                                         'switch_info': 'switch1'}),
        'portgroup_id': kw.get('portgroup_id'),
        'pxe_enabled': kw.get('pxe_enabled', True),
        'internal_info': kw.get('internal_info', {"bar": "buzz"}),
        'physical_network': kw.get('physical_network'),
        'is_smartnic': kw.get('is_smartnic', False),
        'name': kw.get('name'),
        'description': kw.get('description'),
        'vendor': kw.get('vendor'),
        'category': kw.get('category'),
        'available_for_dynamic_portgroup': kw.get(
            'available_for_dynamic_portgroup', True),
    }


def create_test_port(**kw):
    """Create test port entry in DB and return Port DB object.

    Function to be used to create test Port objects in the database.

    :param kw: kwargs with overriding values for port's attributes.
    :returns: Test Port DB object.

    """
    port = get_test_port(**kw)
    # Let DB generate ID if it isn't specified explicitly
    for field in ('id', 'uuid'):
        if field not in kw:
            del port[field]
    dbapi = db_api.get_instance()
    return dbapi.create_port(port)


def get_test_volume_connector(**kw):
    return {
        'id': kw.get('id', 789),
        'version': kw.get('version', volume_connector.VolumeConnector.VERSION),
        'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c781'),
        'node_id': kw.get('node_id', 123),
        'type': kw.get('type', 'iqn'),
        'connector_id': kw.get('connector_id',
                               'iqn.2012-06.com.example:initiator'),
        'extra': kw.get('extra', {}),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_volume_connector(**kw):
    """Create test connector entry in DB and return VolumeConnector DB object.

    Function to be used to create test VolumeConnector objects in the database.

    :param kw: kwargs with overriding values for connector's attributes.
    :returns: Test VolumeConnector DB object.

    """
    connector = get_test_volume_connector(**kw)
    # Let DB generate ID if it isn't specified explicitly
    for field in ('id', 'uuid'):
        if field not in kw:
            del connector[field]
    dbapi = db_api.get_instance()
    return dbapi.create_volume_connector(connector)


def get_test_volume_target(**kw):
    fake_properties = {"target_iqn": "iqn.foo"}
    return {
        'id': kw.get('id', 789),
        'version': kw.get('version', volume_target.VolumeTarget.VERSION),
        'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c781'),
        'node_id': kw.get('node_id', 123),
        'volume_type': kw.get('volume_type', 'iscsi'),
        'properties': kw.get('properties', fake_properties),
        'boot_index': kw.get('boot_index', 0),
        'volume_id': kw.get('volume_id', '12345678'),
        'extra': kw.get('extra', {}),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_volume_target(**kw):
    """Create test target entry in DB and return VolumeTarget DB object.

    Function to be used to create test VolumeTarget objects in the database.

    :param kw: kwargs with overriding values for target's attributes.
    :returns: Test VolumeTarget DB object.

    """
    target = get_test_volume_target(**kw)
    # Let DB generate ID if it isn't specified explicitly
    for field in ('id', 'uuid'):
        if field not in kw:
            del target[field]
    dbapi = db_api.get_instance()
    return dbapi.create_volume_target(target)


def get_test_chassis(**kw):
    return {
        'id': kw.get('id', 42),
        'version': kw.get('version', chassis.Chassis.VERSION),
        'uuid': kw.get('uuid', 'e74c40e0-d825-11e2-a28f-0800200c9a66'),
        'extra': kw.get('extra', {}),
        'description': kw.get('description', 'data-center-1-chassis'),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_chassis(**kw):
    """Create test chassis entry in DB and return Chassis DB object.

    Function to be used to create test Chassis objects in the database.

    :param kw: kwargs with overriding values for chassis's attributes.
    :returns: Test Chassis DB object.

    """
    chassis = get_test_chassis(**kw)
    # Let DB generate ID if it isn't specified explicitly
    for field in ('id', 'uuid'):
        if field not in kw:
            del chassis[field]
    dbapi = db_api.get_instance()
    return dbapi.create_chassis(chassis)


def get_test_conductor(**kw):
    return {
        'id': kw.get('id', 6),
        'version': kw.get('version', conductor.Conductor.VERSION),
        'hostname': kw.get('hostname', 'test-conductor-node'),
        'drivers': kw.get('drivers', ['fake-driver', 'null-driver']),
        'conductor_group': kw.get('conductor_group', ''),
        'created_at': kw.get('created_at', timeutils.utcnow()),
        'updated_at': kw.get('updated_at', timeutils.utcnow()),
        'online': kw.get('online', True)
    }


def create_test_conductor(**kw):
    """Create test conductor entry in DB and return Conductor DB object.

    Function to be used to create test Conductor objects in the database.

    :param kw: kwargs with overriding values for conductor's attributes.
    :returns: Test Conductor DB object.

    """
    conductor = get_test_conductor(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del conductor['id']
    dbapi = db_api.get_instance()
    return dbapi.register_conductor(conductor)


def get_test_redfish_info():
    return {
        "redfish_address": "https://example.com",
        "redfish_system_id": "/redfish/v1/Systems/FAKESYSTEM",
        "redfish_username": "username",
        "redfish_password": "password"
    }


def get_test_portgroup(**kw):
    return {
        'id': kw.get('id', 654),
        'version': kw.get('version', portgroup.Portgroup.VERSION),
        'uuid': kw.get('uuid', '6eb02b44-18a3-4659-8c0b-8d2802581ae4'),
        'name': kw.get('name', 'fooname'),
        'node_id': kw.get('node_id', 123),
        'node_uuid': kw.get('node_uuid',
                            '40481b96-306b-4a33-901f-795a3dc2f397'),
        'address': kw.get('address', '52:54:00:cf:2d:31'),
        'extra': kw.get('extra', {}),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'internal_info': kw.get('internal_info', {"bar": "buzz"}),
        'standalone_ports_supported': kw.get('standalone_ports_supported',
                                             True),
        'mode': kw.get('mode'),
        'properties': kw.get('properties', {}),
        'physical_network': kw.get('physical_network'),
        'category': kw.get('category'),
        'dynamic_portgroup': kw.get('dynamic_portgroup', False),
    }


def create_test_portgroup(**kw):
    """Create test portgroup entry in DB and return Portgroup DB object.

    Function to be used to create test Portgroup objects in the database.

    :param kw: kwargs with overriding values for port's attributes.
    :returns: Test Portgroup DB object.

    """
    portgroup = get_test_portgroup(**kw)
    # Let DB generate ID if it isn't specified explicitly
    for field in ('id', 'uuid'):
        if field not in kw:
            del portgroup[field]
    dbapi = db_api.get_instance()
    return dbapi.create_portgroup(portgroup)


def get_test_node_tag(**kw):
    return {
        # TODO(rloo): Replace None below with the object NodeTag VERSION,
        #             after this lands: https://review.opendev.org/#/c/233357
        'version': kw.get('version', None),
        "tag": kw.get("tag", "tag1"),
        "node_id": kw.get("node_id", "123"),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_node_tag(**kw):
    """Create test node tag entry in DB and return NodeTag DB object.

    Function to be used to create test NodeTag objects in the database.

    :param kw: kwargs with overriding values for tag's attributes.
    :returns: Test NodeTag DB object.

    """
    tag = get_test_node_tag(**kw)
    dbapi = db_api.get_instance()
    return dbapi.add_node_tag(tag['node_id'], tag['tag'])


def get_test_node_trait(**kw):
    return {
        'version': kw.get('version', trait.Trait.VERSION),
        "trait": kw.get("trait", "trait1"),
        "node_id": kw.get("node_id", "123"),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_node_trait(**kw):
    """Create test node trait entry in DB and return NodeTrait DB object.

    Function to be used to create test NodeTrait objects in the database.

    :param kw: kwargs with overriding values for trait's attributes.
    :returns: Test NodeTrait DB object.
    """
    trait = get_test_node_trait(**kw)
    dbapi = db_api.get_instance()
    return dbapi.add_node_trait(trait['node_id'], trait['trait'],
                                trait['version'])


def create_test_node_traits(traits, **kw):
    """Create test node trait entries in DB and return NodeTrait DB objects.

    Function to be used to create test NodeTrait objects in the database.

    :param traits: a list of Strings; traits to create.
    :param kw: kwargs with overriding values for trait's attributes.
    :returns: a list of test NodeTrait DB objects.
    """
    return [create_test_node_trait(trait=trait, **kw) for trait in traits]


def create_test_bios_setting(**kw):
    """Create test bios entry in DB and return BIOSSetting DB object.

    Function to be used to create test BIOSSetting object in the database.

    :param kw: kwargs with overriding values for node bios settings.
    :returns: Test BIOSSetting DB object.

    """
    bios_setting = get_test_bios_setting(**kw)
    dbapi = db_api.get_instance()
    node_id = bios_setting['node_id']
    version = bios_setting['version']
    settings = [{'name': bios_setting['name'],
                 'value': bios_setting['value'],
                 'attribute_type': bios_setting['attribute_type'],
                 'allowable_values': bios_setting['allowable_values'],
                 'read_only': bios_setting['read_only'],
                 'reset_required': bios_setting['reset_required'],
                 'unique': bios_setting['unique']}]
    return dbapi.create_bios_setting_list(node_id, settings, version)[0]


def get_test_bios_setting(**kw):
    return {
        'node_id': kw.get('node_id', '123'),
        'name': kw.get('name', 'virtualization'),
        'value': kw.get('value', 'on'),
        'attribute_type': kw.get('attribute_type', 'Enumeration'),
        'allowable_values': kw.get('allowable_values', ['on', 'off']),
        'lower_bound': kw.get('lower_bound', None),
        'max_length': kw.get('max_length', None),
        'min_length': kw.get('max_length', None),
        'read_only': kw.get('read_only', False),
        'reset_required': kw.get('reset_required', True),
        'unique': kw.get('unique', False),
        'upper_bound': kw.get('upper_bound', None),
        'version': kw.get('version', bios.BIOSSetting.VERSION),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def get_test_bios_setting_setting_list():
    return [
        {'name': 'virtualization', 'value': 'on'},
        {'name': 'hyperthread', 'value': 'enabled'},
        {'name': 'numlock', 'value': 'off'}
    ]


def get_test_allocation(**kw):
    return {
        'candidate_nodes': kw.get('candidate_nodes', []),
        'conductor_affinity': kw.get('conductor_affinity'),
        'created_at': kw.get('created_at'),
        'extra': kw.get('extra', {}),
        'id': kw.get('id', 42),
        'last_error': kw.get('last_error'),
        'name': kw.get('name'),
        'node_id': kw.get('node_id'),
        'resource_class': kw.get('resource_class', 'baremetal'),
        'state': kw.get('state', 'allocating'),
        'traits': kw.get('traits', []),
        'updated_at': kw.get('updated_at'),
        'uuid': kw.get('uuid', uuidutils.generate_uuid()),
        'version': kw.get('version', allocation.Allocation.VERSION),
        'owner': kw.get('owner', None),
    }


def create_test_allocation(**kw):
    allocation = get_test_allocation(**kw)
    if 'id' not in kw:
        del allocation['id']
    dbapi = db_api.get_instance()
    return dbapi.create_allocation(allocation)


def get_test_deploy_template(**kw):
    default_uuid = uuidutils.generate_uuid()
    return {
        'version': kw.get('version', deploy_template.DeployTemplate.VERSION),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'id': kw.get('id', 234),
        'name': kw.get('name', u'CUSTOM_DT1'),
        'uuid': kw.get('uuid', default_uuid),
        'steps': kw.get('steps', [get_test_deploy_template_step(
            deploy_template_id=kw.get('id', 234))]),
        'extra': kw.get('extra', {}),
    }


def get_test_deploy_template_step(**kw):
    return {
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'id': kw.get('id', 345),
        'deploy_template_id': kw.get('deploy_template_id', 234),
        'interface': kw.get('interface', 'raid'),
        'step': kw.get('step', 'create_configuration'),
        'args': kw.get('args', {'logical_disks': []}),
        'priority': kw.get('priority', 10),
    }


def create_test_deploy_template(**kw):
    """Create a deployment template in the DB and return DeployTemplate model.

    :param kw: kwargs with overriding values for the deploy template.
    :returns: Test DeployTemplate DB object.
    """
    template = get_test_deploy_template(**kw)
    dbapi = db_api.get_instance()
    # Let DB generate an ID if one isn't specified explicitly.
    if 'id' not in kw:
        del template['id']
    if 'steps' not in kw:
        for step in template['steps']:
            del step['id']
            del step['deploy_template_id']
    else:
        for kw_step, template_step in zip(kw['steps'], template['steps']):
            if 'id' not in kw_step:
                del template_step['id']
    return dbapi.create_deploy_template(template)


def get_test_runbook(**kw):
    default_uuid = uuidutils.generate_uuid()
    return {
        'version': kw.get('version', runbook.Runbook.VERSION),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'id': kw.get('id', 234),
        'name': kw.get('name', u'CUSTOM_DT1'),
        'uuid': kw.get('uuid', default_uuid),
        'steps': kw.get('steps', [get_test_runbook_step(
            runbook_id=kw.get('id', 234))]),
        'disable_ramdisk': kw.get('disable_ramdisk', False),
        'extra': kw.get('extra', {}),
        'public': kw.get('public', False),
        'owner': kw.get('owner', None),
    }


def get_test_runbook_step(**kw):
    return {
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
        'id': kw.get('id', 345),
        'runbook_id': kw.get('runbook_id', 234),
        'interface': kw.get('interface', 'raid'),
        'step': kw.get('step', 'create_configuration'),
        'args': kw.get('args', {'logical_disks': []}),
        'order': kw.get('order', 1)
    }


def create_test_runbook(**kw):
    """Create a runbook in the DB and return Runbook model.

    :param kw: kwargs with overriding values for the runbook.
    :returns: Test Runbook DB object.
    """
    runbook = get_test_runbook(**kw)
    dbapi = db_api.get_instance()
    # Let DB generate an ID if one isn't specified explicitly.
    if 'id' not in kw:
        del runbook['id']
    if 'steps' not in kw:
        for step in runbook['steps']:
            del step['id']
            del step['runbook_id']
    else:
        for kw_step, runbook_step in zip(kw['steps'], runbook['steps']):
            if 'id' not in kw_step:
                del runbook_step['id']
    return dbapi.create_runbook(runbook)


def get_test_history(**kw):
    return {
        'id': kw.get('id', 345),
        'version': kw.get('version', node_history.NodeHistory.VERSION),
        'uuid': kw.get('uuid', '6f8a5d5c-0f2d-4b2c-a62a-a38e300e3f31'),
        'node_id': kw.get('node_id', 123),
        'event': kw.get('event', 'Something is wrong'),
        'conductor': kw.get('conductor', 'host-1'),
        'severity': kw.get('severity', 'ERROR'),
        'event_type': kw.get('event_type', 'provisioning'),
        'user': kw.get('user', 'fake-user'),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_history(**kw):
    """Create test history entry in DB and return NodeHistory DB object.

    :param kw: kwargs with overriding values for port's attributes.
    :returns: Test NodeHistory DB object.
    """
    history = get_test_history(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del history['id']
    dbapi = db_api.get_instance()
    return dbapi.create_node_history(history)


def get_test_inventory(**kw):
    return {
        'id': kw.get('id', 345),
        'version': kw.get('version', node_inventory.NodeInventory.VERSION),
        'node_id': kw.get('node_id', 123),
        'inventory_data': kw.get('inventory', {"inventory": "test"}),
        'plugin_data': kw.get('plugin_data', {"pdata": {"plugin": "data"}}),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def create_test_inventory(**kw):
    """Create test inventory entry in DB and return NodeInventory DB object.

    :param kw: kwargs with overriding values for inventory attributes.
    :returns: Test NodeInventory DB object.
    """
    inventory = get_test_inventory(**kw)
    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del inventory['id']
    dbapi = db_api.get_instance()
    return dbapi.create_node_inventory(inventory)


def create_test_firmware_component(**kw):
    """Create test Firmware Component entry in DB and return object.

    Function to be used to create test FirmwareComponent object in the DB.

    :param kw: kwargs with overriding values for firmware component
    :returns: Test FirmwareComponent DB object
    """
    fw_cmp_values = get_test_firmware_component(**kw)
    if 'id' not in kw:
        del fw_cmp_values['id']
    dbapi = db_api.get_instance()
    return dbapi.create_firmware_component(fw_cmp_values)


def get_test_firmware_component(**kw):
    # NOTE(iurygregory): update version to get from the object in the object
    # patch.
    return {
        'id': kw.get('id', 256),
        'node_id': kw.get('node_id', 123),
        'component': kw.get('component', 'bmc'),
        'initial_version': kw.get('initial_version', 'v1.0.0'),
        'current_version': kw.get('current_version', 'v1.0.0'),
        'last_version_flashed': kw.get('last_version_flashed', None),
        'version': kw.get('version', firmware.FirmwareComponent.VERSION),
        'created_at': kw.get('created_at'),
        'updated_at': kw.get('updated_at'),
    }


def get_test_firmware_component_list():
    return [
        {'component': 'bmc', 'initial_version': 'v1.0.0',
         'current_version': 'v1.0.0', 'last_version_flashed': None},
        {'component': 'BIOS', 'initial_version': 'v1.5.0',
         'current_version': 'v1.5.0', 'last_version_flashed': None},
    ]


def get_test_inspection_rule(**kw):
    default_uuid = uuidutils.generate_uuid()
    return {
        'version': kw.get('version', rule.InspectionRule.VERSION),
        'uuid': kw.get('uuid', default_uuid),
        'description': kw.get('description', 'an inspection rule'),
        'sensitive': kw.get('sensitive', False),
        'phase': kw.get('phase', 'main'),
        'priority': kw.get('priority', 0),
        'actions': kw.get('actions', [get_test_inspection_rule_action(
            inspection_rule_id=kw.get('uuid'))]),
        'conditions': kw.get('conditions', [
            get_test_inspection_rule_condition(
                inspection_rule_id=kw.get('uuid'))]),
    }


def get_test_inspection_rule_action(**kw):
    action = {
        'op': kw.get('op', 'set-attribute'),
        'args': kw.get('args', ["/driver", "idrac"]),
    }

    if 'id' in kw:
        action['id'] = kw['id']

    action.update({k: v for k, v in kw.items() if k not in action})
    return action


def get_test_inspection_rule_condition(**kw):
    condition = {
        'op': kw.get('op', 'is-true'),
        'args': kw.get('args', ["{node.auto_discovered}"]),
        'multiple': kw.get('multiple', 'any'),
    }

    if 'id' in kw:
        condition['id'] = kw['id']

    condition.update({k: v for k, v in kw.items() if k not in condition})
    return condition


def create_test_inspection_rule(**kw):
    """Create a inspection rule in the DB and return InspectionRule model.

    :param kw: kwargs with overriding values for the inspection rule.
    :returns: Test InspectionRule DB object.
    """
    inspection_rule = get_test_inspection_rule(**kw)
    dbapi = db_api.get_instance()
    if 'uuid' not in kw:
        del inspection_rule['uuid']
    return dbapi.create_inspection_rule(inspection_rule)
