#  pylint: disable=missing-module-docstring
#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
#
# Kickstart-related operations
#

import sys

from spacewalk.common import rhnFlags
from spacewalk.common.rhnLog import log_debug, log_error
from spacewalk.common.rhnException import rhnException
from spacewalk.server import rhnSQL, rhnAction, rhnLib, rhnChannel


def update_kickstart_session(
    server_id, action_id, action_status, kickstart_state, next_action_type
):
    log_debug(3, server_id, action_id, action_status, kickstart_state, next_action_type)

    # Is this a kickstart-related action?
    ks_session_id = get_kickstart_session_id(server_id, action_id)
    if ks_session_id is None:
        # Nothing more to do
        log_debug(4, "Kickstart session not found")
        return None

    # Check the current action state
    if action_status == 2:
        # Completed
        ks_status = kickstart_state
        # Get the next action - it has to be of the right type
        next_action_id = get_next_action_id(action_id, next_action_type)
    elif action_status == 3:
        # Failed
        ks_status = "failed"
        next_action_id = None
    else:
        # pylint: disable-next=consider-using-f-string
        raise rhnException("Invalid action state %s" % action_status)

    update_ks_session_table(ks_session_id, ks_status, next_action_id, server_id)
    return ks_session_id


_query_update_ks_session_table = rhnSQL.Statement(
    """
    update rhnKickstartSession
       set action_id = :action_id,
           state_id = :ks_status_id,
           new_server_id = :server_id
     where id = :ks_session_id
"""
)


def update_ks_session_table(ks_session_id, ks_status, next_action_id, server_id):
    log_debug(4, ks_session_id, ks_status, next_action_id, server_id)
    ks_table = rhnSQL.Table("rhnKickstartSessionState", "label")
    ks_status_id = ks_table[ks_status]["id"]

    h = rhnSQL.prepare(_query_update_ks_session_table)
    h.execute(
        ks_session_id=ks_session_id,
        ks_status_id=ks_status_id,
        action_id=next_action_id,
        server_id=server_id,
    )

    if ks_status == "complete":
        delete_guests(server_id)


_query_lookup_guests_for_host = rhnSQL.Statement(
    """
    select virtual_system_id from rhnVirtualInstance
        where host_system_id = :server_id
"""
)
_query_delete_virtual_instances = rhnSQL.Statement(
    """
    delete from rhnVirtualInstance where host_system_id = :server_id
"""
)


def delete_guests(server_id):
    """
    Callback used after a successful kickstart to remove any guest virtual
    instances, as well as their associated servers.
    """
    # First delete all the guest server objects:
    h = rhnSQL.prepare(_query_lookup_guests_for_host)
    h.execute(server_id=server_id)
    delete_server = rhnSQL.Procedure("delete_server")
    log_debug(4, "Deleting guests")
    while 1:
        row = h.fetchone_dict()
        if not row:
            break
        guest_id = row["virtual_system_id"]
        # pylint: disable-next=consider-using-f-string
        log_debug(4, "Deleting guest server: %s" % guest_id)
        try:
            if guest_id is not None:
                delete_server(guest_id)
        except rhnSQL.SQLError:
            # pylint: disable-next=consider-using-f-string
            log_error("Error deleting server: %s" % guest_id)

    # Finally delete all the virtual instances:
    log_debug(4, "Deleting all virtual instances for host")
    h = rhnSQL.prepare(_query_delete_virtual_instances)
    h.execute(server_id=server_id)

    # Commit all changes:
    try:
        rhnSQL.commit()
    except rhnSQL.SQLError:
        e = sys.exc_info()[1]
        # pylint: disable-next=consider-using-f-string
        log_error("Error committing transaction: %s" % e)
        rhnSQL.rollback()


_query_get_next_action_id = rhnSQL.Statement(
    """
    select a.id
      from rhnAction a, rhnActionType at
     where a.prerequisite = :action_id
       and a.action_type = at.id
       and at.label = :next_action_type
"""
)


