Esempio n. 1
0
    def _fetch_by_bug(self, bug_id):
        # print("fetch_by_bug")
        try:
            url = "/bugs/%i/advisories.json" % bug_id
            rj = self._get(url)

            stored = False
            for e in rj:
                if not stored:
                    stored = True
                    self._fetch(e['id'])
                else:
                    print(
                        'Warning: Ignoring additional erratum ' +
                        str(e['id']) + ' for bug ', str(bug_id))

        except RuntimeError:
            # Requests seems to loop infinitely if this happens...
            raise ErrataException('Pigeon crap. Did it forget to run kinit?')
        except IndexError:
            # errata_id not found
            raise ErrataException('Errata ID field not found in response')
        except LookupError:
            # Errata not found
            pass
        except Exception:
            # Todo: better handling
            raise
Esempio n. 2
0
    def _processResponse(self, r):
        if r.status_code in [200, 201, 202, 203, 204]:
            return  # all good

        # If subclassed as an Erratum and we have an ID, add it
        # to the error message
        err_msg = ''
        try:
            if type(self.errata_id) is int and self.errata_id > 0:
                err_msg += 'Erratum ' + str(self.errata_id) + ': '
        except AttributeError:
            pass

        # Generate a really big message if e.g. bug is in a different
        # erratum
        if r.status_code in [400, 422]:
            rj = r.json()
            if rj is None:
                raise Exception(err_msg + 'No Json returned')
            if 'error' in rj:
                # err_msg += '; '.join(rj['error'])
                err_msg += str(rj['error'])
            else:
                # TODO: drop jsonpath
                pe = parse('errors[*]')
                for match in pe.find(rj):
                    # This grabs the index since the json returns a dict
                    for k in match.value:
                        err_msg += k + ": "
                        petmp = parse('errors.' + k + '[*]')
                        for m in petmp.find(rj):
                            if isinstance(m.value, six.string_types):
                                err_msg += m.value + "\n"
                            elif type(m.value) is int:
                                err_msg += str(m.value) + "\n"
                            else:
                                for n in m.value:
                                    err_msg += str(n) + "\n"
            raise ErrataException(err_msg)

        if r.status_code in [401]:
            # lhh - this is not a typo, and the syntax is correct,
            # I assure you.
            raise ErrataException('Pigeon crap. Did it forget to run kinit?')

        if r.status_code in [500]:
            err_msg += "Broke errata tool!"
            print(r.json())
            raise ErrataException(err_msg)

        if r.status_code in [404]:
            err_msg += 'Bug in your code - wrong method for this api? '
            err_msg += 'Wrong location?'
            print(r.json())
            raise ErrataException(err_msg)

        raise ErrataException(err_msg + "Unhandled HTTP status code: " +
                              str(r.status_code))
    def _get(self, u, **kwargs):
        """_get is convience method that retrives content from server

        Recognized kwargs
            'data' for requests data object to send with get call
            'json' for requests json object to send with get call
            'raw' bool (defaulting to False) for returning response object

        by default the return value is the response.json() object from Requests
        """
        self._set_username()
        url = self.canonical_url(u)
        ret_data = None
        ret_json = None
        start = time.time()
        return_json_decoded_data = True

        if kwargs is not None:
            if 'data' in kwargs:
                ret_data = requests.get(url,
                                        auth=self._auth,
                                        data=kwargs['data'],
                                        verify=self.ssl_verify)
            elif 'json' in kwargs:
                ret_data = requests.get(url,
                                        auth=self._auth,
                                        json=kwargs['json'],
                                        verify=self.ssl_verify)
            if 'raw' in kwargs:
                return_json_decoded_data = not kwargs['raw']

        if ret_data is None:
            ret_data = requests.get(url, auth=self._auth,
                                    verify=self.ssl_verify)

        self._record('GET', url, time.time() - start)

        if ret_json is None and ret_data is not None:
            if ret_data.status_code == 200:
                if return_json_decoded_data:
                    ret_json = ret_data.json()
                else:
                    return ret_data

            elif ret_data.status_code in [401]:
                raise ErrataException(
                    'Pigeon crap. Did it forget to run kinit?')
            elif ret_data.status_code in [403]:
                raise ErrataException(
                    'You need Errata access for this operation!')
            else:
                print("Result not handled: " + str(ret_data.text))
                print("While fetching: " + url)
                raise ErrataException(str(ret_data))

        return ret_json
