Blame bugzilla/_cli.py

Packit Service 4b33e2
#!/usr/libexec/platform-python
Packit Service 4b33e2
#
Packit Service 4b33e2
# bugzilla - a commandline frontend for the python bugzilla module
Packit Service 4b33e2
#
Packit Service 4b33e2
# Copyright (C) 2007-2017 Red Hat Inc.
Packit Service 4b33e2
# Author: Will Woods <wwoods@redhat.com>
Packit Service 4b33e2
# Author: Cole Robinson <crobinso@redhat.com>
Packit Service 4b33e2
#
Packit Service 4b33e2
# This program is free software; you can redistribute it and/or modify it
Packit Service 4b33e2
# under the terms of the GNU General Public License as published by the
Packit Service 4b33e2
# Free Software Foundation; either version 2 of the License, or (at your
Packit Service 4b33e2
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
Packit Service 4b33e2
# the full text of the license.
Packit Service 4b33e2
Packit Service 4b33e2
from __future__ import print_function
Packit Service 4b33e2
Packit Service 4b33e2
import locale
Packit Service 4b33e2
from logging import getLogger, DEBUG, INFO, WARN, StreamHandler, Formatter
Packit Service 4b33e2
import argparse
Packit Service 4b33e2
import os
Packit Service 4b33e2
import re
Packit Service 4b33e2
import socket
Packit Service 4b33e2
import sys
Packit Service 4b33e2
import tempfile
Packit Service 4b33e2
Packit Service 4b33e2
# pylint: disable=import-error
Packit Service 4b33e2
if sys.version_info[0] >= 3:
Packit Service 4b33e2
    # pylint: disable=no-name-in-module,redefined-builtin
Packit Service 4b33e2
    from xmlrpc.client import Fault, ProtocolError
Packit Service 4b33e2
    from urllib.parse import urlparse
Packit Service 4b33e2
    basestring = (str, bytes)
Packit Service 4b33e2
else:
Packit Service 4b33e2
    from xmlrpclib import Fault, ProtocolError
Packit Service 4b33e2
    from urlparse import urlparse
Packit Service 4b33e2
# pylint: enable=import-error
Packit Service 4b33e2
Packit Service 4b33e2
import requests.exceptions
Packit Service 4b33e2
Packit Service 4b33e2
import bugzilla
Packit Service 4b33e2
Packit Service 4b33e2
DEFAULT_BZ = 'https://bugzilla.redhat.com/xmlrpc.cgi'
Packit Service 4b33e2
Packit Service 4b33e2
format_field_re = re.compile("%{([a-z0-9_]+)(?::([^}]*))?}")
Packit Service 4b33e2
Packit Service 4b33e2
log = getLogger(bugzilla.__name__)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
################
Packit Service 4b33e2
# Util helpers #
Packit Service 4b33e2
################
Packit Service 4b33e2
Packit Service 4b33e2
def _is_unittest():
Packit Service 4b33e2
    return bool(os.getenv("__BUGZILLA_UNITTEST"))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _is_unittest_debug():
Packit Service 4b33e2
    return bool(os.getenv("__BUGZILLA_UNITTEST_DEBUG"))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def to_encoding(ustring):
Packit Service 4b33e2
    string = ''
Packit Service 4b33e2
    if isinstance(ustring, basestring):
Packit Service 4b33e2
        string = ustring
Packit Service 4b33e2
    elif ustring is not None:
Packit Service 4b33e2
        string = str(ustring)
Packit Service 4b33e2
Packit Service 4b33e2
    if sys.version_info[0] >= 3:
Packit Service 4b33e2
        return string
Packit Service 4b33e2
Packit Service 4b33e2
    preferred = locale.getpreferredencoding()
Packit Service 4b33e2
    if _is_unittest():
Packit Service 4b33e2
        preferred = "UTF-8"
Packit Service 4b33e2
    return string.encode(preferred, 'replace')
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def open_without_clobber(name, *args):
Packit Service 4b33e2
    """
Packit Service 4b33e2
    Try to open the given file with the given mode; if that filename exists,
Packit Service 4b33e2
    try "name.1", "name.2", etc. until we find an unused filename.
Packit Service 4b33e2
    """
Packit Service 4b33e2
    fd = None
Packit Service 4b33e2
    count = 1
Packit Service 4b33e2
    orig_name = name
Packit Service 4b33e2
    while fd is None:
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            fd = os.open(name, os.O_CREAT | os.O_EXCL, 0o666)
Packit Service 4b33e2
        except OSError as err:
Packit Service 4b33e2
            if err.errno == os.errno.EEXIST:
Packit Service 4b33e2
                name = "%s.%i" % (orig_name, count)
Packit Service 4b33e2
                count += 1
Packit Service 4b33e2
            else:
Packit Service 4b33e2
                raise IOError(err.errno, err.strerror, err.filename)
Packit Service 4b33e2
    fobj = open(name, *args)
Packit Service 4b33e2
    if fd != fobj.fileno():
Packit Service 4b33e2
        os.close(fd)
Packit Service 4b33e2
    return fobj
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def get_default_url():
Packit Service 4b33e2
    """
Packit Service 4b33e2
    Grab a default URL from bugzillarc [DEFAULT] url=X
Packit Service 4b33e2
    """
Packit Service 4b33e2
    from bugzilla.base import _open_bugzillarc
Packit Service 4b33e2
    cfg = _open_bugzillarc()
Packit Service 4b33e2
    if cfg:
Packit Service 4b33e2
        cfgurl = cfg.defaults().get("url", None)
Packit Service 4b33e2
        if cfgurl is not None:
Packit Service 4b33e2
            log.debug("bugzillarc: found cli url=%s", cfgurl)
Packit Service 4b33e2
            return cfgurl
Packit Service 4b33e2
    return DEFAULT_BZ
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def setup_logging(debug, verbose):
Packit Service 4b33e2
    handler = StreamHandler(sys.stderr)
Packit Service 4b33e2
    handler.setFormatter(Formatter(
Packit Service 4b33e2
        "[%(asctime)s] %(levelname)s (%(module)s:%(lineno)d) %(message)s",
Packit Service 4b33e2
        "%H:%M:%S"))
Packit Service 4b33e2
    log.addHandler(handler)
Packit Service 4b33e2
Packit Service 4b33e2
    if debug:
Packit Service 4b33e2
        log.setLevel(DEBUG)
Packit Service 4b33e2
    elif verbose:
Packit Service 4b33e2
        log.setLevel(INFO)
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        log.setLevel(WARN)
Packit Service 4b33e2
Packit Service 4b33e2
    if _is_unittest_debug():
Packit Service 4b33e2
        log.setLevel(DEBUG)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
##################
Packit Service 4b33e2
# Option parsing #
Packit Service 4b33e2
##################
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_root_parser():
Packit Service 4b33e2
    epilog = 'Try "bugzilla COMMAND --help" for command-specific help.'
Packit Service 4b33e2
    p = argparse.ArgumentParser(epilog=epilog)
Packit Service 4b33e2
Packit Service 4b33e2
    default_url = get_default_url()
Packit Service 4b33e2
Packit Service 4b33e2
    # General bugzilla connection options
Packit Service 4b33e2
    p.add_argument('--bugzilla', default=default_url,
Packit Service 4b33e2
            help="bugzilla XMLRPC URI. default: %s" % default_url)
Packit Service 4b33e2
    p.add_argument("--nosslverify", dest="sslverify",
Packit Service 4b33e2
                 action="store_false", default=True,
Packit Service 4b33e2
                 help="Don't error on invalid bugzilla SSL certificate")
Packit Service 4b33e2
    p.add_argument('--cert',
Packit Service 4b33e2
            help="client side certificate file needed by the webserver")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--login', action="store_true",
Packit Service 4b33e2
        help='Run interactive "login" before performing the '
Packit Service 4b33e2
             'specified command.')
Packit Service 4b33e2
    p.add_argument('--username', help="Log in with this username")
Packit Service 4b33e2
    p.add_argument('--password', help="Log in with this password")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--ensure-logged-in', action="store_true",
Packit Service 4b33e2
        help="Raise an error if we aren't logged in to bugzilla. "
Packit Service 4b33e2
             "Consider using this if you are depending on "
Packit Service 4b33e2
             "cached credentials, to ensure that when they expire the "
Packit Service 4b33e2
             "tool errors, rather than subtly change output.")
Packit Service 4b33e2
    p.add_argument('--no-cache-credentials',
Packit Service 4b33e2
        action='store_false', default=True, dest='cache_credentials',
Packit Service 4b33e2
        help="Don't save any bugzilla cookies or tokens to disk, and "
Packit Service 4b33e2
             "don't use any pre-existing credentials.")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--cookiefile', default=None,
