Parse the output from the ``pkg upgrade --dry-run`` command

    Returns a dictionary of the expected actions:

    .. code-block:: yaml

        'upgrade':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
              'new': n.n.n
        'install':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
        'reinstall':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
        'downgrade':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
              'new': n.n.n
        'remove':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n

def _parse_upgrade(stdout):
    '''
    Parse the output from the ``pkg upgrade --dry-run`` command

    Returns a dictionary of the expected actions:

    .. code-block:: yaml

        'upgrade':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
              'new': n.n.n
        'install':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
        'reinstall':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
        'downgrade':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
              'new': n.n.n
        'remove':
          pkgname:
            'repo': repository
            'reason': reason
            'version':
              'current': n.n.n
    '''
    # Match strings like 'python36: 3.6.3 -> 3.6.4 [FreeBSD]'
    upgrade_regex = re.compile(r'^\s+([^:]+):\s([0-9a-z_,.]+)\s+->\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?')
    # Match strings like 'rubygem-bcrypt_pbkdf: 1.0.0 [FreeBSD]'
    install_regex = re.compile(r'^\s+([^:]+):\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?')
    # Match strings like 'py27-yaml-3.11_2 [FreeBSD] (direct dependency changed: py27-setuptools)'
    reinstall_regex = re.compile(r'^\s+(\S+)-(?<=-)([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?')

    result = {'upgrade': {}, 'install': {}, 'reinstall': {}, 'remove': {}, 'downgrade': {}}
    action = None
    for line in salt.utils.itertools.split(stdout, '\n'):

        if not line:
            action = None
            continue

        if line == 'Installed packages to be UPGRADED:':
            action = 'upgrade'
            continue

        if line == 'New packages to be INSTALLED:':
            action = 'install'
            continue

        if line == 'Installed packages to be REINSTALLED:':
            action = 'reinstall'
            continue

        if line == 'Installed packages to be REMOVED:':
            action = 'remove'
            continue

        if line == 'Installed packages to be DOWNGRADED:':
            action = 'downgrade'
            continue

        if action == 'upgrade' or action == 'downgrade':
            match = upgrade_regex.match(line)
            if match:
                result[action][match.group(1)] = {
                    'version': {
                        'current': match.group(2),
                        'new': match.group(3)
                    },
                    'repo': match.group(5),
                    'reason': match.group(7)
                }
            else:
                log.error('Unable to parse %s: \'%s\'', action, line)

        if action == 'install':
            match = install_regex.match(line)
            if match:
                result[action][match.group(1)] = {
                    'version': {
                        'current': match.group(2)
                    },
                    'repo': match.group(4),
                    'reason': match.group(6)
                }
            else:
                log.error('Unable to parse %s: \'%s\'', action, line)

        if (action == 'reinstall') or (action == 'remove'):
            match = reinstall_regex.match(line)
            if match:
                result[action][match.group(1)] = {
                    'version': {
                        'current': match.group(2)
                    },
                    'repo': match.group(4),
                    'reason': match.group(6)
                }
            else:
                log.error('Unable to parse %s: \'%s\'', action, line)

    return result