From: Trevor Bramwell Date: Fri, 12 May 2017 02:17:00 +0000 (-0700) Subject: Add Jenkins to LF Tools X-Git-Tag: v0.3.0~5 X-Git-Url: https://gerrit.linuxfoundation.org/infra/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F66%2F4866%2F7;p=releng%2Flftools.git Add Jenkins to LF Tools 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 --- diff --git a/lftools/cli/__init__.py b/lftools/cli/__init__.py index d1b56cdf..29cc48d0 100644 --- a/lftools/cli/__init__.py +++ b/lftools/cli/__init__.py @@ -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 index 00000000..5ed460d7 --- /dev/null +++ b/lftools/cli/jenkins/__init__.py @@ -0,0 +1,61 @@ +# @License 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 index 00000000..2b52dd65 --- /dev/null +++ b/lftools/cli/jenkins/builds.py @@ -0,0 +1,51 @@ +# @License 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 index 00000000..d8526b61 --- /dev/null +++ b/lftools/cli/jenkins/nodes.py @@ -0,0 +1,41 @@ +# @License 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 index 00000000..078762da --- /dev/null +++ b/lftools/cli/jenkins/plugins.py @@ -0,0 +1,132 @@ +# @License 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) diff --git a/requirements.txt b/requirements.txt index cf8491a6..8de32307 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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