Packit Service 4b33e2
            help="cookie file to use for bugzilla authentication")
Packit Service 4b33e2
    p.add_argument('--tokenfile', default=None,
Packit Service 4b33e2
            help="token file to use for bugzilla authentication")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--verbose', action='store_true',
Packit Service 4b33e2
            help="give more info about what's going on")
Packit Service 4b33e2
    p.add_argument('--debug', action='store_true',
Packit Service 4b33e2
            help="output bunches of debugging info")
Packit Service 4b33e2
    p.add_argument('--version', action='version',
Packit Service 4b33e2
                   version=bugzilla.__version__)
Packit Service 4b33e2
Packit Service 4b33e2
    # Allow user to specify BZClass to initialize. Kinda weird for the
Packit Service 4b33e2
    # CLI, I'd rather people file bugs about this so we can fix our detection.
Packit Service 4b33e2
    # So hide it from the help output but keep it for back compat
Packit Service 4b33e2
    p.add_argument('--bztype', default='auto', help=argparse.SUPPRESS)
Packit Service 4b33e2
Packit Service 4b33e2
    return p
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _parser_add_output_options(p):
Packit Service 4b33e2
    outg = p.add_argument_group("Output format options")
Packit Service 4b33e2
    outg.add_argument('--full', action='store_const', dest='output',
Packit Service 4b33e2
            const='full', default='normal',
Packit Service 4b33e2
            help="output detailed bug info")
Packit Service 4b33e2
    outg.add_argument('-i', '--ids', action='store_const', dest='output',
Packit Service 4b33e2
            const='ids', help="output only bug IDs")
Packit Service 4b33e2
    outg.add_argument('-e', '--extra', action='store_const',
Packit Service 4b33e2
            dest='output', const='extra',
Packit Service 4b33e2
            help="output additional bug information "
Packit Service 4b33e2
                 "(keywords, Whiteboards, etc.)")
Packit Service 4b33e2
    outg.add_argument('--oneline', action='store_const', dest='output',
Packit Service 4b33e2
            const='oneline',
Packit Service 4b33e2
            help="one line summary of the bug (useful for scripts)")
Packit Service 4b33e2
    outg.add_argument('--raw', action='store_const', dest='output',
Packit Service 4b33e2
            const='raw', help="raw output of the bugzilla contents")
Packit Service 4b33e2
    outg.add_argument('--outputformat',
Packit Service 4b33e2
            help="Print output in the form given. "
Packit Service 4b33e2
                 "You can use RPM-style tags that match bug "
Packit Service 4b33e2
                 "fields, e.g.: '%%{id}: %%{summary}'. See the man page "
Packit Service 4b33e2
                 "section 'Output options' for more details.")
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _parser_add_bz_fields(rootp, command):
Packit Service 4b33e2
    cmd_new = (command == "new")
Packit Service 4b33e2
    cmd_query = (command == "query")
Packit Service 4b33e2
    cmd_modify = (command == "modify")
Packit Service 4b33e2
    if cmd_new:
Packit Service 4b33e2
        comment_help = "Set initial bug comment/description"
Packit Service 4b33e2
    elif cmd_query:
Packit Service 4b33e2
        comment_help = "Search all bug comments"
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        comment_help = "Add new bug comment"
Packit Service 4b33e2
Packit Service 4b33e2
    p = rootp.add_argument_group("Standard bugzilla options")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('-p', '--product', help="Product name")
Packit Service 4b33e2
    p.add_argument('-v', '--version', help="Product version")
Packit Service 4b33e2
    p.add_argument('-c', '--component', help="Component name")
Packit Service 4b33e2
    p.add_argument('-t', '--summary', '--short_desc', help="Bug summary")
Packit Service 4b33e2
    p.add_argument('-l', '--comment', '--long_desc', help=comment_help)
Packit Service 4b33e2
    if not cmd_query:
Packit Service 4b33e2
        p.add_argument("--comment-tag", action="append",
Packit Service 4b33e2
                help="Comment tag for the new comment")
Packit Service 4b33e2
    p.add_argument("--sub-component", action="append",
Packit Service 4b33e2
        help="RHBZ sub component field")
Packit Service 4b33e2
    p.add_argument('-o', '--os', help="Operating system")
Packit Service 4b33e2
    p.add_argument('--arch', help="Arch this bug occurs on")
Packit Service 4b33e2
    p.add_argument('-x', '--severity', help="Bug severity")
Packit Service 4b33e2
    p.add_argument('-z', '--priority', help="Bug priority")
Packit Service 4b33e2
    p.add_argument('--alias', help='Bug alias (name)')
Packit Service 4b33e2
    p.add_argument('-s', '--status', '--bug_status',
Packit Service 4b33e2
        help='Bug status (NEW, ASSIGNED, etc.)')
Packit Service 4b33e2
    p.add_argument('-u', '--url', help="URL field")
Packit Service 4b33e2
    p.add_argument('-m', '--target_milestone', help="Target milestone")
Packit Service 4b33e2
    p.add_argument('--target_release', help="RHBZ Target release")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--blocked', action="append",
Packit Service 4b33e2
        help="Bug IDs that this bug blocks")
Packit Service 4b33e2
    p.add_argument('--dependson', action="append",
Packit Service 4b33e2
        help="Bug IDs that this bug depends on")
Packit Service 4b33e2
    p.add_argument('--keywords', action="append",
Packit Service 4b33e2
        help="Bug keywords")
Packit Service 4b33e2
    p.add_argument('--groups', action="append",
Packit Service 4b33e2
        help="Which user groups can view this bug")
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument('--cc', action="append", help="CC list")
Packit Service 4b33e2
    p.add_argument('-a', '--assigned_to', '--assignee', help="Bug assignee")
Packit Service 4b33e2
    p.add_argument('-q', '--qa_contact', help='QA contact')
Packit Service 4b33e2
Packit Service 4b33e2
    if not cmd_new:
Packit Service 4b33e2
        p.add_argument('-f', '--flag', action='append',
Packit Service 4b33e2
            help="Bug flags state. Ex:\n"
Packit Service 4b33e2
                 "  --flag needinfo?\n"
Packit Service 4b33e2
                 "  --flag dev_ack+ \n"
Packit Service 4b33e2
                 "  clear with --flag needinfoX")
Packit Service 4b33e2
        p.add_argument("--tags", action="append",
Packit Service 4b33e2
                help="Tags/Personal Tags field.")
Packit Service 4b33e2
Packit Service 4b33e2
        p.add_argument('-w', "--whiteboard", '--status_whiteboard',
Packit Service 4b33e2
            action="append", help='Whiteboard field')
Packit Service 4b33e2
        p.add_argument("--devel_whiteboard", action="append",
Packit Service 4b33e2
            help='RHBZ devel whiteboard field')
Packit Service 4b33e2
        p.add_argument("--internal_whiteboard", action="append",
Packit Service 4b33e2
            help='RHBZ internal whiteboard field')
Packit Service 4b33e2
        p.add_argument("--qa_whiteboard", action="append",
Packit Service 4b33e2
            help='RHBZ QA whiteboard field')
Packit Service 4b33e2
        p.add_argument('-F', '--fixed_in',
Packit Service 4b33e2
            help="RHBZ 'Fixed in version' field")
Packit Service 4b33e2
Packit Service 4b33e2
    # Put this at the end, so it sticks out more
Packit Service 4b33e2
    p.add_argument('--field',
Packit Service 4b33e2
        metavar="FIELD=VALUE", action="append", dest="fields",
Packit Service 4b33e2
        help="Manually specify a bugzilla XMLRPC field. FIELD is "
Packit Service 4b33e2
        "the raw name used by the bugzilla instance. For example if your "
Packit Service 4b33e2
        "bugzilla instance has a custom field cf_my_field, do:\n"
Packit Service 4b33e2
        "  --field cf_my_field=VALUE")
Packit Service 4b33e2
Packit Service 4b33e2
    # Used by unit tests, not for end user consumption
Packit Service 4b33e2
    p.add_argument('--__test-return-result', action="store_true",
Packit Service 4b33e2
        dest="test_return_result", help=argparse.SUPPRESS)
Packit Service 4b33e2
Packit Service 4b33e2
    if not cmd_modify:
Packit Service 4b33e2
        _parser_add_output_options(rootp)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_new_parser(subparsers):
Packit Service 4b33e2
    description = ("Create a new bug report. "
Packit Service 4b33e2
        "--product, --component, --version, --summary, and --comment "
Packit Service 4b33e2
        "must be specified. "
Packit Service 4b33e2
        "Options that take multiple values accept comma separated lists, "
Packit Service 4b33e2
        "including --cc, --blocks, --dependson, --groups, and --keywords.")