def get_next_action_id(action_id, next_action_type=None):
    if not next_action_type:
        return None
    h = rhnSQL.prepare(_query_get_next_action_id)
    h.execute(action_id=action_id, next_action_type=next_action_type)
    row = h.fetchone_dict()
    if not row:
        return None
    return row["id"]


_query_lookup_kickstart_session_id = rhnSQL.Statement(
    """
    select ks.id
      from rhnKickstartSession ks
     where (
             (ks.old_server_id = :server_id and ks.new_server_id is NULL)
             or ks.new_server_id = :server_id
             or ks.host_server_id = :server_id
           )
       and ks.action_id = :action_id
"""
)


def get_kickstart_session_id(server_id, action_id):
    h = rhnSQL.prepare(_query_lookup_kickstart_session_id)
    h.execute(server_id=server_id, action_id=action_id)

    row = h.fetchone_dict()
    if not row:
        # Nothing to do
        return None
    return row["id"]


_query_insert_package_delta = rhnSQL.Statement(
    """
    insert into rhnPackageDelta (id, label)
    values (:package_delta_id, 'ks-delta-' || :package_delta_id)
"""
)
_query_insert_action_package_delta = rhnSQL.Statement(
    """
    insert into rhnActionPackageDelta (action_id, package_delta_id)
    values (:action_id, :package_delta_id)
"""
)
_query_insert_package_delta_element = rhnSQL.Statement(
    """
    insert into rhnPackageDeltaElement
           (package_delta_id, transaction_package_id)
    values
           (:package_delta_id,
            lookup_transaction_package(:operation, :n, :e, :v, :r, :a))
"""
)


def schedule_kickstart_delta(server_id, kickstart_session_id, installs, removes):
    log_debug(3, server_id, kickstart_session_id)
    row = get_kickstart_session_info(kickstart_session_id, server_id)
    org_id = row["org_id"]
    scheduler = row["scheduler"]

    action_id = rhnAction.schedule_server_action(
        server_id,
        action_type="packages.runTransaction",
        action_name="Package delta",
        delta_time=0,
        scheduler=scheduler,
        org_id=org_id,
    )

    package_delta_id = rhnSQL.Sequence("rhn_packagedelta_id_seq").next()

    h = rhnSQL.prepare(_query_insert_package_delta)
    h.execute(package_delta_id=package_delta_id)

    h = rhnSQL.prepare(_query_insert_action_package_delta)
    h.execute(action_id=action_id, package_delta_id=package_delta_id)

    h = rhnSQL.prepare(_query_insert_package_delta_element)
    col_names = ["n", "v", "r", "e"]
    __execute_many(
        h,
        installs,
        col_names,
        operation="insert",
        a=None,
        package_delta_id=package_delta_id,
    )
    __execute_many(
        h,
        removes,
        col_names,
        operation="delete",
        a=None,
        package_delta_id=package_delta_id,
    )

    update_ks_session_table(
        kickstart_session_id, "package_synch_scheduled", action_id, server_id
    )

    return action_id


def schedule_kickstart_sync(server_id, kickstart_session_id):
    row = get_kickstart_session_info(kickstart_session_id, server_id)
    org_id = row["org_id"]
    scheduler = row["scheduler"]

    # Create a new action
    action_id = rhnAction.schedule_server_action(
        server_id,
        action_type="kickstart.schedule_sync",
        action_name="Schedule a package sync",
        delta_time=0,
        scheduler=scheduler,
        org_id=org_id,
    )
    return action_id


def _get_ks_virt_type(type_id):
    # pylint: disable-next=invalid-name
    _query_kickstart_virt_type = rhnSQL.Statement(
        """
        select  label
        from    rhnKickstartVirtualizationType kvt
        where   kvt.id = :id
    """
    )
    prepared_query = rhnSQL.prepare(_query_kickstart_virt_type)
    prepared_query.execute(id=type_id)
    row = prepared_query.fetchone_dict()

    # XXX: we should have better constraints on the db so this doesn't happen.
    if not row:
        kstype = "auto"
    else:
        kstype = row["label"]
    # pylint: disable-next=consider-using-f-string
    log_debug(1, "KS_TYPE: %s" % kstype)
    return kstype