Esempio n. 4
0
    def addBuilds(self, buildlist, **kwargs):
        if self._new:
            raise ErrataException('Cannot add builds to unfiled erratum')
        if 'release' not in kwargs and self._release is None:
            if len(self.errata_builds) != 1:
                raise ErrataException('Need to specify a release')
            return self.addBuildsDirect(buildlist,
                                        self.errata_builds.keys()[0], **kwargs)
        if 'release' in kwargs:
            release = kwargs['release']
            del kwargs['release']
        else:
            release = self._release

        return self.addBuildsDirect(buildlist, release, **kwargs)
Esempio n. 5
0
    def addBuilds(self, buildlist, **kwargs):
        if self._new:
            raise ErrataException('Cannot add builds to unfiled erratum')

        release = None

        if 'release' in kwargs:
            release = kwargs['release']
            del kwargs['release']

        if release is None and len(self.errata_builds.keys()) == 1:
            release = list(self.errata_builds.keys())[0]

        if release is None:
            raise ErrataException('Need to specify a release')

        return self.addBuildsDirect(buildlist, release, **kwargs)
Esempio n. 6
0
    def _get(self, u, **kwargs):
        self._set_username()
        url = self.canonical_url(u)
        ret_data = None
        ret_json = None
        start = time.time()
        if kwargs is not None:
            if 'data' in kwargs:
                ret_data = requests.get(url,
                                        auth=self._auth,
                                        data=kwargs['data'],
                                        verify=self.ssl_verify)
            elif 'json' in kwargs:
                ret_data = requests.get(url,
                                        auth=self._auth,
                                        json=kwargs['json'],
                                        verify=self.ssl_verify)

        if ret_data is None:
            ret_data = requests.get(url,
                                    auth=self._auth,
                                    verify=self.ssl_verify)

        self._record('GET', url, time.time() - start)

        if ret_json is None and ret_data is not None:
            if ret_data.status_code == 200:
                ret_json = ret_data.json()
            elif ret_data.status_code in [401]:
                raise ErrataException(
                    'Pigeon crap. Did it forget to run kinit?')
            elif ret_data.status_code in [403]:
                raise ErrataException(
                    'You need Errata access for this operation!')
            elif ret_data.status_code in [500]:
                raise LookupError('No matching errata')

            else:
                print("Result not handled: " + str(ret_data))
                print("While fetching: " + url)
                raise ErrataException(str(ret_data))

        return ret_json
Esempio n. 7
0
    def __init__(self, **kwargs):

        self.ssl_verify = security.security_settings.ssl_verify()

        # Blank erratum e.g. if create is required
        self._do_init()
        if 'errata_id' in kwargs:
            self._fetch(kwargs['errata_id'])
            return

        if 'bug_id' in kwargs:
            self._fetch_by_bug(kwargs['bug_id'])
            return

        if 'product' not in kwargs:
            raise ErrataException('Creating errata requires a product')
        if 'release' not in kwargs:
            raise ErrataException('Creating errata requires a release')
        if 'format' in kwargs:
            self._format = kwargs['format']

        self._new = True
        self.errata_name = '(unassigned)'
        self.errata_state = 'NEW_FILES'
        self._product = kwargs['product']
        self._release = kwargs['release']
        self.update(**kwargs)
        if 'solution' not in kwargs:
            self.solution = self.fmt("Before applying this update, \
make sure all previously released errata relevant to your system \
have been applied.\n\
\n\
For details on how to apply this update, refer to:\n\
\n\
https://access.redhat.com/articles/11258")

        # errata tool defaults
        if 'errata_type' in kwargs:
            self.errata_type = kwargs['errata_type']
        else:
            self.errata_type = 'RHBA'
Esempio n. 8
0
 def setState(self, state):
     if self._new:
         raise ErrataException('Cannot simultaneously create and change ' +
                               'an erratum\'s state')
     if self.errata_id == 0:
         raise ErrataException('Cannot change state for uninitialized ' +
                               'erratum')
     if self.errata_state.upper() == 'NEW_FILES':
         if state.upper() == 'QE':
             self.errata_state = 'QE'
     elif self.errata_state.upper() == 'QE':
         if state.upper() == 'NEW_FILES':
             self.errata_state = 'NEW_FILES'
         if state.upper() == 'REL_PREP':
             self.errata_state = 'REL_PREP'
     elif self.errata_state.upper() == 'REL_PREP':
         if state.upper() == 'NEW_FILES':
             self.errata_state = 'NEW_FILES'
         if state.upper() == 'QE':
             self.errata_state = 'QE'
     else:
         raise ErrataException('Cannot change state from ' +
                               self.errata_state.upper() + " to " +
                               state.upper())
