Add Jenkins to LF Tools 66/4866/7
authorTrevor Bramwell <tbramwell@linuxfoundation.org>
Fri, 12 May 2017 02:17:00 +0000 (19:17 -0700)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Tue, 6 Jun 2017 16:40:23 +0000 (12:40 -0400)
Initial supported commands:

- jenkins builds running
- jenkins builds queued
- jenkins nodes list
- jenkins quiet-down
- jenkins plugins list
- jenkins plugins active
- jenkins plugins enabled
- jenkins plugins disabled
- jenkins plugins pinned
- jenkins plugins needs-update

Change-Id: I6f5e2601ed4eae76f3fd58422e8f7ea128f6e9f6
Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
lftools/cli/__init__.py
lftools/cli/jenkins/__init__.py [new file with mode: 0644]
lftools/cli/jenkins/builds.py [new file with mode: 0644]
lftools/cli/jenkins/nodes.py [new file with mode: 0644]
lftools/cli/jenkins/plugins.py [new file with mode: 0644]
requirements.txt

index d1b56cd..29cc48d 100644 (file)
@@ -14,6 +14,7 @@ __author__ = 'Thanh Ha'
 
 import click
 from lftools.cli.deploy import deploy
+from lftools.cli.jenkins import jenkins_cli
 from lftools.cli.nexus import nexus
 from lftools.cli.version import version
 from lftools.openstack.cmd import openstack
@@ -30,6 +31,7 @@ def cli(ctx):
 cli.add_command(deploy)
 cli.add_command(openstack)
 cli.add_command(nexus)
+cli.add_command(jenkins_cli, name='jenkins')
 cli.add_command(version)
 
 
