Exemple #1
0
    def __delitem__(self, shortName):
        if re.match('.*@.*', shortName):
            real_shortName = re.compile('(.*)@(.*)').search(shortName).group(1)
            raise ValueError(
                ("Plugin shortName can't contain version. '%s' should be '%s'")
                % (shortName, real_shortName)
            )
        if shortName not in self:
            raise KeyError(
                'Plugin with ID "%s" not found, cannot uninstall' % shortName)
        if self[shortName].deleted:
            raise JenkinsAPIException(
                'Plugin "%s" already marked for uninstall. '
                'Restart jenkins for uninstall to complete.')
        params = {
            'Submit': 'OK',
            'json': {}
        }
        url = ('%s/pluginManager/plugin/%s/doUninstall'
               % (self.jenkins_obj.baseurl, shortName))
        self.jenkins_obj.requester.post_and_confirm_status(
            url, params={}, data=urlencode(params)
        )

        self.poll()
        if not self[shortName].deleted:
            raise JenkinsAPIException(
                "Problem uninstalling plugin '%s'." % shortName)
Exemple #2
0
    def __setitem__(self, description, credential):
        """
        Creates Credential in Jenkins using username, password and description
        Description must be unique in Jenkins instance
        because it is used to find Credential later.

        If description already exists - this method is going to update
        existing Credential

        :param str description: Credential description
        :param tuple credential_tuple: (username, password, description) tuple.
        """
        if description not in self:
            params = credential.get_attributes()
            url = ('%s/createCredentials' % self.baseurl)
        else:
            raise JenkinsAPIException('Updating credentials is not supported '
                                      'by jenkinsapi')

        try:
            self.jenkins.requester.post_and_confirm_status(
                url, params={}, data=urlencode(params))
        except JenkinsAPIException as jae:
            raise JenkinsAPIException('Latest version of Credentials '
                                      'plugin is required to be able '
                                      'to create/update credentials. '
                                      'Original exception: %s' % str(jae))

        self.poll()
        self.credentials = self._data['credentials']
        if description not in self:
            raise JenkinsAPIException('Problem creating/updating credential.')
Exemple #3
0
 def get_data(self, url, params=None, tree=None):
     try:
         return ast.literal_eval(self.get_raw_data(url, params, tree))
     except Exception:
         logging.exception('Inappropriate content found at %s', url)
         raise JenkinsAPIException('Inappropriate content found at %s' %
                                   url)
Exemple #4
0
    def create(self, job_name, config):
        """
        Create a job
        :param jobname: name of new job, str
        :param config: configuration of new job, xml
        :return: new Job obj
        """
        if job_name in self:
            return self[job_name]

        params = {'name': job_name}
        try:
            if isinstance(
                    config, unicode):  # pylint: disable=undefined-variable
                config = str(config)
        except NameError:
            # Python3 already a str
            pass

        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(),
            data=config,
            params=params
        )
        self.jenkins.poll()
        if job_name not in self:
            raise JenkinsAPIException('Cannot create job %s' % job_name)

        return self[job_name]
 def __delitem__(self, description):
     params = {'Submit': 'OK', 'json': {}}
     url = ('%s/credential-store/domain/_/credential/%s/doDelete' %
            (self.jenkins.baseurl, self[description].credential_id))
     try:
         self.jenkins.requester.post_and_confirm_status(
             url, params={}, data=urlencode(params))
     except JenkinsAPIException as jae:
         raise JenkinsAPIException('Latest version of Credentials '
                                   'required to be able to create '
                                   'credentials. Original exception: %s' %
                                   str(jae))
     self.poll()
     self.credentials = self._data['credentials']
     if description in self:
         raise JenkinsAPIException('Problem deleting credential.')
Exemple #6
0
    def create(self, job_name, config):
        """
        Create a job

        :param str jobname: Name of new job
        :param str config: XML configuration of new job
        :returns Job: new Job object
        """
        if job_name in self:
            return self[job_name]

        if config is None or len(config) == 0:
            raise JenkinsAPIException('Job XML config cannot be empty')

        params = {'name': job_name}
        config = str(config)  # cast unicode in case of Python 2

        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(),
            data=config,
            params=params
        )
        # Reset to get it refreshed from Jenkins
        self._data = []

        return self[job_name]
Exemple #7
0
    def post_and_confirm_status(
            self, url, params=None, data=None, files=None, headers=None,
            valid=None, allow_redirects=True):
        valid = valid or self.VALID_STATUS_CODES
        if not headers and not files:
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        assert data is not None, "Post messages must have data"

        response = self.post_url(
            url,
            params,
            data,
            files,
            headers,
            allow_redirects)
        if response.status_code not in valid:
            raise JenkinsAPIException(
                'Operation failed. url={0}, data={1}, headers={2}, '
                'status={3}, text={4}'.format(
                    response.url, data, headers, response.status_code,
                    response.text.encode('UTF-8')
                )
            )
        return response
