Example #1
0
def _update_to_advisory(builds, kind, advisory, remove, clean):
    click.echo(f"Attaching to advisory {advisory}...")
    if kind not in {"rpm", "image"}:
        raise ValueError(f"{kind} should be one of 'rpm' or 'image'")
    try:
        erratum = Erratum(errata_id=advisory)
        file_type = 'tar' if kind == 'image' else 'rpm'
        product_version_set = {build.product_version for build in builds}
        for pv in product_version_set:
            erratum.addBuilds(buildlist=[
                build.nvr for build in builds if build.product_version == pv
            ],
                              release=pv,
                              file_types={
                                  build.nvr: [file_type]
                                  for build in builds
                              })
            erratum.commit()

        build_nvrs = sorted(build.nvr for build in builds)
        green_print('Attached build(s) successfully:')
        for b in build_nvrs:
            click.echo(' ' + b)
        return erratum

    except GSSError:
        exit_unauthenticated()
    except elliottlib.exceptions.BrewBuildException as ex:
        raise ElliottFatalError(f'Error attaching/removing builds: {str(ex)}')
Example #2
0
def _attach_to_advisory(builds, kind, advisory):
    if kind is None:
        raise ElliottFatalError(
            'Need to specify with --kind=image or --kind=rpm with packages: {}'
            .format(builds))

    try:
        erratum = Erratum(errata_id=advisory)
        file_type = 'tar' if kind == 'image' else 'rpm'

        product_version_set = {build.product_version for build in builds}
        for pv in product_version_set:
            erratum.addBuilds(buildlist=[
                build.nvr for build in builds if build.product_version == pv
            ],
                              release=pv,
                              file_types={
                                  build.nvr: [file_type]
                                  for build in builds
                              })
            erratum.commit()

        build_nvrs = sorted(build.nvr for build in builds)
        green_print('Attached build(s) successfully:')
        for b in build_nvrs:
            click.echo(' ' + b)

    except GSSError:
        exit_unauthenticated()
    except elliottlib.exceptions.BrewBuildException as ex:
        raise ElliottFatalError('Error attaching builds: {}'.format(
            getattr(ex, 'message', repr(ex))))
Example #3
0
def add_bugs_with_retry(advisory, bug_list, retried):
    """
    adding specified bug_list into advisory, retry 2 times: first time
    parse the exception message to get failed bug id list, remove from original
    list then add bug to advisory again, if still has failures raise exceptions

    :param advisory: advisory id
    :param bug_list: bug id list which suppose to attach to advisory
    :param retried: retry 2 times, first attempt fetch failed bugs sift out then attach again
    :return:
    """
    try:
        advs = Erratum(errata_id=advisory)
    except GSSError:
        exit_unauthenticated()

    if advs is False:
        raise exceptions.ElliottFatalError(
            "Error: Could not locate advisory {advs}".format(advs=advisory))

    green_prefix("Adding {count} bugs to advisory {retry_times} times:".format(
        count=len(bug_list), retry_times=1 if retried is False else 2))
    print(" {advs}".format(advs=advs))
    try:
        advs.addBugs(bug_list)
        advs.commit()
    except ErrataException as e:
        print("ErrataException Message: {}, retry it again".format(e))
        if retried is not True:
            black_list = parse_exception_error_message(e)
            retry_list = [x for x in bug_list if x not in black_list]
            if len(retry_list) > 0:
                add_bugs_with_retry(advisory, retry_list, True)
        else:
            raise exceptions.ElliottFatalError(getattr(e, 'message', repr(e)))
Example #4
0
def new_erratum(et_data, errata_type=None, kind=None, release_date=None, create=False,
                assigned_to=None, manager=None, package_owner=None, impact=None, cve=None):
    """5.2.1.1. POST /api/v1/erratum

    Create a new advisory.
    Takes an unrealized advisory object and related attributes using the following format:

    https://errata.devel.redhat.com/developer-guide/api-http-api.html#api-post-apiv1erratum

    :param et_data: The ET data dump we got from our erratatool.yaml file
    :param errata_type: The type of advisory to create (RHBA, RHSA, or RHEA)
    :param string kind: One of 'rpm' or 'image', effects boilerplate text
    :param string release_date: A date in the form YYYY-MM-DD
    :param bool create: If true, create the erratum in the Errata
        tool, by default just the DATA we would have POSTed is
        returned
    :param string assigned_to: The email address of the group responsible for
        examining and approving the advisory entries
    :param string manager: The email address of the manager responsible for
        managing the contents and status of this advisory
    :param string package_owner: The email address of the person who is handling
        the details and status of this advisory
    :param impact: The security impact. Only applies to RHSA
    :param cve: The CVE to attach to the advisory. Only applies to RHSA

    :return: An Erratum object
    :raises: exceptions.ErrataToolUnauthenticatedException if the user is not authenticated to make the request
    """
    if release_date is None:
        release_date = datetime.datetime.now() + datetime.timedelta(days=21)

    if kind is None:
        kind = 'rpm'

    e = Erratum(
            product = et_data['product'],
            release = et_data['release'],
            errata_type = errata_type,
            synopsis = et_data['synopsis'][kind],
            topic = et_data['topic'],
            description = et_data['description'],
            solution = et_data['solution'],
            qe_email = assigned_to,
            qe_group = et_data['quality_responsibility_name'],
            owner_email = package_owner,
            manager_email = manager,
            date = release_date
        )

    if errata_type == 'RHSA':
        e.security_impact = impact
        e.cve_names = cve

    if create:
        # THIS IS NOT A DRILL
        e.commit()
        return e
    else:
        return e