def get_kickstart_session_type(server_id, action_id):
    ks_session_id = get_kickstart_session_id(server_id, action_id)
    ks_session_info = get_kickstart_session_info(ks_session_id, server_id)
    ks_type_id = ks_session_info["virtualization_type"]
    ks_type = _get_ks_virt_type(ks_type_id)
    return ks_type


def subscribe_to_tools_channel(server_id, kickstart_session_id):
    log_debug(3)
    row = get_kickstart_session_info(kickstart_session_id, server_id)
    org_id = row["org_id"]
    scheduler = row["scheduler"]
    ks_type_id = row["virtualization_type"]
    ks_type = _get_ks_virt_type(ks_type_id)

    if ks_type == "para_host":
        action_id = rhnAction.schedule_server_action(
            server_id,
            action_type="kickstart_host.add_tools_channel",
            action_name="Subscribe server to the Tools channel.",
            delta_time=0,
            scheduler=scheduler,
            org_id=org_id,
        )
    elif ks_type == "para_guest":
        action_id = rhnAction.schedule_server_action(
            server_id,
            action_type="kickstart_guest.add_tools_channel",
            action_name="Subscribe guest to the Tools channel.",
            delta_time=0,
            scheduler=scheduler,
            org_id=org_id,
        )
    else:
        action_id = None
    return action_id


def schedule_virt_pkg_install(server_id, kickstart_session_id):
    log_debug(3)
    row = get_kickstart_session_info(kickstart_session_id, server_id)
    org_id = row["org_id"]
    scheduler = row["scheduler"]
    ks_type_id = row["virtualization_type"]
    # pylint: disable-next=consider-using-f-string
    log_debug(1, "VIRTUALIZATION_TYPE: %s" % str(ks_type_id))
    ks_type = _get_ks_virt_type(ks_type_id)
    # pylint: disable-next=consider-using-f-string
    log_debug(1, "VIRTUALZIATION_TYPE_LABEL: %s" % str(ks_type))

    if ks_type == "para_host":
        log_debug(1, "SCHEDULING VIRT HOST PACKAGE INSTALL...")
        action_id = rhnAction.schedule_server_action(
            server_id,
            action_type="kickstart_host.schedule_virt_host_pkg_install",
            action_name="Schedule install of mgr-virtualization-host package.",
            delta_time=0,
            scheduler=scheduler,
            org_id=org_id,
        )
    elif ks_type == "para_guest":
        log_debug(1, "SCHEDULING VIRT GUEST PACKAGE INSTALL...")
        action_id = rhnAction.schedule_server_action(
            server_id,
            action_type="kickstart_guest.schedule_virt_guest_pkg_install",
            action_name="Schedule install of mgr-virtualization-guest package.",
            delta_time=0,
            scheduler=scheduler,
            org_id=org_id,
        )
    else:
        log_debug(1, "NOT A VIRT KICKSTART")
        action_id = None

    return action_id


_query_ak_deploy_config = rhnSQL.Statement(
    """
select rt.deploy_configs
  from rhnKickstartSession ks,
       rhnKickstartDefaultRegToken kdrt,
       rhnRegToken rt
 where ks.kickstart_id = kdrt.kickstart_id
   and kdrt.regtoken_id = rt.id
   and ks.id = :session_id
"""
)
# Make sure the activation keys associated with this kickstart profile
# have enabled deploying config files. Only deploy configs if at least one
# of them has. This is replacing code that didn't work because the
# rhnFlags('registration_token') could not be set during the rhn_check call.


def ks_activation_key_deploy_config(kickstart_session_id):
    h = rhnSQL.prepare(_query_ak_deploy_config)
    h.execute(session_id=kickstart_session_id)
    rows = h.fetchall_dict()
    if rows:
        for row in rows:
            if row["deploy_configs"] and row["deploy_configs"] == "Y":
                return True
    return False