Exemple #8
0
    def create(self, job_name, config):
        """
        Create a job

        :param str jobname: Name of new job
        :param str config: XML configuration of new job
        :returns Job: new Job object
        """
        if job_name in self:
            return self[job_name]

        if config is None or len(config) == 0:
            raise JenkinsAPIException('Job XML config cannot be empty')

        params = {'name': job_name}
        try:
            if isinstance(config, unicode):  # pylint: disable=undefined-variable
                config = str(config)
        except NameError:
            # Python2 already a str
            pass

        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(), data=config, params=params)
        # Reset to get it refreshed from Jenkins
        self._data = []

        return self[job_name]
Exemple #9
0
 def get_data(self, url, params=None):
     requester = self.get_jenkins_obj().requester
     response = requester.get_url(url, params)
     try:
         return eval(response.text)
     except Exception:
         log.exception('Inappropriate content found at %s', url)
         raise JenkinsAPIException('Cannot parse %s' % response.content)
Exemple #10
0
    def __delitem__(self, shortName):
        if shortName not in self:
            raise KeyError('Plugin with ID "%s" not found, cannot uninstall' %
                           shortName)
        if self[shortName].deleted:
            raise JenkinsAPIException(
                'Plugin "%s" already marked for uninstall. '
                'Restart jenkins for uninstall to complete.')
        params = {'Submit': 'OK', 'json': {}}
        url = ('%s/pluginManager/plugin/%s/doUninstall' %
               (self.jenkins_obj.baseurl, shortName))
        self.jenkins_obj.requester.post_and_confirm_status(
            url, params={}, data=urlencode(params))

        self.poll()
        if not self[shortName].deleted:
            raise JenkinsAPIException("Problem uninstalling plugin '%s'." %
                                      shortName)
Exemple #11
0
    def upload_config(self, config_xml):
        """
        Uploads config_xml to the config.xml for the node.
        """
        if self.name == 'master':
            raise JenkinsAPIException('master node does not have config.xml')

        self.jenkins.requester.post_and_confirm_status(
            "%(baseurl)s/config.xml" % self.__dict__, data=config_xml)
Exemple #12
0
    def create_multibranch_pipeline(self,
                                    job_name,
                                    config,
                                    block=True,
                                    delay=60):
        """
        Create a multibranch pipeline job

        :param str jobname: Name of new job
        :param str config: XML configuration of new job
        :param block: block until scan is finished?
        :param delay: max delay to wait for scan to finish (seconds)
        :returns list of new Jobs after scan
        """
        if not config:
            raise JenkinsAPIException('Job XML config cannot be empty')

        params = {'name': job_name}
        try:
            if isinstance(config, unicode):  # pylint: disable=undefined-variable
                config = str(config)
        except NameError:
            # Python2 already a str
            pass
        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(), data=config, params=params)
        # Reset to get it refreshed from Jenkins
        self._data = []

        # Launch a first scan / indexing to discover the branches...
        self.jenkins.requester.post_and_confirm_status(
            '{}/job/{}/build'.format(self.jenkins.baseurl, job_name),
            data='',
            valid=[200, 302],  # expect 302 without redirects
            allow_redirects=False)

        start_time = time.time()
        # redirect-url does not work with indexing;
        # so the only workaround found is to parse the console output untill scan has finished.
        scan_finished = False
        while not scan_finished and block and time.time() < start_time + delay:
            indexing_console_text = self.jenkins.requester.get_url(
                '{}/job/{}/indexing/consoleText'.format(
                    self.jenkins.baseurl, job_name))
            if indexing_console_text.text.strip().split('\n')[-1].startswith(
                    'Finished:'):
                scan_finished = True
            time.sleep(1)

        # now search for all jobs created; those who start with job_name + '/'
        jobs = []
        for name in self.jenkins.get_jobs_list():
            if name.startswith(job_name + '/'):
                jobs.append(self[name])

        return jobs
Exemple #13
0
 def get_and_confirm_status(self, url, params=None, headers=None, valid=None):
     valid = valid or self.VALID_STATUS_CODES
     response = self.get_url(url, params, headers)
     if response.status_code not in valid:
         if response.status_code == 405:         # POST required
             raise PostRequired('POST required for url {0}'.format(url))
         else:
             raise JenkinsAPIException('Operation failed. url={0}, headers={1}, status={2}, text={3}'.format(
                 response.url, headers, response.status_code, response.text.encode('UTF-8')))
     return response
Exemple #14
0
 def get_data(self, url, params=None):
     requester = self.get_jenkins_obj().requester
     response = requester.get_url(url, params)
     if response.status_code != 200:
         response.raise_for_status()
     try:
         return ast.literal_eval(response.text)
     except Exception:
         logging.exception('Inappropriate content found at %s', url)
         raise JenkinsAPIException('Cannot parse %s' % response.content)