Example #5
0
def new_erratum(et_data,
                kind=None,
                release_date=None,
                create=False,
                assigned_to=None,
                manager=None,
                package_owner=None):
    """5.2.1.1. POST /api/v1/erratum

    Create a new advisory.
    Takes an unrealized advisory object and related attributes using the following format:

    https://errata.devel.redhat.com/developer-guide/api-http-api.html#api-post-apiv1erratum

    :param string kind: One of 'rpm' or 'image', effects boilerplate text
    :param string release_date: A date in the form YYYY-m-DD
    :param bool create: If true, create the erratum in the Errata
        tool, by default just the DATA we would have POSTed is
        returned
    :param str/int minor: The minor release to substitute into the
        errata boilerplate text (see:
        :mod:`constants`). E.g., if this is a '3.9'
        release, you would provide '9' as the value for 'minor'
    :param string assigned_to: The email address of the group responsible for
        examining and approving the advisory entries
    :param string manager: The email address of the manager responsible for
        managing the contents and status of this advisory
    :param string package_owner: The email address of the person who is handling
        the details and status of this advisory

    :return: An Erratum object
    :raises: exceptions.ErrataToolUnauthenticatedException if the user is not authenticated to make the request
    """
    if release_date is None:
        release_date = datetime.datetime.now() + datetime.timedelta(days=21)

    if kind is None:
        kind = 'rpm'

    e = Erratum(product=et_data['product'],
                release=et_data['release'],
                errata_type=et_data.get('errata_type', 'RHBA'),
                synopsis=et_data['synopsis'][kind],
                topic=et_data['topic'],
                description=et_data['description'],
                solution=et_data['solution'],
                qe_email=assigned_to,
                qe_group=et_data['quality_responsibility_name'],
                owner_email=package_owner,
                manager_email=manager,
                date=release_date)

    if create:
        # THIS IS NOT A DRILL
        e.commit()
        return e
    else:
        return e
Example #6
0
def add_bugzilla_bugs_with_retry(
        advisory_id: int,
        bugids: List,
        noop: bool = False,
        batch_size: int = constants.BUG_ATTACH_CHUNK_SIZE):
    """
    adding specified bugs into advisory, retry 2 times: first time
    parse the exception message to get failed bug id list, remove from original
    list then add bug to advisory again, if still has failures raise exceptions

    :param advisory_id: advisory id
    :param bugids: iterable of bugzilla bug ids to attach to advisory
    :param noop: do not modify anything
    :param batch_size: perform operation in batches of given size
    :return:
    """
    logger.info(
        f'Request to attach {len(bugids)} bugs to the advisory {advisory_id}')
    try:
        advisory = Erratum(errata_id=advisory_id)
    except GSSError:
        exit_unauthenticated()

    if not advisory:
        raise exceptions.ElliottFatalError(
            f"Error: Could not locate advisory {advisory_id}")

    existing_bugs = bzutil.BugzillaBugTracker.advisory_bug_ids(advisory)
    new_bugs = set(bugids) - set(existing_bugs)
    logger.info(
        f'Bugs already attached: {len(existing_bugs)}. New bugs: {len(new_bugs)}'
    )
    if not new_bugs:
        return

    for chunk_of_bugs in chunk(list(new_bugs), batch_size):
        if noop:
            logger.info('Dry run: Would have attached bugs')
            continue
        try:
            advisory.addBugs(chunk_of_bugs)
            advisory.commit()
        except ErrataException as e:
            logger.info(f"ErrataException Message: {e}\nRetrying...")
            block_list = parse_exception_error_message(e)
            retry_list = [x for x in chunk_of_bugs if x not in block_list]
            if len(retry_list) == 0:
                continue
            try:
                advisory = Erratum(errata_id=advisory_id)
                advisory.addBugs(retry_list)
                advisory.commit()
            except ErrataException as e:
                raise exceptions.ElliottFatalError(
                    getattr(e, 'message', repr(e)))
            logger.info("remaining bugs attached")
        logger.info("All bugzilla bugs attached")
Example #7
0
def add_bugs_with_retry(advisory, bugs, retried=False, noop=False):
    """
    adding specified bugs into advisory, retry 2 times: first time
    parse the exception message to get failed bug id list, remove from original
    list then add bug to advisory again, if still has failures raise exceptions

    :param advisory: advisory id
    :param bugs: iterable of bzutil.bug to attach to advisory
    :param retried: retry 2 times, first attempt fetch failed bugs sift out then attach again
    :return:
    """
    print(f'Request to attach {len(bugs)} bugs to the advisory {advisory}')

    try:
        advs = Erratum(errata_id=advisory)
    except GSSError:
        exit_unauthenticated()

    if advs is False:
        raise exceptions.ElliottFatalError(
            "Error: Could not locate advisory {advs}".format(advs=advisory))

    existing_bugs = advs.errata_bugs
    new_bugs = set(bug.id for bug in bugs) - set(existing_bugs)
    print(f'Bugs already attached: {len(existing_bugs)}')
    print(f'New bugs ({len(new_bugs)}) : {sorted(new_bugs)}')

    if noop:
        print('Dry run. Exiting.')
        return

    if not new_bugs:
        print('No new bugs to attach. Exiting.')
        return

    green_prefix("Adding {count} bugs to advisory {retry_times} times:".format(
        count=len(bugs), retry_times=1 if retried is False else 2))

    try:
        advs.addBugs([bug.id for bug in bugs])
        advs.commit()
    except ErrataException as e:
        print("ErrataException Message: {}, retry it again".format(e))
        if retried is not True:
            block_list = parse_exception_error_message(e)
            retry_list = [x for x in bugs if x.id not in block_list]
            if len(retry_list) > 0:
                add_bugs_with_retry(advisory,
                                    retry_list,
                                    retried=True,
                                    noop=noop)
        else:
            raise exceptions.ElliottFatalError(getattr(e, 'message', repr(e)))
