Upgrade NX-OS switch.

    system_image (Mandatory Option)
        Path on bootflash: to system image upgrade file.

    kickstart_image
        Path on bootflash: to kickstart image upgrade file.
        (Not required if using combined system/kickstart image file)
        Default: None

    issu
        Set this option to True when an In Service Software Upgrade or
        non-disruptive upgrade is required. The upgrade will abort if issu is
        not possible.
        Default: True

    timeout
        Timeout in seconds for long running 'install all' upgrade command.
        Default: 900

    error_pattern
        Use the option to pass in a regular expression to search for in the
        output of the 'install all upgrade command that indicates an error
        has occurred.  This option is only used when proxy minion connection
        type is ssh and otherwise ignored.

    .. code-block:: bash

        salt 'n9k' nxos.upgrade system_image=nxos.9.2.1.bin
        salt 'n7k' nxos.upgrade system_image=n7000-s2-dk9.8.1.1.bin \\
            kickstart_image=n7000-s2-kickstart.8.1.1.bin issu=False

def upgrade(system_image, kickstart_image=None, issu=True, **kwargs):
    '''
    Upgrade NX-OS switch.

    system_image (Mandatory Option)
        Path on bootflash: to system image upgrade file.

    kickstart_image
        Path on bootflash: to kickstart image upgrade file.
        (Not required if using combined system/kickstart image file)
        Default: None

    issu
        Set this option to True when an In Service Software Upgrade or
        non-disruptive upgrade is required. The upgrade will abort if issu is
        not possible.
        Default: True

    timeout
        Timeout in seconds for long running 'install all' upgrade command.
        Default: 900

    error_pattern
        Use the option to pass in a regular expression to search for in the
        output of the 'install all upgrade command that indicates an error
        has occurred.  This option is only used when proxy minion connection
        type is ssh and otherwise ignored.

    .. code-block:: bash

        salt 'n9k' nxos.upgrade system_image=nxos.9.2.1.bin
        salt 'n7k' nxos.upgrade system_image=n7000-s2-dk9.8.1.1.bin \\
            kickstart_image=n7000-s2-kickstart.8.1.1.bin issu=False
    '''
    # Input Validation
    if not isinstance(issu, bool):
        return 'Input Error: The [issu] parameter must be either True or False'

    impact = None
    upgrade = None
    maxtry = 60
    for attempt in range(1, maxtry):
        # Gather impact data first.  It's possible to loose upgrade data
        # when the switch reloads or switches over to the inactive supervisor.
        # The impact data will be used if data being collected during the
        # upgrade is lost.
        if impact is None:
            log.info('Gathering impact data')
            impact = check_upgrade_impact(system_image, kickstart_image,
                                          issu, **kwargs)
            if impact['installing']:
                log.info('Another show impact in progress... wait and retry')
                time.sleep(30)
                continue
            # If we are upgrading from a system running a separate system and
            # kickstart image to a combined image or vice versa then the impact
            # check will return a syntax error as it's not supported.
            # Skip the impact check in this case and attempt the upgrade.
            if impact['invalid_command']:
                impact = False
                continue
            log.info('Impact data gathered:\n%s', impact)

            # Check to see if conditions are sufficent to return the impact
            # data and not proceed with the actual upgrade.
            #
            # Impact data indicates the upgrade or downgrade will fail
            if impact['error_data']:
                return impact
            # Requested ISSU but ISSU is not possible
            if issu and not impact['upgrade_non_disruptive']:
                impact['error_data'] = impact['upgrade_data']
                return impact
            # Impact data indicates a failure and no module_data collected
            if not impact['succeeded'] and not impact['module_data']:
                impact['error_data'] = impact['upgrade_data']
                return impact
            # Impact data indicates switch already running desired image
            if not impact['upgrade_required']:
                impact['succeeded'] = True
                return impact
        # If we get here, impact data indicates upgrade is needed.
        upgrade = _upgrade(system_image, kickstart_image, issu, **kwargs)
        if upgrade['installing']:
            log.info('Another install is in progress... wait and retry')
            time.sleep(30)
            continue
        # If the issu option is False and this upgrade request includes a
        # kickstart image then the 'force' option is used.  This option is
        # only available in certain image sets.
        if upgrade['invalid_command']:
            log_msg = 'The [issu] option was set to False for this upgrade.'
            log_msg = log_msg + ' Attempt was made to ugrade using the force'
            log_msg = log_msg + ' keyword which is not supported in this'
            log_msg = log_msg + ' image.  Set [issu=True] and re-try.'
            upgrade['upgrade_data'] = log_msg
            break
        break

    # Check for errors and return upgrade result:
    if upgrade['backend_processing_error']:
        # This means we received a backend processing error from the transport
        # and lost the upgrade data.  This also indicates that the upgrade
        # is in progress so use the impact data for logging purposes.
        impact['upgrade_in_progress'] = True
        return impact
    return upgrade