Exemple #15
0
 def update_center_install_status(self):
     """
     Jenkins 2.x specific
     """
     url = "%s/updateCenter/installStatus" % self.jenkins_obj.baseurl
     status = self.jenkins_obj.requester.get_url(url)
     if status.status_code == 404:
         raise JenkinsAPIException(
             'update_center_install_status not available for Jenkins 1.X')
     return status.json()
Exemple #16
0
    def load_config(self):
        """
        Loads the config.xml for the node allowing it to be re-queried
        without generating new requests.
        """
        if self.name == 'master':
            raise JenkinsAPIException('master node does not have config.xml')

        self._config = self.get_config()
        self._get_config_element_tree()
Exemple #17
0
 def iteritems(self):
     for item in self._data['computer']:
         nodename = item['displayName']
         if nodename.lower() == 'master':
             nodeurl = '%s/(%s)' % (self.baseurl, nodename)
         else:
             nodeurl = '%s/%s' % (self.baseurl, nodename)
         try:
             yield item['displayName'], Node(self.jenkins, nodeurl,
                                             nodename, node_dict={})
         except Exception:
             raise JenkinsAPIException('Unable to iterate nodes')
Exemple #18
0
    def itervalues(self):
        """
        Return an iterator over the container's nodes.

        Using itervalues() while creating nodes may raise a RuntimeError or fail to iterate over
        all entries.
        """
        for item in self._data['computer']:
            try:
                yield self._make_node(item['displayName'])
            except Exception:
                raise JenkinsAPIException('Unable to iterate nodes')
Exemple #19
0
 def get_and_confirm_status(self,
                            url,
                            params=None,
                            headers=None,
                            valid=None):
     valid = valid or self.VALID_STATUS_CODES
     response = self.get_url(url, params, headers)
     if not response.status_code in valid:
         raise JenkinsAPIException(
             'Operation failed. url={0}, headers={1}, status={2}, text={3}'.
             format(response.url, headers, response.status_code,
                    response.text.encode('UTF-8')))
     return response
Exemple #20
0
    def post_and_confirm_status(self, url, params=None, data=None, files=None, headers=None, valid=None):
        valid = valid or self.VALID_STATUS_CODES
        assert isinstance(data, (
            str, dict)), \
            "Unexpected type of parameter 'data': %s. Expected (str, dict)" % type(data)

        if not headers and not files:
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        response = self.post_url(url, params, data, files, headers)
        if response.status_code not in valid:
            raise JenkinsAPIException('Operation failed. url={0}, data={1}, headers={2}, status={3}, text={4}'.format(
                response.url, data, headers, response.status_code, response.text.encode('UTF-8')))
        return response
Exemple #21
0
    def get_credentials(self):
        """
        Return credentials
        """

        if 'credentials' not in self.plugins:
            raise JenkinsAPIException('Credentials plugin not installed')

        if int(self.plugins['credentials'].version[0:1]) == 1:
            url = '%s/credential-store/domain/_/' % self.baseurl
            return Credentials(url, self)
        else:
            url = '%s/credentials/store/system/domain/_/' % self.baseurl
            return Credentials2x(url, self)
Exemple #22
0
 def _wait_until_plugin_installed(self, plugin, maxwait=120, interval=1):
     for _ in range(maxwait, 0, -interval):
         self.poll()
         if self._plugin_has_finished_installation(plugin):
             return
         if plugin.shortName in self:
             return True  # for Jenkins 1.X
         time.sleep(interval)
     if self.jenkins_obj.version.startswith('2'):
         raise JenkinsAPIException("Problem installing plugin '%s'." %
                                   plugin.shortName)
     else:
         log.warning(
             "Plugin '%s' not found in loaded plugins."
             "You may need to restart Jenkins.", plugin.shortName)
Exemple #23
0
 def get_console(self):
     """
     Return the current state of the text console.
     """
     url = "%s/consoleText" % self.baseurl
     content = self.job.jenkins.requester.get_url(url).content
     # This check was made for Python 3.x
     # In this version content is a bytes string
     # By contract this function must return string
     if isinstance(content, str):
         return content
     elif isinstance(content, bytes):
         return content.decode('ISO-8859-1')
     else:
         raise JenkinsAPIException('Unknown content type for console')
Exemple #24
0
    def get_data(self, url, params=None, tree=None):
        requester = self.get_jenkins_obj().requester
        if tree:
            if not params:
                params = {'tree': tree}
            else:
                params.update({'tree': tree})

        response = requester.get_url(url, params)
        if response.status_code != 200:
            logging.error('Failed request at %s with params: %s %s', url,
                          params, tree if tree else '')
            response.raise_for_status()
        try:
            return ast.literal_eval(response.text)
        except Exception:
            logging.exception('Inappropriate content found at %s', url)
            raise JenkinsAPIException('Cannot parse %s' % response.content)