Example #8
0
def remove_bugs(runtime, advisory, default_advisory_type, id):
    """Remove given BUGS from ADVISORY.

    Remove bugs that have been attached an advisory:

\b
    $ elliott --group openshift-3.7 remove-bugs --id 123456 --advisory 1234123

    Remove two bugs from default rpm advisory. Note that --group is required
    because default advisory is from ocp-build-data:

\b
    $ elliott --group openshift-3.7 remove-bugs --id 123456 --id 3412311 --use-default-advisory rpm


"""
    if bool(advisory) == bool(default_advisory_type):
        raise click.BadParameter(
            "Specify exactly one of --use-default-advisory or advisory arg")

    runtime.initialize()
    bz_data = runtime.gitdata.load_data(key='bugzilla').data
    bzapi = elliottlib.bzutil.get_bzapi(bz_data)

    bug_ids = [bzapi.getbug(i) for i in cli_opts.id_convert(id)]

    green_prefix("Found {} bugs:".format(len(bug_ids)))
    click.echo(" {}".format(", ".join([str(b.bug_id) for b in bug_ids])))

    if default_advisory_type is not None:
        advisory = find_default_advisory(runtime, default_advisory_type)

    if advisory is not False:
        try:
            advs = Erratum(errata_id=advisory)
        except GSSError:
            exit_unauthenticated()

        if advs is False:
            raise ElliottFatalError(
                "Error: Could not locate advisory {advs}".format(
                    advs=advisory))

        try:
            green_prefix("Removing {count} bugs from advisory:".format(
                count=len(bug_ids)))
            click.echo(" {advs}".format(advs=advisory))
            advs.removeBugs([bug.id for bug in bug_ids])
            advs.commit()
        except ErrataException as ex:
            raise ElliottFatalError(getattr(ex, 'message', repr(ex)))
Example #9
0
def create_placeholder_cli(runtime, kind, advisory, default_advisory_type):
    """Create a placeholder bug for attaching to an advisory.

    KIND - The kind of placeholder to create ({}).
    ADVISORY - Optional. The advisory to attach the bug to.

    $ elliott --group openshift-4.1 create-placeholder --kind rpm --attach 12345
""".format('/'.join(elliottlib.constants.standard_advisory_types))

    runtime.initialize()
    if advisory and default_advisory_type:
        raise click.BadParameter(
            "Use only one of --use-default-advisory or --advisory")

    if default_advisory_type is not None:
        advisory = find_default_advisory(runtime, default_advisory_type)
        kind = default_advisory_type

    if kind is None:
        raise click.BadParameter(
            "--kind must be specified when not using --use-default-advisory")

    bz_data = runtime.gitdata.load_data(key='bugzilla').data
    target_release = bz_data['target_release'][0]
    newbug = elliottlib.bzutil.create_placeholder(bz_data, kind,
                                                  target_release)

    click.echo("Created BZ: {} {}".format(newbug.id, newbug.weburl))

    if advisory is not False:
        click.echo("Attaching to advisory...")

        try:
            advs = Erratum(errata_id=advisory)
        except GSSError:
            exit_unauthenticated()

        if advs is False:
            raise ElliottFatalError(
                "Error: Could not locate advisory {advs}".format(
                    advs=advisory))

        try:
            green_prefix("Adding placeholder bug to advisory:")
            click.echo(" {advs}".format(advs=advisory))
            advs.addBugs([newbug.id])
            advs.commit()
        except ErrataException as ex:
            raise ElliottFatalError(getattr(ex, 'message', repr(ex)))
Example #10
0
def create_textonly(runtime, errata_type, date, assigned_to, manager,
                    package_owner, topic, synopsis, description, solution,
                    bug_title, bug_description, yes, bug_tracker: BugTracker):
    et_data = runtime.gitdata.load_data(key='erratatool').data
    try:
        erratum = Erratum(
            product=et_data['product'],
            release=et_data['release'],
            qe_group=et_data['quality_responsibility_name'],
            synopsis=synopsis,
            topic=topic,
            description=description,
            solution=solution,
            qe_email=assigned_to,
            errata_type=errata_type,
            owner_email=package_owner,
            manager_email=manager,
            date=date,
            text_only=1,
        )
    except elliottlib.exceptions.ErrataToolUnauthorizedException:
        exit_unauthorized()
    except elliottlib.exceptions.ErrataToolError as ex:
        raise repr(ex)

    cdn_repos = et_data.get('cdn_repos')
    if cdn_repos:
        click.echo(f"Configuring CDN repos {', '.join(cdn_repos)}...")
        erratum.textOnlyRepos(enable=cdn_repos)
    if yes:
        erratum.commit()
        green_prefix("Created new text only advisory: ")
        click.echo(str(erratum))
        bug = bug_tracker.create_textonly(bug_title, bug_description)
        click.echo(f"Created placeholder bug: {bug.id} {bug.webur}")
        click.echo("Attaching placeholder bug...")
        bug_tracker.attach_bugs(erratum.errata_id, [bug.id])
    else:
        green_prefix("Would have created advisory: ")
        click.echo("")
        click.echo(erratum)