Esempio n. 9
0
 def _set_username(self, **kwargs):
     if self._username is not None:
         return
     try:
         (ret, ctx) = kerberos.authGSSClientInit('*****@*****.**')
         assert (ret == kerberos.AUTH_GSS_COMPLETE)
         ret = kerberos.authGSSClientInquireCred(ctx)
         assert (ret == kerberos.AUTH_GSS_COMPLETE)
         # XXX What if you have >1 ticket?
         ret = kerberos.authGSSClientUserName(ctx)
         if '@' in ret:
             self._username = ret.split('@')[0]
         else:
             self._username = ret
     except AssertionError:
         raise ErrataException('Pigeon crap. Did it forget to run kinit?')
Esempio n. 10
0
    def fetch_releases(self):
        page = 1
        releases = {}
        while True:
            ret = self._get('/api/v1/releases?page[number]=' + str(page))
            if ret is None:
                break
            if 'data' not in ret:
                raise ErrataException('Malformed response from server')
            if len(ret['data']) == 0:
                break

            for r in ret['data']:
                attrs = r['attributes']
                if r['type'] != 'releases':
                    continue

                info = {}
                info['id'] = int(r['id'])
                info['name'] = attrs['name']
                info['description'] = attrs['description']
                info['async'] = attrs['is_async']

                info['brew_tags'] = {}
                for t in r['relationships']['brew_tags']:
                    info['brew_tags'][int(t['id'])] = t['name']

                if attrs['enabled']:
                    info['enabled'] = True
                else:
                    info['enabled'] = False

                info['bz_flags'] = []
                for f in attrs['blocker_flags']:
                    if f not in ('devel_ack', 'pm_ack', 'qa_ack'):
                        info['bz_flags'].append(f)

                info['versions'] = {}
                info['products'] = {}
                if 'product_versions' in r['relationships']:
                    for t in r['relationships']['product_versions']:
                        info['versions'][int(t['id'])] = t['name']

                releases[int(r['id'])] = info

            page = page + 1
        self.releases = releases
Esempio n. 11
0
 def commit(self):
     if self.retry_times <= self.none_throw_threshold:
         self.retry_times = self.retry_times + 1
         raise ErrataException("this is an exception from testErratum")
     else:
         pass