diff --git a/lftools/cli/jenkins/__init__.py b/lftools/cli/jenkins/__init__.py
new file mode 100644 (file)
index 0000000..5ed460d
--- /dev/null
@@ -0,0 +1,61 @@
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Jenkins information."""
+
+__author__ = 'Trevor Bramwell'
+
+
+import click
+import jenkins as jenkins_python  # Don't confuse this with the function ...
+from lftools.cli.jenkins.builds import builds
+from lftools.cli.jenkins.nodes import nodes
+from lftools.cli.jenkins.plugins import plugins_init
+
+from six.moves.urllib.error import HTTPError
+
+
+@click.group()
+@click.option('-s', '--server', type=str, required=True, envvar='JENKINS_URL')
+@click.option('-u', '--user', type=str, required=True, envvar='JENKINS_USER')
+@click.option('-p', '--password', type=str, required=True,
+              envvar='JENKINS_PASSWORD')
+@click.pass_context
+def jenkins_cli(ctx, server, user, password):
+    """Query information about the Jenkins Server."""
+    # Initial the Jenkins object and pass it to sub-commands
+    ctx.obj['server'] = jenkins_python.Jenkins(
+        server,
+        username=user,
+        password=password)
+
+
+@click.command()
+@click.option("-n/-y", is_flag=True, prompt="Quiet down Jenkins?", required=True)
+@click.pass_context
+def quiet_down(ctx, n):
+    """Put Jenkins into 'Quiet Down' mode."""
+    version = ctx.obj['server'].get_version()
+    # Ask permission first
+    if n:
+        try:
+            ctx.obj['server'].quiet_down()
+        except HTTPError as m:
+            if m.code == 405:
+                print("\n[%s]\nJenkins %s does not support Quiet Down "
+                      "without a CSRF Token. (CVE-2017-04-26)\nPlease "
+                      "file a bug with 'python-jenkins'" % (m, version))
+            else:
+                raise m
+
+
+jenkins_cli.add_command(plugins_init, name='plugins')
+jenkins_cli.add_command(nodes)
+jenkins_cli.add_command(builds)
+jenkins_cli.add_command(quiet_down, name='quiet-down')
diff --git a/lftools/cli/jenkins/builds.py b/lftools/cli/jenkins/builds.py
new file mode 100644 (file)
index 0000000..2b52dd6
--- /dev/null
@@ -0,0 +1,51 @@
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Jenkins build information."""
+
+__author__ = 'Trevor Bramwell'
+
+import click
+
+
+@click.group()
+@click.pass_context
+def builds(ctx):
+    """Information regarding current builds and the queue."""
+    pass
+
+
+@click.command()
+@click.pass_context
+def running(ctx):
+    """Show all the currently running builds."""
+    running_builds = ctx.obj['server'].get_running_builds()
+
+    for build in running_builds:
+        print("- %s on %s" % (build['name'], build['node']))
+
+
+@click.command()
+@click.pass_context
+def queued(ctx):
+    """Show all jobs waiting in the queue and their status."""
+    queue = ctx.obj['server'].get_queue_info()
+
+    queue_length = len(queue)
+    print("Build Queue (%s)" % queue_length)
+    for build in queue:
+        print(" - %s" % (build['task']['name'])),
+        if build['stuck']:
+            print("[Stuck]")
+        if build['blocked']:
+            print("[Blocked]")
+
+
+builds.add_command(running)
+builds.add_command(queued)
diff --git a/lftools/cli/jenkins/nodes.py b/lftools/cli/jenkins/nodes.py
new file mode 100644 (file)
index 0000000..d8526b6
--- /dev/null
@@ -0,0 +1,41 @@
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Jenkins node information."""
+
+__author__ = 'Trevor Bramwell'
+
+import click
+
+
+def offline_str(status):
+    """Convert the offline node status from a boolean to a string."""
+    if status:
+        return "Offline"
+    return "Online"
+
+
+@click.group()
+@click.pass_context
+def nodes(ctx):
+    """Find information about builders connected to Jenkins Master."""
+    ctx.obj['nodes'] = ctx.obj['server'].get_nodes()
+
+
+@click.command()
+@click.pass_context
+def list_nodes(ctx):
+    """List Jenkins nodes."""
+    node_list = ctx.obj['nodes']
+
+    for node in node_list:
+        print("%s [%s]" % (node['name'], offline_str(node['offline'])))
+
+
+nodes.add_command(list_nodes, name='list')
diff --git a/lftools/cli/jenkins/plugins.py b/lftools/cli/jenkins/plugins.py
new file mode 100644 (file)
index 0000000..078762d
--- /dev/null
@@ -0,0 +1,132 @@
+# @License EPL-1.0 <http://spdx.org/licenses/EPL-1.0>
+##############################################################################
+# Copyright (c) 2017 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Jenkins plugin information."""
+
+__author__ = 'Trevor Bramwell'
+
+import click
+
+
+def checkmark(truthy):
+    """Return a UTF-8 Checkmark or Cross depending on the truthiness of the argument."""
+    if truthy:
+        return u'\u2713'
+    return u'\u2717'
+
+
+def print_plugin(plugin, namefield='longName'):
+    """Print the plugin longName and version."""
+    print("%s:%s" % (plugin[namefield], plugin['version']))
+
+
+@click.group()
+@click.pass_context
+def plugins_init(ctx):
+    """Inspect Jenkins plugins on the server."""
+    ctx.obj['plugins'] = ctx.obj['server'].get_plugins()
+
+
+@click.command()
+@click.pass_context
+def list_plugins(ctx):
+    """List installed plugins.
+
+    Defaults to listing all installed plugins and their current versions
+    """
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def pinned(ctx):
+    """List pinned plugins."""
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if plugin['pinned']:
+            print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def dynamic(ctx):
+    """List dynamically reloadable plugins."""
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if plugin['supportsDynamicLoad'] == "YES":
+            print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def needs_update(ctx):
+    """List pending plugin updates."""
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if plugin['hasUpdate']:
+            print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def enabled(ctx):
+    """List enabled plugins."""
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if plugin['enabled']:
+            print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def disabled(ctx):
+    """List disabled plugins.
+
+    TODO: In the future this should be part of a command alias and pass a flag
+    to 'enabled' so that we don't duplicate code.
+    """
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if not plugin['enabled']:
+            print_plugin(plugin)
+
+
+@click.command()
+@click.pass_context
+def active(ctx):
+    """List active plugins."""
+    plugins = ctx.obj['plugins']
+    for key in plugins.keys():
+        _, plugin_name = key
+        plugin = plugins[plugin_name]
+        if plugin['active']:
+            print_plugin(plugin)
+
+
+plugins_init.add_command(list_plugins, name='list')
+plugins_init.add_command(pinned)
+plugins_init.add_command(dynamic)
+plugins_init.add_command(needs_update, name='needs-update')
+plugins_init.add_command(active)
+plugins_init.add_command(enabled)
+plugins_init.add_command(disabled)
index cf8491a..8de3230 100644 (file)
@@ -6,6 +6,7 @@ shade
 sphinx>=1.4.9
 sphinxcontrib-programoutput
 sphinx_bootstrap_theme>=0.4.14
+python-jenkins
 
 # workarounds to prevent upstream from breaking us
 babel<2.4.0