Example #11
0
def advisory_commons_cli(runtime, advisories, field, new, version_replace,
                         yes):
    """Display or Change a common field (like date) across multiple advisories.

Advisories created for an OCP version have common fields, that sometimes
will need updating. This command helps with that.

    NOTE: The two advisory input options (--assembly and --advisories)
    are mutually exclusive and can not be used together.

    Show the field "publish_date" for all advisories for an assembly/group/advisories

    $ elliott -g openshift-4.8 --assembly 4.8.8 advisory-commons --field "publish_date"

    $ elliott -g openshift-3.11 advisory-commons --field "publish_date"

    $ elliott advisory-commons 80825 80824 --field "publish_date"

    (Preview) update field "publish_date" for all advisories for an assembly

    $ elliott -g openshift-4.8 --assembly 4.8.8 advisory-commons --field "publish_date" --new "2021-Aug-31"

    (Commit) update field "publish_date" for all advisories for an assembly

    $ elliott -g openshift-4.8 --assembly 4.8.8 advisory-commons --field "publish_date" --new "2021-Aug-31" --yes
"""
    noop = not yes
    count_flags = sum(map(bool, [runtime.group, advisories]))
    if count_flags > 1:
        raise click.BadParameter("Use only one of --group or advisories param")

    count_flags = sum(map(bool, [field, version_replace]))
    if count_flags > 1:
        raise click.BadParameter(
            "Use only one of --field or --version-replace")

    if new:
        if field not in supported_update_fields:
            raise click.BadParameter(
                f"Only these fields are supported for update: {supported_update_fields}"
            )
    if not advisories:
        runtime.initialize()
        advisories = runtime.group_config.advisories.values()

    errors = []
    for advisory_id in advisories:
        update = False
        try:
            advisory = Erratum(errata_id=advisory_id)
            green_prefix(f"{advisory_id}: ")

            if field:
                current = getattr(advisory, field)
                click.echo(f"{field} = {current}")
            if new:
                if new == current:
                    click.echo(
                        f"No change. New value is same as current value: {field} = {current}"
                    )
                else:
                    click.echo(
                        f"Preparing update to {field}: {current} ➔ {new}")
                    advisory = _update_advisory(field, new, advisory)
                    update = True
            elif version_replace:
                rex = r"^(\d.\d+.\d+):(\d.\d+.\d+)$"
                match = re.search(rex, version_replace)
                if not match:
                    click.BadParameter(
                        f"--version-replace needs to be of format {rex}. example '4.5.6:4.7.8'"
                    )
                search_version, replace_version = match.groups()

                # special case for description
                f = "description"
                click.echo(f"<{f}>")
                current = getattr(advisory, f)
                lines_to_match = [
                    "This advisory contains the RPM packages for Red Hat OpenShift Container Platform {version}",
                    "quay.io/openshift-release-dev/ocp-release:{version}"
                ]
                for line in lines_to_match:
                    search_line = line.format(version=search_version)
                    if search_line in current:
                        replace_line = line.format(version=replace_version)
                        new_value = current.replace(search_line, replace_line)
                        click.echo(
                            f"Preparing line update: {search_line} ➔ {replace_line}"
                        )
                        update = True
                if update:
                    _update_advisory(f, new_value, advisory)
                else:
                    click.echo("No change. New value is same as current value")

                # rest of the fields
                fields = ["synopsis", "topic"]
                for f in fields:
                    click.echo(f"<{f}>")
                    current = getattr(advisory, f)
                    new_value = current.replace(search_version,
                                                replace_version)
                    if current == new_value:
                        click.echo(
                            "No change. New value is same as current value")
                    else:
                        click.echo(
                            f"Preparing update: {current} ➔ {new_value}")
                        _update_advisory(f, new_value, advisory)
                        update = True

            if not noop and update:
                advisory.commit()
                click.echo("Committed change")
        except ErrataException as ex:
            click.echo(f'Error fetching/changing {advisory_id}: {ex}')
            errors.append(ex)
    if errors:
        raise Exception(errors)
