diff --git a/README.rst b/README.rst index 1794450..dc35a92 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,7 @@ the things you can use it for: * Get information on nodes * Create/delete/reconfig views * Put server in shutdown mode (quiet down) +* List running builds * and many more.. To install:: diff --git a/jenkins/__init__.py b/jenkins/__init__.py index f110b9d..7e67040 100644 --- a/jenkins/__init__.py +++ b/jenkins/__init__.py @@ -57,7 +57,7 @@ import six from six.moves.http_client import BadStatusLine from six.moves.urllib.error import HTTPError from six.moves.urllib.error import URLError -from six.moves.urllib.parse import quote, urlencode, urljoin +from six.moves.urllib.parse import quote, urlencode, urljoin, urlparse from six.moves.urllib.request import Request, urlopen if sys.version_info < (2, 7, 0): @@ -726,6 +726,46 @@ class Jenkins(object): self.jenkins_open(Request( self._build_url(STOP_BUILD, locals()), b'')) + def get_running_builds(self): + '''Return list of running builds. + + Each build is a dict with keys 'name', 'number', 'url', 'node', + and 'executor'. + + :returns: List of builds, + ``[ { str: str, str: int, str:str, str: str, str: int} ]`` + + Example:: + >>> builds = server.get_running_builds() + >>> print(builds) + [{'node': 'foo-slave', 'url': 'https://localhost/job/test/15/', + 'executor': 0, 'name': 'test', 'number': 15}] + ''' + builds = [] + nodes = self.get_nodes() + for node in nodes: + # the name returned is not the name to lookup when + # dealing with master :/ + if node['name'] == 'master': + node_name = '(master)' + else: + node_name = node['name'] + info = self.get_node_info(node_name, depth=2) + for executor in info['executors']: + executable = executor['currentExecutable'] + if executable: + executor_number = executor['number'] + build_number = executable['number'] + url = executable['url'] + m = re.match(r'/job/([^/]+)/.*', urlparse(url).path) + job_name = m.group(1) + builds.append({'name': job_name, + 'number': build_number, + 'url': url, + 'node': node_name, + 'executor': executor_number}) + return builds + def get_nodes(self): '''Get a list of nodes connected to the Master diff --git a/tests/test_build.py b/tests/test_build.py index 75ed0e2..0e76e2f 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -127,3 +127,186 @@ class JenkinsStopBuildTest(JenkinsTestBase): jenkins_mock.call_args[0][0].get_full_url(), u'http://example.com/job/Test%20Job/52/stop') self._check_requests(jenkins_mock.call_args_list) + + +class JenkinsListRunningBuildsTest(JenkinsTestBase): + @patch.object(jenkins.Jenkins, 'get_node_info') + @patch.object(jenkins.Jenkins, 'get_nodes') + def test_with_builds_master(self, nodes_mock, node_info_mock): + nodes_to_return = [{ + 'name': "master", 'offline': False + }] + nodes_mock.return_value = nodes_to_return + build = { + "actions": [ + { + "parameters": [ + { + "name": "FOO", + "value": "foo" + }, + { + "name": "BAR", + "value": "bar" + } + ] + }, + { + "causes": [ + { + "shortDescription": "Started by user foo", + "userId": "foo", + "userName": "Foo Bar" + } + ] + } + ], + "artifacts": [], + "building": True, + "description": None, + "duration": 0, + "estimatedDuration": 20148, + "executor": {}, + "fullDisplayName": "test #1", + "id": "2015-09-14_20-25-42", + "keepLog": False, + "number": 1, + "result": None, + "timestamp": 1442262342729, + "url": "https://localhost/job/test/1/", + "builtOn": "", + "changeSet": { + "items": [], + "kind": None + }, + "culprits": [] + } + node_info_to_return = { + "executors": [ + { + "currentExecutable": None, + "currentWorkUnit": None, + "idle": True, + "likelyStuck": False, + "number": 0, + "progress": -1 + }, + { + "currentExecutable": build, + "currentWorkUnit": {}, + "idle": False, + "likelyStuck": False, + "number": 1, + "progress": 14 + } + ], + } + node_info_mock.return_value = node_info_to_return + builds = self.j.get_running_builds() + self.assertEqual([{'name': 'test', + 'number': 1, + 'node': '(master)', + 'executor': 1, + 'url': 'https://localhost/job/test/1/'}], builds) + + @patch.object(jenkins.Jenkins, 'get_node_info') + @patch.object(jenkins.Jenkins, 'get_nodes') + def test_with_builds_non_master(self, nodes_mock, node_info_mock): + nodes_to_return = [{ + 'name': "foo-slave", 'offline': False + }] + nodes_mock.return_value = nodes_to_return + build = { + "actions": [ + { + "parameters": [ + { + "name": "FOO", + "value": "foo" + }, + { + "name": "BAR", + "value": "bar" + } + ] + }, + { + "causes": [ + { + "shortDescription": "Started by user foo", + "userId": "foo", + "userName": "Foo Bar" + } + ] + } + ], + "artifacts": [], + "building": True, + "description": None, + "duration": 0, + "estimatedDuration": 20148, + "executor": {}, + "fullDisplayName": "test #1", + "id": "2015-09-14_20-25-42", + "keepLog": False, + "number": 15, + "result": None, + "timestamp": 1442262342729, + "url": "https://localhost/job/test/15/", + "builtOn": "", + "changeSet": { + "items": [], + "kind": None + }, + "culprits": [] + } + node_info_to_return = { + "executors": [ + { + "currentExecutable": None, + "currentWorkUnit": None, + "idle": True, + "likelyStuck": False, + "number": 1, + "progress": -1 + }, + { + "currentExecutable": build, + "currentWorkUnit": {}, + "idle": False, + "likelyStuck": False, + "number": 0, + "progress": 14 + } + ], + } + node_info_mock.return_value = node_info_to_return + builds = self.j.get_running_builds() + self.assertEqual([{'name': 'test', + 'number': 15, + 'node': 'foo-slave', + 'executor': 0, + 'url': 'https://localhost/job/test/15/'}], builds) + + @patch.object(jenkins.Jenkins, 'get_node_info') + @patch.object(jenkins.Jenkins, 'get_nodes') + def test_with_no_builds(self, nodes_mock, node_info_mock): + nodes_to_return = [{ + 'name': "master", 'offline': False + }] + nodes_mock.return_value = nodes_to_return + node_info_to_return = { + "executors": [ + { + "currentExecutable": None, + "currentWorkUnit": None, + "idle": True, + "likelyStuck": False, + "number": 0, + "progress": -1 + } + ] + } + node_info_mock.return_value = node_info_to_return + builds = self.j.get_running_builds() + self.assertEqual([], builds)