Exemple #25
0
    def create(self, view_name, view_type=LIST_VIEW, config=None):
        """
        Create a view
        :param view_name: name of new view, str
        :param view_type: type of the view, one of the constants in Views, str
        :param config: XML configuration of the new view
        :return: new View obj or None if view was not created
        """
        log.info('Creating "%s" view "%s"', view_type, view_name)

        if view_name in self:
            log.warning('View "%s" already exists', view_name)
            return self[view_name]

        url = '%s/createView' % self.jenkins.baseurl

        if view_type == self.CATEGORIZED_VIEW:
            if config is None or len(config) == 0:
                raise JenkinsAPIException(
                    'Job XML config cannot be empty for CATEGORIZED_VIEW')

            params = {'name': view_name}

            self.jenkins.requester.post_xml_and_confirm_status(url,
                                                               data=config,
                                                               params=params)
        else:
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
            data = {
                "name": view_name,
                "mode": view_type,
                "Submit": "OK",
                "json": json.dumps({
                    "name": view_name,
                    "mode": view_type
                })
            }

            self.jenkins.requester.post_and_confirm_status(url,
                                                           data=data,
                                                           headers=headers)

        self.poll()
        return self[view_name]
Exemple #26
0
 def update_job(self, jobname, config):
     """
     Update a job
     :param jobname: name of job, str
     :param config: new configuration of job, xml
     :return: updated Job obj
     """
     if self.has_job(jobname):
         if isinstance(config, unicode):
             config = str(config)
         self.requester.post_xml_and_confirm_status('%s/job/%s/config.xml' %
                                                    (self.baseurl, jobname),
                                                    data=config)
         self.poll()
         if (not self.has_job(jobname)
                 and self.jobs[jobname].get_config() != config):
             raise JenkinsAPIException('Cannot update job %s' % jobname)
     else:
         raise UnknownJob(jobname)
     return self[jobname]
Exemple #27
0
    def create(self, job_name, config):
        """
        Create a job
        :param jobname: name of new job, str
        :param config: configuration of new job, xml
        :return: new Job obj
        """
        if job_name in self:
            return self[job_name]

        params = {'name': job_name}
        if isinstance(config, unicode):
            config = str(config)
        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(), data=config, params=params)
        self.jenkins.poll()
        if job_name not in self:
            raise JenkinsAPIException('Cannot create job %s' % job_name)

        return self[job_name]
Exemple #28
0
 def stream_logs(self, interval=0):
     """
     Return generator which streams parts of text console.
     """
     url = "%s/logText/progressiveText" % self.baseurl
     size = 0
     more_data = True
     while more_data:
         resp = self.job.jenkins.requester.get_url(url,
                                                   params={'start': size})
         content = resp.content
         if content:
             if isinstance(content, str):
                 yield content
             elif isinstance(content, bytes):
                 yield content.decode('ISO-8859-1')
             else:
                 raise JenkinsAPIException(
                     'Unknown content type for console')
         size = resp.headers['X-Text-Size']
         more_data = resp.headers.get('X-More-Data')
         sleep(interval)
Exemple #29
0
    def run_groovy_script(self, script):
        """
        Runs the requested groovy script on the Jenkins server returning the
        result as text.
        Raises a JenkinsAPIException if the returned HTTP response code from
        the POST request is not 200 OK.

        Example:

            server = Jenkins(...)
            script = 'println "Hello world!"'
            result = server.run_groovy_script(script)
            print(result) # will print "Hello world!"
        """
        url = "%s/scriptText" % self.baseurl
        data = urlencode({'script': script})

        response = self.requester.post_and_confirm_status(url, data=data)
        if response.status_code != 200:
            raise JenkinsAPIException('Unexpected response %d.' % response.status_code)

        return response.text
Exemple #30
0
    def create(self, job_name, config):
        """
        Create a job

        :param str job_name: Name of new job
        :param str config: XML configuration of new job
        :returns Job: new Job object
        """
        if job_name in self:
            return self[job_name]

        if config is None or len(config) == 0:
            raise JenkinsAPIException('Job XML config cannot be empty')

        params = {'name': job_name}
        try:
            if isinstance(
                    config, unicode):  # pylint: disable=undefined-variable
                config = str(config)
        except NameError:
            # Python2 already a str
            pass

        self.jenkins.requester.post_xml_and_confirm_status(
            self.jenkins.get_create_url(),
            data=config,
            params=params
        )
        # Call above would fail if Jenkins is unhappy
        # If Jenkins is happy - we don't poll it, but insert job into
        # internal cache
        self._data.append({
            'name': job_name,
            'color': 'notbuilt'
        })

        return self[job_name]