Example #12
0
def attach_cve_flaws_cli(runtime, advisory_id, noop, default_advisory_type):
    """Attach corresponding flaw bugs for trackers in advisory (first-fix only).

    Also converts advisory to RHSA, if not already.

    Example:

    $ elliott --group openshift-4.6 attach-cve-flaws --use-default-advisory image
    INFO Cloning config data from https://github.com/openshift/ocp-build-data.git
    INFO Using branch from group.yml: rhaos-4.6-rhel-8
    INFO found 114 tracker bugs attached to the advisory
    INFO found 58 corresponding flaw bugs
    INFO 23 out of 58 flaw bugs considered "first-fix"
    INFO Adding the following BZs to the advisory: [1880456, 1858827, 1880460,
    1847310, 1857682, 1857550, 1857551, 1857559, 1848089, 1848092, 1849503,
    1851422, 1866148, 1858981, 1852331, 1861044, 1857081, 1857977, 1848647,
    1849044, 1856529, 1843575, 1840253]
    """
    runtime.initialize()
    bzurl = runtime.gitdata.bz_server_url()
    bzapi = bugzilla.Bugzilla(bzurl)

    if not advisory_id and default_advisory_type is not None:
        advisory_id = find_default_advisory(runtime, default_advisory_type)

    # get attached bugs from advisory
    attached_tracker_bugs = get_attached_tracker_bugs(bzapi, advisory_id)
    runtime.logger.info(
        'found {} tracker bugs attached to the advisory: {}'.format(
            len(attached_tracker_bugs),
            sorted(bug.id for bug in attached_tracker_bugs)))
    if len(attached_tracker_bugs) == 0:
        exit(0)

    # validate and get target_release
    current_target_release, err = util.get_target_release(
        attached_tracker_bugs)
    if err:
        runtime.logger.error(err)
        exit(1)
    runtime.logger.info(
        'current_target_release: {}'.format(current_target_release))

    corresponding_flaw_bugs = get_corresponding_flaw_bugs(
        bzapi, attached_tracker_bugs)
    runtime.logger.info('found {} corresponding flaw bugs: {}'.format(
        len(corresponding_flaw_bugs),
        sorted(bug.id for bug in corresponding_flaw_bugs)))

    # current_target_release is digit.digit.[z|0]
    # if current_target_release is GA then run first-fix bug filtering
    # for GA not every flaw bug is considered first-fix
    # for z-stream every flaw bug is considered first-fix
    if current_target_release[-1] == 'z':
        runtime.logger.info(
            "detected z-stream target release, every flaw bug is considered first-fix"
        )
        first_fix_flaw_bugs = corresponding_flaw_bugs
    else:
        runtime.logger.info(
            "detected GA release, applying first-fix filtering..")
        attached_tracker_ids = [
            tracker.id for tracker in attached_tracker_bugs
        ]
        first_fix_flaw_bugs = [
            flaw_bug for flaw_bug in corresponding_flaw_bugs if is_first_fix(
                bzapi, flaw_bug, current_target_release, attached_tracker_ids)
        ]

    runtime.logger.info('{} out of {} flaw bugs considered "first-fix"'.format(
        len(first_fix_flaw_bugs),
        len(corresponding_flaw_bugs),
    ))

    if not first_fix_flaw_bugs:
        runtime.logger.info('No "first-fix" bugs found, exiting')
        exit(0)

    advisory = Erratum(errata_id=advisory_id)
    if not is_security_advisory(advisory):
        runtime.logger.info(
            'Advisory type is {}, converting it to RHSA'.format(
                advisory.errata_type))
        cve_boilerplate = runtime.gitdata.load_data(
            key='erratatool').data['boilerplates']['cve']
        advisory.update(
            errata_type='RHSA',
            security_reviewer=cve_boilerplate['security_reviewer'],
            synopsis=cve_boilerplate['synopsis'],
            description=cve_boilerplate['description'],
            topic=cve_boilerplate['topic'],
            solution=cve_boilerplate['solution'],
            security_impact='Low',
        )

    cves = ' '.join([flaw_bug.alias[0] for flaw_bug in first_fix_flaw_bugs])

    cve_str = cves
    if advisory.cve_names and cves not in advisory.cve_names:
        cve_str = "{} {}".format(advisory.cve_names, cves).strip()
    advisory.update(cve_names=cve_str)
    runtime.logger.info('List of *new* CVEs: {}'.format(cves))

    highest_impact = get_highest_security_impact(first_fix_flaw_bugs)
    if is_advisory_impact_smaller_than(advisory, highest_impact):
        runtime.logger.info(
            'Adjusting advisory security impact from {} to {}'.format(
                advisory.security_impact, highest_impact))
        advisory.update(security_impact=highest_impact)

    flaw_ids = [flaw_bug.id for flaw_bug in first_fix_flaw_bugs]

    runtime.logger.info(
        f'Request to attach {len(flaw_ids)} bugs to the advisory')
    existing_bug_ids = advisory.errata_bugs
    new_bugs = set(flaw_ids) - set(existing_bug_ids)
    print(f'Bugs already attached: {len(existing_bug_ids)}')
    print(f'New bugs ({len(new_bugs)}) : {sorted(new_bugs)}')

    if new_bugs:
        advisory.addBugs(flaw_ids)

    if noop:
        print(
            'DRY-RUN: The following changes would have been applied to the advisory:'
        )
        print(advisory)
        return True

    return advisory.commit()
Example #13
0
elif args.errata_type == "RHSA":
    pass

# Ensure bugs are in proper state (MODIFIED or VERIFIED) and that they have
# the appropriate rhel-fast-datapath flag set.
s = utilities.open_session()
bz_bugs = utilities.get_bz_bugs(s, args.bugs)
for bug in bz_bugs:
    if bug['status'] != 'MODIFIED' and bug['status'] != 'VERIFIED':
        utilities.update_bz(s, bug['id'], json={'status': 'MODIFIED'})
    utilities.set_bz_flag(s, bug['id'], f'fast-datapath-rhel-{release[-1]}',
                          '+')

e = Erratum(product='Fast-Datapath',
            release=release,
            errata_type=args.errata_type,
            security_impact=args.security_impact,
            synopsis=synopsis,
            topic=topic,
            description=description,
            solution=solution,
            qe_email='*****@*****.**',
            qe_group='OVS QE',
            owner_email=args.owner_email,
            manager_email='*****@*****.**')