_query_schedule_config_files = rhnSQL.Statement(
    """
    insert into rhnActionConfigRevision
           (id, action_id, server_id, config_revision_id)
    select sequence_nextval('rhn_actioncr_id_seq'), :action_id,
           server_id, config_revision_id
      from (
            select distinct scc.server_id,
                   cf.latest_config_revision_id config_revision_id
              from rhnServerConfigChannel scc,
                   rhnConfigChannelType cct,
                   rhnConfigChannel cc,
                   rhnConfigFile cf,
                   rhnConfigFileState cfs
             where scc.server_id = :server_id
               and scc.config_channel_id = cf.config_channel_id
               and cf.config_channel_id = cc.id
               and cc.confchan_type_id = cct.id
               and cct.label in ('normal', 'local_override')
               and cf.latest_config_revision_id is not null
               and cf.state_id = cfs.id
               and cfs.label = 'alive'
            ) X
"""
)


def schedule_config_deploy(server_id, action_id, kickstart_session_id, server_profile):
    """schedule a configfiles.deploy action dependent on the current action"""
    log_debug(3, server_id, action_id, kickstart_session_id)
    row = get_kickstart_session_info(kickstart_session_id, server_id)
    org_id = row["org_id"]
    scheduler = row["scheduler"]
    deploy_configs = row["deploy_configs"] == "Y" and ks_activation_key_deploy_config(
        kickstart_session_id
    )

    if not deploy_configs:
        # Nothing more to do here
        update_ks_session_table(
            kickstart_session_id, "complete", next_action_id=None, server_id=server_id
        )
        return None

    if server_profile:
        # Have to schedule a package deploy action
        aid = schedule_rhncfg_install(server_id, action_id, scheduler, server_profile)
    else:
        aid = action_id

    next_action_id = rhnAction.schedule_server_action(
        server_id,
        action_type="configfiles.deploy",
        action_name="Deploy config files",
        delta_time=0,
        scheduler=scheduler,
        org_id=org_id,
        prerequisite=aid,
    )
    # Deploy all of the config files that are part of this server's config
    # channels
    h = rhnSQL.prepare(_query_schedule_config_files)
    h.execute(server_id=server_id, action_id=next_action_id)
    update_ks_session_table(
        kickstart_session_id, "configuration_deploy", next_action_id, server_id
    )
    return next_action_id


class MissingBaseChannelError(Exception):
    pass


def schedule_rhncfg_install(server_id, action_id, scheduler, server_profile=None):
    capability = "rhn-config-action"
    try:
        packages = _subscribe_server_to_capable_channels(
            server_id, scheduler, capability
        )
    except MissingBaseChannelError:
        log_debug(2, "No base channel", server_id)
        return action_id

    if not packages:
        # No channels offer this capability
        log_debug(
            3,
            server_id,
            action_id,
            # pylint: disable-next=consider-using-f-string
            "No channels to provide %s found" % capability,
        )
        # No new action needed here
        return action_id

    if not server_profile:
        server_profile = get_server_package_profile(server_id)

    # Make the package profile a hash, for easier checking
    sphash = {}
    for p in server_profile:
        sphash[tuple(p)] = None

    packages_to_install = []
    for p in packages:
        key = (p["name"], p["version"], p["release"], p["epoch"])
        if key not in sphash:
            packages_to_install.append(p["package_id"])

    if not packages_to_install:
        # We already have these packages installed
        log_debug(4, "No packages needed to be installed")
        return action_id

    log_debug(4, "Scheduling package install action")
    new_action_id = schedule_package_install(
        server_id, action_id, scheduler, packages_to_install
    )
    return new_action_id