Packit Service 4b33e2
    p = subparsers.add_parser("new", description=description)
Packit Service 4b33e2
Packit Service 4b33e2
    _parser_add_bz_fields(p, "new")
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_query_parser(subparsers):
Packit Service 4b33e2
    description = ("List bug reports that match the given criteria. "
Packit Service 4b33e2
        "Certain options can accept a comma separated list to query multiple "
Packit Service 4b33e2
        "values, including --status, --component, --product, --version, --id.")
Packit Service 4b33e2
    epilog = ("Note: querying via explicit command line options will only "
Packit Service 4b33e2
        "get you so far. See the --from-url option for a way to use powerful "
Packit Service 4b33e2
        "Web UI queries from the command line.")
Packit Service 4b33e2
    p = subparsers.add_parser("query",
Packit Service 4b33e2
        description=description, epilog=epilog)
Packit Service 4b33e2
Packit Service 4b33e2
    _parser_add_bz_fields(p, "query")
Packit Service 4b33e2
Packit Service 4b33e2
    g = p.add_argument_group("'query' specific options")
Packit Service 4b33e2
    g.add_argument('-b', '--id', '--bug_id',
Packit Service 4b33e2
        help="specify individual bugs by IDs, separated with commas")
Packit Service 4b33e2
    g.add_argument('-r', '--reporter',
Packit Service 4b33e2
        help="Email: search reporter email for given address")
Packit Service 4b33e2
    g.add_argument('--quicksearch',
Packit Service 4b33e2
        help="Search using bugzilla's quicksearch functionality.")
Packit Service 4b33e2
    g.add_argument('--savedsearch',
Packit Service 4b33e2
        help="Name of a bugzilla saved search. If you don't own this "
Packit Service 4b33e2
            "saved search, you must passed --savedsearch_sharer_id.")
Packit Service 4b33e2
    g.add_argument('--savedsearch-sharer-id',
Packit Service 4b33e2
        help="Owner ID of the --savedsearch. You can get this ID from "
Packit Service 4b33e2
            "the URL bugzilla generates when running the saved search "
Packit Service 4b33e2
            "from the web UI.")
Packit Service 4b33e2
Packit Service 4b33e2
    # Keep this at the end so it sticks out more
Packit Service 4b33e2
    g.add_argument('--from-url', metavar="WEB_QUERY_URL",
Packit Service 4b33e2
        help="Make a working query via bugzilla's 'Advanced search' web UI, "
Packit Service 4b33e2
             "grab the url from your browser (the string with query.cgi or "
Packit Service 4b33e2
             "buglist.cgi in it), and --from-url will run it via the "
Packit Service 4b33e2
             "bugzilla API. Don't forget to quote the string! "
Packit Service 4b33e2
             "This only works for Bugzilla 5 and Red Hat bugzilla")
Packit Service 4b33e2
Packit Service 4b33e2
    # Deprecated options