e.addBugs(args.bugs)
e.commit()
e.addBuilds([args.build], release=f"RHEL-{release[-1]}-Fast-Datapath")
print(e.url())
Example #14
0
def change_state_cli(runtime, state, advisory, default_advisory_type, noop):
    """Change the state of an ADVISORY. Additional permissions may be
required to change an advisory to certain states.

An advisory may not move between some states until all criteria have
been met. For example, an advisory can not move from NEW_FILES to QE
unless Bugzilla Bugs or JIRA Issues have been attached.

    NOTE: The two advisory options are mutually exclusive and can not
    be used together.

See the find-bugs help for additional information on adding
Bugzilla Bugs.

    Move the advisory 123456 from NEW_FILES to QE state:

    $ elliott change-state --state QE --advisory 123456

    Move the advisory 123456 back to NEW_FILES (short option flag):

    $ elliott change-state -s NEW_FILES -a 123456

    Do not actually change state, just check that the command could
    have ran (for example, when testing out pipelines)

    $ elliott change-state -s NEW_FILES -a 123456 --noop
"""
    if not (bool(advisory) ^ bool(default_advisory_type)):
        raise click.BadParameter(
            "Use only one of --use-default-advisory or --advisory")

    runtime.initialize(no_group=default_advisory_type is None)

    if default_advisory_type is not None:
        advisory = find_default_advisory(runtime, default_advisory_type)

    if noop:
        prefix = "[NOOP] "
    else:
        prefix = ""

    try:
        e = Erratum(errata_id=advisory)

        if e.errata_state == state:
            green_prefix("{}No change to make: ".format(prefix))
            click.echo("Target state is same as current state")
            return
        # we have 5 different states we can only change the state if it's in NEW_FILES or QE
        # "NEW_FILES",
        # "QE",
        # "REL_PREP",
        # "PUSH_READY",
        # "IN_PUSH"
        if e.errata_state != 'NEW_FILES' and e.errata_state != 'QE':
            if default_advisory_type is not None:
                raise ElliottFatalError(
                    "Error: Could not change '{state}' advisory {advs}, group.yml is probably pointing at old one"
                    .format(state=e.errata_state, advs=advisory))
            else:
                raise ElliottFatalError(
                    "Error: we can only change the state if it's in NEW_FILES or QE, current state is {s}"
                    .format(s=e.errata_state))
        else:
            if noop:
                green_prefix("{}Would have changed state: ".format(prefix))
                click.echo("{} ➔ {}".format(e.errata_state, state))
                return
            else:
                # Capture current state because `e.commit()` will
                # refresh the `e.errata_state` attribute
                old_state = e.errata_state
                e.setState(state)
                e.commit()
                green_prefix("Changed state: ")
                click.echo("{old_state} ➔ {new_state}".format(
                    old_state=old_state, new_state=state))
    except ErrataException as ex:
        raise ElliottFatalError(getattr(ex, 'message', repr(ex)))

    green_print("Successfully changed advisory state")
Example #15
0
def add_bugs_with_retry(advisory, bugs, noop=False, batch_size=100):
    """
    adding specified bugs into advisory, retry 2 times: first time
    parse the exception message to get failed bug id list, remove from original
    list then add bug to advisory again, if still has failures raise exceptions

    :param advisory: advisory id
    :param bugs: iterable of bzutil.bug to attach to advisory
    :return:
    """
    print(f'Request to attach {len(bugs)} bugs to the advisory {advisory}')

    try:
        advs = Erratum(errata_id=advisory)
    except GSSError:
        exit_unauthenticated()

    if advs is False:
        raise exceptions.ElliottFatalError(
            "Error: Could not locate advisory {advs}".format(advs=advisory))

    existing_bugs = advs.errata_bugs
    new_bugs = set(bug.id for bug in bugs) - set(existing_bugs)
    print(f'Bugs already attached: {len(existing_bugs)}')
    print(f'New bugs ({len(new_bugs)}) : {sorted(new_bugs)}')

    if not new_bugs:
        print('No new bugs to attach. Exiting.')
        return

    bugs = list(new_bugs)
    batches = list(range(0, len(bugs), batch_size))
    if len(bugs) % batch_size != 0:
        batches.append(len(bugs))

    green_prefix(
        f"Adding bugs in batches of {batch_size}. Number of batches: {len(batches)-1}\n"
    )
    for i in range(len(batches) - 1):
        start, end = batches[i], batches[i + 1]
        print(f"Attaching Batch {i+1}")
        if noop:
            print('Dry run: Would have attached bugs')
            continue
        try:
            advs.addBugs(bugs[start:end])
            advs.commit()
        except ErrataException as e:
            print("ErrataException Message: {}, retry it again".format(e))
            block_list = parse_exception_error_message(e)
            retry_list = [x for x in bugs[start:end] if x not in block_list]
            if len(retry_list) == 0:
                continue

            try:
                advs = Erratum(errata_id=advisory)
                advs.addBugs(retry_list)
                advs.commit()
            except ErrataException as e:
                raise exceptions.ElliottFatalError(
                    getattr(e, 'message', repr(e)))
            print("remaining bugs attached")