_query_lookup_subscribed_server_channels = rhnSQL.Statement(
    """
    select sc.channel_id,
           case when c.parent_channel is not null then 0 else 1 end is_base_channel
      from rhnServerChannel sc, rhnChannel c
     where sc.server_id = :server_id
       and sc.channel_id = c.id
"""
)
_query_lookup_unsubscribed_server_channels = rhnSQL.Statement(
    """
select c.id
  from
      -- Get all the channels available to this org
      ( select cfm.channel_id
          from rhnChannelFamilyMembers cfm,
               rhnPrivateChannelFamily pcf
         where pcf.org_id = :org_id
           and pcf.channel_family_id = cfm.channel_family_id
        union
        select cfm.channel_id
          from rhnChannelFamilyMembers cfm,
               rhnPublicChannelFamily pcf
         where pcf.channel_family_id = cfm.channel_family_id) ac,
       rhnChannel c
 where c.parent_channel = :base_channel_id
   and c.id = ac.channel_id
   and  not exists (
        select 1
          from rhnServerChannel
         where server_id = :server_id
         and channel_id = c.id)
"""
)


def _subscribe_server_to_capable_channels(server_id, scheduler, capability):
    log_debug(4, server_id, scheduler, capability)
    # Look through the channels this server is already subscribed to
    h = rhnSQL.prepare(_query_lookup_subscribed_server_channels)
    h.execute(server_id=server_id)
    base_channel_id = None
    channels = []
    while 1:
        row = h.fetchone_dict()
        if not row:
            break
        channel_id = row["channel_id"]
        if row["is_base_channel"]:
            base_channel_id = channel_id
        channels.append((channel_id, 1))
    if base_channel_id is None:
        raise MissingBaseChannelError()

    org_id = rhnSQL.Table("rhnServer", "id")[server_id]["org_id"]

    # Get the child channels this system is *not* subscribed to
    h = rhnSQL.prepare(_query_lookup_unsubscribed_server_channels)
    h.execute(server_id=server_id, org_id=org_id, base_channel_id=base_channel_id)
    l = [(x["id"], 0) for x in h.fetchall_dict() or []]
    channels.extend(l)
    # We now have a list of channels; look for one that provides the
    # capability
    for channel_id, is_subscribed in channels:
        log_debug(5, "Checking channel:", channel_id, "; subscribed:", is_subscribed)
        packages = _channel_provides_capability(channel_id, capability)
        if packages:
            if is_subscribed:
                log_debug(4, "Already subscribed; found packages", packages)
                return packages

            # Try to subscribe to it
            rhnChannel.subscribe_sql(server_id, channel_id, 0)
            log_debug(4, "Subscribed to", channel_id, "Found packages", packages)
            # We subscribed to this channel - we're done
            return packages

    # No channels provide this capability - we're done
    log_debug(4, "No channels to provide capability", capability)
    return None


_query_channel_provides_capability = rhnSQL.Statement(
    """
    select distinct pp.package_id, pn.name, pe.version, pe.release, pe.epoch
      from rhnChannelNewestPackage cnp,
           rhnPackageProvides pp,
           rhnPackageCapability pc,
           rhnPackageName pn,
           rhnPackageEVR pe
     where cnp.channel_id = :channel_id
       and cnp.package_id = pp.package_id
       and pp.capability_id = pc.id
       and pc.name = :capability
       and cnp.name_id = pn.id
       and cnp.evr_id = pe.id
"""
)


def _channel_provides_capability(channel_id, capability):
    log_debug(4, channel_id, capability)
    h = rhnSQL.prepare(_query_channel_provides_capability)
    h.execute(channel_id=channel_id, capability=capability)
    ret = h.fetchall_dict()
    if not ret:
        return ret
    return ret


_query_insert_action_packages = rhnSQL.Statement(
    """
    insert into rhnActionPackage
           (id, action_id, name_id, evr_id, package_arch_id, parameter)
    select sequence_nextval('rhn_act_p_id_seq'), :action_id, name_id, evr_id,
           package_arch_id, 'upgrade'
      from rhnPackage
     where id = :package_id
"""
)


def schedule_package_install(server_id, action_id, scheduler, packages):
    if not packages:
        # Nothing to do
        return action_id
    new_action_id = rhnAction.schedule_server_action(
        server_id,
        action_type="packages.update",
        action_name="Package update to enable configuration deployment",
        delta_time=0,
        scheduler=scheduler,
        prerequisite=action_id,
    )
    # Add entries to rhnActionPackage
    action_ids = [new_action_id] * len(packages)
    h = rhnSQL.prepare(_query_insert_action_packages)
    h.executemany(action_id=action_ids, package_id=packages)
    return new_action_id