Packit Service 4b33e2
    p.add_argument('-E', '--emailtype', help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('--components_file', help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('-U', '--url_type',
Packit Service 4b33e2
            help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('-K', '--keywords_type',
Packit Service 4b33e2
            help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('-W', '--status_whiteboard_type',
Packit Service 4b33e2
            help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('-B', '--booleantype',
Packit Service 4b33e2
            help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('--boolean_query', action="append",
Packit Service 4b33e2
            help=argparse.SUPPRESS)
Packit Service 4b33e2
    p.add_argument('--fixed_in_type', help=argparse.SUPPRESS)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_info_parser(subparsers):
Packit Service 4b33e2
    description = ("List products or component information about the "
Packit Service 4b33e2
        "bugzilla server.")
Packit Service 4b33e2
    p = subparsers.add_parser("info", description=description)
Packit Service 4b33e2
Packit Service 4b33e2
    x = p.add_mutually_exclusive_group(required=True)
Packit Service 4b33e2
    x.add_argument('-p', '--products', action='store_true',
Packit Service 4b33e2
            help='Get a list of products')
Packit Service 4b33e2
    x.add_argument('-c', '--components', metavar="PRODUCT",
Packit Service 4b33e2
            help='List the components in the given product')
Packit Service 4b33e2
    x.add_argument('-o', '--component_owners', metavar="PRODUCT",
Packit Service 4b33e2
            help='List components (and their owners)')
Packit Service 4b33e2
    x.add_argument('-v', '--versions', metavar="PRODUCT",
Packit Service 4b33e2
            help='List the versions for the given product')
Packit Service 4b33e2
    p.add_argument('--active-components', action="store_true",
Packit Service 4b33e2
            help='Only show active components. Combine with --components*')
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_modify_parser(subparsers):
Packit Service 4b33e2
    usage = ("bugzilla modify [options] BUGID [BUGID...]\n"
Packit Service 4b33e2
        "Fields that take multiple values have a special input format.\n"
Packit Service 4b33e2
        "Append:    --cc=foo@example.com\n"
Packit Service 4b33e2
        "Overwrite: --cc==foo@example.com\n"
Packit Service 4b33e2
        "Remove:    --cc=-foo@example.com\n"
Packit Service 4b33e2
        "Options that accept this format: --cc, --blocked, --dependson,\n"
Packit Service 4b33e2
        "    --groups, --tags, whiteboard fields.")
Packit Service 4b33e2
    p = subparsers.add_parser("modify", usage=usage)
Packit Service 4b33e2
Packit Service 4b33e2
    _parser_add_bz_fields(p, "modify")
Packit Service 4b33e2
Packit Service 4b33e2
    g = p.add_argument_group("'modify' specific options")
Packit Service 4b33e2
    g.add_argument("ids", nargs="+", help="Bug IDs to modify")
Packit Service 4b33e2
    g.add_argument('-k', '--close', metavar="RESOLUTION",
Packit Service 4b33e2
        help='Close with the given resolution (WONTFIX, NOTABUG, etc.)')
Packit Service 4b33e2
    g.add_argument('-d', '--dupeid', metavar="ORIGINAL",
Packit Service 4b33e2
        help='ID of original bug. Implies --close DUPLICATE')
Packit Service 4b33e2
    g.add_argument('--private', action='store_true', default=False,
Packit Service 4b33e2
        help='Mark new comment as private')
Packit Service 4b33e2
    g.add_argument('--reset-assignee', action="store_true",
Packit Service 4b33e2
        help='Reset assignee to component default')
Packit Service 4b33e2
    g.add_argument('--reset-qa-contact', action="store_true",
Packit Service 4b33e2
        help='Reset QA contact to component default')
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_attach_parser(subparsers):
Packit Service 4b33e2
    usage = """
Packit Service 4b33e2
bugzilla attach --file=FILE --desc=DESC [--type=TYPE] BUGID [BUGID...]
Packit Service 4b33e2
bugzilla attach --get=ATTACHID --getall=BUGID [...]
Packit Service 4b33e2
bugzilla attach --type=TYPE BUGID [BUGID...]"""
Packit Service 4b33e2
    description = "Attach files or download attachments."
Packit Service 4b33e2
    p = subparsers.add_parser("attach", description=description, usage=usage)
Packit Service 4b33e2
Packit Service 4b33e2
    p.add_argument("ids", nargs="*", help="BUGID references")
Packit Service 4b33e2
    p.add_argument('-f', '--file', metavar="FILENAME",
Packit Service 4b33e2
            help='File to attach, or filename for data provided on stdin')
Packit Service 4b33e2
    p.add_argument('-d', '--description', '--summary',
Packit Service 4b33e2
            metavar="SUMMARY", dest='desc',
Packit Service 4b33e2
            help="A short summary of the file being attached")
Packit Service 4b33e2
    p.add_argument('-t', '--type', metavar="MIMETYPE",
Packit Service 4b33e2
            help="Mime-type for the file being attached")
Packit Service 4b33e2
    p.add_argument('-g', '--get', metavar="ATTACHID", action="append",
Packit Service 4b33e2
            default=[], help="Download the attachment with the given ID")
Packit Service 4b33e2
    p.add_argument("--getall", "--get-all", metavar="BUGID", action="append",
Packit Service 4b33e2
            default=[], help="Download all attachments on the given bug")
Packit Service 4b33e2
    p.add_argument('-l', '--comment', '--long_desc',
Packit Service 4b33e2
            help="Add comment with attachment")
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _setup_action_login_parser(subparsers):
Packit Service 4b33e2
    usage = 'bugzilla login [username [password]]'
Packit Service 4b33e2
    description = "Log into bugzilla and save a login cookie or token."
Packit Service 4b33e2
    p = subparsers.add_parser("login", description=description, usage=usage)
Packit Service 4b33e2
    p.add_argument("pos_username", nargs="?", help="Optional username",
Packit Service 4b33e2
            metavar="username")
Packit Service 4b33e2
    p.add_argument("pos_password", nargs="?", help="Optional password",
Packit Service 4b33e2
            metavar="password")
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def setup_parser():
Packit Service 4b33e2
    rootparser = _setup_root_parser()
Packit Service 4b33e2
    subparsers = rootparser.add_subparsers(dest="command")
Packit Service 4b33e2
    subparsers.required = True
Packit Service 4b33e2
    _setup_action_new_parser(subparsers)
Packit Service 4b33e2
    _setup_action_query_parser(subparsers)
Packit Service 4b33e2
    _setup_action_info_parser(subparsers)
Packit Service 4b33e2
    _setup_action_modify_parser(subparsers)
Packit Service 4b33e2
    _setup_action_attach_parser(subparsers)
Packit Service 4b33e2
    _setup_action_login_parser(subparsers)
Packit Service 4b33e2
    return rootparser
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
####################
Packit Service 4b33e2
# Command routines #
Packit Service 4b33e2
####################
Packit Service 4b33e2
Packit Service 4b33e2
def _merge_field_opts(query, opt, parser):
Packit Service 4b33e2
    # Add any custom fields if specified
Packit Service 4b33e2
    if opt.fields is None:
Packit Service 4b33e2
        return
Packit Service 4b33e2
Packit Service 4b33e2
    for f in opt.fields:
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            f, v = f.split('=', 1)
Packit Service 4b33e2
            query[f] = v
Packit Service 4b33e2
        except Exception:
Packit Service 4b33e2
            parser.error("Invalid field argument provided: %s" % (f))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_query(bz, opt, parser):
Packit Service 4b33e2
    q = {}
Packit Service 4b33e2
Packit Service 4b33e2
    # Parse preconstructed queries.
Packit Service 4b33e2
    u = opt.from_url
Packit Service 4b33e2
    if u:
Packit Service 4b33e2
        q = bz.url_to_query(u)
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.components_file:
Packit Service 4b33e2
        # Components slurped in from file (one component per line)
Packit Service 4b33e2
        # This can be made more robust
Packit Service 4b33e2
        clist = []
Packit Service 4b33e2
        f = open(opt.components_file, 'r')
Packit Service 4b33e2
        for line in f.readlines():
Packit Service 4b33e2
            line = line.rstrip("\n")
Packit Service 4b33e2
            clist.append(line)
Packit Service 4b33e2
        opt.component = clist
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.status:
Packit Service 4b33e2
        val = opt.status
Packit Service 4b33e2
        stat = val
Packit Service 4b33e2
        if val == 'ALL':
Packit Service 4b33e2
            # leaving this out should return bugs of any status
Packit Service 4b33e2
            stat = None
Packit Service 4b33e2
        elif val == 'DEV':
Packit Service 4b33e2
            # Alias for all development bug statuses
Packit Service 4b33e2
            stat = ['NEW', 'ASSIGNED', 'NEEDINFO', 'ON_DEV',
Packit Service 4b33e2
                'MODIFIED', 'POST', 'REOPENED']
Packit Service 4b33e2
        elif val == 'QE':
Packit Service 4b33e2
            # Alias for all QE relevant bug statuses
Packit Service 4b33e2
            stat = ['ASSIGNED', 'ON_QA', 'FAILS_QA', 'PASSES_QA']
Packit Service 4b33e2
        elif val == 'EOL':
Packit Service 4b33e2
            # Alias for EndOfLife bug statuses
Packit Service 4b33e2
            stat = ['VERIFIED', 'RELEASE_PENDING', 'CLOSED']
Packit Service 4b33e2
        elif val == 'OPEN':
Packit Service 4b33e2
            # non-Closed statuses
Packit Service 4b33e2
            stat = ['NEW', 'ASSIGNED', 'MODIFIED', 'ON_DEV', 'ON_QA',
Packit Service 4b33e2
                'VERIFIED', 'RELEASE_PENDING', 'POST']
Packit Service 4b33e2
        opt.status = stat
Packit Service 4b33e2
Packit Service 4b33e2
    # Convert all comma separated list parameters to actual lists,
Packit Service 4b33e2
    # which is what bugzilla wants
Packit Service 4b33e2
    # According to bugzilla docs, any parameter can be a list, but
Packit Service 4b33e2
    # let's only do this for options we explicitly mention can be
Packit Service 4b33e2
    # comma separated.
Packit Service 4b33e2
    for optname in ["severity", "id", "status", "component",
Packit Service 4b33e2
                    "priority", "product", "version"]:
Packit Service 4b33e2
        val = getattr(opt, optname, None)
Packit Service 4b33e2
        if not isinstance(val, str):
Packit Service 4b33e2
            continue
Packit Service 4b33e2
        setattr(opt, optname, val.split(","))
Packit Service 4b33e2
Packit Service 4b33e2
    include_fields = None
Packit Service 4b33e2
    if opt.output == 'raw':
Packit Service 4b33e2
        # 'raw' always does a getbug() call anyways, so just ask for ID back
Packit Service 4b33e2
        include_fields = ['id']
Packit Service 4b33e2
Packit Service 4b33e2
    elif opt.outputformat:
Packit Service 4b33e2
        include_fields = []
Packit Service 4b33e2
        for fieldname, rest in format_field_re.findall(opt.outputformat):
Packit Service 4b33e2
            if fieldname == "whiteboard" and rest:
Packit Service 4b33e2
                fieldname = rest + "_" + fieldname
Packit Service 4b33e2
            elif fieldname == "flag":
Packit Service 4b33e2
                fieldname = "flags"
Packit Service 4b33e2
            elif fieldname == "cve":
Packit Service 4b33e2
                fieldname = ["keywords", "blocks"]
Packit Service 4b33e2
            elif fieldname == "__unicode__":
Packit Service 4b33e2
                # Needs to be in sync with bug.__unicode__
Packit Service 4b33e2
                fieldname = ["id", "status", "assigned_to", "summary"]
Packit Service 4b33e2
Packit Service 4b33e2
            flist = isinstance(fieldname, list) and fieldname or [fieldname]
Packit Service 4b33e2
            for f in flist:
Packit Service 4b33e2
                if f not in include_fields:
Packit Service 4b33e2
                    include_fields.append(f)
Packit Service 4b33e2
Packit Service 4b33e2
    if include_fields is not None:
Packit Service 4b33e2
        include_fields.sort()
Packit Service 4b33e2
Packit Service 4b33e2
    built_query = bz.build_query(
Packit Service 4b33e2
        product=opt.product or None,
Packit Service 4b33e2
        component=opt.component or None,
Packit Service 4b33e2
        sub_component=opt.sub_component or None,
Packit Service 4b33e2
        version=opt.version or None,
Packit Service 4b33e2
        reporter=opt.reporter or None,
Packit Service 4b33e2
        bug_id=opt.id or None,
Packit Service 4b33e2
        short_desc=opt.summary or None,
Packit Service 4b33e2
        long_desc=opt.comment or None,
Packit Service 4b33e2
        cc=opt.cc or None,
Packit Service 4b33e2
        assigned_to=opt.assigned_to or None,
Packit Service 4b33e2
        qa_contact=opt.qa_contact or None,
Packit Service 4b33e2
        status=opt.status or None,
Packit Service 4b33e2
        blocked=opt.blocked or None,
Packit Service 4b33e2
        dependson=opt.dependson or None,
Packit Service 4b33e2
        keywords=opt.keywords or None,
Packit Service 4b33e2
        keywords_type=opt.keywords_type or None,
Packit Service 4b33e2
        url=opt.url or None,
Packit Service 4b33e2
        url_type=opt.url_type or None,
Packit Service 4b33e2
        status_whiteboard=opt.whiteboard or None,
Packit Service 4b33e2
        status_whiteboard_type=opt.status_whiteboard_type or None,
Packit Service 4b33e2
        fixed_in=opt.fixed_in or None,
Packit Service 4b33e2
        fixed_in_type=opt.fixed_in_type or None,
Packit Service 4b33e2
        flag=opt.flag or None,
Packit Service 4b33e2
        alias=opt.alias or None,
Packit Service 4b33e2
        qa_whiteboard=opt.qa_whiteboard or None,
Packit Service 4b33e2
        devel_whiteboard=opt.devel_whiteboard or None,
Packit Service 4b33e2
        boolean_query=opt.boolean_query or None,
Packit Service 4b33e2
        bug_severity=opt.severity or None,
Packit Service 4b33e2
        priority=opt.priority or None,
Packit Service 4b33e2
        target_release=opt.target_release or None,
Packit Service 4b33e2
        target_milestone=opt.target_milestone or None,
Packit Service 4b33e2
        emailtype=opt.emailtype or None,
Packit Service 4b33e2
        booleantype=opt.booleantype or None,
Packit Service 4b33e2
        include_fields=include_fields,
Packit Service 4b33e2
        quicksearch=opt.quicksearch or None,
Packit Service 4b33e2
        savedsearch=opt.savedsearch or None,
Packit Service 4b33e2
        savedsearch_sharer_id=opt.savedsearch_sharer_id or None,
Packit Service 4b33e2
        tags=opt.tags or None)
Packit Service 4b33e2
Packit Service 4b33e2
    _merge_field_opts(built_query, opt, parser)
Packit Service 4b33e2
Packit Service 4b33e2
    built_query.update(q)
Packit Service 4b33e2
    q = built_query
Packit Service 4b33e2
Packit Service 4b33e2
    if not q:
Packit Service 4b33e2
        parser.error("'query' command requires additional arguments")
Packit Service 4b33e2
    if opt.test_return_result:
Packit Service 4b33e2
        return q
Packit Service 4b33e2
    return bz.query(q)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_info(bz, opt):
Packit Service 4b33e2
    """
Packit Service 4b33e2
    Handle the 'info' subcommand
Packit Service 4b33e2
    """
Packit Service 4b33e2
    # All these commands call getproducts internally, so do it up front
Packit Service 4b33e2
    # with minimal include_fields for speed
Packit Service 4b33e2
    def _filter_components(compdetails):
Packit Service 4b33e2
        ret = {}
Packit Service 4b33e2
        for k, v in compdetails.items():
Packit Service 4b33e2
            if v.get("is_active", True):
Packit Service 4b33e2
                ret[k] = v
Packit Service 4b33e2
        return ret
Packit Service 4b33e2
Packit Service 4b33e2
    productname = (opt.components or opt.component_owners or opt.versions)
Packit Service 4b33e2
    include_fields = ["name", "id"]
Packit Service 4b33e2
    fastcomponents = (opt.components and not opt.active_components)
Packit Service 4b33e2
    if opt.versions:
Packit Service 4b33e2
        include_fields += ["versions"]
Packit Service 4b33e2
    if opt.component_owners:
Packit Service 4b33e2
        include_fields += [
Packit Service 4b33e2
            "components.default_assigned_to",
Packit Service 4b33e2
            "components.name",
Packit Service 4b33e2
        ]
Packit Service 4b33e2
    if (opt.active_components and
Packit Service 4b33e2
        any(["components" in i for i in include_fields])):
Packit Service 4b33e2
        include_fields += ["components.is_active"]
Packit Service 4b33e2
Packit Service 4b33e2
    bz.refresh_products(names=productname and [productname] or None,
Packit Service 4b33e2
            include_fields=include_fields)
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.products:
Packit Service 4b33e2
        for name in sorted([p["name"] for p in bz.getproducts()]):
Packit Service 4b33e2
            print(name)
Packit Service 4b33e2
Packit Service 4b33e2
    elif fastcomponents:
Packit Service 4b33e2
        for name in sorted(bz.getcomponents(productname)):
Packit Service 4b33e2
            print(name)
Packit Service 4b33e2
Packit Service 4b33e2
    elif opt.components:
Packit Service 4b33e2
        details = bz.getcomponentsdetails(productname)
Packit Service 4b33e2
        for name in sorted(_filter_components(details)):
Packit Service 4b33e2
            print(name)
Packit Service 4b33e2
Packit Service 4b33e2
    elif opt.versions:
Packit Service 4b33e2
        proddict = bz.getproducts()[0]
Packit Service 4b33e2
        for v in proddict['versions']:
Packit Service 4b33e2
            print(to_encoding(v["name"]))
Packit Service 4b33e2
Packit Service 4b33e2
    elif opt.component_owners:
Packit Service 4b33e2
        details = bz.getcomponentsdetails(productname)
Packit Service 4b33e2
        for c in sorted(_filter_components(details)):
Packit Service 4b33e2
            print(to_encoding(u"%s: %s" % (c,
Packit Service 4b33e2
                details[c]['default_assigned_to'])))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _convert_to_outputformat(output):
Packit Service 4b33e2
    fmt = ""
Packit Service 4b33e2
Packit Service 4b33e2
    if output == "normal":
Packit Service 4b33e2
        fmt = "%{__unicode__}"
Packit Service 4b33e2
Packit Service 4b33e2
    elif output == "ids":
Packit Service 4b33e2
        fmt = "%{id}"
Packit Service 4b33e2
Packit Service 4b33e2
    elif output == 'full':
Packit Service 4b33e2
        fmt += "%{__unicode__}\n"
Packit Service 4b33e2
        fmt += "Component: %{component}\n"
Packit Service 4b33e2
        fmt += "CC: %{cc}\n"
Packit Service 4b33e2
        fmt += "Blocked: %{blocks}\n"
Packit Service 4b33e2
        fmt += "Depends: %{depends_on}\n"
Packit Service 4b33e2
        fmt += "%{comments}\n"
Packit Service 4b33e2
Packit Service 4b33e2
    elif output == 'extra':
Packit Service 4b33e2
        fmt += "%{__unicode__}\n"
Packit Service 4b33e2
        fmt += " +Keywords: %{keywords}\n"
Packit Service 4b33e2
        fmt += " +QA Whiteboard: %{qa_whiteboard}\n"
Packit Service 4b33e2
        fmt += " +Status Whiteboard: %{status_whiteboard}\n"
Packit Service 4b33e2
        fmt += " +Devel Whiteboard: %{devel_whiteboard}\n"
Packit Service 4b33e2
Packit Service 4b33e2
    elif output == 'oneline':
Packit Service 4b33e2
        fmt += "#%{bug_id} %{status} %{assigned_to} %{component}\t"
Packit Service 4b33e2
        fmt += "[%{target_milestone}] %{flags} %{cve}"
Packit Service 4b33e2
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        raise RuntimeError("Unknown output type '%s'" % output)
Packit Service 4b33e2
Packit Service 4b33e2
    return fmt
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _format_output(bz, opt, buglist):
Packit Service 4b33e2
    if opt.output == 'raw':
Packit Service 4b33e2
        buglist = bz.getbugs([b.bug_id for b in buglist])
Packit Service 4b33e2
        for b in buglist:
Packit Service 4b33e2
            print("Bugzilla %s: " % b.bug_id)
Packit Service 4b33e2
            for attrname in sorted(b.__dict__):
Packit Service 4b33e2
                print(to_encoding(u"ATTRIBUTE[%s]: %s" %
Packit Service 4b33e2
                                  (attrname, b.__dict__[attrname])))
Packit Service 4b33e2
            print("\n\n")
Packit Service 4b33e2
        return
Packit Service 4b33e2
Packit Service 4b33e2
    def bug_field(matchobj):
Packit Service 4b33e2
        # whiteboard and flag allow doing
Packit Service 4b33e2
        #   %{whiteboard:devel} and %{flag:needinfo}
Packit Service 4b33e2
        # That's what 'rest' matches
Packit Service 4b33e2
        (fieldname, rest) = matchobj.groups()
Packit Service 4b33e2
Packit Service 4b33e2
        if fieldname == "whiteboard" and rest:
Packit Service 4b33e2
            fieldname = rest + "_" + fieldname
Packit Service 4b33e2
Packit Service 4b33e2
        if fieldname == "flag" and rest:
Packit Service 4b33e2
            val = b.get_flag_status(rest)
Packit Service 4b33e2
Packit Service 4b33e2
        elif fieldname == "flags" or fieldname == "flags_requestee":
Packit Service 4b33e2
            tmpstr = []
Packit Service 4b33e2
            for f in getattr(b, "flags", []):
Packit Service 4b33e2
                requestee = f.get('requestee', "")
Packit Service 4b33e2
                if fieldname == "flags":
Packit Service 4b33e2
                    requestee = ""
Packit Service 4b33e2
                if fieldname == "flags_requestee":
Packit Service 4b33e2
                    if requestee == "":
Packit Service 4b33e2
                        continue
Packit Service 4b33e2
                    tmpstr.append("%s" % requestee)
Packit Service 4b33e2
                else:
Packit Service 4b33e2
                    tmpstr.append("%s%s%s" %
Packit Service 4b33e2
                            (f['name'], f['status'], requestee))
Packit Service 4b33e2
Packit Service 4b33e2
            val = ",".join(tmpstr)
Packit Service 4b33e2
Packit Service 4b33e2
        elif fieldname == "cve":
Packit Service 4b33e2
            cves = []
Packit Service 4b33e2
            for key in getattr(b, "keywords", []):
Packit Service 4b33e2
                # grab CVE from keywords and blockers
Packit Service 4b33e2
                if key.find("Security") == -1:
Packit Service 4b33e2
                    continue
Packit Service 4b33e2
                for bl in b.blocks:
Packit Service 4b33e2
                    cvebug = bz.getbug(bl)
Packit Service 4b33e2
                    for cb in cvebug.alias:
Packit Service 4b33e2
                        if cb.find("CVE") == -1:
Packit Service 4b33e2
                            continue
Packit Service 4b33e2
                        if cb.strip() not in cves:
Packit Service 4b33e2
                            cves.append(cb)
Packit Service 4b33e2
            val = ",".join(cves)
Packit Service 4b33e2
Packit Service 4b33e2
        elif fieldname == "comments":
Packit Service 4b33e2
            val = ""
Packit Service 4b33e2
            for c in getattr(b, "comments", []):
Packit Service 4b33e2
                val += ("\n* %s - %s:\n%s\n" % (c['time'],
Packit Service 4b33e2
                         c.get("creator", c.get("author", "")), c['text']))
Packit Service 4b33e2
Packit Service 4b33e2
        elif fieldname == "external_bugs":
Packit Service 4b33e2
            val = ""
Packit Service 4b33e2
            for e in getattr(b, "external_bugs", []):
Packit Service 4b33e2
                url = e["type"]["full_url"].replace("%id%", e["ext_bz_bug_id"])
Packit Service 4b33e2
                if not val:
Packit Service 4b33e2
                    val += "\n"
Packit Service 4b33e2
                val += "External bug: %s\n" % url
Packit Service 4b33e2
Packit Service 4b33e2
        elif fieldname == "__unicode__":
Packit Service 4b33e2
            val = b.__unicode__()
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            val = getattr(b, fieldname, "")
Packit Service 4b33e2
Packit Service 4b33e2
        vallist = isinstance(val, list) and val or [val]
Packit Service 4b33e2
        val = ','.join([to_encoding(v) for v in vallist])
Packit Service 4b33e2
Packit Service 4b33e2
        return val
Packit Service 4b33e2
Packit Service 4b33e2
    for b in buglist:
Packit Service 4b33e2
        print(format_field_re.sub(bug_field, opt.outputformat))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _parse_triset(vallist, checkplus=True, checkminus=True, checkequal=True,
Packit Service 4b33e2
                  splitcomma=False):
Packit Service 4b33e2
    add_val = []
Packit Service 4b33e2
    rm_val = []
Packit Service 4b33e2
    set_val = None
Packit Service 4b33e2
Packit Service 4b33e2
    def make_list(v):
Packit Service 4b33e2
        if not v:
Packit Service 4b33e2
            return []
Packit Service 4b33e2
        if splitcomma:
Packit Service 4b33e2
            return v.split(",")
Packit Service 4b33e2
        return [v]
Packit Service 4b33e2
Packit Service 4b33e2
    for val in isinstance(vallist, list) and vallist or [vallist]:
Packit Service 4b33e2
        val = val or ""
Packit Service 4b33e2
Packit Service 4b33e2
        if val.startswith("+") and checkplus:
Packit Service 4b33e2
            add_val += make_list(val[1:])
Packit Service 4b33e2
        elif val.startswith("-") and checkminus:
Packit Service 4b33e2
            rm_val += make_list(val[1:])
Packit Service 4b33e2
        elif val.startswith("=") and checkequal:
Packit Service 4b33e2
            # Intentionally overwrite this
Packit Service 4b33e2
            set_val = make_list(val[1:])
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            add_val += make_list(val)
Packit Service 4b33e2
Packit Service 4b33e2
    return add_val, rm_val, set_val
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_new(bz, opt, parser):
Packit Service 4b33e2
    # Parse options that accept comma separated list
Packit Service 4b33e2
    def parse_multi(val):
Packit Service 4b33e2
        return _parse_triset(val, checkplus=False, checkminus=False,
Packit Service 4b33e2
                             checkequal=False, splitcomma=True)[0]
Packit Service 4b33e2
Packit Service 4b33e2
    ret = bz.build_createbug(
Packit Service 4b33e2
        blocks=parse_multi(opt.blocked) or None,
Packit Service 4b33e2
        cc=parse_multi(opt.cc) or None,
Packit Service 4b33e2
        component=opt.component or None,
Packit Service 4b33e2
        depends_on=parse_multi(opt.dependson) or None,
Packit Service 4b33e2
        description=opt.comment or None,
Packit Service 4b33e2
        groups=parse_multi(opt.groups) or None,
Packit Service 4b33e2
        keywords=parse_multi(opt.keywords) or None,
Packit Service 4b33e2
        op_sys=opt.os or None,
Packit Service 4b33e2
        platform=opt.arch or None,
Packit Service 4b33e2
        priority=opt.priority or None,
Packit Service 4b33e2
        product=opt.product or None,
Packit Service 4b33e2
        severity=opt.severity or None,
Packit Service 4b33e2
        summary=opt.summary or None,
Packit Service 4b33e2
        url=opt.url or None,
Packit Service 4b33e2
        version=opt.version or None,
Packit Service 4b33e2
        assigned_to=opt.assigned_to or None,
Packit Service 4b33e2
        qa_contact=opt.qa_contact or None,
Packit Service 4b33e2
        sub_component=opt.sub_component or None,
Packit Service 4b33e2
        alias=opt.alias or None,
Packit Service 4b33e2
        comment_tags=opt.comment_tag or None,
Packit Service 4b33e2
    )
Packit Service 4b33e2
Packit Service 4b33e2
    _merge_field_opts(ret, opt, parser)
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.test_return_result:
Packit Service 4b33e2
        return ret
Packit Service 4b33e2
Packit Service 4b33e2
    b = bz.createbug(ret)
Packit Service 4b33e2
    b.refresh()
Packit Service 4b33e2
    return [b]
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_modify(bz, parser, opt):
Packit Service 4b33e2
    bugid_list = [bugid for a in opt.ids for bugid in a.split(',')]
Packit Service 4b33e2
Packit Service 4b33e2
    add_wb, rm_wb, set_wb = _parse_triset(opt.whiteboard)
Packit Service 4b33e2
    add_devwb, rm_devwb, set_devwb = _parse_triset(opt.devel_whiteboard)
Packit Service 4b33e2
    add_intwb, rm_intwb, set_intwb = _parse_triset(opt.internal_whiteboard)
Packit Service 4b33e2
    add_qawb, rm_qawb, set_qawb = _parse_triset(opt.qa_whiteboard)
Packit Service 4b33e2
Packit Service 4b33e2
    add_blk, rm_blk, set_blk = _parse_triset(opt.blocked, splitcomma=True)
Packit Service 4b33e2
    add_deps, rm_deps, set_deps = _parse_triset(opt.dependson, splitcomma=True)
Packit Service 4b33e2
    add_key, rm_key, set_key = _parse_triset(opt.keywords)
Packit Service 4b33e2
    add_cc, rm_cc, ignore = _parse_triset(opt.cc,
Packit Service 4b33e2
                                          checkplus=False,
Packit Service 4b33e2
                                          checkequal=False)
Packit Service 4b33e2
    add_groups, rm_groups, ignore = _parse_triset(opt.groups,
Packit Service 4b33e2
                                                  checkequal=False,
Packit Service 4b33e2
                                                  splitcomma=True)
Packit Service 4b33e2
    add_tags, rm_tags, ignore = _parse_triset(opt.tags, checkequal=False)
Packit Service 4b33e2
Packit Service 4b33e2
    status = opt.status or None
Packit Service 4b33e2
    if opt.dupeid is not None:
Packit Service 4b33e2
        opt.close = "DUPLICATE"
Packit Service 4b33e2
    if opt.close:
Packit Service 4b33e2
        status = "CLOSED"
Packit Service 4b33e2
Packit Service 4b33e2
    flags = []
Packit Service 4b33e2
    if opt.flag:
Packit Service 4b33e2
        # Convert "foo+" to tuple ("foo", "+")
Packit Service 4b33e2
        for f in opt.flag:
Packit Service 4b33e2
            flags.append({"name": f[:-1], "status": f[-1]})
Packit Service 4b33e2
Packit Service 4b33e2
    update = bz.build_update(
Packit Service 4b33e2
        assigned_to=opt.assigned_to or None,
Packit Service 4b33e2
        comment=opt.comment or None,
Packit Service 4b33e2
        comment_private=opt.private or None,
Packit Service 4b33e2
        component=opt.component or None,
Packit Service 4b33e2
        product=opt.product or None,
Packit Service 4b33e2
        blocks_add=add_blk or None,
Packit Service 4b33e2
        blocks_remove=rm_blk or None,
Packit Service 4b33e2
        blocks_set=set_blk,
Packit Service 4b33e2
        url=opt.url or None,
Packit Service 4b33e2
        cc_add=add_cc or None,
Packit Service 4b33e2
        cc_remove=rm_cc or None,
Packit Service 4b33e2
        depends_on_add=add_deps or None,
Packit Service 4b33e2
        depends_on_remove=rm_deps or None,
Packit Service 4b33e2
        depends_on_set=set_deps,
Packit Service 4b33e2
        groups_add=add_groups or None,
Packit Service 4b33e2
        groups_remove=rm_groups or None,
Packit Service 4b33e2
        keywords_add=add_key or None,
Packit Service 4b33e2
        keywords_remove=rm_key or None,
Packit Service 4b33e2
        keywords_set=set_key,
Packit Service 4b33e2
        op_sys=opt.os or None,
Packit Service 4b33e2
        platform=opt.arch or None,
Packit Service 4b33e2
        priority=opt.priority or None,
Packit Service 4b33e2
        qa_contact=opt.qa_contact or None,
Packit Service 4b33e2
        severity=opt.severity or None,
Packit Service 4b33e2
        status=status,
Packit Service 4b33e2
        summary=opt.summary or None,
Packit Service 4b33e2
        version=opt.version or None,
Packit Service 4b33e2
        reset_assigned_to=opt.reset_assignee or None,
Packit Service 4b33e2
        reset_qa_contact=opt.reset_qa_contact or None,
Packit Service 4b33e2
        resolution=opt.close or None,
Packit Service 4b33e2
        target_release=opt.target_release or None,
Packit Service 4b33e2
        target_milestone=opt.target_milestone or None,
Packit Service 4b33e2
        dupe_of=opt.dupeid or None,
Packit Service 4b33e2
        fixed_in=opt.fixed_in or None,
Packit Service 4b33e2
        whiteboard=set_wb and set_wb[0] or None,
Packit Service 4b33e2
        devel_whiteboard=set_devwb and set_devwb[0] or None,
Packit Service 4b33e2
        internal_whiteboard=set_intwb and set_intwb[0] or None,
Packit Service 4b33e2
        qa_whiteboard=set_qawb and set_qawb[0] or None,
Packit Service 4b33e2
        sub_component=opt.sub_component or None,
Packit Service 4b33e2
        alias=opt.alias or None,
Packit Service 4b33e2
        flags=flags or None,
Packit Service 4b33e2
        comment_tags=opt.comment_tag or None,
Packit Service 4b33e2
    )
Packit Service 4b33e2
Packit Service 4b33e2
    # We make this a little convoluted to facilitate unit testing
Packit Service 4b33e2
    wbmap = {
Packit Service 4b33e2
        "whiteboard": (add_wb, rm_wb),
Packit Service 4b33e2
        "internal_whiteboard": (add_intwb, rm_intwb),
Packit Service 4b33e2
        "qa_whiteboard": (add_qawb, rm_qawb),
Packit Service 4b33e2
        "devel_whiteboard": (add_devwb, rm_devwb),
Packit Service 4b33e2
    }
Packit Service 4b33e2
Packit Service 4b33e2
    for k, v in wbmap.copy().items():
Packit Service 4b33e2
        if not v[0] and not v[1]:
Packit Service 4b33e2
            del(wbmap[k])
Packit Service 4b33e2
Packit Service 4b33e2
    _merge_field_opts(update, opt, parser)
Packit Service 4b33e2
Packit Service 4b33e2
    log.debug("update bug dict=%s", update)
Packit Service 4b33e2
    log.debug("update whiteboard dict=%s", wbmap)
Packit Service 4b33e2
Packit Service 4b33e2
    if not any([update, wbmap, add_tags, rm_tags]):
Packit Service 4b33e2
        parser.error("'modify' command requires additional arguments")
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.test_return_result:
Packit Service 4b33e2
        return (update, wbmap, add_tags, rm_tags)
Packit Service 4b33e2
Packit Service 4b33e2
    if add_tags or rm_tags:
Packit Service 4b33e2
        ret = bz.update_tags(bugid_list,
Packit Service 4b33e2
            tags_add=add_tags, tags_remove=rm_tags)
Packit Service 4b33e2
        log.debug("bz.update_tags returned=%s", ret)
Packit Service 4b33e2
    if update:
Packit Service 4b33e2
        ret = bz.update_bugs(bugid_list, update)
Packit Service 4b33e2
        log.debug("bz.update_bugs returned=%s", ret)
Packit Service 4b33e2
Packit Service 4b33e2
    if not wbmap:
Packit Service 4b33e2
        return
Packit Service 4b33e2
Packit Service 4b33e2
    # Now for the things we can't blindly batch.
Packit Service 4b33e2
    # Being able to prepend/append to whiteboards, which are just
Packit Service 4b33e2
    # plain string values, is an old rhbz semantic that we try to maintain
Packit Service 4b33e2
    # here. This is a bit weird for traditional bugzilla XMLRPC
Packit Service 4b33e2
    log.debug("Adjusting whiteboard fields one by one")
Packit Service 4b33e2
    for bug in bz.getbugs(bugid_list):
Packit Service 4b33e2
        for wb, (add_list, rm_list) in wbmap.items():
Packit Service 4b33e2
            for tag in add_list:
Packit Service 4b33e2
                newval = getattr(bug, wb) or ""
Packit Service 4b33e2
                if newval:
Packit Service 4b33e2
                    newval += " "
Packit Service 4b33e2
                newval += tag
Packit Service 4b33e2
                bz.update_bugs([bug.id],
Packit Service 4b33e2
                               bz.build_update(**{wb: newval}))
Packit Service 4b33e2
Packit Service 4b33e2
            for tag in rm_list:
Packit Service 4b33e2
                newval = (getattr(bug, wb) or "").split()
Packit Service 4b33e2
                for t in newval[:]:
Packit Service 4b33e2
                    if t == tag:
Packit Service 4b33e2
                        newval.remove(t)
Packit Service 4b33e2
                bz.update_bugs([bug.id],
Packit Service 4b33e2
                               bz.build_update(**{wb: " ".join(newval)}))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_get_attach(bz, opt):
Packit Service 4b33e2
    for bug in bz.getbugs(opt.getall):
Packit Service 4b33e2
        opt.get += bug.get_attachment_ids()
Packit Service 4b33e2
Packit Service 4b33e2
    for attid in set(opt.get):
Packit Service 4b33e2
        att = bz.openattachment(attid)
Packit Service 4b33e2
        outfile = open_without_clobber(att.name, "wb")
Packit Service 4b33e2
        data = att.read(4096)
Packit Service 4b33e2
        while data:
Packit Service 4b33e2
            outfile.write(data)
Packit Service 4b33e2
            data = att.read(4096)
Packit Service 4b33e2
        print("Wrote %s" % outfile.name)
Packit Service 4b33e2
Packit Service 4b33e2
    return
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _do_set_attach(bz, opt, parser):
Packit Service 4b33e2
    if not opt.ids:
Packit Service 4b33e2
        parser.error("Bug ID must be specified for setting attachments")
Packit Service 4b33e2
Packit Service 4b33e2
    if sys.stdin.isatty():
Packit Service 4b33e2
        if not opt.file:
Packit Service 4b33e2
            parser.error("--file must be specified")
Packit Service 4b33e2
        fileobj = open(opt.file, "rb")
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        # piped input on stdin
Packit Service 4b33e2
        if not opt.desc:
Packit Service 4b33e2
            parser.error("--description must be specified if passing "
Packit Service 4b33e2
                         "file on stdin")
Packit Service 4b33e2
Packit Service 4b33e2
        fileobj = tempfile.NamedTemporaryFile(prefix="bugzilla-attach.")
Packit Service 4b33e2
        data = sys.stdin.read(4096)
Packit Service 4b33e2
Packit Service 4b33e2
        while data:
Packit Service 4b33e2
            fileobj.write(data.encode(locale.getpreferredencoding()))
Packit Service 4b33e2
            data = sys.stdin.read(4096)
Packit Service 4b33e2
        fileobj.seek(0)
Packit Service 4b33e2
Packit Service 4b33e2
    kwargs = {}
Packit Service 4b33e2
    if opt.file:
Packit Service 4b33e2
        kwargs["filename"] = os.path.basename(opt.file)
Packit Service 4b33e2
    if opt.type:
Packit Service 4b33e2
        kwargs["contenttype"] = opt.type
Packit Service 4b33e2
    if opt.type in ["text/x-patch"]:
Packit Service 4b33e2
        kwargs["ispatch"] = True
Packit Service 4b33e2
    if opt.comment:
Packit Service 4b33e2
        kwargs["comment"] = opt.comment
Packit Service 4b33e2
    desc = opt.desc or os.path.basename(fileobj.name)
Packit Service 4b33e2
Packit Service 4b33e2
    # Upload attachments
Packit Service 4b33e2
    for bugid in opt.ids:
Packit Service 4b33e2
        attid = bz.attachfile(bugid, fileobj, desc, **kwargs)
Packit Service 4b33e2
        print("Created attachment %i on bug %s" % (attid, bugid))
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
#################
Packit Service 4b33e2
# Main handling #
Packit Service 4b33e2
#################
Packit Service 4b33e2
Packit Service 4b33e2
def _make_bz_instance(opt):
Packit Service 4b33e2
    """
Packit Service 4b33e2
    Build the Bugzilla instance we will use
Packit Service 4b33e2
    """
Packit Service 4b33e2
    if opt.bztype != 'auto':
Packit Service 4b33e2
        log.info("Explicit --bztype is no longer supported, ignoring")
Packit Service 4b33e2
Packit Service 4b33e2
    cookiefile = None
Packit Service 4b33e2
    tokenfile = None
Packit Service 4b33e2
    if opt.cache_credentials:
Packit Service 4b33e2
        cookiefile = opt.cookiefile or -1
Packit Service 4b33e2
        tokenfile = opt.tokenfile or -1
Packit Service 4b33e2
Packit Service 4b33e2
    bz = bugzilla.Bugzilla(
Packit Service 4b33e2
        url=opt.bugzilla,
Packit Service 4b33e2
        cookiefile=cookiefile,
Packit Service 4b33e2
        tokenfile=tokenfile,
Packit Service 4b33e2
        sslverify=opt.sslverify,
Packit Service 4b33e2
        cert=opt.cert)
Packit Service 4b33e2
    return bz
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _handle_login(opt, action, bz):
Packit Service 4b33e2
    """
Packit Service 4b33e2
    Handle all login related bits
Packit Service 4b33e2
    """
Packit Service 4b33e2
    is_login_command = (action == 'login')
Packit Service 4b33e2
Packit Service 4b33e2
    do_interactive_login = (is_login_command or
Packit Service 4b33e2
        opt.login or opt.username or opt.password)
Packit Service 4b33e2
    username = getattr(opt, "pos_username", None) or opt.username
Packit Service 4b33e2
    password = getattr(opt, "pos_password", None) or opt.password
Packit Service 4b33e2
Packit Service 4b33e2
    try:
Packit Service 4b33e2
        if do_interactive_login:
Packit Service 4b33e2
            if bz.url:
Packit Service 4b33e2
                print("Logging into %s" % urlparse(bz.url)[1])
Packit Service 4b33e2
            bz.interactive_login(username, password)
Packit Service 4b33e2
    except bugzilla.BugzillaError as e:
Packit Service 4b33e2
        print(str(e))
Packit Service 4b33e2
        sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
    if opt.ensure_logged_in and not bz.logged_in:
Packit Service 4b33e2
        print("--ensure-logged-in passed but you aren't logged in to %s" %
Packit Service 4b33e2
            bz.url)
Packit Service 4b33e2
        sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
    if is_login_command:
Packit Service 4b33e2
        msg = "Login successful."
Packit Service 4b33e2
        if bz.cookiefile or bz.tokenfile:
Packit Service 4b33e2
            msg = "Login successful, token cache updated."
Packit Service 4b33e2
Packit Service 4b33e2
        print(msg)
Packit Service 4b33e2
        sys.exit(0)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def _main(unittest_bz_instance):
Packit Service 4b33e2
    parser = setup_parser()
Packit Service 4b33e2
    opt = parser.parse_args()
Packit Service 4b33e2
    action = opt.command
Packit Service 4b33e2
    setup_logging(opt.debug, opt.verbose)
Packit Service 4b33e2
Packit Service 4b33e2
    log.debug("Launched with command line: %s", " ".join(sys.argv))
Packit Service 4b33e2
    log.debug("Bugzilla module: %s", bugzilla)
Packit Service 4b33e2
Packit Service 4b33e2
    # Connect to bugzilla
Packit Service 4b33e2
    log.info('Connecting to %s', opt.bugzilla)
Packit Service 4b33e2
Packit Service 4b33e2
    if unittest_bz_instance:
Packit Service 4b33e2
        bz = unittest_bz_instance
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        bz = _make_bz_instance(opt)
Packit Service 4b33e2
Packit Service 4b33e2
    # Handle login options
Packit Service 4b33e2
    _handle_login(opt, action, bz)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
    ###########################
Packit Service 4b33e2
    # Run the actual commands #
Packit Service 4b33e2
    ###########################
Packit Service 4b33e2
Packit Service 4b33e2
    if hasattr(opt, "outputformat"):
Packit Service 4b33e2
        if not opt.outputformat and opt.output not in ['raw', None]:
Packit Service 4b33e2
            opt.outputformat = _convert_to_outputformat(opt.output)
Packit Service 4b33e2
Packit Service 4b33e2
    buglist = []
Packit Service 4b33e2
    if action == 'info':
Packit Service 4b33e2
        if not (opt.products or
Packit Service 4b33e2
                opt.components or
Packit Service 4b33e2
                opt.component_owners or
Packit Service 4b33e2
                opt.versions):
Packit Service 4b33e2
            parser.error("'info' command requires additional arguments")
Packit Service 4b33e2
Packit Service 4b33e2
        _do_info(bz, opt)
Packit Service 4b33e2
Packit Service 4b33e2
    elif action == 'query':
Packit Service 4b33e2
        buglist = _do_query(bz, opt, parser)
Packit Service 4b33e2
        if opt.test_return_result:
Packit Service 4b33e2
            return buglist
Packit Service 4b33e2
Packit Service 4b33e2
    elif action == 'new':
Packit Service 4b33e2
        buglist = _do_new(bz, opt, parser)
Packit Service 4b33e2
        if opt.test_return_result:
Packit Service 4b33e2
            return buglist
Packit Service 4b33e2
Packit Service 4b33e2
    elif action == 'attach':
Packit Service 4b33e2
        if opt.get or opt.getall:
Packit Service 4b33e2
            if opt.ids:
Packit Service 4b33e2
                parser.error("Bug IDs '%s' not used for "
Packit Service 4b33e2
                    "getting attachments" % opt.ids)
Packit Service 4b33e2
            _do_get_attach(bz, opt)
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            _do_set_attach(bz, opt, parser)
Packit Service 4b33e2
Packit Service 4b33e2
    elif action == 'modify':
Packit Service 4b33e2
        modout = _do_modify(bz, parser, opt)
Packit Service 4b33e2
        if opt.test_return_result:
Packit Service 4b33e2
            return modout
Packit Service 4b33e2
    else:
Packit Service 4b33e2
        raise RuntimeError("Unexpected action '%s'" % action)
Packit Service 4b33e2
Packit Service 4b33e2
    # If we're doing new/query/modify, output our results
Packit Service 4b33e2
    if action in ['new', 'query']:
Packit Service 4b33e2
        _format_output(bz, opt, buglist)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def main(unittest_bz_instance=None):
Packit Service 4b33e2
    try:
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            return _main(unittest_bz_instance)
Packit Service 4b33e2
        except (Exception, KeyboardInterrupt):
Packit Service 4b33e2
            log.debug("", exc_info=True)
Packit Service 4b33e2
            raise
Packit Service 4b33e2
    except (Fault, bugzilla.BugzillaError) as e:
Packit Service 4b33e2
        print("\nServer error: %s" % str(e))
Packit Service 4b33e2
        sys.exit(3)
Packit Service 4b33e2
    except requests.exceptions.SSLError as e:
Packit Service 4b33e2
        # Give SSL recommendations
Packit Service 4b33e2
        print("SSL error: %s" % e)
Packit Service 4b33e2
        print("\nIf you trust the remote server, you can work "
Packit Service 4b33e2
              "around this error with:\n"
Packit Service 4b33e2
              "  bugzilla --nosslverify ...")
Packit Service 4b33e2
        sys.exit(4)
Packit Service 4b33e2
    except (socket.error,
Packit Service 4b33e2
            requests.exceptions.HTTPError,
Packit Service 4b33e2
            requests.exceptions.ConnectionError,
Packit Service 4b33e2
            ProtocolError) as e:
Packit Service 4b33e2
        print("\nConnection lost/failed: %s" % str(e))
Packit Service 4b33e2
        sys.exit(2)
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
def cli():
Packit Service 4b33e2
    try:
Packit Service 4b33e2
        main()
Packit Service 4b33e2
    except KeyboardInterrupt:
Packit Service 4b33e2
        log.debug("", exc_info=True)
Packit Service 4b33e2
        print("\nExited at user request.")
Packit Service 4b33e2
        sys.exit(1)