Example #16
0
def change_state_cli(runtime, state, advisory, default_advisories,
                     default_advisory_type, noop):
    """Change the state of an ADVISORY. Additional permissions may be
required to change an advisory to certain states.

An advisory may not move between some states until all criteria have
been met. For example, an advisory can not move from NEW_FILES to QE
unless Bugzilla Bugs or JIRA Issues have been attached.

    NOTE: The two advisory options are mutually exclusive and can not
    be used together.

See the find-bugs help for additional information on adding
Bugs.

    Move assembly release advisories to QE

    $ elliott -g openshift-4.10 --assembly 4.10.4 change-state -s QE

    Move group release advisories to QE:

    $ elliott -g openshift-4.5 change-state -s QE --default-advisories

    Move the advisory 123456 to QE:

    $ elliott change-state --state QE --advisory 123456

    Move the advisory 123456 back to NEW_FILES (short option flag):

    $ elliott change-state -s NEW_FILES -a 123456

    Do not actually change state, just check that the command could
    have ran (for example, when testing out pipelines)

    $ elliott change-state -s NEW_FILES -a 123456 --noop
"""
    count_flags = sum(
        map(bool, [advisory, default_advisory_type, default_advisories]))
    if count_flags > 1:
        raise click.BadParameter(
            "Use only one of --use-default-advisory or --advisory or --default-advisories"
        )

    runtime.initialize(no_group=bool(advisory))

    advisories = []
    if default_advisory_type is not None:
        advisory = find_default_advisory(runtime, default_advisory_type)

    if advisory:
        advisories.append(advisory)

    if not advisories:
        advisories = list(runtime.group_config.advisories.values())

    click.echo(f"Attempting to move advisories {advisories} to {state}")
    errors = []
    for advisory in advisories:
        try:
            e = Erratum(errata_id=advisory)

            if e.errata_state == state:
                green_prefix(f"No Change ({advisory}): ")
                click.echo(f"Target state is same as current state: {state}")
            # we have 5 different states we can only change the state if it's in NEW_FILES or QE
            # "NEW_FILES",
            # "QE",
            # "REL_PREP",
            # "PUSH_READY",
            # "IN_PUSH"
            elif e.errata_state != 'NEW_FILES' and e.errata_state != 'QE':
                red_prefix(f"Error ({advisory}): ")
                if default_advisory_type is not None:
                    click.echo(
                        f"Could not change '{e.errata_state}', group.yml is probably pointing at old one"
                    )
                else:
                    click.echo(
                        f"Can only change the state if it's in NEW_FILES or QE, current state is {e.errata_state}"
                    )
            else:
                if noop:
                    green_prefix(f"NOOP ({advisory}): ")
                    click.echo(
                        f"Would have changed state {e.errata_state} ➔ {state}")
                else:
                    # Capture current state because `e.commit()` will
                    # refresh the `e.errata_state` attribute
                    old_state = e.errata_state
                    e.setState(state)
                    e.commit()
                    green_prefix(f"Changed state ({advisory}): ")
                    click.echo(f"{old_state} ➔ {state}")
        except ErrataException as ex:
            click.echo(f'Error fetching/changing state of {advisory}: {ex}')
            errors.append(ex)

    if errors:
        raise Exception(errors)
Example #17
0
def new_erratum(et_data,
                errata_type=None,
                boilerplate_name=None,
                kind=None,
                release_date=None,
                create=False,
                assigned_to=None,
                manager=None,
                package_owner=None,
                impact=None,
                cves=None):
    """5.2.1.1. POST /api/v1/erratum

    Create a new advisory.
    Takes an unrealized advisory object and related attributes using the following format:

    https://errata.devel.redhat.com/developer-guide/api-http-api.html#api-post-apiv1erratum

    :param et_data: The ET data dump we got from our erratatool.yaml file
    :param errata_type: The type of advisory to create (RHBA, RHSA, or RHEA)
    :param string kind: One of [rpm, image].
        Only used for backward compatibility.
    :param string boilerplate_name: One of [rpm, image, extras, metadata, cve].
        The name of boilerplate for creating this advisory
    :param string release_date: A date in the form YYYY-Mon-DD
    :param bool create: If true, create the erratum in the Errata
        tool, by default just the DATA we would have POSTed is
        returned
    :param string assigned_to: The email address of the group responsible for
        examining and approving the advisory entries
    :param string manager: The email address of the manager responsible for
        managing the contents and status of this advisory
    :param string package_owner: The email address of the person who is handling
        the details and status of this advisory
    :param impact: The security impact. Only applies to RHSA
    :param cves: The CVE(s) to attach to the advisory. Separate multiple CVEs with a space. Only applies to RHSA

    :return: An Erratum object
    :raises: exceptions.ErrataToolUnauthenticatedException if the user is not authenticated to make the request
    """
    if not release_date:
        release_date = datetime.datetime.now() + datetime.timedelta(days=21)

    if not kind:
        kind = 'rpm'

    if not boilerplate_name:
        boilerplate_name = kind

    if "boilerplates" in et_data:
        boilerplate = et_data['boilerplates'][boilerplate_name]
    else:  # FIXME: For backward compatibility.
        boilerplate = {
            "synopsis":
            (et_data['synopsis'][boilerplate_name]
             if boilerplate_name != "cve" else et_data['synopsis'][kind]),
            "topic":
            et_data["topic"],
            "description":
            et_data["description"],
            "solution":
            et_data["solution"],
        }

    e = Erratum(product=et_data['product'],
                release=et_data['release'],
                errata_type=errata_type,
                synopsis=boilerplate['synopsis'],
                topic=boilerplate['topic'],
                description=boilerplate['description'],
                solution=boilerplate['solution'],
                qe_email=assigned_to,
                qe_group=et_data['quality_responsibility_name'],
                owner_email=package_owner,
                manager_email=manager,
                date=release_date)

    if errata_type == 'RHSA':
        e.security_impact = impact
        e.cve_names = cves

    if create:
        # THIS IS NOT A DRILL
        e.commit()
        return e
    else:
        return e