Esempio n. 12
0
    def _write(self):
        pdata = {}

        # See below for APIs used when talking to the errata tool.
        if self._new:
            if self.package_owner_email is None:
                raise ErrataException("Can't create erratum without " +
                                      "package owner email")
            if self.manager_email is None:
                if self.manager_id:
                    manager = User(self.manager_id)
                    self.manager_email = manager.email_address
                else:
                    raise ErrataException("Can't create erratum without " +
                                          "manager email or manager id")
            if self._product is None:
                raise ErrataException("Can't create erratum with no " +
                                      "product specified")
            if self._release is None:
                raise ErrataException("Can't create erratum with no " +
                                      "release specified")
            if self.errata_type is None:
                self.errata_type = 'RHBA'

            if self.errata_type == 'RHSA':
                val = 'None'
                if self.security_impact is not None:
                    val = self.security_impact
                pdata['advisory[security_impact]'] = val

            pdata['product'] = self._product
            pdata['release'] = self._release
            pdata['advisory[package_owner_email]'] = self.package_owner_email
            pdata['advisory[manager_email]'] = self.manager_email

        if self.qe_email is not None and self.qe_email != '':
            pdata['advisory[assigned_to_email]'] = self.qe_email
        if self.qe_group is not None and self.qe_group != '':
            pdata['advisory[quality_responsibility_name]'] = self.qe_group

        if self.synopsis is None:
            raise ErrataException("Can't write erratum without synopsis")
        if self.topic is None:
            raise ErrataException("Can't write erratum without topic")
        if self.description is None:
            raise ErrataException("Can't write erratum without description")
        if self.solution is None:
            raise ErrataException("Can't write erratum without a solution")

        if self.errata_bugs is None:
            raise ErrataException("Can't write erratum without a list of " +
                                  "bugs")

        # Default from errata tool
        pdata['advisory[errata_type]'] = self.errata_type

        # POST/PUT a 1 or 0 value for this text_only boolean
        pdata['advisory[text_only]'] = int(self.text_only)

        if self.text_only_cpe:
            pdata['advisory[text_only_cpe]'] = self.text_only_cpe

        if self.publish_date_override:
            pdata['advisory[publish_date_override]'] = \
                self.publish_date_override

        # ET automagically handles the severity for the synopsis in RHSA's
        # but will still see it as a docs change if we write the same one
        # back again, so remove it.
        if self.errata_type == 'RHSA':
            severity = r'^(Low|Moderate|Important|Critical): '
            self.synopsis = re.sub(severity, "", self.synopsis)
            pdata['advisory[cve]'] = self.cve_names
        pdata['advisory[synopsis]'] = self.synopsis
        pdata['advisory[topic]'] = self.topic
        pdata['advisory[description]'] = self.description
        pdata['advisory[solution]'] = self.solution

        # XXX Delete all bugs is a special case
        last_bug = None
        if len(self.errata_bugs) == 0 and len(self._original_bugs) > 0:
            last_bug = self._original_bugs[0]
            self.errata_bugs = [last_bug]

        # Add back any Vulnerability bugs
        allbugs = list(set(self.errata_bugs) | set(self._cve_bugs))
        idsfixed = ' '.join(str(i) for i in allbugs)
        pdata['advisory[idsfixed]'] = idsfixed

        # Sync newly added bug states
        newbugs = list(set(allbugs) - set(self._original_bugs))
        if len(newbugs):
            # url = '/api/v1/bug/refresh'
            # print(allbugs)
            # r = self._post(url, data=newbugs)
            # self._processResponse(r)
            # ^ XXX broken
            #
            # XXX Sync bug states by force using UI
            # Note: UI limits syncs to 100 bugs per run, so split
            # up into chunks
            syncs = [newbugs[x:x + 100] for x in range(0, len(newbugs), 100)]
            bug_list = {}
            for s in syncs:
                bug_list['issue_list'] = ' '.join(str(i) for i in s)
                url = "/bugs/sync_bug_list"
                r = self._post(url, data=bug_list)
                # XXX should we process return code?

        # Push it
        if self._new:
            # REFERENCE

            # New is 'POST'
            url = "/api/v1/erratum"
            r = self._post(url, data=pdata)
            self._processResponse(r)
            rj = r.json()
            # This might need a "pdc_" prefix, rhbz 1493773 for background
            json_errata_type = self.errata_type.lower()
            try:
                rj['errata'][json_errata_type]
            except KeyError:
                json_errata_type = 'pdc_%s' % json_errata_type
                rj['errata'][json_errata_type]
            self.errata_id = rj['errata'][json_errata_type]['errata_id']
            # XXX return JSON returns full advisory name but not
            # typical advisory name - e.g. RHSA-2015:19999-01, but not
            # RHSA-2015:19999, but it's close enough
            self.errata_name = rj['errata'][json_errata_type]['fulladvisory']
        else:
            # REFERENCE

            # Update is 'PUT'
            url = "/api/v1/erratum/%i" % self.errata_id
            r = self._put(url, data=pdata)
        self._processResponse(r)

        # XXX WOW VERY HACK
        # If deleting last bug...
        if last_bug is not None:
            # This doesn't work to remove the last bug, nor does setting
            # idsfixed to empty-string
            # url = "/api/v1/erratum/%i/remove_bug" % self.errata_id
            # pdata = {'bug': str(last_bug)}

            # Solution: Use hacks to pretend we're using the remove-bugs
            # web UI :(
            url = '/bugs/remove_bugs_from_errata/%i' % self.errata_id
            pdata = {}
            pdata['bug[' + str(last_bug) + ']'] = 1

            # Handle weird interaction we get in this particular case
            try:
                r = self._post(url, data=pdata)
            except requests_kerberos.exceptions.MutualAuthenticationError:
                pass
            self._processResponse(r)