# pylint: disable-next=invalid-name
def __execute_many(cursor, array, col_names, **kwargs):
    """Execute the cursor, with arguments extracted from the array
    The array is converted into a hash having col_names as keys, and adds
    whatever kwarg was specified too.
    """
    linecount = len(array)
    if not linecount:
        return
    # Transpose the array into a hash with col_names as keys
    params = rhnLib.transpose_to_hash(array, col_names)
    for k, v in list(kwargs.items()):
        params[k] = [v] * linecount

    cursor.executemany(**params)


def _packages_from_cursor(cursor):
    result = []
    while 1:
        row = cursor.fetchone_dict()
        if not row:
            break
        p_name = row["name"]
        if p_name == "gpg-pubkey":
            # We ignore GPG public keys since they are too weird to schedule
            # as a package delta
            continue
        result.append((p_name, row["version"], row["release"], row["epoch"]))
    return result


_query_lookup_pending_kickstart_sessions = rhnSQL.Statement(
    """
    select ks.id, ks.action_id, NULL other_server_id
      from rhnKickstartSessionState kss,
           rhnKickstartSession ks
     where (
             (ks.old_server_id = :server_id and ks.new_server_id is null)
             or ks.new_server_id = :server_id
           )
       and ks.state_id = kss.id
       and kss.label not in ('complete', 'failed')
       and (:ks_session_id is null or ks.id != :ks_session_id)
"""
)

_query_terminate_pending_kickstart_sessions = rhnSQL.Statement(
    """
    update rhnKickstartSession
       set action_id = NULL,
           state_id = :state_id
     where id = :kickstart_session_id
"""
)


def terminate_kickstart_sessions(server_id):
    log_debug(3, server_id)
    history = []
    tokens_obj = rhnFlags.get("registration_token")
    current_ks_session_id = tokens_obj.get_kickstart_session_id()
    # ks_session_id can be null
    h = rhnSQL.prepare(_query_lookup_pending_kickstart_sessions)
    h.execute(server_id=server_id, ks_session_id=current_ks_session_id)
    log_debug(4, "current_ks_session_id", current_ks_session_id)
    ks_session_ids = []
    action_ids = []
    while 1:
        row = h.fetchone_dict()
        if not row:
            break
        ks_session_ids.append(row["id"])
        action_ids.append(row["action_id"])

    if not ks_session_ids:
        # Nothing to do
        log_debug(4, "Nothing to do", server_id, current_ks_session_id)
        return []

    ks_session_table = rhnSQL.Table("rhnKickstartSessionState", "label")
    state_id_failed = ks_session_table["failed"]["id"]
    state_ids = [state_id_failed] * len(ks_session_ids)

    # Add a history item
    for ks_session_id in ks_session_ids:
        log_debug(4, "Adding history entry for session id", ks_session_id)
        history.append(
            (
                "Kickstart session canceled",
                # pylint: disable-next=consider-using-f-string
                "A kickstart session for this system was canceled because "
                "the system was re-registered with token <strong>%s</strong>"
                % tokens_obj.get_names(),
            )
        )

    h = rhnSQL.prepare(_query_terminate_pending_kickstart_sessions)

    params = {
        "kickstart_session_id": ks_session_ids,
        "state_id": state_ids,
    }
    # Terminate pending actions
    log_debug(4, "Terminating sessions", params)
    h.executemany(**params)

    # Invalidate pending actions
    for action_id in action_ids:
        if action_id is None:
            continue
        rhnAction.invalidate_action(server_id, action_id)
    return history