Example #18
0
def create_textonly_cli(ctx, runtime, errata_type, date, assigned_to, manager,
                        package_owner, topic, synopsis, description, solution,
                        bugtitle, bugdescription, yes):
    """
    Create a text only advisory with all required input passed from args, need to manually decide the statement for each release.
    Also will create the notification bug along with the text only advisory, the bug also need some special comment and title.
    These args need to be designated manually for text only advisory:
    - topic
    - synopsis
    - description
    - solution
    - assigned
    These args need to be designated manually for text only bug:
    - bugtitle
    - bugdescription
    """

    runtime.initialize()

    # create textonly bug
    bz_data = runtime.gitdata.load_data(key='bugzilla').data
    newbug = elliottlib.bzutil.create_textonly(bz_data, bugtitle,
                                               bugdescription)
    click.echo("Created BZ: {} {}".format(newbug.id, newbug.weburl))

    # create textonly advisory
    et_data = runtime.gitdata.load_data(key='erratatool').data
    try:
        erratum = Erratum(
            product=et_data['product'],
            release=et_data['release'],
            qe_group=et_data['quality_responsibility_name'],
            synopsis=synopsis,
            topic=topic,
            description=description,
            solution=solution,
            qe_email=assigned_to,
            errata_type=errata_type,
            owner_email=package_owner,
            manager_email=manager,
            date=date,
            text_only=1,
        )
    except elliottlib.exceptions.ErrataToolUnauthorizedException:
        exit_unauthorized()
    except elliottlib.exceptions.ErrataToolError as ex:
        raise repr(ex)

    erratum.addBugs(newbug.id)
    cdn_repos = et_data.get('cdn_repos')
    if cdn_repos:
        click.echo(f"Configuring CDN repos {', '.join(cdn_repos)}...")
        erratum.textOnlyRepos(enable=cdn_repos)
    if yes:
        erratum.commit()
        green_prefix("Created new text only advisory: ")
        click.echo(str(erratum))
    else:
        green_prefix("Would have created advisory: ")
        click.echo("")
        click.echo(erratum)
Example #19
0
def attach_cve_flaws_cli(runtime, advisory_id, noop, default_advisory_type):
    """Attach corresponding flaw bugs for trackers in advisory (first-fix only).

    Also converts advisory to RHSA, if not already.

    Example:

    $ elliott --group openshift-4.6 attach-cve-flaws --use-default-advisory image
    INFO Cloning config data from https://github.com/openshift/ocp-build-data.git
    INFO Using branch from group.yml: rhaos-4.6-rhel-8
    INFO found 114 tracker bugs attached to the advisory
    INFO found 58 corresponding flaw bugs
    INFO 23 out of 58 flaw bugs considered "first-fix"
    INFO Adding the following BZs to the advisory: [1880456, 1858827, 1880460,
    1847310, 1857682, 1857550, 1857551, 1857559, 1848089, 1848092, 1849503,
    1851422, 1866148, 1858981, 1852331, 1861044, 1857081, 1857977, 1848647,
    1849044, 1856529, 1843575, 1840253]
    """
    runtime.initialize()
    bzurl = runtime.gitdata.load_data(key='bugzilla').data['server']
    bzapi = bugzilla.Bugzilla(bzurl)

    if not advisory_id and default_advisory_type is not None:
        advisory_id = find_default_advisory(runtime, default_advisory_type)

    attached_tracker_bugs = get_attached_tracker_bugs(bzapi, advisory_id)
    runtime.logger.info(
        'found {} tracker bugs attached to the advisory'.format(
            len(attached_tracker_bugs)))

    corresponding_flaw_bugs = get_corresponding_flaw_bugs(
        bzapi, attached_tracker_bugs)
    runtime.logger.info('found {} corresponding flaw bugs'.format(
        len(corresponding_flaw_bugs)))

    attached_tracker_ids = [tracker.id for tracker in attached_tracker_bugs]
    current_target_release = runtime.gitdata.load_data(
        key='bugzilla').data['target_release']
    first_fix_flaw_bugs = [
        flaw_bug for flaw_bug in corresponding_flaw_bugs if is_first_fix(
            bzapi, flaw_bug, current_target_release, attached_tracker_ids)
    ]
    runtime.logger.info('{} out of {} flaw bugs considered "first-fix"'.format(
        len(first_fix_flaw_bugs),
        len(corresponding_flaw_bugs),
    ))

    if not first_fix_flaw_bugs:
        runtime.logger.info('No "first-fix" bugs found, exiting')
        exit(0)

    advisory = Erratum(errata_id=advisory_id)
    if not is_security_advisory(advisory):
        runtime.logger.info(
            'Advisory type is {}, converting it to RHSA'.format(
                advisory.errata_type))
        cve_boilerplate = runtime.gitdata.load_data(
            key='erratatool').data['boilerplates']['cve']
        advisory.update(
            errata_type='RHSA',
            security_reviewer=cve_boilerplate['security_reviewer'],
            synopsis=cve_boilerplate['synopsis'],
            description=cve_boilerplate['description'],
            topic=cve_boilerplate['topic'],
            solution=cve_boilerplate['solution'],
            security_impact='Low',
        )

    cves = ' '.join([flaw_bug.alias[0] for flaw_bug in first_fix_flaw_bugs])
    advisory.update(cve_names="{} {}".format(advisory.cve_names, cves).strip())
    print('List of *new* CVEs: {}'.format(cves))

    highest_impact = get_highest_security_impact(first_fix_flaw_bugs)
    if is_advisory_impact_smaller_than(advisory, highest_impact):
        runtime.logger.info(
            'Adjusting advisory security impact from {} to {}'.format(
                advisory.security_impact, highest_impact))
        advisory.update(security_impact=highest_impact)

    flaw_ids = [flaw_bug.id for flaw_bug in first_fix_flaw_bugs]
    runtime.logger.info(
        'Adding the following BZs to the advisory: {}'.format(flaw_ids))
    advisory.addBugs(flaw_ids)

    if noop:
        print(
            'DRY-RUN: The following changes would have been applied to the advisory:'
        )
        print(advisory)
        return True

    return advisory.commit()