Esempio n. 13
0
    def _fetch(self, errata_id):
        self._new = False
        self._update = False
        self._buildschanged = False
        self.errata_builds = {}
        self.current_flags = []

        try:
            # TODO: remove call to /advisory/X.json once new API
            # supports all the information
            endpoint_list = [
                '/advisory/' + str(errata_id) + '.json',
                '/api/v1/erratum/' + str(errata_id),
            ]
            # Want to ditch advisory_old eventually
            advisory_old = None
            advisory = None
            erratum = None
            for endpoint in endpoint_list:
                r = self._get(endpoint)
                if r is None:
                    continue
                if advisory is None and 'erratum' in endpoint:
                    advisory = r
                    continue
                # Fallthrough
                if advisory_old is None:
                    advisory_old = r
            if advisory is None:
                print('do not have requested data bailing')
                return None

            # Short circuit to get the advisory
            for key in advisory['errata']:
                erratum = advisory['errata'][key]
                self.errata_type = key.upper()
                # We cannot PUT a "PDC_"-prefixed value back to the server.
                # The server expects "RHBA", not "PDC_RHBA". We will just
                # remove the type internally here.
                # (See bz 1493773 for background on why the key has the pdc_
                # prefix here.)
                if self.errata_type.startswith('PDC_'):
                    self.errata_type = self.errata_type[4:]
                break

            self.errata_id = erratum['id']
            # NEW_FILES QE etc.
            self.errata_state = erratum['status']
            self._original_state = self.errata_state

            # Check if the erratum is under embargo
            self.embargoed = False
            self.release_date = erratum['release_date']
            if self.release_date is not None:
                cur = datetime.datetime.utcnow()
                cur = str(cur).split()[0]
                if self.release_date > cur:
                    self.embargoed = True

            # Target Ship date
            d = erratum['publish_date_override']
            if d is not None:
                pd = time.strptime(str(d), '%Y-%m-%dT%H:%M:%SZ')
                self.publish_date_override = time.strftime('%Y-%b-%d', pd)

            # Actual ship date (if in SHIPPED_LIVE)
            if self.errata_state in ('SHIPPED_LIVE'):
                d = erratum['actual_ship_date']
                d = time.strptime(str(d), '%Y-%m-%dT%H:%M:%SZ')
                self.ship_date = time.strftime('%Y-%b-%d', d)

            # File date
            d = erratum['created_at']
            d = time.strptime(str(d), '%Y-%m-%dT%H:%M:%SZ')
            self.creation_date = time.strftime('%Y-%b-%d', d)

            d = time.strftime('%Y-%b-%d', time.gmtime())
            if self.ship_date is not None:
                d = self.ship_date

            filed = datetime.datetime.strptime(self.creation_date, '%Y-%b-%d')
            ship = datetime.datetime.strptime(d, '%Y-%b-%d')
            age = ship - filed
            self.age = age.days

            # Baseline flags.
            if self.errata_state in ('QE'):
                if 'sign_requested' in erratum and \
                   erratum['sign_requested'] == 0:
                    self.addFlags('request_sigs')
                if 'rhnqa' in erratum and erratum['rhnqa'] == 0:
                    self.addFlags('needs_distqa')

            if 'doc_complete' in erratum and erratum['doc_complete'] == 0:
                self.addFlags('needs_docs')

            if self.errata_state == 'NEW_FILES':
                self.addFlags('needs_devel')

            # Note: new errata return values will have other bits.
            self.errata_name = erratum['fulladvisory']

            # Grab immutable fields
            self._product = advisory_old['product']['short_name']
            self._release = advisory_old['release']['name']

            # A maybe-empty list, containing eg. "rpm" or "docker"
            self.content_types = erratum['content_types']

            # store product and release IDs
            self.product_id = advisory_old['product']['id']
            self.release_id = advisory_old['release']['id']

            self.package_owner_email = advisory_old['people']['package_owner']
            self.reporter = advisory_old['people']['reporter']
            self.qe_email = advisory_old['people']['assigned_to']
            self.qe_group = advisory_old['people']['qe_group']

            # XXX Errata tool doesn't report manager?
            # https://bugzilla.redhat.com/show_bug.cgi?id=1664884
            # self.manager_email = ???
            self.manager_id = erratum.get('manager_id')

            # Grab mutable errata content
            self.text_only = erratum['text_only']
            self.synopsis = erratum['synopsis']
            content = advisory['content']['content']
            self.text_only_cpe = content['text_only_cpe']
            self.topic = content['topic']
            self.description = content['description']
            self.solution = content['solution']
            self.errata_bugs = [
                int(b['bug']['id']) for b in advisory['bugs']['bugs']
            ]
            self.cve_names = content['cve']
            if self.cve_names == '':
                self.cve_names = None
            self._original_bugs = list(self.errata_bugs)

            self._cache_bug_info(self._original_bugs)

            # Try to check to see if we need devel assistance, qe assistance or
            # rel prep assistance
            if self.errata_state == 'QE':
                self._check_tps()
                self._check_bugs()
                self._check_need_rel_prep()

            # Check for security review
            if 'rhsa' in advisory['errata']:
                sa = advisory['errata']['rhsa']['security_approved']
                self.security_impact = advisory['errata']['rhsa'][
                    'security_impact']  # NOQA

                if sa is None:
                    self.addFlags('request_security')
                elif sa is False:
                    self.addFlags('needs_security')

            check_signatures = self.errata_state != 'NEW_FILES'
            self._get_build_list(check_signatures)
            self.batch_id = erratum.get('batch_id')

            return

        except RuntimeError:
            # Requests seems to loop infinitely if this happens...
            raise ErrataException('Pigeon crap. Did it forget to run kinit?')
        except IndexError:
            # errata_id not found
            raise ErrataException('Errata ID field not found in response')
        except Exception:
            # Todo: better handling
            raise