def get_kickstart_profile_package_profile(kickstart_session_id):
    """Fetches the package profile from the kickstart profile (Not the session)"""
    h = rhnSQL.prepare(
        """
        select pn.name, pe.version, pe.release, pe.epoch, pa.label
          from rhnKickstartSession ks,
               rhnKickstartDefaults kd,
               rhnServerProfilePackage spp,
               rhnPackageName pn,
               rhnPackageEVR pe,
               rhnPackageArch pa
         where ks.id = :kickstart_session_id
           and kd.server_profile_id = spp.server_profile_id
           and spp.name_id = pn.id
           and spp.evr_id = pe.id
           and spp.package_arch_id = pa.id
           and kd.kickstart_id = ks.kickstart_id
    """
    )
    h.execute(kickstart_session_id=kickstart_session_id)
    return _packages_from_cursor(h)


def get_kisckstart_session_package_profile(kickstart_session_id):
    """Fetches the package profile from the kickstart session"""
    h = rhnSQL.prepare(
        """
        select pn.name, pe.version, pe.release, pe.epoch, pa.label
          from rhnKickstartSession ks,
               rhnServerProfilePackage spp,
               rhnPackageName pn,
               rhnPackageEVR pe,
               rhnPackageArch pa
         where ks.id = :kickstart_session_id
           and ks.server_profile_id = spp.server_profile_id
           and spp.name_id = pn.id
           and spp.evr_id = pe.id
           and spp.package_arch_id = pa.id
    """
    )
    h.execute(kickstart_session_id=kickstart_session_id)
    return _packages_from_cursor(h)


def get_server_package_profile(server_id):
    # XXX misa 2005-05-25  May need to look at package arches too
    h = rhnSQL.prepare(
        """
        select pn.name, pe.version, pe.release, pe.epoch, pa.label
          from rhnServerPackage sp,
               rhnPackageName pn,
               rhnPackageEVR pe,
               rhnPackageArch pa
         where sp.server_id = :server_id
           and sp.name_id = pn.id
           and sp.evr_id = pe.id
           and sp.package_arch_id = pa.id
    """
    )
    h.execute(server_id=server_id)
    return _packages_from_cursor(h)


_query_get_kickstart_session_info = rhnSQL.Statement(
    """
    select org_id, scheduler, deploy_configs, virtualization_type
      from rhnKickstartSession
     where id = :kickstart_session_id
"""
)


def get_kickstart_session_info(kickstart_session_id, server_id):
    h = rhnSQL.prepare(_query_get_kickstart_session_info)
    h.execute(kickstart_session_id=kickstart_session_id)
    row = h.fetchone_dict()
    if not row:
        raise rhnException(
            # pylint: disable-next=consider-using-f-string
            "Could not fetch kickstart session id %s "
            "for server %s" % (kickstart_session_id, server_id)
        )

    return row


_query_lookup_ks_server_profile = rhnSQL.Statement(
    """
    select kss.server_profile_id
      from rhnServerProfileType spt,
           rhnServerProfile sp,
           rhnKickstartSession kss
     where kss.id = :ks_session_id
       and kss.server_profile_id = sp.id
       and sp.profile_type_id = spt.id
       and spt.label = :profile_type_label
"""
)
_query_delete_server_profile = rhnSQL.Statement(
    """
    delete from rhnServerProfile where id = :server_profile_id
"""
)


# pylint: disable-next=unused-argument
def cleanup_profile(server_id, action_id, ks_session_id, action_status):
    if ks_session_id is None:
        log_debug(4, "No kickstart session")
        return
    if action_status != 2:
        # pylint: disable-next=consider-using-f-string
        log_debug(4, "Action status: %s; nothing to do" % action_status)
        return

    h = rhnSQL.prepare(_query_lookup_ks_server_profile)
    h.execute(ks_session_id=ks_session_id, profile_type_label="sync_profile")
    row = h.fetchone_dict()
    if not row:
        log_debug(4, "No server profile of the right type found; nothing to do")
        return

    server_profile_id = row["server_profile_id"]
    if server_profile_id is None:
        log_debug(4, "No server profile associated with this kickstart session")
        return

    # There is an "on delete cascade" constraint on
    # rhnKickstartSession.server_profile_id and on
    # rhnServerProfilePacakge.server_profile_id
    h = rhnSQL.prepare(_query_delete_server_profile)
    h.execute(server_profile_id=server_profile_id)
