From patchwork Thu Sep 19 17:55:40 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Terceiro X-Patchwork-Id: 20467 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-yh0-f69.google.com (mail-yh0-f69.google.com [209.85.213.69]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id BBE1423906 for ; Thu, 19 Sep 2013 17:55:43 +0000 (UTC) Received: by mail-yh0-f69.google.com with SMTP id c41sf9240942yho.0 for ; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:mime-version:to:from:subject :message-id:date:reply-to:sender:errors-to:precedence :x-original-sender:x-original-authentication-results:mailing-list :list-id:list-post:list-help:list-archive:list-unsubscribe :content-type; bh=UTP+PYTXm9jKIPvmjFC8s1tfvYZg/XyTgor0LN/g6qs=; b=a0enLja3adoDNw2wki8GIExfynVNCStlyoKA+gfW6+p80avZhDcpasWUvd/k++3ccK GfgPI0hp297yNBF2E6nOt6aR5jurcZ1huFIMSC9QiPNV0jyg3V90OK6/bXqv/4Vv539I IKY9c7A8RWcDdZiVS1Fiz38xfX8POW0GrJSrARdcrZjZuYwAlFF9AxgcySnguUQ3C5nX Bw8s37U6ExqML6CjXFFgIoCqbeYs0XZyPBhH9mJJntLLt2vAlYq1Ych3KgF6yVZbxT93 wDjHZXSngAegiSKnstnG6hGu7q/Izq/8jwOqU80ue2Uyfauegi3kpzU/UXEd9bRvfeGM CO5A== X-Gm-Message-State: ALoCoQmRp1x/S8C1+NNtOyn/WX3SQRYah64a+qKIhLiaZd5hiI1ySddsaRDe0z5888WQ2TTpgpHz X-Received: by 10.236.52.228 with SMTP id e64mr989130yhc.45.1379613343484; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.50.169 with SMTP id d9ls3954445qeo.21.gmail; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) X-Received: by 10.221.44.136 with SMTP id ug8mr2250914vcb.13.1379613343221; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) Received: from mail-ve0-f181.google.com (mail-ve0-f181.google.com [209.85.128.181]) by mx.google.com with ESMTPS id fx4si2058968vdc.117.1969.12.31.16.00.00 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 19 Sep 2013 10:55:43 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.128.181 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.128.181; Received: by mail-ve0-f181.google.com with SMTP id oy12so6980425veb.40 for ; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) X-Received: by 10.220.1.203 with SMTP id 11mr2241160vcg.15.1379613343036; Thu, 19 Sep 2013 10:55:43 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.174.196 with SMTP id u4csp291375vcz; Thu, 19 Sep 2013 10:55:42 -0700 (PDT) X-Received: by 10.229.73.6 with SMTP id o6mr6293916qcj.2.1379613342058; Thu, 19 Sep 2013 10:55:42 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id i9si1811494qce.75.1969.12.31.16.00.00 (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 19 Sep 2013 10:55:42 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1VMiS4-0002OS-Gw for ; Thu, 19 Sep 2013 17:55:40 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 6BF3FE0504 for ; Thu, 19 Sep 2013 17:55:40 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-tool X-Launchpad-Branch: ~linaro-validation/lava-tool/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 196 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-tool/trunk] Rev 196: This package is obsolete Message-Id: <20130919175540.25181.25074.launchpad@ackee.canonical.com> Date: Thu, 19 Sep 2013 17:55:40 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: list X-Generated-By: Launchpad (canonical.com); Revision="16765"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: eab349b27bfc9e2ad194b63b2ecbee3b46bcf3a7 X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: noreply@launchpad.net X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.128.181 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , ------------------------------------------------------------ revno: 196 committer: Antonio Terceiro branch nick: trunk timestamp: Thu 2013-09-19 14:47:45 -0300 message: This package is obsolete removed: COPYING HACKING INSTALL NEWS README ci-build entry_points.ini integration-tests integration-tests.d/ integration-tests.d/lava-job-new-existing.sh integration-tests.d/lava-job-new-with-config.sh integration-tests.d/lava-job-submit.sh integration-tests.d/lib/ integration-tests.d/lib/fixed_response integration-tests.d/lib/lava_config integration-tests.d/lib/server.py integration-tests.d/sample/ integration-tests.d/sample/nexus.json lava/ lava/__init__.py lava/commands.py lava/config.py lava/device/ lava/device/__init__.py lava/device/commands.py lava/device/templates.py lava/device/tests/ lava/device/tests/__init__.py lava/device/tests/test_commands.py lava/device/tests/test_device.py lava/helper/ lava/helper/__init__.py lava/helper/command.py lava/helper/dispatcher.py lava/helper/template.py lava/helper/tests/ lava/helper/tests/__init__.py lava/helper/tests/helper_test.py lava/helper/tests/test_command.py lava/helper/tests/test_dispatcher.py lava/helper/tests/test_template.py lava/job/ lava/job/__init__.py lava/job/commands.py lava/job/templates.py lava/job/tests/ lava/job/tests/__init__.py lava/job/tests/test_commands.py lava/job/tests/test_job.py lava/parameter.py lava/script/ lava/script/__init__.py lava/script/commands.py lava/script/tests/ lava/script/tests/__init__.py lava/script/tests/test_commands.py lava/script/tests/test_script.py lava/testdef/ lava/testdef/__init__.py lava/testdef/commands.py lava/testdef/templates.py lava/testdef/tests/ lava/testdef/tests/__init__.py lava/testdef/tests/test_commands.py lava/tests/ lava/tests/__init__.py lava/tests/test_commands.py lava/tests/test_config.py lava/tests/test_parameter.py lava/tool/ lava/tool/__init__.py lava/tool/command.py lava/tool/commands/ lava/tool/commands/__init__.py lava/tool/commands/help.py lava/tool/dispatcher.py lava/tool/errors.py lava/tool/main.py lava_dashboard_tool/ lava_dashboard_tool/__init__.py lava_dashboard_tool/commands.py lava_dashboard_tool/main.py lava_dashboard_tool/tests/ lava_dashboard_tool/tests/__init__.py lava_dashboard_tool/tests/test_commands.py lava_scheduler_tool/ lava_scheduler_tool/__init__.py lava_scheduler_tool/commands.py lava_tool/ lava_tool/__init__.py lava_tool/authtoken.py lava_tool/commands/ lava_tool/commands/__init__.py lava_tool/commands/auth.py lava_tool/dispatcher.py lava_tool/interface.py lava_tool/tests/ lava_tool/tests/__init__.py lava_tool/tests/test_auth_commands.py lava_tool/tests/test_authtoken.py lava_tool/tests/test_commands.py lava_tool/tests/test_utils.py lava_tool/utils.py setup.cfg setup.py added: README.obsolete.txt --- lp:lava-tool https://code.launchpad.net/~linaro-validation/lava-tool/trunk You are subscribed to branch lp:lava-tool. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-tool/trunk/+edit-subscription === removed file 'COPYING' --- COPYING 2010-11-25 12:07:53 +0000 +++ COPYING 1970-01-01 00:00:00 +0000 @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. === removed file 'HACKING' --- HACKING 2013-06-25 08:57:34 +0000 +++ HACKING 1970-01-01 00:00:00 +0000 @@ -1,17 +0,0 @@ -Tests Code Coverage -=================== - -To have a nicely HTML viewable report on tests code coverage, do as follows: - -* Install `python-coverage` (`pip install coverage` in case you use pip) -* Run the following command: - - python-coverage run -m unittest lava_tool.tests.test_suite 2>/dev/null && python-coverage html - -* The report will be saved in a directory called `lava_tool_coverage`: open -the `index.html` file in there to see the report. - -Notes: - - * To re-run the coverage report, you have to delete the `lava_tool_coverage` -directory first, otherwise `python-coverage` will fail. === removed file 'INSTALL' --- INSTALL 2011-05-04 12:00:41 +0000 +++ INSTALL 1970-01-01 00:00:00 +0000 @@ -1,22 +0,0 @@ -Installation -============ - -Installation needs to be done via setup.py. For the impatient, - -$ python setup.py develop --user - -is a least effort, minimally disruptive starting point. - -This will put a script you can invoke in ~/.local/bin/. You may need to include -that directory into your path. - -To undo this process do: - -$ python setup.py develop --user --uninstall -$ rm -f ~/.local/bin/lava-tool - -Note that for various reasons the actual program (lava-tool) is not removed by -this step. Because this program is automatically generated it is safe to remove -it manually at any time. - - === removed file 'NEWS' === removed file 'README' --- README 2013-06-03 18:06:49 +0000 +++ README 1970-01-01 00:00:00 +0000 @@ -1,54 +0,0 @@ -About -===== - -This source package contains the command-line tool for interacting -with the various services built by the Linaro (www.linaro.org) -Infrastructure Team. - -Note that this package only contains the core tool; to actually -interact with a service you'll need to install a corresponding plugin. -XXX explain where to get some plugins. - -Installation -============ - -See INSTALL - -Usage -===== - -Dealing with jobs - - $ lava job new file.json # creates file.json from a template - $ lava job submit file.json # submits file.json to a remote LAVA server - $ lava job run file.json # runs file.json on a local LAVA device - -Dealing with LAVA Test Shell Test Definitions - - $ lava testdef new file.yml # creates file.yml from a template - $ lava testdef submit file.yml # submits file.yml to a remote LAVA server - $ lava testdef run file.yml # runs file.yml on a local LAVA device - -Dealing with LAVA Test Shell Scripts - - $ lava script submit SCRIPT # submits SCRIPT to a remote LAVA server - $ lava script run SCRIPT # runs SCRIPT on a local LAVA device - -Bash completion -=============== - -Once lava-tool is installed, you can turn bash completion on for the `lava` and -`lava-tool` programs with the following commands (which you can also paste in -your ~/.bashrc): - - eval "$(register-python-argcomplete lava)" - eval "$(register-python-argcomplete lava-tool)" - -Then if you type for example "lava-tool su", it will complete that "su" -with "submit-job" for you. - -Reporting Bugs -============== - -All bugs should be reported to the launchpad project at -https://bugs.launchpad.net/lava-tool/+filebug === added file 'README.obsolete.txt' --- README.obsolete.txt 1970-01-01 00:00:00 +0000 +++ README.obsolete.txt 2013-09-19 17:47:45 +0000 @@ -0,0 +1,3 @@ +This package is obsolete. + +See http://git.linaro.org/gitweb?p=lava/lava-tool.git instead === removed file 'ci-build' --- ci-build 2013-08-07 08:54:55 +0000 +++ ci-build 1970-01-01 00:00:00 +0000 @@ -1,58 +0,0 @@ -#!/bin/sh - -VENV_DIR="/tmp/ci-build-venv" -# Directory where coverage HTML report will be written. -COVERAGE_REPORT_DIR="lava_tool_coverage" - -set -e - -if test -z "$VIRTUAL_ENV"; then - set -x - virtualenv $VENV_DIR - . $VENV_DIR/bin/activate - python setup.py develop -fi - -# requirement for integration tests -if ! pip show Flask | grep -q Flask; then - pip install 'Flask==0.9' -fi -if ! pip show PyYAML | grep -q PyYAML; then - pip install PyYAML -fi -# requirement for unit tests -if ! pip show mocker | grep -q mocker; then - pip install mocker -fi - -if ! pip show mock | grep -q mock; then - pip install mock -fi -# Requirement to run code coverage tests. -if ! pip show coverage | grep -q coverage; then - pip install coverage -fi - -if test -z "$DISPLAY"; then - # actual CI - - # will install tests dependencies automatically. The output is also more - # verbose - python setup.py test < /dev/null - - # integration-tests will pick this up and provide detailed output - export VERBOSE=1 -else - # in a development workstation, this will produce shorter/nicer output, but - # requires the test dependencies to be installed manually (or by running - # `python setup.py test` before). - python -m unittest lava_tool.tests.test_suite < /dev/null -fi - -if test -d $COVERAGE_REPORT_DIR; then - rm -rf $COVERAGE_REPORT_DIR -fi -# Runs python-coverage. -python-coverage run -m unittest lava_tool.tests.test_suite 2>/dev/null && python-coverage html - -./integration-tests === removed file 'entry_points.ini' --- entry_points.ini 2013-07-29 10:11:29 +0000 +++ entry_points.ini 1970-01-01 00:00:00 +0000 @@ -1,97 +0,0 @@ -[console_scripts] -lava-tool = lava_tool.dispatcher:main -lava = lava.tool.main:LavaDispatcher.run -lava-dashboard-tool=lava_dashboard_tool.main:main - -[lava.commands] -help = lava.tool.commands.help:help -scheduler = lava_scheduler_tool.commands:scheduler -dashboard = lava_dashboard_tool.commands:dashboard -job = lava.job.commands:job -device = lava.device.commands:device -testdef = lava.testdef.commands:testdef -init = lava.commands:init -submit = lava.commands:submit -run = lava.commands:run -status = lava.job.commands:status -update = lava.commands:update -script = lava.script.commands:script - -[lava_tool.commands] -help = lava.tool.commands.help:help -auth-add = lava_tool.commands.auth:auth_add -submit-job = lava_scheduler_tool.commands:submit_job -resubmit-job = lava_scheduler_tool.commands:resubmit_job -cancel-job = lava_scheduler_tool.commands:cancel_job -job-output = lava_scheduler_tool.commands:job_output -job-status = lava_scheduler_tool.commands:job_status -backup=lava_dashboard_tool.commands:backup -bundles=lava_dashboard_tool.commands:bundles -data_views=lava_dashboard_tool.commands:data_views -deserialize=lava_dashboard_tool.commands:deserialize -get=lava_dashboard_tool.commands:get -make_stream=lava_dashboard_tool.commands:make_stream -pull=lava_dashboard_tool.commands:pull -put=lava_dashboard_tool.commands:put -query_data_view=lava_dashboard_tool.commands:query_data_view -restore=lava_dashboard_tool.commands:restore -server_version=lava_dashboard_tool.commands:server_version -streams=lava_dashboard_tool.commands:streams -version=lava_dashboard_tool.commands:version - -[lava.scheduler.commands] -submit-job = lava_scheduler_tool.commands:submit_job -resubmit-job = lava_scheduler_tool.commands:resubmit_job -cancel-job = lava_scheduler_tool.commands:cancel_job -job-output = lava_scheduler_tool.commands:job_output -job-status = lava_scheduler_tool.commands:job_status - -[lava.dashboard.commands] -backup=lava_dashboard_tool.commands:backup -bundles=lava_dashboard_tool.commands:bundles -data_views=lava_dashboard_tool.commands:data_views -deserialize=lava_dashboard_tool.commands:deserialize -get=lava_dashboard_tool.commands:get -make_stream=lava_dashboard_tool.commands:make_stream -pull=lava_dashboard_tool.commands:pull -put=lava_dashboard_tool.commands:put -query_data_view=lava_dashboard_tool.commands:query_data_view -restore=lava_dashboard_tool.commands:restore -server_version=lava_dashboard_tool.commands:server_version -streams=lava_dashboard_tool.commands:streams -version=lava_dashboard_tool.commands:version - -[lava_dashboard_tool.commands] -backup=lava_dashboard_tool.commands:backup -bundles=lava_dashboard_tool.commands:bundles -data_views=lava_dashboard_tool.commands:data_views -deserialize=lava_dashboard_tool.commands:deserialize -get=lava_dashboard_tool.commands:get -make_stream=lava_dashboard_tool.commands:make_stream -pull=lava_dashboard_tool.commands:pull -put=lava_dashboard_tool.commands:put -query_data_view=lava_dashboard_tool.commands:query_data_view -restore=lava_dashboard_tool.commands:restore -server_version=lava_dashboard_tool.commands:server_version -streams=lava_dashboard_tool.commands:streams -version=lava_dashboard_tool.commands:version - -[lava.job.commands] -new = lava.job.commands:new -submit = lava.job.commands:submit -status = lava.job.commands:status -run = lava.job.commands:run - -[lava.device.commands] -add = lava.device.commands:add -remove = lava.device.commands:remove -config = lava.device.commands:config - -[lava.testdef.commands] -new = lava.testdef.commands:new -run = lava.testdef.commands:run -submit = lava.testdef.commands:submit - -[lava.script.commands] -run = lava.script.commands:run -submit = lava.script.commands:submit === removed file 'integration-tests' --- integration-tests 2013-06-03 19:54:23 +0000 +++ integration-tests 1970-01-01 00:00:00 +0000 @@ -1,80 +0,0 @@ -#!/bin/sh - -set -e - -green() { - test -t 1 && printf "\033[0;32;40m$@\033[m\n" || echo "$@" -} - -red() { - test -t 2 && printf "\033[0;31;40m$@\033[m\n" >&2 || echo "$2" >&2 -} - -start_server() { - server_dir="${base_tmpdir}/_server" - mkdir -p "${server_dir}" - server_log="${server_dir}/log" - python integration-tests.d/lib/server.py > "${server_log}" 2>&1 & - server_pid=$? -} - -stop_server() { - curl -q http://localhost:5000/exit -} - -run_test() { - local testfile="$1" - local logfile="$2" - rc=0 - if test -n "$VERBOSE"; then - sh -x "$testfile" < /dev/null || rc=$? - else - sh -x "$testfile" > "${logfile}" 2>&1 < /dev/null || rc=$? - fi - if test $rc -eq 0; then - green "$testname: PASS" - passed=$(($passed + 1)) - else - failed=$(($failed + 1)) - red "$testname: FAIL" - if test -f "$logfile"; then - cat "$logfile" - fi - fi -} - -passed=0 -failed=0 -base_tmpdir=$(mktemp -d) -logs="${base_tmpdir}/logs" -mkdir "$logs" - -export PATH="$(dirname $0)"/integration-tests.d/lib:$PATH - -start_server - -tests="$@" -if test -z "$tests"; then - tests=$(echo integration-tests.d/*.sh) -fi - -for testfile in $tests; do - testname=$(basename "$testfile") - logfile="${logs}/${testname}.log" - export tmpdir="${base_tmpdir}/${testname}" - export LAVACONFIG="${tmpdir}/config" - mkdir "${tmpdir}" - run_test "$testfile" "$logfile" -done - -stop_server - -rm -rf "${base_tmpdir}" - -echo -if [ "$failed" -eq 0 ]; then - green "$passed tests passed, $failed tests failed." -else - red "$passed tests passed, $failed tests failed." - exit 1 -fi === removed directory 'integration-tests.d' === removed file 'integration-tests.d/lava-job-new-existing.sh' --- integration-tests.d/lava-job-new-existing.sh 2013-05-27 21:24:06 +0000 +++ integration-tests.d/lava-job-new-existing.sh 1970-01-01 00:00:00 +0000 @@ -1,5 +0,0 @@ -touch "${tmpdir}/foo.json" -lava job new "${tmpdir}/foo.json" -rc="$?" -test "$rc" -gt 0 - === removed file 'integration-tests.d/lava-job-new-with-config.sh' --- integration-tests.d/lava-job-new-with-config.sh 2013-08-06 08:10:58 +0000 +++ integration-tests.d/lava-job-new-with-config.sh 1970-01-01 00:00:00 +0000 @@ -1,10 +0,0 @@ -cat > "${tmpdir}/config" < $tmpdir/output -grep "Job submitted with job ID 999" $tmpdir/output === removed directory 'integration-tests.d/lib' === removed file 'integration-tests.d/lib/fixed_response' --- integration-tests.d/lib/fixed_response 2013-06-03 18:06:49 +0000 +++ integration-tests.d/lib/fixed_response 1970-01-01 00:00:00 +0000 @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import xmlrpclib - -data = eval(sys.argv[1]) - -output = os.path.join(os.path.dirname(__file__), 'fixed_response.txt') -with open(output, 'w') as f: - f.write(xmlrpclib.dumps((data,), methodresponse=True)) === removed file 'integration-tests.d/lib/lava_config' --- integration-tests.d/lib/lava_config 2013-06-03 18:06:49 +0000 +++ integration-tests.d/lib/lava_config 1970-01-01 00:00:00 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh - -set -e - -cat > "$LAVACONFIG" === removed file 'integration-tests.d/lib/server.py' --- integration-tests.d/lib/server.py 2013-07-31 13:26:03 +0000 +++ integration-tests.d/lib/server.py 1970-01-01 00:00:00 +0000 @@ -1,58 +0,0 @@ -import os -import yaml - -from flask import ( - Flask, - request, -) - -app = Flask(__name__) - -aliases = { - 'ok': 200, - 'forbidden': 403, - 'notfound': 404, -} - -fixed_response = None - -@app.route('/exit') -def exit(): - # http://werkzeug.pocoo.org/docs/serving/#shutting-down-the-server - if not 'werkzeug.server.shutdown' in request.environ: - raise RuntimeError('Not running the development server') - request.environ['werkzeug.server.shutdown']() - return "" - -@app.route('//', methods=['GET', 'POST']) -def reply(status_code): - - status = int(aliases.get(status_code, status_code)) - - headers = {} - for k,v in request.headers: - headers[k] = v - - response_file = os.path.join(os.path.dirname(__file__), 'fixed_response.txt') - if os.path.exists(response_file): - response = open(response_file).read() - os.remove(response_file) - else: - response = yaml.dump( - { - 'status': status, - 'headers': headers, - 'body': request.form.keys(), - 'query': dict(request.args), - }, - encoding='utf-8', - default_flow_style=False, - ) - return response, status, { 'Content-Type': 'text/plain' } - -@app.route('/', methods=['GET','POST']) -def root(): - return reply(200) - -if __name__ == '__main__': - app.run(debug=('DEBUG' in os.environ)) === removed directory 'integration-tests.d/sample' === removed file 'integration-tests.d/sample/nexus.json' --- integration-tests.d/sample/nexus.json 2013-06-03 18:06:49 +0000 +++ integration-tests.d/sample/nexus.json 1970-01-01 00:00:00 +0000 @@ -1,15 +0,0 @@ -{ - "device_type": "nexus", - "job_name": "Boot test", - "actions": [ - { - "command": "deploy_linaro_image", - "parameters": { - "image": "http:///url/to/nexus.img" - } - }, - { - "command": "boot_linaro_image" - } - ] -} \ No newline at end of file === removed directory 'lava' === removed file 'lava/__init__.py' --- lava/__init__.py 2012-03-13 15:34:36 +0000 +++ lava/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) -# DO NOT ADD ANYTHING TO THIS FILE! -# IT MUST STAY AS IS (empty apart from the two lines above) === removed file 'lava/commands.py' --- lava/commands.py 2013-07-29 07:47:19 +0000 +++ lava/commands.py 1970-01-01 00:00:00 +0000 @@ -1,227 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Lava init commands. - -When invoking: - - `lava init [DIR]` - -the command will create a default directory and files structure as follows: - -DIR/ - | - +- JOB_FILE.json - +- tests/ - | - + mytest.sh - + lavatest.yaml - -If DIR is not passed, it will use the current working directory. -JOB_FILE is a file name that will be asked to the user, along with -other necessary information to define the tests. - -If the user manually updates either the lavatest.yaml or mytest.sh file, it is -necessary to run the following command in order to update the job definition: - - `lava update [JOB|DIR]` -""" - -import copy -import json -import os -import sys - -from lava.helper.command import BaseCommand -from lava.helper.template import ( - expand_template, - set_value -) -from lava.job import ( - JOB_FILE_EXTENSIONS, -) -from lava.job.templates import ( - LAVA_TEST_SHELL_TAR_REPO_KEY, -) -from lava.parameter import ( - Parameter, -) -from lava.testdef import ( - DEFAULT_TESTDEF_FILENAME, -) -from lava.tool.errors import CommandError -from lava_tool.utils import ( - base64_encode, - create_dir, - create_tar, - edit_file, - retrieve_file, - write_file, -) - -# Default directory structure name. -TESTS_DIR = "tests" - -# Internal parameter ids. -JOBFILE_ID = "jobfile" - -JOBFILE_PARAMETER = Parameter(JOBFILE_ID) -JOBFILE_PARAMETER.store = False - -INIT_TEMPLATE = { - JOBFILE_ID: JOBFILE_PARAMETER, -} - - -class init(BaseCommand): - """Set-ups the base directory structure.""" - - @classmethod - def register_arguments(cls, parser): - super(init, cls).register_arguments(parser) - parser.add_argument("DIR", - help=("The name of the directory to initialize. " - "Defaults to current working directory."), - nargs="?", - default=os.getcwd()) - - def invoke(self): - full_path = os.path.abspath(self.args.DIR) - - if os.path.isfile(full_path): - raise CommandError("'{0}' already exists, and is a " - "file.".format(self.args.DIR)) - - create_dir(full_path) - data = self._update_data() - - # Create the directory that will contain the test definition and - # shell script. - test_path = create_dir(full_path, TESTS_DIR) - shell_script = self.create_shell_script(test_path) - # Let the user modify the file. - edit_file(shell_script) - - testdef_file = self.create_test_definition( - os.path.join(test_path, DEFAULT_TESTDEF_FILENAME)) - - job = data[JOBFILE_ID] - self.create_tar_repo_job( - os.path.join(full_path, job), testdef_file, test_path) - - def _update_data(self): - """Updates the template and ask values to the user. - - The template in this case is a layout of the directory structure as it - would be written to disk. - - :return A dictionary containing all the necessary file names to create. - """ - data = copy.deepcopy(INIT_TEMPLATE) - expand_template(data, self.config) - - return data - - -class run(BaseCommand): - """Runs a job on the local dispatcher.""" - - @classmethod - def register_arguments(cls, parser): - super(run, cls).register_arguments(parser) - parser.add_argument("JOB", - help=("The job file to run, or a directory " - "containing a job file. If nothing is " - "passed, it uses the current working " - "directory."), - nargs="?", - default=os.getcwd()) - - def invoke(self): - full_path = os.path.abspath(self.args.JOB) - job_file = retrieve_file(full_path, JOB_FILE_EXTENSIONS) - - super(run, self).run(job_file) - - -class submit(BaseCommand): - """Submits a job to LAVA.""" - - @classmethod - def register_arguments(cls, parser): - super(submit, cls).register_arguments(parser) - parser.add_argument("JOB", - help=("The job file to send, or a directory " - "containing a job file. If nothing is " - "passed, it uses the current working " - "directory."), - nargs="?", - default=os.getcwd()) - - def invoke(self): - full_path = os.path.abspath(self.args.JOB) - job_file = retrieve_file(full_path, JOB_FILE_EXTENSIONS) - - super(submit, self).submit(job_file) - - -class update(BaseCommand): - """Updates a job file with the correct data.""" - - @classmethod - def register_arguments(cls, parser): - super(update, cls).register_arguments(parser) - parser.add_argument("JOB", - help=("Automatically updates a job file " - "definition. If nothing is passed, it uses" - "the current working directory."), - nargs="?", - default=os.getcwd()) - - def invoke(self): - full_path = os.path.abspath(self.args.JOB) - job_file = self.retrieve_file(full_path, JOB_FILE_EXTENSIONS) - job_dir = os.path.dirname(job_file) - tests_dir = os.path.join(job_dir, TESTS_DIR) - - if os.path.isdir(tests_dir): - tar_repo = None - try: - tar_repo = create_tar(tests_dir) - encoded_tests = base64_encode(tar_repo) - - json_data = None - with open(job_file, "r") as json_file: - try: - json_data = json.load(json_file) - set_value(json_data, LAVA_TEST_SHELL_TAR_REPO_KEY, - encoded_tests) - except Exception: - raise CommandError("Cannot read job file " - "'{0}'.".format(job_file)) - - content = json.dumps(json_data, indent=4) - write_file(job_file, content) - - print >> sys.stdout, "Job definition updated." - finally: - if tar_repo and os.path.isfile(tar_repo): - os.unlink(tar_repo) - else: - raise CommandError("Cannot find tests directory.") === removed file 'lava/config.py' --- lava/config.py 2013-07-31 12:27:59 +0000 +++ lava/config.py 1970-01-01 00:00:00 +0000 @@ -1,294 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Config class. -""" - -import atexit -import os -import readline -import xdg.BaseDirectory as xdgBaseDir - -from ConfigParser import ( - ConfigParser, - NoOptionError, - NoSectionError, -) - -from lava.parameter import Parameter -from lava.tool.errors import CommandError - -__all__ = ['Config', 'InteractiveCache', 'InteractiveConfig'] - -# Store for function calls to be made at exit time. -AT_EXIT_CALLS = set() -# Config default section. -DEFAULT_SECTION = "DEFAULT" -# This is the default base name used to create XDG resources. -DEFAULT_XDG_RESOURCE = "linaro" -# This is the default name for lava-tool resources. -DEFAULT_LAVA_TOOL_RESOURCE = "lava-tool" - -HISTORY = os.path.join(os.path.expanduser("~"), ".lava_history") -try: - readline.read_history_file(HISTORY) -except IOError: - pass -atexit.register(readline.write_history_file, HISTORY) - - -def _run_at_exit(): - """Runs all the function at exit.""" - for call in list(AT_EXIT_CALLS): - call() -atexit.register(_run_at_exit) - - -class Config(object): - """A generic config object.""" - - def __init__(self): - # The cache where to store parameters. - self._cache = {} - self._config_file = None - self._config_backend = None - AT_EXIT_CALLS.add(self.save) - - @property - def config_file(self): - if self._config_file is None: - self._config_file = (os.environ.get('LAVACONFIG') or - os.path.join(self._ensure_xdg_dirs(), - 'lava-tool.ini')) - return self._config_file - - @config_file.setter - def config_file(self, value): - self._config_file = value - - @property - def config_backend(self): - if self._config_backend is None: - self._config_backend = ConfigParser() - self._config_backend.read([self.config_file]) - return self._config_backend - - def _ensure_xdg_dirs(self): - """Make sure we have the default resource. - - :return The path to the XDG resource. - """ - return xdgBaseDir.save_config_path(DEFAULT_XDG_RESOURCE, - DEFAULT_LAVA_TOOL_RESOURCE) - - def _calculate_config_section(self, parameter): - """Calculates the config section of the specified parameter. - - :param parameter: The parameter to calculate the section of. - :type Parameter - :return The config section. - """ - section = DEFAULT_SECTION - if parameter.depends: - section = "{0}={1}".format(parameter.depends.id, - self.get(parameter.depends)) - return section - - def get(self, parameter, section=None): - """Retrieves a Parameter value. - - The value is taken either from the Parameter itself, or from the cache, - or from the config file. - - :param parameter: The parameter to search. - :type Parameter - :return The parameter value, or None if it is not found. - """ - if not section: - section = self._calculate_config_section(parameter) - # Try to get the parameter value first if it has one. - if parameter.value is not None: - value = parameter.value - else: - value = self._get_from_cache(parameter, section) - - if value is None: - value = self._get_from_backend(parameter, section) - return value - - def get_from_backend(self, parameter, section=None): - """Gets a configuration parameter directly from the config file.""" - if not section: - section = self._calculate_config_section(parameter) - return self._get_from_backend(parameter, section) - - def _get_from_backend(self, parameter, section): - """Gets the parameter value from the config backend. - - :param parameter: The Parameter to look up. - :param section: The section in the Config. - """ - value = None - try: - value = self.config_backend.get(section, parameter.id) - except (NoOptionError, NoSectionError): - # Ignore, we return None. - pass - return value - - def _get_from_cache(self, parameter, section): - """Looks for the specified parameter in the internal cache. - - :param parameter: The parameter to search. - :type Parameter - :return The parameter value, of None if it is not found. - """ - value = None - if section in self._cache.keys(): - if parameter.id in self._cache[section].keys(): - value = self._cache[section][parameter.id] - return value - - def _put_in_cache(self, key, value, section=DEFAULT_SECTION): - """Insert the passed parameter in the internal cache. - - :param parameter: The parameter to insert. - :type Parameter - :param section: The name of the section in the config file. - :type str - """ - if section not in self._cache.keys(): - self._cache[section] = {} - self._cache[section][key] = value - - def put(self, key, value, section=DEFAULT_SECTION): - """Adds a parameter to the config file. - - :param key: The key to add. - :param value: The value to add. - :param section: The name of the section as in the config file. - """ - if (not self.config_backend.has_section(section) and - section != DEFAULT_SECTION): - self.config_backend.add_section(section) - - # This is done to serialize a list when ConfigParser is written to - # file. Since there is no real support for list in ConfigParser, we - # serialized it in a common way that can get easily deserialized. - if isinstance(value, list): - value = Parameter.serialize(value) - - self.config_backend.set(section, key, value) - # Store in the cache too. - self._put_in_cache(key, value, section) - - def put_parameter(self, parameter, value=None, section=None): - """Adds a Parameter to the config file and cache. - - :param Parameter: The parameter to add. - :type Parameter - :param value: The value of the parameter. Defaults to None. - :param section: The section where this parameter should be stored. - Defaults to None. - """ - if not section: - section = self._calculate_config_section(parameter) - - if value is None and parameter.value is not None: - value = parameter.value - elif value is None: - raise CommandError("No value assigned to '{0}'.".format( - parameter.id)) - self.put(parameter.id, value, section) - - def save(self): - """Saves the config to file.""" - # Since we lazy load the config_backend property, this check is needed - # when a user enters a wrong command or it will overwrite the 'config' - # file with empty contents. - if self._config_backend: - with open(self.config_file, "w") as write_file: - self.config_backend.write(write_file) - - -class InteractiveConfig(Config): - """An interactive config. - - If a value is not found in the config file, it will ask it and then stores - it. - """ - def __init__(self, force_interactive=True): - super(InteractiveConfig, self).__init__() - self._force_interactive = force_interactive - - @property - def force_interactive(self): - return self._force_interactive - - @force_interactive.setter - def force_interactive(self, value): - self._force_interactive = value - - def get(self, parameter, section=None): - """Overrides the parent one. - - The only difference with the parent one, is that it will ask to type - a parameter value in case it is not found. - """ - if not section: - section = self._calculate_config_section(parameter) - value = super(InteractiveConfig, self).get(parameter, section) - - if value is None or self.force_interactive: - value = parameter.prompt(old_value=value) - - if value is not None and parameter.store: - self.put(parameter.id, value, section) - return value - - -class InteractiveCache(InteractiveConfig): - - """An interactive cache where parameters that can change are stored. - - This class is basically the same as the Confing and InteractiveConfig ones, - only the base directory where the cache file is stored is different. - - In this case it will use the $XDG_CACHE_HOME value as defined in XDG. - """ - - @property - def config_file(self): - if self._config_file is None: - self._config_file = (os.environ.get('LAVACACHE') or - os.path.join(self._ensure_xdg_dirs(), - 'parameters.ini')) - return self._config_file - - @config_file.setter - def config_file(self, value): - self._config_file = value - - def _ensure_xdg_dirs(self): - """Make sure we have the default resource. - - :return The path to the XDG resource. - """ - return xdgBaseDir.save_cache_path(DEFAULT_XDG_RESOURCE, - DEFAULT_LAVA_TOOL_RESOURCE) === removed directory 'lava/device' === removed file 'lava/device/__init__.py' --- lava/device/__init__.py 2013-07-26 08:10:16 +0000 +++ lava/device/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,97 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Device class.""" - -import re - -from copy import deepcopy - -from lava.device.templates import ( - DEFAULT_TEMPLATE, - HOSTNAME_PARAMETER, - KNOWN_TEMPLATES, -) -from lava.helper.template import expand_template - - -def __re_compile(name): - """Creates a generic regex for the specified device name. - - :param name: The name of the device. - :return A Pattern object. - """ - return re.compile('^.*{0}.*'.format(name), re.I) - - -# Dictionary of know devices. -# Keys are the general device name taken from lava.device.templates, values -# are tuples of: a regex matcher to match the device, and the device associated -# template. -KNOWN_DEVICES = dict([(device, (__re_compile(device), template)) - for device, template in KNOWN_TEMPLATES.iteritems()]) - - -class Device(object): - - """A generic device.""" - - def __init__(self, data, hostname=None): - self.data = deepcopy(data) - self.hostname = hostname - - def write(self, conf_file): - """Writes the object to file. - - :param conf_file: The full path of the file where to write.""" - with open(conf_file, 'w') as write_file: - write_file.write(str(self)) - - def update(self, config): - """Updates the Device object values based on the provided config. - - :param config: A Config instance. - """ - # We should always have a hostname, since it defaults to the name - # given on the command line for the config file. - if self.hostname is not None: - # We do not ask the user again this parameter. - self.data[HOSTNAME_PARAMETER.id].value = self.hostname - self.data[HOSTNAME_PARAMETER.id].asked = True - - expand_template(self.data, config) - - def __str__(self): - string_list = [] - for key, value in self.data.iteritems(): - string_list.append("{0} = {1}\n".format(str(key), str(value))) - return "".join(string_list) - - -def get_known_device(name): - """Tries to match a device name with a known device type. - - :param name: The name of the device we want matched to a real device. - :return A Device instance. - """ - instance = Device(DEFAULT_TEMPLATE, hostname=name) - for _, (matcher, dev_template) in KNOWN_DEVICES.iteritems(): - if matcher.match(name): - instance = Device(dev_template, hostname=name) - break - return instance === removed file 'lava/device/commands.py' --- lava/device/commands.py 2013-07-25 13:49:44 +0000 +++ lava/device/commands.py 1970-01-01 00:00:00 +0000 @@ -1,122 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Device specific commands class. -""" - -import os -import sys - -from lava.device import get_known_device -from lava.helper.command import ( - BaseCommand, -) -from lava.helper.dispatcher import ( - get_device_file, - get_devices_path, -) -from lava.tool.command import CommandGroup -from lava.tool.errors import CommandError -from lava_tool.utils import ( - can_edit_file, - edit_file, -) - -DEVICE_FILE_SUFFIX = "conf" - - -class device(CommandGroup): - """LAVA devices handling.""" - - namespace = "lava.device.commands" - - -class add(BaseCommand): - """Adds a new device.""" - - @classmethod - def register_arguments(cls, parser): - super(add, cls).register_arguments(parser) - parser.add_argument("DEVICE", help="The name of the device to add.") - - def invoke(self): - real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX]) - - if get_device_file(real_file_name) is not None: - print >> sys.stdout, ("A device configuration file named '{0}' " - "already exists.".format(real_file_name)) - print >> sys.stdout, ("Use 'lava device config {0}' to edit " - "it.".format(self.args.DEVICE)) - sys.exit(-1) - - devices_path = get_devices_path() - device_conf_file = os.path.abspath(os.path.join(devices_path, - real_file_name)) - - device = get_known_device(self.args.DEVICE) - device.update(self.config) - device.write(device_conf_file) - - print >> sys.stdout, ("Created device file '{0}' in: {1}".format( - real_file_name, devices_path)) - edit_file(device_conf_file) - - -class remove(BaseCommand): - """Removes the specified device.""" - - @classmethod - def register_arguments(cls, parser): - super(remove, cls).register_arguments(parser) - parser.add_argument("DEVICE", - help="The name of the device to remove.") - - def invoke(self): - real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX]) - device_conf = get_device_file(real_file_name) - - if device_conf: - try: - os.remove(device_conf) - print >> sys.stdout, ("Device configuration file '{0}' " - "removed.".format(real_file_name)) - except OSError: - raise CommandError("Cannot remove file '{0}' at: {1}.".format( - real_file_name, os.path.dirname(device_conf))) - else: - print >> sys.stdout, ("No device configuration file '{0}' " - "found.".format(real_file_name)) - - -class config(BaseCommand): - """Opens the specified device config file.""" - @classmethod - def register_arguments(cls, parser): - super(config, cls).register_arguments(parser) - parser.add_argument("DEVICE", - help="The name of the device to edit.") - - def invoke(self): - real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX]) - device_conf = get_device_file(real_file_name) - - if device_conf and can_edit_file(device_conf): - edit_file(device_conf) - else: - raise CommandError("Cannot edit file '{0}'".format(real_file_name)) === removed file 'lava/device/templates.py' --- lava/device/templates.py 2013-07-26 08:14:22 +0000 +++ lava/device/templates.py 1970-01-01 00:00:00 +0000 @@ -1,82 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -This is just a place where to store a template like dictionary that -will be used to serialize a Device object. -""" - -from copy import copy - -from lava.parameter import Parameter - -# The hostname parameter is always in the DEFAULT config section. -HOSTNAME_PARAMETER = Parameter("hostname") -DEVICE_TYPE_PARAMETER = Parameter("device_type", depends=HOSTNAME_PARAMETER) -CONNECTION_COMMAND_PARMAETER = Parameter("connection_command", - depends=DEVICE_TYPE_PARAMETER) - -DEFAULT_TEMPLATE = { - 'hostname': HOSTNAME_PARAMETER, - 'device_type': DEVICE_TYPE_PARAMETER, - 'connection_command': CONNECTION_COMMAND_PARMAETER, -} - -# Specialized copies of the parameters. -# We need this or we might end up asking the user twice the same parameter due -# to different object references when one Parameter depends on a "specialized" -# one, different from the defaults. -PANDA_DEVICE_TYPE = copy(DEVICE_TYPE_PARAMETER) -PANDA_DEVICE_TYPE.value = "panda" -PANDA_DEVICE_TYPE.asked = True - -PANDA_CONNECTION_COMMAND = copy(CONNECTION_COMMAND_PARMAETER) -PANDA_CONNECTION_COMMAND.depends = PANDA_DEVICE_TYPE - -VEXPRESS_DEVICE_TYPE = copy(DEVICE_TYPE_PARAMETER) -VEXPRESS_DEVICE_TYPE.value = "vexpress" -VEXPRESS_DEVICE_TYPE.asked = True - -VEXPRESS_CONNECTION_COMMAND = copy(CONNECTION_COMMAND_PARMAETER) -VEXPRESS_CONNECTION_COMMAND.depends = VEXPRESS_DEVICE_TYPE - -QEMU_DEVICE_TYPE = copy(DEVICE_TYPE_PARAMETER) -QEMU_DEVICE_TYPE.value = "qemu" -QEMU_DEVICE_TYPE.asked = True - -QEMU_CONNECTION_COMMAND = copy(CONNECTION_COMMAND_PARMAETER) -QEMU_CONNECTION_COMMAND.depends = QEMU_DEVICE_TYPE - -# Dictionary with templates of known devices. -KNOWN_TEMPLATES = { - 'panda': { - 'hostname': HOSTNAME_PARAMETER, - 'device_type': PANDA_DEVICE_TYPE, - 'connection_command': PANDA_CONNECTION_COMMAND, - }, - 'vexpress': { - 'hostname': HOSTNAME_PARAMETER, - 'device_type': VEXPRESS_DEVICE_TYPE, - 'connection_command': VEXPRESS_CONNECTION_COMMAND, - }, - 'qemu': { - 'hostname': HOSTNAME_PARAMETER, - 'device_type': QEMU_DEVICE_TYPE, - 'connection_command': QEMU_CONNECTION_COMMAND, - } -} === removed directory 'lava/device/tests' === removed file 'lava/device/tests/__init__.py' === removed file 'lava/device/tests/test_commands.py' --- lava/device/tests/test_commands.py 2013-07-25 13:49:44 +0000 +++ lava/device/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,182 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.device.commands unit tests. -""" - -import os - -from mock import ( - MagicMock, - call, - patch, -) - -from lava.device.commands import ( - add, - config, - remove, -) -from lava.helper.tests.helper_test import HelperTest -from lava.tool.errors import CommandError - - -class AddCommandTest(HelperTest): - - def test_register_argument(self): - # Make sure that the parser add_argument is called and we have the - # correct argument. - add_command = add(self.parser, self.args) - add_command.register_arguments(self.parser) - name, args, kwargs = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - name, args, kwargs = self.parser.method_calls[1] - self.assertIn("DEVICE", args) - - @patch("lava.device.commands.edit_file", create=True) - @patch("lava.device.Device.__str__") - @patch("lava.device.Device.update") - @patch("lava.device.commands.get_device_file") - @patch("lava.device.commands.get_devices_path") - def test_add_invoke_0(self, mocked_get_devices_path, - mocked_get_device_file, mocked_update, mocked_str, - mocked_edit_file): - # Tests invocation of the add command. Verifies that the conf file is - # written to disk. - mocked_get_devices_path.return_value = self.temp_dir - mocked_get_device_file.return_value = None - mocked_str.return_value = "" - - add_command = add(self.parser, self.args) - add_command.invoke() - - expected_path = os.path.join(self.temp_dir, - ".".join([self.device, "conf"])) - self.assertTrue(os.path.isfile(expected_path)) - - @patch("lava.device.commands.edit_file", create=True) - @patch("lava.device.commands.get_known_device") - @patch("lava.device.commands.get_devices_path") - @patch("lava.device.commands.sys.exit") - @patch("lava.device.commands.get_device_file") - def test_add_invoke_1(self, mocked_get_device_file, mocked_sys_exit, - mocked_get_devices_path, mocked_get_known_device, - mocked_edit_file): - mocked_get_devices_path.return_value = self.temp_dir - mocked_get_device_file.return_value = self.temp_file.name - - add_command = add(self.parser, self.args) - add_command.invoke() - - self.assertTrue(mocked_sys_exit.called) - - -class RemoveCommandTests(HelperTest): - - def test_register_argument(self): - # Make sure that the parser add_argument is called and we have the - # correct argument. - command = remove(self.parser, self.args) - command.register_arguments(self.parser) - name, args, kwargs = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - name, args, kwargs = self.parser.method_calls[1] - self.assertIn("DEVICE", args) - - @patch("lava.device.commands.edit_file", create=True) - @patch("lava.device.Device.__str__", return_value="") - @patch("lava.device.Device.update") - @patch("lava.device.commands.get_device_file") - @patch("lava.device.commands.get_devices_path") - def test_remove_invoke(self, get_devices_path_mock, get_device_file_mock, - mocked_update, mocked_str, mocked_edit_file): - # Tests invocation of the remove command. Verifies that the conf file - # has been correctly removed. - # First we add a new conf file, then we remove it. - get_device_file_mock.return_value = None - get_devices_path_mock.return_value = self.temp_dir - - add_command = add(self.parser, self.args) - add_command.invoke() - - expected_path = os.path.join(self.temp_dir, - ".".join([self.device, "conf"])) - - # Set new values for the mocked function. - get_device_file_mock.return_value = expected_path - - remove_command = remove(self.parser, self.args) - remove_command.invoke() - - self.assertFalse(os.path.isfile(expected_path)) - - @patch("lava.device.commands.get_device_file", - new=MagicMock(return_value="/root")) - def test_remove_invoke_raises(self): - # Tests invocation of the remove command, with a non existent device - # configuration file. - remove_command = remove(self.parser, self.args) - self.assertRaises(CommandError, remove_command.invoke) - - -class ConfigCommanTests(HelperTest): - - def test_register_argument(self): - # Make sure that the parser add_argument is called and we have the - # correct argument. - command = config(self.parser, self.args) - command.register_arguments(self.parser) - name, args, kwargs = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - name, args, kwargs = self.parser.method_calls[1] - self.assertIn("DEVICE", args) - - @patch("lava.device.commands.can_edit_file", create=True) - @patch("lava.device.commands.edit_file", create=True) - @patch("lava.device.commands.get_device_file") - def test_config_invoke_0(self, mocked_get_device_file, mocked_edit_file, - mocked_can_edit_file): - command = config(self.parser, self.args) - - mocked_can_edit_file.return_value = True - mocked_get_device_file.return_value = self.temp_file.name - command.invoke() - - self.assertTrue(mocked_edit_file.called) - self.assertEqual([call(self.temp_file.name)], - mocked_edit_file.call_args_list) - - @patch("lava.device.commands.get_device_file", - new=MagicMock(return_value=None)) - def test_config_invoke_raises_0(self): - # Tests invocation of the config command, with a non existent device - # configuration file. - config_command = config(self.parser, self.args) - self.assertRaises(CommandError, config_command.invoke) - - @patch("lava.device.commands.get_device_file", - new=MagicMock(return_value="/etc/password")) - def test_config_invoke_raises_1(self): - # Tests invocation of the config command, with a non writable file. - # Hopefully tests are not run as root. - config_command = config(self.parser, self.args) - self.assertRaises(CommandError, config_command.invoke) === removed file 'lava/device/tests/test_device.py' --- lava/device/tests/test_device.py 2013-07-25 09:56:48 +0000 +++ lava/device/tests/test_device.py 1970-01-01 00:00:00 +0000 @@ -1,119 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Device class unit tests. -""" - -from mock import patch - -from lava.config import Config -from lava.device import ( - Device, - get_known_device, -) -from lava.device.templates import ( - HOSTNAME_PARAMETER, - PANDA_DEVICE_TYPE, - PANDA_CONNECTION_COMMAND, -) -from lava.helper.tests.helper_test import HelperTest -from lava.parameter import Parameter - - -class DeviceTest(HelperTest): - - def test_get_known_device_panda_0(self): - # User creates a new device with a guessable name for a device. - instance = get_known_device('panda_new_01') - self.assertIsInstance(instance, Device) - self.assertEqual(instance.data['device_type'].value, 'panda') - - def test_get_known_device_panda_1(self): - # User creates a new device with a guessable name for a device. - # Name passed has capital letters. - instance = get_known_device('new_PanDa_02') - self.assertIsInstance(instance, Device) - self.assertEqual(instance.data['device_type'].value, 'panda') - - def test_get_known_device_vexpress_0(self): - # User creates a new device with a guessable name for a device. - # Name passed has capital letters. - instance = get_known_device('a_VexPress_Device') - self.assertIsInstance(instance, Device) - self.assertEqual(instance.data['device_type'].value, 'vexpress') - - def test_get_known_device_vexpress_1(self): - # User creates a new device with a guessable name for a device. - instance = get_known_device('another-vexpress') - self.assertIsInstance(instance, Device) - self.assertIsInstance(instance.data['device_type'], Parameter) - self.assertEqual(instance.data['device_type'].value, 'vexpress') - - @patch("lava.config.Config.save") - def test_device_update_1(self, patched_save): - # Tests that when calling update() on a Device, the template gets - # updated with the correct values from a Config instance. - hostname = "panda_device" - - config = Config() - config._config_file = self.temp_file.name - config.put_parameter(HOSTNAME_PARAMETER, hostname) - config.put_parameter(PANDA_DEVICE_TYPE, "panda") - config.put_parameter(PANDA_CONNECTION_COMMAND, "test") - - expected = { - "hostname": hostname, - "device_type": "panda", - "connection_command": "test" - } - - instance = get_known_device(hostname) - instance.update(config) - - self.assertEqual(expected, instance.data) - - @patch("lava.config.Config.save") - def test_device_write(self, mocked_save): - # User tries to create a new panda device. The conf file is written - # and contains the expected results. - hostname = "panda_device" - - config = Config() - config._config_file = self.temp_file.name - config.put_parameter(HOSTNAME_PARAMETER, hostname) - config.put_parameter(PANDA_DEVICE_TYPE, "panda") - config.put_parameter(PANDA_CONNECTION_COMMAND, "test") - - expected = { - "hostname": hostname, - "device_type": "panda", - "connection_command": "test" - } - - instance = get_known_device(hostname) - instance.update(config) - instance.write(self.temp_file.name) - - expected = ("hostname = panda_device\nconnection_command = test\n" - "device_type = panda\n") - - obtained = "" - with open(self.temp_file.name) as f: - obtained = f.read() - self.assertEqual(expected, obtained) === removed directory 'lava/helper' === removed file 'lava/helper/__init__.py' === removed file 'lava/helper/command.py' --- lava/helper/command.py 2013-08-07 09:42:35 +0000 +++ lava/helper/command.py 1970-01-01 00:00:00 +0000 @@ -1,244 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Base command class common to lava commands series.""" - -import os -import sys -import xmlrpclib - -from lava.config import ( - InteractiveCache, -) -from lava.helper.dispatcher import get_devices -from lava.job import Job -from lava.job.templates import ( - LAVA_TEST_SHELL_TAR_REPO, - LAVA_TEST_SHELL_TAR_REPO_KEY, - LAVA_TEST_SHELL_TESDEF_KEY, -) -from lava.parameter import ( - Parameter, - SingleChoiceParameter, -) -from lava.script import ( - ShellScript, - DEFAULT_TESTDEF_SCRIPT, -) -from lava.testdef import TestDefinition -from lava.testdef.templates import ( - TESTDEF_STEPS_KEY, - TESTDEF_TEMPLATE, -) -from lava.tool.command import Command -from lava.tool.errors import CommandError -from lava_tool.authtoken import ( - AuthenticatingServerProxy, - KeyringAuthBackend -) -from lava_tool.utils import ( - base64_encode, - create_tar, - execute, - has_command, - to_list, - verify_and_create_url, -) - -CONFIG = InteractiveCache() - - -class BaseCommand(Command): - - """Base command class for all lava commands.""" - - def __init__(self, parser, args): - super(BaseCommand, self).__init__(parser, args) - self.config = CONFIG - self.config.force_interactive = self.args.non_interactive - - @classmethod - def register_arguments(cls, parser): - super(BaseCommand, cls).register_arguments(parser) - parser.add_argument("--non-interactive", "-n", - action='store_false', - help=("Do not ask for input parameters.")) - - def authenticated_server(self): - """Returns a connection to a LAVA server. - - It will ask the user the necessary parameters to establish the - connection. - """ - print >> sys.stdout, "\nServer connection parameters:" - - server_name_parameter = Parameter("server") - rpc_endpoint_parameter = Parameter("rpc_endpoint", - depends=server_name_parameter) - - self.config.get(server_name_parameter) - endpoint = self.config.get(rpc_endpoint_parameter) - - rpc_url = verify_and_create_url(endpoint) - server = AuthenticatingServerProxy(rpc_url, - auth_backend=KeyringAuthBackend()) - return server - - def submit(self, job_file): - """Submits a job file to a LAVA server. - - :param job_file: The job file to submit. - :return The job ID on success. - """ - if os.path.isfile(job_file): - try: - jobdata = open(job_file, 'rb').read() - server = self.authenticated_server() - - job_id = server.scheduler.submit_job(jobdata) - print >> sys.stdout, ("Job submitted with job " - "ID {0}.".format(job_id)) - - return job_id - except xmlrpclib.Fault, exc: - raise CommandError(str(exc)) - else: - raise CommandError("Job file '{0}' does not exists, or is not " - "a file.".format(job_file)) - - def run(self, job_file): - """Runs a job file on the local LAVA dispatcher. - - :param job_file: The job file to run. - """ - if os.path.isfile(job_file): - if has_command("lava-dispatch"): - devices = get_devices() - if devices: - if len(devices) > 1: - device_names = [device.hostname for device in devices] - device_param = SingleChoiceParameter("device", - device_names) - device = device_param.prompt("Device to use: ") - else: - device = devices[0].hostname - execute(["lava-dispatch", "--target", device, job_file]) - else: - raise CommandError("Cannot find lava-dispatcher installation.") - else: - raise CommandError("Job file '{0}' does not exists, or it is not " - "a file.".format(job_file)) - - def status(self, job_id): - """Retrieves the status of a LAVA job. - - :param job_id: The ID of the job to look up. - """ - job_id = str(job_id) - - try: - server = self.authenticated_server() - job_status = server.scheduler.job_status(job_id) - - status = job_status["job_status"].lower() - bundle = job_status["bundle_sha1"] - - print >> sys.stdout, "\nJob id: {0}".format(job_id) - print >> sys.stdout, "Status: {0}".format(status) - print >> sys.stdout, "Bundle: {0}".format(bundle) - except xmlrpclib.Fault, exc: - raise CommandError(str(exc)) - - def create_tar_repo_job(self, job_file, testdef_file, tar_content): - """Creates a job file based on the tar-repo template. - - The tar repo is not kept on the file system. - - :param job_file: The path of the job file to create. - :param testdef_file: The path of the test definition file. - :param tar_content: What should go into the tarball repository. - :return The path of the job file created. - """ - - print >> sys.stdout, "\nCreating job file..." - - try: - tar_repo = create_tar(tar_content) - - job_instance = Job(LAVA_TEST_SHELL_TAR_REPO, job_file) - job_instance.update(self.config) - - job_instance.set(LAVA_TEST_SHELL_TAR_REPO_KEY, - base64_encode(tar_repo)) - job_instance.set(LAVA_TEST_SHELL_TESDEF_KEY, - os.path.basename(testdef_file)) - - job_instance.write() - - basename = os.path.basename(job_instance.file_name) - print >> sys.stdout, ("\nCreated job file " - "'{0}'.".format(basename)) - - return job_instance.file_name - finally: - if os.path.isfile(tar_repo): - os.unlink(tar_repo) - - def create_test_definition(self, testdef_file, template=TESTDEF_TEMPLATE, - steps=None): - """Creates a test definition YAML file. - - :param testdef_file: The file to create. - :return The path of the file created. - """ - - print >> sys.stdout, "\nCreating test definition file..." - - testdef = TestDefinition(template, testdef_file) - if steps: - steps = to_list(steps) - testdef.set(TESTDEF_STEPS_KEY, steps) - testdef.update(self.config) - testdef.write() - - basename = os.path.basename(testdef.file_name) - print >> sys.stdout, ("\nCreated test definition " - "'{0}'.".format(basename)) - - return testdef.file_name - - def create_shell_script(self, test_path, - script_name=DEFAULT_TESTDEF_SCRIPT): - """Creates a shell script with some default content. - - :param test_path: The directory where to create the script. - :param script_name: The name of the script. - :return The full path to the script file. - """ - default_script = os.path.join(test_path, script_name) - - if not os.path.isfile(default_script): - print >> sys.stdout, "Creating shell script..." - - shell_script = ShellScript(default_script) - shell_script.write() - - print >> sys.stdout, ("\nCreated shell script " - "'{0}'.".format(script_name)) - - return default_script === removed file 'lava/helper/dispatcher.py' --- lava/helper/dispatcher.py 2013-06-19 13:59:43 +0000 +++ lava/helper/dispatcher.py 1970-01-01 00:00:00 +0000 @@ -1,110 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Classes and functions to interact with the lava-dispatcher.""" - -import random -import string -import os - -from lava.tool.errors import CommandError - -# Default devices path, has to be joined with the dispatcher path. -DEFAULT_DEVICES_PATH = "devices" - - -def get_dispatcher_paths(): - """Tries to get the dispatcher paths from lava-dispatcher. - - :return A list of paths. - """ - try: - from lava_dispatcher.config import write_path - return write_path() - except ImportError: - raise CommandError("Cannot find lava-dispatcher installation.") - - -def get_devices(): - """Gets the devices list from the dispatcher. - - :return A list of DeviceConfig. - """ - try: - from lava_dispatcher.config import get_devices - return get_devices() - except ImportError: - raise CommandError("Cannot find lava-dispatcher installation.") - - -def get_device_file(file_name): - """Retrieves the config file name specified, if it exists. - - :param file_name: The config file name to search. - :return The path to the file, or None if it does not exist. - """ - try: - from lava_dispatcher.config import get_config_file - return get_config_file(os.path.join(DEFAULT_DEVICES_PATH, - file_name)) - except ImportError: - raise CommandError("Cannot find lava-dispatcher installation.") - - -def choose_devices_path(paths): - """Picks the first path that is writable by the user. - - :param paths: A list of paths. - :return The first path where it is possible to write. - """ - valid_path = None - for path in paths: - path = os.path.join(path, DEFAULT_DEVICES_PATH) - if os.path.exists(path): - name = "".join(random.choice(string.ascii_letters) - for x in range(6)) - test_file = os.path.join(path, name) - try: - fp = open(test_file, 'a') - fp.close() - except IOError: - # Cannot write here. - continue - else: - valid_path = path - if os.path.isfile(test_file): - os.unlink(test_file) - break - else: - try: - os.makedirs(path) - except OSError: - # Cannot write here either. - continue - else: - valid_path = path - break - else: - raise CommandError("Insufficient permissions to create new " - "devices.") - return valid_path - - -def get_devices_path(): - """Gets the path to the devices in the LAVA dispatcher.""" - return choose_devices_path(get_dispatcher_paths()) === removed file 'lava/helper/template.py' --- lava/helper/template.py 2013-07-25 09:56:48 +0000 +++ lava/helper/template.py 1970-01-01 00:00:00 +0000 @@ -1,124 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Helper functions for a template.""" - -from lava.parameter import Parameter - - -def expand_template(template, config): - """Updates a template based on the values from the provided config. - - :param template: A template to be updated. - :param config: A Config instance where values should be taken. - """ - - def update(data): - """Internal recursive function.""" - if isinstance(data, dict): - keys = data.keys() - elif isinstance(data, list): - keys = range(len(data)) - else: - return - for key in keys: - entry = data[key] - if isinstance(entry, Parameter): - data[key] = config.get(entry) - else: - update(entry) - - update(template) - - -def get_key(data, search_key): - """Goes through a template looking for a key. - - :param data: The template to traverse. - :param search_key: The key to look for. - :return The key value. - """ - return_value = None - found = False - - if isinstance(data, dict): - bucket = [] - - for key, value in data.iteritems(): - if key == search_key: - return_value = value - found = True - break - else: - bucket.append(value) - - if bucket and not found: - for element in bucket: - if isinstance(element, list): - for element in element: - bucket.append(element) - elif isinstance(element, dict): - for key, value in element.iteritems(): - if key == search_key: - return_value = value - found = True - break - else: - bucket.append(value) - if found: - break - - return return_value - - -def set_value(data, search_key, new_value): - """Sets a new value for a template key. - - :param data: The data structure to update. - :type dict - :param search_key: The key to search and update. - :param new_value: The new value to set. - """ - is_set = False - - if isinstance(data, dict): - bucket = [] - - for key, value in data.iteritems(): - if key == search_key: - data[key] = new_value - is_set = True - break - else: - bucket.append(value) - - if bucket and not is_set: - for element in bucket: - if isinstance(element, list): - for element in element: - bucket.append(element) - elif isinstance(element, dict): - for key, value in element.iteritems(): - if key == search_key: - element[key] = new_value - is_set = True - break - else: - bucket.append(value) - if is_set: - break === removed directory 'lava/helper/tests' === removed file 'lava/helper/tests/__init__.py' === removed file 'lava/helper/tests/helper_test.py' --- lava/helper/tests/helper_test.py 2013-07-24 14:00:37 +0000 +++ lava/helper/tests/helper_test.py 1970-01-01 00:00:00 +0000 @@ -1,81 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -A test helper class. - -Here we define a general test class and its own setUp and tearDown methods that -all other test classes can inherit from. -""" - -import os -import shutil -import sys -import tempfile - -from unittest import TestCase -from mock import ( - MagicMock, - patch -) - - -class HelperTest(TestCase): - """Helper test class that all tests under the lava package can inherit.""" - - def setUp(self): - # Need to patch it here, not as a decorator, or running the tests - # via `./setup.py test` will fail. - self.at_exit_patcher = patch("lava.config.AT_EXIT_CALLS", spec=set) - self.at_exit_patcher.start() - self.original_stdout = sys.stdout - sys.stdout = open("/dev/null", "w") - self.original_stderr = sys.stderr - sys.stderr = open("/dev/null", "w") - self.original_stdin = sys.stdin - - self.device = "a_fake_panda02" - - self.temp_file = tempfile.NamedTemporaryFile(delete=False) - self.temp_dir = tempfile.mkdtemp() - self.parser = MagicMock() - self.args = MagicMock() - self.args.interactive = MagicMock(return_value=False) - self.args.DEVICE = self.device - - def tearDown(self): - self.at_exit_patcher.stop() - sys.stdin = self.original_stdin - sys.stdout = self.original_stdout - sys.stderr = self.original_stderr - shutil.rmtree(self.temp_dir) - os.unlink(self.temp_file.name) - - def tmp(self, name): - """ - Returns the full path to a file, or directory, called `name` in a - temporary directory. - - This method does not create the file, it only gives a full filename - where you can actually write some data. The file will not be removed - by this method. - - :param name: The name the file/directory should have. - :return A path. - """ - return os.path.join(tempfile.gettempdir(), name) === removed file 'lava/helper/tests/test_command.py' --- lava/helper/tests/test_command.py 2013-07-26 09:35:44 +0000 +++ lava/helper/tests/test_command.py 1970-01-01 00:00:00 +0000 @@ -1,47 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""lava.helper.command module tests.""" - -from mock import MagicMock, patch - - -from lava.helper.command import BaseCommand -from lava.helper.tests.helper_test import HelperTest - - -class BaseCommandTests(HelperTest): - - def test_register_argument(self): - # Make sure that the parser add_argument is called and we have the - # correct argument. - command = BaseCommand(self.parser, self.args) - command.register_arguments(self.parser) - name, args, kwargs = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - @patch("lava.helper.command.AuthenticatingServerProxy", create=True) - def test_authenticated_server(self, mocked_auth_server): - command = BaseCommand(self.parser, self.args) - command.config = MagicMock() - command.config.get = MagicMock() - command.config.get.side_effect = ["www.example.org", "RPC"] - - command.authenticated_server() - - self.assertTrue(mocked_auth_server.called) === removed file 'lava/helper/tests/test_dispatcher.py' --- lava/helper/tests/test_dispatcher.py 2013-06-26 08:58:00 +0000 +++ lava/helper/tests/test_dispatcher.py 1970-01-01 00:00:00 +0000 @@ -1,77 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""lava.helper.dispatcher tests.""" - -import os -import tempfile - -from mock import patch - -from lava.tool.errors import CommandError -from lava.helper.tests.helper_test import HelperTest -from lava.helper.dispatcher import ( - choose_devices_path, -) - - -class DispatcherTests(HelperTest): - - def setUp(self): - super(DispatcherTests, self).setUp() - self.devices_dir = os.path.join(tempfile.gettempdir(), "devices") - os.makedirs(self.devices_dir) - - def tearDown(self): - super(DispatcherTests, self).tearDown() - os.removedirs(self.devices_dir) - - def test_choose_devices_path_0(self): - # Tests that when passing more than one path, the first writable one - # is returned. - obtained = choose_devices_path( - ["/", "/root", self.temp_dir, os.path.expanduser("~")]) - expected = os.path.join(self.temp_dir, "devices") - self.assertEqual(expected, obtained) - - def test_choose_devices_path_1(self): - # Tests that when passing a path that is not writable, CommandError - # is raised. - self.assertRaises(CommandError, choose_devices_path, - ["/", "/root", "/root/tmpdir"]) - - def test_choose_devices_path_2(self): - # Tests that the correct path for devices is created on the filesystem. - expected_path = os.path.join(self.temp_dir, "devices") - obtained = choose_devices_path([self.temp_dir]) - self.assertEqual(expected_path, obtained) - self.assertTrue(os.path.isdir(expected_path)) - - def test_choose_devices_path_3(self): - # Tests that returns the already existing devices path. - obtained = choose_devices_path([tempfile.gettempdir()]) - self.assertEqual(self.devices_dir, obtained) - - @patch("__builtin__.open") - def test_choose_devices_path_4(self, mocked_open): - # Tests that when IOError is raised and we pass only one dir - # CommandError is raised. - mocked_open.side_effect = IOError() - self.assertRaises(CommandError, choose_devices_path, - [tempfile.gettempdir()]) - self.assertTrue(mocked_open.called) === removed file 'lava/helper/tests/test_template.py' --- lava/helper/tests/test_template.py 2013-07-17 17:35:56 +0000 +++ lava/helper/tests/test_template.py 1970-01-01 00:00:00 +0000 @@ -1,102 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" """ - -import copy -from unittest import TestCase - -from lava.helper.template import ( - get_key, - set_value -) - - -TEST_TEMPLATE = { - "key1": "value1", - "key2": [ - "value2", "value3" - ], - "key3": [ - { - "key4": "value4", - "key5": "value5" - }, - { - "key6": "value6", - "key7": "value7" - }, - [ - { - "key8": "value8" - } - ] - ], - "key10": { - "key11": "value11" - } -} - - -class TestParameter(TestCase): - - def test_get_key_simple_key(self): - expected = "value1" - obtained = get_key(TEST_TEMPLATE, "key1") - self.assertEquals(expected, obtained) - - def test_get_key_nested_key(self): - expected = "value4" - obtained = get_key(TEST_TEMPLATE, "key4") - self.assertEquals(expected, obtained) - - def test_get_key_nested_key_1(self): - expected = "value7" - obtained = get_key(TEST_TEMPLATE, "key7") - self.assertEquals(expected, obtained) - - def test_get_key_nested_key_2(self): - expected = "value8" - obtained = get_key(TEST_TEMPLATE, "key8") - self.assertEquals(expected, obtained) - - def test_get_key_nested_key_3(self): - expected = "value11" - obtained = get_key(TEST_TEMPLATE, "key11") - self.assertEquals(expected, obtained) - - def test_set_value_0(self): - data = copy.deepcopy(TEST_TEMPLATE) - expected = "foo" - set_value(data, "key1", expected) - obtained = get_key(data, "key1") - self.assertEquals(expected, obtained) - - def test_set_value_1(self): - data = copy.deepcopy(TEST_TEMPLATE) - expected = "foo" - set_value(data, "key6", expected) - obtained = get_key(data, "key6") - self.assertEquals(expected, obtained) - - def test_set_value_2(self): - data = copy.deepcopy(TEST_TEMPLATE) - expected = "foo" - set_value(data, "key11", expected) - obtained = get_key(data, "key11") - self.assertEquals(expected, obtained) === removed directory 'lava/job' === removed file 'lava/job/__init__.py' --- lava/job/__init__.py 2013-07-26 10:25:39 +0000 +++ lava/job/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,73 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Job class.""" - -import json - -from copy import deepcopy - -from lava.helper.template import ( - expand_template, - set_value, -) -from lava_tool.utils import ( - verify_file_extension, - verify_path_existance, - write_file -) - -# A default name for job files. -DEFAULT_JOB_FILENAME = "lava-tool-job.json" -# Default job file extension. -DEFAULT_JOB_EXTENSION = "json" -# Possible extension for a job file. -JOB_FILE_EXTENSIONS = [DEFAULT_JOB_EXTENSION] - - -class Job(object): - - """A Job object. - - This class should be used to create new job files. The initialization - enforces a default file name extension, and makes sure that the file is - not already present on the file system. - """ - - def __init__(self, data, file_name): - self.file_name = verify_file_extension(file_name, - DEFAULT_JOB_EXTENSION, - JOB_FILE_EXTENSIONS) - verify_path_existance(self.file_name) - self.data = deepcopy(data) - - def set(self, key, value): - """Set key to the specified value. - - :param key: The key to look in the object data. - :param value: The value to set. - """ - set_value(self.data, key, value) - - def update(self, config): - """Updates the Job object based on the provided config.""" - expand_template(self.data, config) - - def write(self): - """Writes the Job object to file.""" - write_file(self.file_name, json.dumps(self.data, indent=4)) === removed file 'lava/job/commands.py' --- lava/job/commands.py 2013-07-25 16:50:18 +0000 +++ lava/job/commands.py 1970-01-01 00:00:00 +0000 @@ -1,107 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -LAVA job commands. -""" - -import os - -from lava.helper.command import BaseCommand -from lava.job import Job -from lava.job.templates import ( - BOOT_TEST_KEY, - JOB_TYPES, -) -from lava.tool.command import CommandGroup -from lava.tool.errors import CommandError - - -class job(CommandGroup): - """LAVA job file handling.""" - namespace = 'lava.job.commands' - - -class new(BaseCommand): - """Creates a new job file.""" - - @classmethod - def register_arguments(cls, parser): - super(new, cls).register_arguments(parser) - parser.add_argument("FILE", help=("Job file to be created.")) - parser.add_argument("--type", - help=("The type of job to create. Defaults to " - "'{0}'.".format(BOOT_TEST_KEY)), - choices=JOB_TYPES.keys(), - default=BOOT_TEST_KEY) - - def invoke(self, job_template=None): - if not job_template: - job_template = JOB_TYPES.get(self.args.type) - - full_path = os.path.abspath(self.args.FILE) - - job_instance = Job(job_template, full_path) - job_instance.update(self.config) - job_instance.write() - - -class submit(BaseCommand): - - """Submits the specified job file.""" - - @classmethod - def register_arguments(cls, parser): - super(submit, cls).register_arguments(parser) - parser.add_argument("FILE", help=("The job file to submit.")) - - def invoke(self): - super(submit, self).submit(self.args.FILE) - - -class run(BaseCommand): - - """Runs the specified job file on the local dispatcher.""" - - @classmethod - def register_arguments(cls, parser): - super(run, cls).register_arguments(parser) - parser.add_argument("FILE", help=("The job file to submit.")) - - def invoke(self): - super(run, self).run(self.args.FILE) - - -class status(BaseCommand): - - """Retrieves the status of a job.""" - - @classmethod - def register_arguments(cls, parser): - super(status, cls).register_arguments(parser) - parser.add_argument("JOB_ID", - help=("Prints status information about the " - "provided job id."), - nargs="?", - default=None) - - def invoke(self): - if self.args.JOB_ID: - super(status, self).status(self.args.JOB_ID) - else: - raise CommandError("It is necessary to specify a job id.") === removed file 'lava/job/templates.py' --- lava/job/templates.py 2013-08-29 01:14:17 +0000 +++ lava/job/templates.py 1970-01-01 00:00:00 +0000 @@ -1,106 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -from lava.parameter import ( - ListParameter, - Parameter, -) - -LAVA_TEST_SHELL_TAR_REPO_KEY = "tar-repo" -LAVA_TEST_SHELL_TESDEF_KEY = "testdef" - -DEVICE_TYPE_PARAMETER = Parameter("device_type") -PREBUILT_IMAGE_PARAMETER = Parameter("image", depends=DEVICE_TYPE_PARAMETER) - -TESTDEF_URLS_PARAMETER = ListParameter("testdef_urls") -TESTDEF_URLS_PARAMETER.store = False - -BOOT_TEST = { - "timeout": 18000, - "job_name": "Boot test", - "device_type": DEVICE_TYPE_PARAMETER, - "actions": [ - { - "command": "deploy_linaro_image", - "parameters": { - "image": PREBUILT_IMAGE_PARAMETER - } - }, - { - "command": "boot_linaro_image" - } - ] -} - -LAVA_TEST_SHELL = { - "job_name": "LAVA Test Shell", - "timeout": 18000, - "device_type": DEVICE_TYPE_PARAMETER, - "actions": [ - { - "command": "deploy_linaro_image", - "parameters": { - "image": PREBUILT_IMAGE_PARAMETER, - } - }, - { - "command": "lava_test_shell", - "parameters": { - "timeout": 1800, - "testdef_urls": TESTDEF_URLS_PARAMETER, - } - } - ] -} - -# This is a special case template, only use when automatically create job files -# starting from a testdef or a script. Never to be used directly by the user. -LAVA_TEST_SHELL_TAR_REPO = { - "job_name": "LAVA Test Shell", - "timeout": 18000, - "device_type": DEVICE_TYPE_PARAMETER, - "actions": [ - { - "command": "deploy_linaro_image", - "parameters": { - "image": PREBUILT_IMAGE_PARAMETER, - } - }, - { - "command": "lava_test_shell", - "parameters": { - "timeout": 1800, - "testdef_repos": [ - { - LAVA_TEST_SHELL_TESDEF_KEY: None, - LAVA_TEST_SHELL_TAR_REPO_KEY: None, - } - ] - } - } - ] -} - -BOOT_TEST_KEY = "boot-test" -LAVA_TEST_SHELL_KEY = "lava-test-shell" - -# Dict with all the user available job templates. -JOB_TYPES = { - BOOT_TEST_KEY: BOOT_TEST, - LAVA_TEST_SHELL_KEY: LAVA_TEST_SHELL, -} === removed directory 'lava/job/tests' === removed file 'lava/job/tests/__init__.py' === removed file 'lava/job/tests/test_commands.py' --- lava/job/tests/test_commands.py 2013-07-26 08:17:45 +0000 +++ lava/job/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,155 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unit tests for the commands classes -""" - -import json -import os - -from mock import patch - -from lava.config import Config -from lava.helper.tests.helper_test import HelperTest -from lava.job.commands import ( - new, - run, - submit, - status, -) -from lava.parameter import Parameter -from lava.tool.errors import CommandError - - -class CommandTest(HelperTest): - - def setUp(self): - super(CommandTest, self).setUp() - self.args.FILE = self.temp_file.name - self.args.type = "boot-test" - - self.device_type = Parameter('device_type') - self.prebuilt_image = Parameter('prebuilt_image', - depends=self.device_type) - self.config = Config() - self.config.put_parameter(self.device_type, 'foo') - self.config.put_parameter(self.prebuilt_image, 'bar') - - -class JobNewTest(CommandTest): - - def setUp(self): - super(JobNewTest, self).setUp() - self.args.FILE = self.tmp("new_file.json") - self.new_command = new(self.parser, self.args) - self.new_command.config = self.config - - def tearDown(self): - super(JobNewTest, self).tearDown() - if os.path.exists(self.args.FILE): - os.unlink(self.args.FILE) - - def test_register_arguments(self): - new_cmd = new(self.parser, self.args) - new_cmd.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(3, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("FILE", args) - - _, args, _ = self.parser.method_calls[2] - self.assertIn("--type", args) - - def test_create_new_file(self): - self.new_command.invoke() - self.assertTrue(os.path.exists(self.args.FILE)) - - def test_fills_in_template_parameters(self): - self.new_command.invoke() - - data = json.loads(open(self.args.FILE).read()) - self.assertEqual(data['device_type'], 'foo') - - def test_wont_overwrite_existing_file(self): - with open(self.args.FILE, 'w') as f: - f.write("CONTENTS") - - self.assertRaises(CommandError, self.new_command.invoke) - self.assertEqual("CONTENTS", open(self.args.FILE).read()) - - -class JobSubmitTest(CommandTest): - - def test_receives_job_file_in_cmdline(self): - command = submit(self.parser, self.args) - command.register_arguments(self.parser) - name, args, kwargs = self.parser.method_calls[1] - self.assertIn("FILE", args) - - -class JobRunTest(CommandTest): - - def test_register_arguments(self): - run_cmd = run(self.parser, self.args) - run_cmd.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("FILE", args) - - def test_invoke_raises_0(self): - # Users passes a non existing job file to the run command. - self.args.FILE = self.tmp("test_invoke_raises_0.json") - command = run(self.parser, self.args) - self.assertRaises(CommandError, command.invoke) - - @patch("lava.helper.command.has_command", create=True) - def test_invoke_raises_1(self, mocked_has_command): - # User passes a valid file to the run command, but she does not have - # the dispatcher installed. - mocked_has_command.return_value = False - command = run(self.parser, self.args) - self.assertRaises(CommandError, command.invoke) - - -class TestsStatusCommand(CommandTest): - - def test_register_arguments(self): - self.args.JOB_ID = "1" - status_cmd = status(self.parser, self.args) - status_cmd.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("JOB_ID", args) === removed file 'lava/job/tests/test_job.py' --- lava/job/tests/test_job.py 2013-07-25 12:44:55 +0000 +++ lava/job/tests/test_job.py 1970-01-01 00:00:00 +0000 @@ -1,92 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unit tests for the Job class -""" - -import os -import json -import tempfile - -from mock import patch - -from lava.config import Config -from lava.helper.tests.helper_test import HelperTest -from lava.job import Job -from lava.job.templates import BOOT_TEST -from lava.parameter import Parameter - - -class JobTest(HelperTest): - - @patch("lava.config.Config.save") - def setUp(self, mocked_config): - super(JobTest, self).setUp() - self.config = Config() - self.config.config_file = self.temp_file.name - - def test_from_template(self): - template = {} - job = Job(template, self.temp_file.name) - self.assertEqual(job.data, template) - self.assertIsNot(job.data, template) - - def test_update_data(self): - image = "/path/to/panda.img" - param1 = Parameter("device_type") - param2 = Parameter("image", depends=param1) - self.config.put_parameter(param1, "panda") - self.config.put_parameter(param2, image) - - job = Job(BOOT_TEST, self.temp_file.name) - job.update(self.config) - - self.assertEqual(job.data['device_type'], "panda") - self.assertEqual(job.data['actions'][0]["parameters"]["image"], image) - - def test_write(self): - try: - orig_data = {"foo": "bar"} - job_file = os.path.join(tempfile.gettempdir(), "a_json_file.json") - job = Job(orig_data, job_file) - job.write() - - output = "" - with open(job_file) as read_file: - output = read_file.read() - - data = json.loads(output) - self.assertEqual(data, orig_data) - finally: - os.unlink(job_file) - - def test_writes_nicely_formatted_json(self): - try: - orig_data = {"foo": "bar"} - job_file = os.path.join(tempfile.gettempdir(), "b_json_file.json") - job = Job(orig_data, job_file) - job.write() - - output = "" - with open(job_file) as read_file: - output = read_file.read() - - self.assertTrue(output.startswith("{\n")) - finally: - os.unlink(job_file) === removed file 'lava/parameter.py' --- lava/parameter.py 2013-07-26 08:16:30 +0000 +++ lava/parameter.py 1970-01-01 00:00:00 +0000 @@ -1,251 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Antonio Terceiro -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Parameter class and its accessory methods/functions. -""" - -import sys -import types - -from lava_tool.utils import to_list - -# Character used to join serialized list parameters. -LIST_SERIALIZE_DELIMITER = "," - - -class Parameter(object): - """A parameter with an optional dependency.""" - def __init__(self, id, value=None, depends=None): - """Creates a new parameter. - - :param id: The name of this parameter. - :param value: The value of this parameter. Defaults to None. - :param depends: If this Parameter depends on another one. Defaults - to None. - :type Parameter - """ - self.id = id - self.value = value - self.depends = depends - self.asked = False - # Whether to store or not the parameter in the user config file. - self.store = True - - def set(self, value): - """Sets the value of the parameter. - - :param value: The value to set. - """ - self.value = value - - def prompt(self, old_value=None): - """Gets the parameter value from the user. - - To get user input, the builtin `raw_input` function will be used. Input - will also be stripped of possible whitespace chars. If Enter or any - sort of whitespace chars in typed, the old Parameter value will be - returned. - - :param old_value: The old parameter value. - :return The input as typed by the user, or the old value. - """ - if not self.asked: - if old_value is not None: - prompt = "{0} [{1}]: ".format(self.id, old_value) - else: - prompt = "{0}: ".format(self.id) - - user_input = self.get_user_input(prompt) - - if user_input is not None: - if len(user_input) == 0 and old_value: - # Keep the old value when user press enter or another - # whitespace char. - self.value = old_value - else: - self.value = user_input - - self.asked = True - - return self.value - - @classmethod - def get_user_input(cls, prompt=""): - """Asks the user for input data. - - :param prompt: The prompt that should be given to the user. - :return A string with what the user typed. - """ - data = None - try: - data = raw_input(prompt).strip() - except EOFError: - # Force to return None. - data = None - except KeyboardInterrupt: - sys.exit(-1) - return data - - @classmethod - def serialize(cls, value): - """Serializes the passed value to be friendly written to file. - - Lists are serialized as a comma separated string of values. - - :param value: The value to serialize. - :return The serialized value as string. - """ - serialized = "" - if isinstance(value, list): - serialized = LIST_SERIALIZE_DELIMITER.join( - str(x) for x in value if x) - else: - serialized = str(value) - return serialized - - @classmethod - def deserialize(cls, value): - """Deserialize a value into a list. - - The value must have been serialized with the class instance serialize() - method. - - :param value: The string value to be deserialized. - :type str - :return A list of values. - """ - deserialized = [] - if isinstance(value, types.StringTypes): - deserialized = filter(None, (x.strip() for x in value.split( - LIST_SERIALIZE_DELIMITER))) - else: - deserialized = list(value) - return deserialized - - -class SingleChoiceParameter(Parameter): - """A parameter implemeting a single choice between multiple choices.""" - def __init__(self, id, choices): - super(SingleChoiceParameter, self).__init__(id) - self.choices = to_list(choices) - - def prompt(self, prompt, old_value=None): - """Asks the user for their choice.""" - # Sliglty different than the other parameters: here we first present - # the user with what the choices are about. - print >> sys.stdout, prompt - - index = 1 - for choice in self.choices: - print >> sys.stdout, "\t{0:d}. {1}".format(index, choice) - index += 1 - - choices_len = len(self.choices) - while True: - user_input = self.get_user_input("Choice: ") - - if len(user_input) == 0 and old_value: - choice = old_value - break - elif user_input in [str(x) for x in range(1, choices_len + 1)]: - choice = self.choices[int(user_input) - 1] - break - - return choice - - -class ListParameter(Parameter): - """A specialized Parameter to handle list values.""" - - # This is used as a deletion character. When we have an old value and the - # user enters this char, it sort of deletes the value. - DELETE_CHAR = "-" - - def __init__(self, id, value=None, depends=None): - super(ListParameter, self).__init__(id, depends=depends) - self.value = [] - if value: - self.set(value) - - def set(self, value): - """Sets the value of the parameter. - - :param value: The value to set. - """ - self.value = to_list(value) - - def add(self, value): - """Adds a new value to the list of values of this parameter. - - :param value: The value to add. - """ - if isinstance(value, list): - self.value.extend(value) - else: - self.value.append(value) - - def prompt(self, old_value=None): - """Gets the parameter in a list form. - - To exit the input procedure it is necessary to insert an empty line. - - :return The list of values. - """ - - if not self.asked: - if old_value is not None: - # We might get the old value read from file via ConfigParser, - # and usually it comes in string format. - old_value = self.deserialize(old_value) - - print >> sys.stdout, "Values for '{0}': ".format(self.id) - - index = 1 - while True: - user_input = None - if old_value is not None and (0 < len(old_value) >= index): - prompt = "{0:>3d}.\n\told: {1}\n\tnew: ".format( - index, old_value[index-1]) - user_input = self.get_user_input(prompt) - else: - prompt = "{0:>3d}. ".format(index) - user_input = self.get_user_input(prompt) - - if user_input is not None: - # The user has pressed Enter. - if len(user_input) == 0: - if old_value is not None and \ - (0 < len(old_value) >= index): - user_input = old_value[index-1] - else: - break - - if len(user_input) == 1 and user_input == \ - self.DELETE_CHAR and (0 < len(old_value) >= index): - # We have an old value, user presses the DELETE_CHAR - # and we do not store anything. This is done to delete - # an old entry. - pass - else: - self.value.append(user_input) - index += 1 - - self.asked = True - - return self.value === removed directory 'lava/script' === removed file 'lava/script/__init__.py' --- lava/script/__init__.py 2013-07-25 16:42:18 +0000 +++ lava/script/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Scripts handling class.""" - -import os -import stat - -from lava_tool.utils import write_file - - -DEFAULT_MOD = stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH -DEFAULT_TESTDEF_SCRIPT_CONTENT = """#!/bin/sh -# Automatic generated content by lava-tool. -# Please add your own instructions. -# -# You can use all the avialable Bash commands. -# -# For the available LAVA commands, see: -# http://lava.readthedocs.org/ -# -""" -DEFAULT_TESTDEF_SCRIPT = "mytest.sh" - - -class ShellScript(object): - - """Creates a shell script on the file system with some content.""" - - def __init__(self, file_name): - self.file_name = file_name - - def write(self): - write_file(self.file_name, DEFAULT_TESTDEF_SCRIPT_CONTENT) - # Make sure the script is executable. - os.chmod(self.file_name, DEFAULT_MOD) === removed file 'lava/script/commands.py' --- lava/script/commands.py 2013-07-26 14:08:28 +0000 +++ lava/script/commands.py 1970-01-01 00:00:00 +0000 @@ -1,115 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Commands to run or submit a script.""" - -import os -import tempfile - -from lava.helper.command import BaseCommand -from lava.job import DEFAULT_JOB_FILENAME -from lava.testdef import DEFAULT_TESTDEF_FILENAME -from lava.tool.command import CommandGroup -from lava_tool.utils import verify_path_non_existance - - -class script(CommandGroup): - - """LAVA script file handling.""" - - namespace = "lava.script.commands" - - -class ScriptBaseCommand(BaseCommand): - - def _create_tmp_job_file(self, script_file): - """Creates a temporary job file to run or submit the passed file. - - The temporary job file and its accessory test definition file are - not removed by this method. - - :param script_file: The script file that has to be run or submitted. - :return A tuple with the job file path, and the test definition path. - """ - script_file = os.path.abspath(script_file) - verify_path_non_existance(script_file) - - temp_dir = tempfile.gettempdir() - - # The name of the job and testdef files. - job_file = os.path.join(temp_dir, DEFAULT_JOB_FILENAME) - testdef_file = os.path.join(temp_dir, DEFAULT_TESTDEF_FILENAME) - - # The steps that the testdef file should have. We need to change it - # from the default one, since the users are passing their own file. - steps = "./" + os.path.basename(script_file) - testdef_file = self.create_test_definition(testdef_file, - steps=steps) - - # The content of the tar file. - tar_content = [script_file, testdef_file] - job_file = self.create_tar_repo_job(job_file, testdef_file, - tar_content) - - return (job_file, testdef_file) - - -class run(ScriptBaseCommand): - - """Runs the specified shell script on a local device.""" - - @classmethod - def register_arguments(cls, parser): - super(run, cls).register_arguments(parser) - parser.add_argument("FILE", help="Shell script file to run.") - - def invoke(self): - job_file = "" - testdef_file = "" - - try: - job_file, testdef_file = self._create_tmp_job_file(self.args.FILE) - super(run, self).run(job_file) - finally: - if os.path.isfile(job_file): - os.unlink(job_file) - if os.path.isfile(testdef_file): - os.unlink(testdef_file) - - -class submit(ScriptBaseCommand): - - """Submits the specified shell script to a LAVA server.""" - - @classmethod - def register_arguments(cls, parser): - super(submit, cls).register_arguments(parser) - parser.add_argument("FILE", help="Shell script file to send.") - - def invoke(self): - job_file = "" - testdef_file = "" - - try: - job_file, testdef_file = self._create_tmp_job_file(self.args.FILE) - super(submit, self).submit(job_file) - finally: - if os.path.isfile(job_file): - os.unlink(job_file) - if os.path.isfile(testdef_file): - os.unlink(testdef_file) === removed directory 'lava/script/tests' === removed file 'lava/script/tests/__init__.py' === removed file 'lava/script/tests/test_commands.py' --- lava/script/tests/test_commands.py 2013-07-26 08:45:05 +0000 +++ lava/script/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,59 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Tests for lava.script.commands. -""" - -from lava.helper.tests.helper_test import HelperTest -from lava.script.commands import ( - run, - submit, -) - - -class RunCommandTests(HelperTest): - - def test_register_arguments(self): - run_cmd = run(self.parser, self.args) - run_cmd.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("FILE", args) - - -class SubmitCommandTests(HelperTest): - - def test_register_arguments(self): - submit_cmd = submit(self.parser, self.args) - submit_cmd.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("FILE", args) === removed file 'lava/script/tests/test_script.py' --- lava/script/tests/test_script.py 2013-07-26 08:45:05 +0000 +++ lava/script/tests/test_script.py 1970-01-01 00:00:00 +0000 @@ -1,80 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unittests for the ShellScript class. -""" - -import os -import stat - -from lava.helper.tests.helper_test import HelperTest -from lava.script import ShellScript - - -class ShellScriptTests(HelperTest): - - """ShellScript tests.""" - - def test_create_file(self): - # Tests that a shell script is actually written. - try: - temp_file = self.tmp("a_shell_test") - script = ShellScript(temp_file) - script.write() - - self.assertTrue(os.path.isfile(temp_file)) - finally: - os.unlink(temp_file) - - def test_assure_executable(self): - # Tests that the shell script created is executable. - try: - temp_file = self.tmp("a_shell_test") - script = ShellScript(temp_file) - script.write() - - expected = (stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | - stat.S_IXOTH) - - obtained = stat.S_IMODE(os.stat(temp_file).st_mode) - self.assertEquals(expected, obtained) - finally: - os.unlink(temp_file) - - def test_shell_script_content(self): - # Tests that the shell script created contains the exepcted content. - try: - temp_file = self.tmp("a_shell_test") - script = ShellScript(temp_file) - script.write() - - obtained = "" - with open(temp_file) as read_file: - obtained = read_file.read() - - expected = ("#!/bin/sh\n# Automatic generated " - "content by lava-tool.\n# Please add your own " - "instructions.\n#\n# You can use all the avialable " - "Bash commands.\n#\n# For the available LAVA " - "commands, see:\n# http://lava.readthedocs.org/\n" - "#\n") - - self.assertEquals(expected, obtained) - finally: - os.unlink(temp_file) === removed directory 'lava/testdef' === removed file 'lava/testdef/__init__.py' --- lava/testdef/__init__.py 2013-07-26 10:25:39 +0000 +++ lava/testdef/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,82 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""TestDefinition class.""" - -import yaml - -from copy import deepcopy - -from lava.helper.template import ( - expand_template, - set_value, -) -from lava_tool.utils import ( - write_file, - verify_path_existance, - verify_file_extension, -) - -# Default name for a test definition. -DEFAULT_TESTDEF_FILENAME = "lavatest.yaml" -# Default test def file extension. -DEFAULT_TESTDEF_EXTENSION = "yaml" -# Possible extensions for a test def file. -TESTDEF_FILE_EXTENSIONS = [DEFAULT_TESTDEF_EXTENSION] - - -class TestDefinition(object): - - """A test definition object. - - This class should be used to create test definitions. The initialization - enforces a default file name extension, and makes sure that the file is - not already present on the file system. - """ - - def __init__(self, data, file_name): - """Initialize the object. - - :param data: The serializable data to be used, usually a template. - :type dict - :param file_name: Where the test definition will be written. - :type str - """ - self.file_name = verify_file_extension(file_name, - DEFAULT_TESTDEF_EXTENSION, - TESTDEF_FILE_EXTENSIONS) - verify_path_existance(self.file_name) - - self.data = deepcopy(data) - - def set(self, key, value): - """Set key to the specified value. - - :param key: The key to look in the object data. - :param value: The value to set. - """ - set_value(self.data, key, value) - - def write(self): - """Writes the test definition to file.""" - content = yaml.dump(self.data, default_flow_style=False, indent=4) - write_file(self.file_name, content) - - def update(self, config): - """Updates the TestDefinition object based on the provided config.""" - expand_template(self.data, config) === removed file 'lava/testdef/commands.py' --- lava/testdef/commands.py 2013-07-29 07:47:19 +0000 +++ lava/testdef/commands.py 1970-01-01 00:00:00 +0000 @@ -1,104 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Test definition commands class. -""" - -import os -import tempfile - -from lava.helper.command import BaseCommand -from lava.job import DEFAULT_JOB_FILENAME -from lava.tool.command import CommandGroup -from lava_tool.utils import verify_path_non_existance - - -class testdef(CommandGroup): - - """LAVA test definitions handling.""" - - namespace = "lava.testdef.commands" - - -class TestdefBaseCommand(BaseCommand): - - def _create_tmp_job_file(self, testdef_file): - testdef_file = os.path.abspath(testdef_file) - verify_path_non_existance(testdef_file) - - job_file = os.path.join(tempfile.gettempdir(), - DEFAULT_JOB_FILENAME) - - tar_content = [testdef_file] - job_file = self.create_tar_repo_job(job_file, testdef_file, - tar_content) - - return job_file - - -class new(TestdefBaseCommand): - - """Creates a new test definition file.""" - - @classmethod - def register_arguments(cls, parser): - super(new, cls).register_arguments(parser) - parser.add_argument("FILE", help="Test definition file to create.") - - def invoke(self): - full_path = os.path.abspath(self.args.FILE) - self.create_test_definition(full_path) - - -class run(TestdefBaseCommand): - - """Runs the specified test definition on a local device.""" - - @classmethod - def register_arguments(cls, parser): - super(run, cls).register_arguments(parser) - parser.add_argument("FILE", help="Test definition file to run.") - - def invoke(self): - job_file = "" - try: - job_file = self._create_tmp_job_file(self.args.FILE) - super(run, self).run(job_file) - finally: - if os.path.isfile(job_file): - os.unlink(job_file) - - -class submit(TestdefBaseCommand): - - """Submits the specified test definition to a LAVA server.""" - - @classmethod - def register_arguments(cls, parser): - super(submit, cls).register_arguments(parser) - parser.add_argument("FILE", help="Test definition file to send.") - - def invoke(self): - job_file = "" - try: - job_file = self._create_tmp_job_file(self.args.FILE) - super(submit, self).submit(job_file) - finally: - if os.path.isfile(job_file): - os.unlink(job_file) === removed file 'lava/testdef/templates.py' --- lava/testdef/templates.py 2013-07-26 10:25:39 +0000 +++ lava/testdef/templates.py 1970-01-01 00:00:00 +0000 @@ -1,52 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""Test definition templates.""" - -from lava.parameter import ( - Parameter, -) - -DEFAULT_TESTDEF_VERSION = "1.0" -DEFAULT_TESTDEF_FORMAT = "Lava-Test Test Definition 1.0" -DEFAULT_ENVIRONMET_VALUE = "lava_test_shell" - -# All these parameters will not be stored on the local config file. -NAME_PARAMETER = Parameter("name") -NAME_PARAMETER.store = False - -DESCRIPTION_PARAMETER = Parameter("description", depends=NAME_PARAMETER) -DESCRIPTION_PARAMETER.store = False - -TESTDEF_STEPS_KEY = "steps" - -TESTDEF_TEMPLATE = { - "metadata": { - "name": NAME_PARAMETER, - "format": DEFAULT_TESTDEF_FORMAT, - "version": DEFAULT_TESTDEF_VERSION, - "description": DESCRIPTION_PARAMETER, - "environment": [DEFAULT_ENVIRONMET_VALUE], - }, - "run": { - TESTDEF_STEPS_KEY: ["./mytest.sh"] - }, - "parse": { - "pattern": r'^\s*(?P\w+)=(?P\w+)\s*$' - } -} === removed directory 'lava/testdef/tests' === removed file 'lava/testdef/tests/__init__.py' === removed file 'lava/testdef/tests/test_commands.py' --- lava/testdef/tests/test_commands.py 2013-07-31 12:36:18 +0000 +++ lava/testdef/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,158 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Tests for lava.testdef.commands. -""" - -import os -import tempfile -import yaml - -from mock import ( - MagicMock, - patch, -) - -from lava.config import InteractiveCache -from lava.helper.tests.helper_test import HelperTest -from lava.testdef.commands import ( - new, -) -from lava.tool.errors import CommandError - - -class NewCommandTest(HelperTest): - """Class for the lava.testdef new command tests.""" - - def setUp(self): - super(NewCommandTest, self).setUp() - self.file_name = "fake_testdef.yaml" - self.file_path = os.path.join(tempfile.gettempdir(), self.file_name) - self.args.FILE = self.file_path - - self.temp_yaml = tempfile.NamedTemporaryFile(suffix=".yaml", - delete=False) - - self.config_file = tempfile.NamedTemporaryFile(delete=False) - self.config = InteractiveCache() - self.config.save = MagicMock() - self.config.config_file = self.config_file.name - # Patch class raw_input, start it, and stop it on tearDown. - self.patcher1 = patch("lava.parameter.raw_input", create=True) - self.mocked_raw_input = self.patcher1.start() - - def tearDown(self): - super(NewCommandTest, self).tearDown() - if os.path.isfile(self.file_path): - os.unlink(self.file_path) - os.unlink(self.config_file.name) - os.unlink(self.temp_yaml.name) - self.patcher1.stop() - - def test_register_arguments(self): - # Make sure that the parser add_argument is called and we have the - # correct argument. - new_command = new(self.parser, self.args) - new_command.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("FILE", args) - - def test_invoke_0(self): - # Test that passing a file on the command line, it is created on the - # file system. - self.mocked_raw_input.return_value = "\n" - new_command = new(self.parser, self.args) - new_command.config = self.config - new_command.invoke() - self.assertTrue(os.path.exists(self.file_path)) - - def test_invoke_1(self): - # Test that when passing an already existing file, an exception is - # thrown. - self.args.FILE = self.temp_yaml.name - new_command = new(self.parser, self.args) - new_command.config = self.config - self.assertRaises(CommandError, new_command.invoke) - - def test_invoke_2(self): - # Tests that when adding a new test definition and writing it to file - # a correct YAML structure is created. - self.mocked_raw_input.return_value = "\n" - new_command = new(self.parser, self.args) - new_command.config = self.config - new_command.invoke() - expected = {'run': {'steps': ["./mytest.sh"]}, - 'metadata': { - 'environment': ['lava_test_shell'], - 'format': 'Lava-Test Test Definition 1.0', - 'version': '1.0', - 'description': '', - 'name': ''}, - 'parse': { - 'pattern': - '^\\s*(?P\\w+)=(?P\\w+)\\s*$' - }, - } - obtained = None - with open(self.file_path, 'r') as read_file: - obtained = yaml.load(read_file) - self.assertEqual(expected, obtained) - - def test_invoke_3(self): - # Tests that when adding a new test definition and writing it to a file - # in a directory withour permissions, exception is raised. - self.args.FILE = "/test_file.yaml" - self.mocked_raw_input.return_value = "\n" - new_command = new(self.parser, self.args) - new_command.config = self.config - self.assertRaises(CommandError, new_command.invoke) - self.assertFalse(os.path.exists(self.args.FILE)) - - def test_invoke_4(self): - # Tests that when passing values for the "steps" ListParameter, we get - # back the correct data structure. - self.mocked_raw_input.side_effect = ["foo", "\n", "\n", "\n", "\n", - "\n"] - new_command = new(self.parser, self.args) - new_command.config = self.config - new_command.invoke() - expected = {'run': {'steps': ["./mytest.sh"]}, - 'metadata': { - 'environment': ['lava_test_shell'], - 'format': 'Lava-Test Test Definition 1.0', - 'version': '1.0', - 'description': '', - 'name': 'foo' - }, - 'parse': { - 'pattern': - '^\\s*(?P\\w+)=(?P\\w+)\\s*$' - }, - } - obtained = None - with open(self.file_path, 'r') as read_file: - obtained = yaml.load(read_file) - self.assertEqual(expected, obtained) === removed directory 'lava/tests' === removed file 'lava/tests/__init__.py' === removed file 'lava/tests/test_commands.py' --- lava/tests/test_commands.py 2013-07-26 08:16:30 +0000 +++ lava/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,127 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Tests for lava.commands. -""" - -import os -import tempfile - -from mock import ( - MagicMock, - patch -) - -from lava.commands import ( - init, - submit, -) -from lava.config import Config -from lava.helper.tests.helper_test import HelperTest -from lava.tool.errors import CommandError - - -class InitCommandTests(HelperTest): - - def setUp(self): - super(InitCommandTests, self).setUp() - self.config_file = self.tmp("init_command_tests") - self.config = Config() - self.config.config_file = self.config_file - - def tearDown(self): - super(InitCommandTests, self).tearDown() - if os.path.isfile(self.config_file): - os.unlink(self.config_file) - - def test_register_arguments(self): - self.args.DIR = os.path.join(tempfile.gettempdir(), "a_fake_dir") - init_command = init(self.parser, self.args) - init_command.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("DIR", args) - - @patch("lava.commands.edit_file", create=True) - def test_command_invoke_0(self, mocked_edit_file): - # Invoke the init command passing a path to a file. Should raise an - # exception. - self.args.DIR = self.temp_file.name - init_command = init(self.parser, self.args) - self.assertRaises(CommandError, init_command.invoke) - - def test_command_invoke_2(self): - # Invoke the init command passing a path where the user cannot write. - try: - self.args.DIR = "/root/a_temp_dir" - init_command = init(self.parser, self.args) - self.assertRaises(CommandError, init_command.invoke) - finally: - if os.path.exists(self.args.DIR): - os.removedirs(self.args.DIR) - - def test_update_data(self): - # Make sure the template is updated accordingly with the provided data. - self.args.DIR = self.temp_file.name - - init_command = init(self.parser, self.args) - init_command.config.get = MagicMock() - init_command.config.save = MagicMock() - init_command.config.get.side_effect = ["a_job.json"] - - expected = { - "jobfile": "a_job.json", - } - - obtained = init_command._update_data() - self.assertEqual(expected, obtained) - - -class SubmitCommandTests(HelperTest): - def setUp(self): - super(SubmitCommandTests, self).setUp() - self.config_file = self.tmp("submit_command_tests") - self.config = Config() - self.config.config_file = self.config_file - self.config.save = MagicMock() - - def tearDown(self): - super(SubmitCommandTests, self).tearDown() - if os.path.isfile(self.config_file): - os.unlink(self.config_file) - - def test_register_arguments(self): - self.args.JOB = os.path.join(tempfile.gettempdir(), "a_fake_file") - submit_command = submit(self.parser, self.args) - submit_command.register_arguments(self.parser) - - # Make sure we do not forget about this test. - self.assertEqual(2, len(self.parser.method_calls)) - - _, args, _ = self.parser.method_calls[0] - self.assertIn("--non-interactive", args) - - _, args, _ = self.parser.method_calls[1] - self.assertIn("JOB", args) === removed file 'lava/tests/test_config.py' --- lava/tests/test_config.py 2013-08-07 08:38:50 +0000 +++ lava/tests/test_config.py 1970-01-01 00:00:00 +0000 @@ -1,320 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.config unit tests. -""" - -import os -import shutil -import sys -import tempfile - -from StringIO import StringIO -from mock import ( - MagicMock, - call, - patch, -) - -from lava.config import ( - Config, - InteractiveCache, - InteractiveConfig, -) -from lava.helper.tests.helper_test import HelperTest -from lava.parameter import ( - Parameter, - ListParameter, -) -from lava.tool.errors import CommandError - - -class ConfigTestCase(HelperTest): - """General test case class for the different Config classes.""" - def setUp(self): - super(ConfigTestCase, self).setUp() - self.param1 = Parameter("foo") - self.param2 = Parameter("bar", depends=self.param1) - - -class TestConfigSave(ConfigTestCase): - - """Used to test the save() method of config class. - - Done here since in the other tests we want to mock the atexit save call - in order not to write the file, or accidentaly overwrite the real - user file. - """ - - def setUp(self): - super(TestConfigSave, self).setUp() - self.config = Config() - self.config.config_file = self.temp_file.name - - def test_config_save(self): - self.config.put_parameter(self.param1, "foo") - self.config.save() - - expected = "[DEFAULT]\nfoo = foo\n\n" - obtained = "" - with open(self.temp_file.name) as tmp_file: - obtained = tmp_file.read() - self.assertEqual(expected, obtained) - - def test_save_list_param(self): - # Tests that when saved to file, the ListParameter parameter is stored - # correctly. - param_values = ["foo", "more than one words", "bar"] - list_param = ListParameter("list") - list_param.set(param_values) - - self.config.put_parameter(list_param, param_values) - self.config.save() - - expected = "[DEFAULT]\nlist = " + ",".join(param_values) + "\n\n" - obtained = "" - with open(self.temp_file.name, "r") as read_file: - obtained = read_file.read() - self.assertEqual(expected, obtained) - - -class ConfigTest(ConfigTestCase): - - def setUp(self): - super(ConfigTest, self).setUp() - - self.config_dir = os.path.join(tempfile.gettempdir(), "config") - self.xdg_resource = os.path.join(self.config_dir, "linaro") - self.lavatool_resource = os.path.join(self.xdg_resource, "lava-tool") - - os.makedirs(self.lavatool_resource) - - self.config = Config() - self.config._ensure_xdg_dirs = MagicMock( - return_value=self.lavatool_resource) - self.config.save = MagicMock() - - def tearDown(self): - super(ConfigTest, self).tearDown() - if os.path.isdir(self.config_dir): - shutil.rmtree(self.config_dir) - - def test_ensure_xdg_dirs(self): - # Test that xdg can create the correct cache path, we remove it - # at the end since we patch the default value. - obtained = self.config._ensure_xdg_dirs() - self.assertEquals(self.lavatool_resource, obtained) - - def test_config_file(self): - expected = os.path.join(self.lavatool_resource, "lava-tool.ini") - obtained = self.config.config_file - self.assertEquals(expected, obtained) - - def test_config_put_in_cache_0(self): - self.config._put_in_cache("key", "value", "section") - self.assertEqual(self.config._cache["section"]["key"], "value") - - def test_config_get_from_cache_0(self): - self.config._put_in_cache("key", "value", "section") - obtained = self.config._get_from_cache(Parameter("key"), "section") - self.assertEqual("value", obtained) - - def test_config_get_from_cache_1(self): - self.config._put_in_cache("key", "value", "DEFAULT") - obtained = self.config._get_from_cache(Parameter("key"), "DEFAULT") - self.assertEqual("value", obtained) - - def test_config_put_0(self): - # Puts a value in the DEFAULT section. - self.config._put_in_cache = MagicMock() - self.config.put("foo", "foo") - expected = "foo" - obtained = self.config._config_backend.get("DEFAULT", "foo") - self.assertEqual(expected, obtained) - - def test_config_put_1(self): - # Puts a value in a new section. - self.config._put_in_cache = MagicMock() - self.config.put("foo", "foo", "bar") - expected = "foo" - obtained = self.config._config_backend.get("bar", "foo") - self.assertEqual(expected, obtained) - - def test_config_put_parameter_0(self): - self.config._calculate_config_section = MagicMock(return_value="") - self.assertRaises(CommandError, self.config.put_parameter, self.param1) - - @patch("lava.config.Config.put") - def test_config_put_parameter_1(self, mocked_config_put): - self.config._calculate_config_section = MagicMock( - return_value="DEFAULT") - - self.param1.value = "bar" - self.config.put_parameter(self.param1) - - self.assertEqual(mocked_config_put.mock_calls, - [call("foo", "bar", "DEFAULT")]) - - def test_config_get_0(self): - # Tests that with a non existing parameter, it returns None. - param = Parameter("baz") - self.config._get_from_cache = MagicMock(return_value=None) - self.config._calculate_config_section = MagicMock( - return_value="DEFAULT") - - expected = None - obtained = self.config.get(param) - self.assertEqual(expected, obtained) - - def test_config_get_1(self): - self.config.put_parameter(self.param1, "foo") - self.config._get_from_cache = MagicMock(return_value=None) - self.config._calculate_config_section = MagicMock( - return_value="DEFAULT") - - expected = "foo" - obtained = self.config.get(self.param1) - self.assertEqual(expected, obtained) - - def test_calculate_config_section_0(self): - expected = "DEFAULT" - obtained = self.config._calculate_config_section(self.param1) - self.assertEqual(expected, obtained) - - def test_calculate_config_section_1(self): - self.config.put_parameter(self.param1, "foo") - expected = "foo=foo" - obtained = self.config._calculate_config_section(self.param2) - self.assertEqual(expected, obtained) - - def test_config_get_from_backend_public(self): - # Need to to this, since we want a clean Config instance, with - # a config_file with some content. - with open(self.config.config_file, "w") as write_config: - write_config.write("[DEFAULT]\nfoo=bar\n") - param = Parameter("foo") - obtained = self.config.get_from_backend(param) - self.assertEquals("bar", obtained) - - -class InteractiveConfigTest(ConfigTestCase): - - def setUp(self): - super(InteractiveConfigTest, self).setUp() - self.config = InteractiveConfig() - self.config.save = MagicMock() - self.config.config_file = self.temp_file.name - - @patch("lava.config.Config.get", new=MagicMock(return_value=None)) - def test_non_interactive_config_0(self): - # Try to get a value that does not exists, users just press enter when - # asked for a value. Value will be empty. - self.config.force_interactive = False - sys.stdin = StringIO("\n") - value = self.config.get(Parameter("foo")) - self.assertEqual("", value) - - @patch("lava.config.Config.get", new=MagicMock(return_value="value")) - def test_non_interactive_config_1(self): - # Parent class config returns value, but we are not interactive. - self.config.force_interactive = False - value = self.config.get(Parameter("foo")) - self.assertEqual("value", value) - - @patch("lava.config.Config.get", new=MagicMock(return_value=None)) - def test_non_interactive_config_2(self): - self.config.force_interactive = False - expected = "bar" - sys.stdin = StringIO(expected) - value = self.config.get(Parameter("foo")) - self.assertEqual(expected, value) - - @patch("lava.config.Config.get", new=MagicMock(return_value="value")) - def test_interactive_config_0(self): - # We force to be interactive, meaning that even if a value is found, - # it will be asked anyway. - self.config.force_interactive = True - expected = "a_new_value" - sys.stdin = StringIO(expected) - value = self.config.get(Parameter("foo")) - self.assertEqual(expected, value) - - @patch("lava.config.Config.get", new=MagicMock(return_value="value")) - def test_interactive_config_1(self): - # Force to be interactive, but when asked for the new value press - # Enter. The old value should be returned. - self.config.force_interactive = True - sys.stdin = StringIO("\n") - value = self.config.get(Parameter("foo")) - self.assertEqual("value", value) - - def test_calculate_config_section_0(self): - self.config.force_interactive = True - obtained = self.config._calculate_config_section(self.param1) - expected = "DEFAULT" - self.assertEqual(expected, obtained) - - def test_calculate_config_section_1(self): - self.param1.set("foo") - self.param2.depends.asked = True - self.config.force_interactive = True - obtained = self.config._calculate_config_section(self.param2) - expected = "foo=foo" - self.assertEqual(expected, obtained) - - def test_calculate_config_section_2(self): - self.config.force_interactive = True - self.config.config_backend.get = MagicMock(return_value=None) - sys.stdin = StringIO("baz") - expected = "foo=baz" - obtained = self.config._calculate_config_section(self.param2) - self.assertEqual(expected, obtained) - - def test_calculate_config_section_3(self): - # Tests that when a parameter has its value in the cache and also on - # file, we honor the cached version. - self.param1.set("bar") - self.param2.depends.asked = True - self.config.force_interactive = True - expected = "foo=bar" - obtained = self.config._calculate_config_section(self.param2) - self.assertEqual(expected, obtained) - - @patch("lava.config.Config.get", new=MagicMock(return_value=None)) - @patch("lava.parameter.sys.exit") - @patch("lava.parameter.raw_input", create=True) - def test_interactive_config_exit(self, mocked_raw, mocked_sys_exit): - self.config._calculate_config_section = MagicMock( - return_value="DEFAULT") - - mocked_raw.side_effect = KeyboardInterrupt() - - self.config.force_interactive = True - self.config.get(self.param1) - self.assertTrue(mocked_sys_exit.called) - - @patch("lava.parameter.raw_input", create=True) - def test_interactive_config_with_list_parameter(self, mocked_raw_input): - # Tests that we get a list back in the Config class when using - # ListParameter and that it contains the expected values. - expected = ["foo", "bar"] - mocked_raw_input.side_effect = expected + ["\n"] - obtained = self.config.get(ListParameter("list")) - self.assertIsInstance(obtained, list) - self.assertEqual(expected, obtained) === removed file 'lava/tests/test_parameter.py' --- lava/tests/test_parameter.py 2013-07-26 08:16:30 +0000 +++ lava/tests/test_parameter.py 1970-01-01 00:00:00 +0000 @@ -1,206 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.parameter unit tests. -""" - -from mock import patch - -from lava.helper.tests.helper_test import HelperTest -from lava.parameter import ( - ListParameter, - Parameter, - SingleChoiceParameter, -) -from lava_tool.utils import to_list - - -class GeneralParameterTest(HelperTest): - """General class with setUp and tearDown methods for Parameter tests.""" - def setUp(self): - super(GeneralParameterTest, self).setUp() - # Patch class raw_input, start it, and stop it on tearDown. - self.patcher1 = patch("lava.parameter.raw_input", create=True) - self.mocked_raw_input = self.patcher1.start() - - def tearDown(self): - super(GeneralParameterTest, self).tearDown() - self.patcher1.stop() - - -class ParameterTest(GeneralParameterTest): - """Tests for the Parameter class.""" - - def setUp(self): - super(ParameterTest, self).setUp() - self.parameter1 = Parameter("foo", value="baz") - - def test_prompt_0(self): - # Tests that when we have a value in the parameters and the user press - # Enter, we get the old value back. - self.mocked_raw_input.return_value = "\n" - obtained = self.parameter1.prompt() - self.assertEqual(self.parameter1.value, obtained) - - def test_prompt_1(self,): - # Tests that with a value stored in the parameter, if and EOFError is - # raised when getting user input, we get back the old value. - self.mocked_raw_input.side_effect = EOFError() - obtained = self.parameter1.prompt() - self.assertEqual(self.parameter1.value, obtained) - - def test_to_list_0(self): - value = "a_value" - expected = [value] - obtained = to_list(value) - self.assertIsInstance(obtained, list) - self.assertEquals(expected, obtained) - - def test_to_list_1(self): - expected = ["a_value", "b_value"] - obtained = to_list(expected) - self.assertIsInstance(obtained, list) - self.assertEquals(expected, obtained) - - -class ListParameterTest(GeneralParameterTest): - - """Tests for the specialized ListParameter class.""" - - def setUp(self): - super(ListParameterTest, self).setUp() - self.list_parameter = ListParameter("list") - - def test_prompt_0(self): - # Test that when pressing Enter, the prompt stops and the list is - # returned. - expected = [] - self.mocked_raw_input.return_value = "\n" - obtained = self.list_parameter.prompt() - self.assertEqual(expected, obtained) - - def test_prompt_1(self): - # Tests that when passing 3 values, a list with those values - # is returned - expected = ["foo", "bar", "foobar"] - self.mocked_raw_input.side_effect = expected + ["\n"] - obtained = self.list_parameter.prompt() - self.assertEqual(expected, obtained) - - def test_serialize_0(self): - # Tests the serialize method of ListParameter passing a list. - expected = "foo,bar,baz,1" - to_serialize = ["foo", "bar", "baz", "", 1] - - obtained = self.list_parameter.serialize(to_serialize) - self.assertEqual(expected, obtained) - - def test_serialize_1(self): - # Tests the serialize method of ListParameter passing an int. - expected = "1" - to_serialize = 1 - - obtained = self.list_parameter.serialize(to_serialize) - self.assertEqual(expected, obtained) - - def test_deserialize_0(self): - # Tests the deserialize method of ListParameter with a string - # of values. - expected = ["foo", "bar", "baz"] - to_deserialize = "foo,bar,,baz," - obtained = self.list_parameter.deserialize(to_deserialize) - self.assertEqual(expected, obtained) - - def test_deserialize_1(self): - # Tests the deserialization method of ListParameter passing a list. - expected = ["foo", 1, "", "bar"] - obtained = self.list_parameter.deserialize(expected) - self.assertEqual(expected, obtained) - - def test_set_value_0(self): - # Pass a string to a ListParameter, expect a list. - set_value = "foo" - expected = [set_value] - self.list_parameter.set(set_value) - self.assertEquals(expected, self.list_parameter.value) - - def test_set_value_1(self): - # Pass a list to a ListParameter, expect the same list. - expected = ["foo", "bar"] - self.list_parameter.set(expected) - self.assertEquals(expected, self.list_parameter.value) - - def test_add_value_0(self): - # Add a value to a ListParameter, expect a list back. - add_value = "foo" - expected = [add_value] - self.list_parameter.add(add_value) - self.assertEquals(expected, self.list_parameter.value) - - def test_add_value_1(self): - # Add a list value to a ListParameter with already a value set, expect - # a list with both values. - # The ListParameter is initialized with a string. - add_value = ["foo"] - list_param = ListParameter("list", value="bar") - expected = ["bar", "foo"] - list_param.add(add_value) - self.assertEquals(expected, list_param.value) - - def test_add_value_2(self): - # Add a list value to a ListParameter with already a value set, expect - # a list with both values. - # The ListParameter is initialized with a list. - add_value = ["foo"] - list_param = ListParameter("list", value=["bar", "baz"]) - expected = ["bar", "baz", "foo"] - list_param.add(add_value) - self.assertEquals(expected, list_param.value) - - -class TestsSingleChoiceParameter(GeneralParameterTest): - - def setUp(self): - super(TestsSingleChoiceParameter, self).setUp() - self.choices = ["foo", "bar", "baz", "bam"] - self.param_id = "single_choice" - self.single_choice_param = SingleChoiceParameter(self.param_id, - self.choices) - - def test_with_old_value(self): - # There is an old value for a single choice parameter, the user - # is prompted to select from the list of values, but she presses - # enter. The old value is returned. - old_value = "bat" - self.mocked_raw_input.side_effect = ["\n"] - obtained = self.single_choice_param.prompt("", old_value=old_value) - self.assertEquals(old_value, obtained) - - def test_without_old_value(self): - # There is no old value, user just select the first choice. - self.mocked_raw_input.side_effect = ["1"] - obtained = self.single_choice_param.prompt("") - self.assertEquals("foo", obtained) - - def test_with_wrong_user_input(self): - # No old value, user inserts at least two wrong choices, and the select - # the third one. - self.mocked_raw_input.side_effect = ["1000", "0", "3"] - obtained = self.single_choice_param.prompt("") - self.assertEquals("baz", obtained) === removed directory 'lava/tool' === removed file 'lava/tool/__init__.py' --- lava/tool/__init__.py 2013-05-03 18:51:54 +0000 +++ lava/tool/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,27 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.tool -========= - -Generic code for command line utilities for LAVA -""" - -__version__ = (0, 7, 1, "final", 0) === removed file 'lava/tool/command.py' --- lava/tool/command.py 2012-03-22 18:03:03 +0000 +++ lava/tool/command.py 1970-01-01 00:00:00 +0000 @@ -1,166 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Interface for all lava-tool commands -""" - -import inspect - - -class Command(object): - """ - Base class for all command line tool sub-commands. - """ - - def __init__(self, parser, args): - """ - Prepare instance for executing commands. - - This method is called immediately after all arguments are parsed and - results are available. This gives subclasses a chance to configure - themselves. The provided parser is an instance of - argparse.ArgumentParser but it may not be the top-level parser (it will - be a parser specific for this command) - - The default implementation stores both arguments as instance - attributes. - """ - self.parser = parser - self.args = args - - def say(self, message, *args, **kwargs): - """ - Handy wrapper for print + format - """ - self.args.dispatcher.say(self, message, *args, **kwargs) - - def invoke(self): - """ - Invoke command action. - """ - raise NotImplementedError() - - def reparse_arguments(self, parser, raw_args): - """ - Re-parse raw arguments into normal arguments - - Parser is the same as in register_arguments (a sub-parser) The true, - topmost parser is in self.parser. - - This method is only needed for specific commands that need to peek at - the arguments before being able to truly redefine the parser and - re-parse the raw arguments again. - """ - raise NotImplementedError() - - @classmethod - def get_name(cls): - """ - Return the name of this command. - - The default implementation strips any leading underscores and replaces - all other underscores with dashes. - """ - return cls.__name__.lstrip("_").replace("_", "-") - - @classmethod - def get_help(cls): - """ - Return the help message of this command - """ - doc = inspect.getdoc(cls) - if doc is not None and " " in doc: - doc = doc[:doc.index(" ")].rstrip() - return doc - - @classmethod - def get_epilog(cls): - """ - Return the epilog of the help message - """ - doc = inspect.getdoc(cls) - if doc is not None and " " in doc: - doc = doc[doc.index(" ") + 1:].lstrip() - else: - doc = None - return doc - - @classmethod - def register_arguments(cls, parser): - """ - Register arguments if required. - - Subclasses can override this to add any arguments that will be - exposed to the command line interface. - """ - pass - - -class CommandGroup(Command): - """ - Base class for all command sub-command hubs. - - This class is needed when one wants to get a custom level of command - options that can be freely extended, just like the top-level lava-tool - command. - - For example, a CommandGroup 'actions' will load additional commands from a - the 'lava.actions' namespace. For the end user it will be available as:: - - $ lava-tool foo actions xxx - - Where xxx is one of the Commands that is declared to live in the namespace - provided by 'foo actions'. - """ - - namespace = None - - @classmethod - def get_namespace(cls): - """ - Return the pkg-resources entry point namespace name from which - sub-commands will be loaded. - """ - return cls.namespace - - @classmethod - def register_subcommands(cls, parser): - """ - Register sub commands. - - This method is called around the same time as register_arguments() - would be called for the plain command classes. It loads commands from - the entry point namespace returned by get_namespace() and registeres - them with a Dispatcher class. The parsers used by that dispatcher - are linked to the calling dispatcher parser so the new commands enrich - the top-level parser tree. - - In addition, the provided parser stores a dispatcher instance in its - defaults. This is useful when one wants to access it later. To a final - command instance it shall be available as self.args.dispatcher. - """ - from lava.tool.dispatcher import Dispatcher - dispatcher = Dispatcher(parser, name=cls.get_name()) - namespace = cls.get_namespace() - if namespace is not None: - dispatcher.import_commands(namespace) - parser.set_defaults(dispatcher=dispatcher) - - -SubCommand = CommandGroup === removed directory 'lava/tool/commands' === removed file 'lava/tool/commands/__init__.py' --- lava/tool/commands/__init__.py 2012-03-13 18:17:45 +0000 +++ lava/tool/commands/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,83 +0,0 @@ -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Package with command line commands -""" - -import argparse -import re - - -class ExperimentalNoticeAction(argparse.Action): - """ - Argparse action that implements the --experimental-notice - """ - - message = """ - Some lc-tool sub-commands are marked as EXPERIMENTAL. Those commands are - not guaranteed to work identically, or have identical interface between - subsequent lc-tool releases. - - We do that to make it possible to provide good user interface and - server-side API when working on new features. Once a feature is stabilized - the UI will be frozen and all subsequent changes will retain backwards - compatibility. - """ - message = message.lstrip() - message = re.sub(re.compile("[ \t]+", re.M), " ", message) - message = re.sub(re.compile("^ ", re.M), "", message) - - def __init__(self, - option_strings, dest, default=None, required=False, - help=None): - super(ExperimentalNoticeAction, self).__init__( - option_strings=option_strings, dest=dest, default=default, nargs=0, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - parser.exit(message=self.message) - - -class ExperimentalCommandMixIn(object): - """ - Experimental command. - - Prints a warning message on each call to invoke() - """ - - def invoke(self): - self.print_experimental_notice() - return super(ExperimentalCommandMixIn, self).invoke() - - @classmethod - def register_arguments(cls, parser): - retval = super(ExperimentalCommandMixIn, - cls).register_arguments(parser) - parser.register("action", "experimental_notice", - ExperimentalNoticeAction) - group = parser.add_argument_group("experimental commands") - group.add_argument("--experimental-notice", - action="experimental_notice", - default=argparse.SUPPRESS, - help="Explain the nature of experimental commands") - return retval - - def print_experimental_notice(self): - print ("EXPERIMENTAL - SUBJECT TO CHANGE" - " (See --experimental-notice for more info)") === removed file 'lava/tool/commands/help.py' --- lava/tool/commands/help.py 2012-03-13 18:21:16 +0000 +++ lava/tool/commands/help.py 1970-01-01 00:00:00 +0000 @@ -1,35 +0,0 @@ -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.tool.commands.help -======================= - -Implementation of `lava help` -""" - -from lava.tool.command import Command - - -class help(Command): - """ - Show a summary of all available commands - """ - - def invoke(self): - self.parser.print_help() === removed file 'lava/tool/dispatcher.py' --- lava/tool/dispatcher.py 2013-05-17 19:21:51 +0000 +++ lava/tool/dispatcher.py 1970-01-01 00:00:00 +0000 @@ -1,158 +0,0 @@ -# Copyright (C) 2010, 2011, 2012 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Module with LavaDispatcher - the command dispatcher -""" - -import argparse -import argcomplete -import logging -import pkg_resources -import sys - -from lava.tool.errors import CommandError - - -class Dispatcher(object): - """ - Class implementing command line interface for launch control - """ - - description = None - epilog = None - - def __init__(self, parser=None, name=None): - self.parser = parser or self.construct_parser() - self.subparsers = self.parser.add_subparsers( - title="Sub-command to invoke") - self.name = name - - def __repr__(self): - return "%r(name=%r)" % (self.__class__.__name__, self.name) - - @classmethod - def construct_parser(cls): - """ - Construct a parser for this dispatcher. - - This is only used if the parser is not provided by the parent - dispatcher instance. - """ - parser_args = dict(add_help=True) - # Set description based on class description - if cls.description is not None: - parser_args['description'] = cls.description - # Set the epilog based on class epilog - if cls.epilog is not None: - parser_args['epilog'] = cls.epilog - # Return the fresh parser - return argparse.ArgumentParser(**parser_args) - - def import_commands(self, entrypoint_name): - """ - Import commands from given entry point namespace - """ - logging.debug("Loading commands in entry point %r", entrypoint_name) - for entrypoint in pkg_resources.iter_entry_points(entrypoint_name): - try: - command_cls = entrypoint.load() - except (ImportError, pkg_resources.DistributionNotFound) as exc: - logging.exception("Unable to load command: %s", entrypoint.name) - else: - self.add_command_cls(command_cls) - - def add_command_cls(self, command_cls): - """ - Add a new command class to this dispatcher. - - The command must be a subclass of Command or SubCommand. - """ - logging.debug("Loading command class %r", command_cls) - # Create a sub-parser where the command/sub-command can register - # things. - sub_parser = self.subparsers.add_parser( - command_cls.get_name(), - help=command_cls.get_help(), - epilog=command_cls.get_epilog()) - from lava.tool.command import CommandGroup - if issubclass(command_cls, CommandGroup): - # Handle CommandGroup somewhat different. Instead of calling - # register_arguments we call register_subcommands - command_cls.register_subcommands(sub_parser) - # Let's also call register arguments in case we need both - command_cls.register_arguments(sub_parser) - else: - # Handle plain commands by recording their commands in the - # dedicated sub-parser we've crated for them. - command_cls.register_arguments(sub_parser) - # In addition, since we don't want to require all sub-classes of - # Command to super-call register_arguments (everyone would forget - # this anyway) we manually register the command class for that - # sub-parser so that dispatch() can look it up later. - sub_parser.set_defaults( - command_cls=command_cls, - parser=sub_parser) - # Make sure the sub-parser knows about this dispatcher - sub_parser.set_defaults(dispatcher=self) - - def _adjust_logging_level(self, args): - """ - Adjust logging level after seeing the initial arguments - """ - - def dispatch(self, raw_args=None): - """ - Dispatch a command with the specified arguments. - - If arguments are left out they are looked up in sys.argv automatically - """ - # Before anything, hook into the bash completion - argcomplete.autocomplete(self.parser) - # First parse whatever input arguments we've got - args = self.parser.parse_args(raw_args) - # Adjust logging level after seeing arguments - self._adjust_logging_level(args) - # Then look up the command class and construct it with the parser it - # belongs to and the parsed arguments. - command = args.command_cls(args.parser, args) - try: - # Give the command a chance to re-parse command line arguments - command.reparse_arguments(args.parser, raw_args) - except NotImplementedError: - pass - try: - return command.invoke() - except CommandError as ex: - print >> sys.stderr, "ERROR: %s" % (ex,) - return 1 - - @classmethod - def run(cls, args=None): - """ - Dispatch commandsd and exit - """ - raise SystemExit(cls().dispatch(args)) - - def say(self, command, message, *args, **kwargs): - """ - Handy wrapper for print + format - """ - print "{0} >>> {1}".format( - command.get_name(), - message.format(*args, **kwargs)) === removed file 'lava/tool/errors.py' --- lava/tool/errors.py 2012-03-13 16:17:50 +0000 +++ lava/tool/errors.py 1970-01-01 00:00:00 +0000 @@ -1,31 +0,0 @@ -# Copyright (C) 2010, 2011, 2012 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.tool.errors -================ - -Error classes for LAVA Tool. -""" - -class CommandError(Exception): - """ - Raise this from a Command's invoke() method to display an error nicely. - - lava-tool will exit with a status of 1 if this is raised. - """ === removed file 'lava/tool/main.py' --- lava/tool/main.py 2012-03-22 18:03:03 +0000 +++ lava/tool/main.py 1970-01-01 00:00:00 +0000 @@ -1,132 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -lava.tool.main -============== - -Implementation of the `lava` shell command. -""" - -import logging -import sys - -from lava.tool.dispatcher import Dispatcher - - -class LavaDispatcher(Dispatcher): - """ - Dispatcher implementing the `lava` shell command - - This dispatcher imports plugins from `lava.commands` pkg_resources - namespace. Additional plugins can be registered as either - :class:`lava.command.Command` or :class:`lava.command.SubCommand` - sub-classes. - """ - - def __init__(self): - # Call this early so that we don't get logging.basicConfig - # being called by accident. Otherwise we'd have to - # purge all loggers from the root logger and that sucks - self.setup_logging() - # Initialize the base dispatcher - super(LavaDispatcher, self).__init__() - # And import the non-flat namespace commands - self.import_commands('lava.commands') - - @classmethod - def construct_parser(cls): - """ - Construct a parser for this dispatcher. - - This is only used if the parser is not provided by the parent - dispatcher instance. - """ - # Construct a basic parser - parser = super(LavaDispatcher, cls).construct_parser() - # Add the --verbose flag - parser.add_argument( - "-v", "--verbose", - default=False, - action="store_true", - help="Be more verbose (displays more messages globally)") - # Add the --debug flag - parser.add_argument( - "-D", "--debug", - action="store_true", - default=False, - help="Enable debugging on all loggers") - # Add the --trace flag - parser.add_argument( - "-T", "--trace", - action="append", - default=[], - help="Enable debugging of the specified logger, can be specified multiple times") - # Return the improved parser - return parser - - def setup_logging(self): - """ - Setup logging for the root dispatcher - """ - # Enable warning/error message handler - class OnlyProblemsFilter(logging.Filterer): - def filter(self, record): - if record.levelno >= logging.WARN: - return 1 - return 0 - err_handler = logging.StreamHandler(sys.stderr) - err_handler.setLevel(logging.WARN) - err_handler.setFormatter( - logging.Formatter("%(levelname)s: %(message)s")) - err_handler.addFilter(OnlyProblemsFilter()) - logging.getLogger().addHandler(err_handler) - # Enable the debug handler - class DebugFilter(logging.Filter): - def filter(self, record): - if record.levelno == logging.DEBUG: - return 1 - return 0 - dbg_handler = logging.StreamHandler(sys.stderr) - dbg_handler.setLevel(logging.DEBUG) - dbg_handler.setFormatter( - logging.Formatter("%(levelname)s %(name)s: %(message)s")) - dbg_handler.addFilter(DebugFilter()) - logging.getLogger().addHandler(dbg_handler) - - def _adjust_logging_level(self, args): - # Enable verbose message handler - if args.verbose: - logging.getLogger().setLevel(logging.INFO) - class OnlyInfoFilter(logging.Filterer): - def filter(self, record): - if record.levelno == logging.INFO: - return 1 - return 0 - msg_handler = logging.StreamHandler(sys.stdout) - msg_handler.setLevel(logging.INFO) - msg_handler.setFormatter( - logging.Formatter("%(message)s")) - msg_handler.addFilter(OnlyInfoFilter()) - logging.getLogger().addHandler(msg_handler) - # Enable debugging - if args.debug: - logging.getLogger().setLevel(logging.DEBUG) - # Enable trace loggers - for name in args.trace: - logging.getLogger(name).setLevel(logging.DEBUG) === removed directory 'lava_dashboard_tool' === removed file 'lava_dashboard_tool/__init__.py' --- lava_dashboard_tool/__init__.py 2013-04-22 17:33:58 +0000 +++ lava_dashboard_tool/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,23 +0,0 @@ -# Copyright (C) 2010,2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-dashboard-tool. -# -# lava-dashboard-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-dashboard-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-dashboard-tool. If not, see . - -""" -Launch Control Tool package -""" - -__version__ = (0, 8, 0, "dev", 0) === removed file 'lava_dashboard_tool/commands.py' --- lava_dashboard_tool/commands.py 2013-05-02 14:43:20 +0000 +++ lava_dashboard_tool/commands.py 1970-01-01 00:00:00 +0000 @@ -1,981 +0,0 @@ -# Copyright (C) 2010,2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-dashboard-tool. -# -# lava-dashboard-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-dashboard-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-dashboard-tool. If not, see . - -""" -Module with command-line tool commands that interact with the dashboard -server. All commands listed here should have counterparts in the -launch_control.dashboard_app.xml_rpc package. -""" - -import argparse -import contextlib -import errno -import os -import re -import socket -import sys -import urllib -import urlparse -import xmlrpclib - -import simplejson -from json_schema_validator.extensions import datetime_extension - -from lava_tool.authtoken import AuthenticatingServerProxy, KeyringAuthBackend -from lava.tool.commands import ExperimentalCommandMixIn -from lava.tool.command import Command, CommandGroup - - -class dashboard(CommandGroup): - """ - Commands for interacting with LAVA Dashboard - """ - - namespace = "lava.dashboard.commands" - - -class InsufficientServerVersion(Exception): - """ - Exception raised when server version that a command interacts with is too - old to support required features. - """ - def __init__(self, server_version, required_version): - self.server_version = server_version - self.required_version = required_version - - -class DataSetRenderer(object): - """ - Support class for rendering a table out of list of dictionaries. - - It supports several features that make printing tabular data easier. - * Automatic layout - * Custom column headers - * Custom cell formatting - * Custom table captions - * Custom column ordering - * Custom Column separators - * Custom dataset notification - - The primary method is render() which does all of the above. You - need to pass a dataset argument which is a list of dictionaries. - Each dictionary must have the same keys. In particular the first row - is used to determine columns. - """ - def __init__(self, column_map=None, row_formatter=None, empty=None, - order=None, caption=None, separator=" ", header_separator=None): - if column_map is None: - column_map = {} - if row_formatter is None: - row_formatter = {} - if empty is None: - empty = "There is no data to display" - self.column_map = column_map - self.row_formatter = row_formatter - self.empty = empty - self.order = order - self.separator = separator - self.caption = caption - self.header_separator = header_separator - - def _analyze_dataset(self, dataset): - """ - Determine the columns that will be displayed and the maximum - length of each of those columns. - - Returns a tuple (dataset, columms, maxlen) where columns is a - list of column names and maxlen is a dictionary mapping from - column name to maximum length of any value in the row or the - column header and the dataset is a copy of the dataset altered - as necessary. - - Some examples: - - First the dataset, an array of dictionaries - >>> dataset = [ - ... {'a': 'shorter', 'bee': ''}, - ... {'a': 'little longer', 'bee': 'b'}] - - Note that column 'bee' is actually three characters long as the - column name made it wider. - >>> dataset_out, columns, maxlen = DataSetRenderer( - ... )._analyze_dataset(dataset) - - Unless you format rows with a custom function the data is not altered. - >>> dataset_out is dataset - True - - Columns come out in sorted alphabetic order - >>> columns - ['a', 'bee'] - - Maximum length determines the width of each column. Note that - the header affects the column width. - >>> maxlen - {'a': 13, 'bee': 3} - - You can constrain or reorder columns. In that case columns you - decided to ignore are simply left out of the output. - >>> dataset_out, columns, maxlen = DataSetRenderer( - ... order=['bee'])._analyze_dataset(dataset) - >>> columns - ['bee'] - >>> maxlen - {'bee': 3} - - You can format values anyway you like: - >>> dataset_out, columns, maxlen = DataSetRenderer(row_formatter={ - ... 'bee': lambda value: "%10s" % value} - ... )._analyze_dataset(dataset) - - Dataset is altered to take account of the row formatting - function. The original dataset argument is copied. - >>> dataset_out - [{'a': 'shorter', 'bee': ' '}, {'a': 'little longer', 'bee': ' b'}] - >>> dataset_out is not dataset - True - - Columns stay the same though: - >>> columns - ['a', 'bee'] - - Note how formatting altered the width of the column 'bee' - >>> maxlen - {'a': 13, 'bee': 10} - - You can also format columns (with nice aliases).Note how - column 'bee' maximum width is now dominated by the long column - name: - >>> dataset_out, columns, maxlen = DataSetRenderer(column_map={ - ... 'bee': "Column B"})._analyze_dataset(dataset) - >>> maxlen - {'a': 13, 'bee': 8} - """ - if self.order: - columns = self.order - else: - columns = sorted(dataset[0].keys()) - if self.row_formatter: - dataset_out = [dict(row) for row in dataset] - else: - dataset_out = dataset - for row in dataset_out: - for column in row: - if column in self.row_formatter: - row[column] = self.row_formatter[column](row[column]) - maxlen = dict( - [(column, max( - len(self.column_map.get(column, column)), - max([ - len(str(row[column])) for row in dataset_out]))) - for column in columns]) - return dataset_out, columns, maxlen - - def _render_header(self, dataset, columns, maxlen): - """ - Render a header, possibly with a caption string - - Caption is controlled by the constructor. - >>> dataset = [ - ... {'a': 'shorter', 'bee': ''}, - ... {'a': 'little longer', 'bee': 'b'}] - >>> columns = ['a', 'bee'] - >>> maxlen = {'a': 13, 'bee': 3} - - By default there is no caption, just column names: - >>> DataSetRenderer()._render_header( - ... dataset, columns, maxlen) - a bee - - If you enable the header separator then column names will be visually - separated from the first row of data. - >>> DataSetRenderer(header_separator=True)._render_header( - ... dataset, columns, maxlen) - a bee - ----------------- - - If you provide a caption it gets rendered as a centered - underlined text before the data: - >>> DataSetRenderer(caption="Dataset")._render_header( - ... dataset, columns, maxlen) - Dataset - ================= - a bee - - You can use both caption and header separator - >>> DataSetRenderer(caption="Dataset", header_separator=True)._render_header( - ... dataset, columns, maxlen) - Dataset - ================= - a bee - ----------------- - - Observe how the total length of the output horizontal line - depends on the separator! Also note the columns labels are - aligned to the center of the column - >>> DataSetRenderer(caption="Dataset", separator=" | ")._render_header( - ... dataset, columns, maxlen) - Dataset - =================== - a | bee - """ - total_len = sum(maxlen.itervalues()) - if len(columns): - total_len += len(self.separator) * (len(columns) - 1) - # Print the caption - if self.caption: - print "{0:^{1}}".format(self.caption, total_len) - print "=" * total_len - # Now print the coulum names - print self.separator.join([ - "{0:^{1}}".format(self.column_map.get(column, column), - maxlen[column]) for column in columns]) - # Finally print the header separator - if self.header_separator: - print "-" * total_len - - def _render_rows(self, dataset, columns, maxlen): - """ - Render rows of the dataset. - - Each row is printed on one line using the maxlen argument to - determine correct column size. Text is aligned left. - - First the dataset, columns and maxlen as produced by - _analyze_dataset() - >>> dataset = [ - ... {'a': 'shorter', 'bee': ''}, - ... {'a': 'little longer', 'bee': 'b'}] - >>> columns = ['a', 'bee'] - >>> maxlen = {'a': 13, 'bee': 3} - - Now a plain table. Note! To really understand this test - you should check out the length of the strings below. There - are two more spaces after 'b' in the second row - >>> DataSetRenderer()._render_rows(dataset, columns, maxlen) - shorter - little longer b - """ - for row in dataset: - print self.separator.join([ - "{0!s:{1}}".format(row[column], maxlen[column]) - for column in columns]) - - def _render_dataset(self, dataset): - """ - Render the header followed by the rows of data. - """ - dataset, columns, maxlen = self._analyze_dataset(dataset) - self._render_header(dataset, columns, maxlen) - self._render_rows(dataset, columns, maxlen) - - def _render_empty_dataset(self): - """ - Render empty dataset. - - By default it just prints out a fixed sentence: - >>> DataSetRenderer()._render_empty_dataset() - There is no data to display - - This can be changed by passing an argument to the constructor - >>> DataSetRenderer(empty="there is no data")._render_empty_dataset() - there is no data - """ - print self.empty - - def render(self, dataset): - if len(dataset) > 0: - self._render_dataset(dataset) - else: - self._render_empty_dataset() - - -class XMLRPCCommand(Command): - """ - Abstract base class for commands that interact with dashboard server - over XML-RPC. - - The only difference is that you should implement invoke_remote() - instead of invoke(). The provided implementation catches several - socket and XML-RPC errors and prints a pretty error message. - """ - - @staticmethod - def _construct_xml_rpc_url(url): - """ - Construct URL to the XML-RPC service out of the given URL - """ - parts = urlparse.urlsplit(url) - if not parts.path.endswith("/RPC2/"): - path = parts.path.rstrip("/") + "/xml-rpc/" - else: - path = parts.path - return urlparse.urlunsplit( - (parts.scheme, parts.netloc, path, "", "")) - - @staticmethod - def _strict_server_version(version): - """ - Calculate strict server version (as defined by - distutils.version.StrictVersion). This works by discarding .candidate - and .dev release-levels. - >>> XMLRPCCommand._strict_server_version("0.4.0.candidate.5") - '0.4.0' - >>> XMLRPCCommand._strict_server_version("0.4.0.dev.126") - '0.4.0' - >>> XMLRPCCommand._strict_server_version("0.4.0.alpha.1") - '0.4.0a1' - >>> XMLRPCCommand._strict_server_version("0.4.0.beta.2") - '0.4.0b2' - """ - try: - major, minor, micro, releaselevel, serial = version.split(".") - except ValueError: - raise ValueError( - ("version %r does not follow pattern " - "'major.minor.micro.releaselevel.serial'") % version) - if releaselevel in ["dev", "candidate", "final"]: - return "%s.%s.%s" % (major, minor, micro) - elif releaselevel == "alpha": - return "%s.%s.%sa%s" % (major, minor, micro, serial) - elif releaselevel == "beta": - return "%s.%s.%sb%s" % (major, minor, micro, serial) - else: - raise ValueError( - ("releaselevel %r is not one of 'final', 'alpha', 'beta', " - "'candidate' or 'final'") % releaselevel) - - def _check_server_version(self, server_obj, required_version): - """ - Check that server object has is at least required_version. - - This method may raise InsufficientServerVersion. - """ - from distutils.version import StrictVersion, LooseVersion - # For backwards compatibility the server reports - # major.minor.micro.releaselevel.serial which is not PEP-386 compliant - server_version = StrictVersion( - self._strict_server_version(server_obj.version())) - required_version = StrictVersion(required_version) - if server_version < required_version: - raise InsufficientServerVersion(server_version, required_version) - - def __init__(self, parser, args): - super(XMLRPCCommand, self).__init__(parser, args) - xml_rpc_url = self._construct_xml_rpc_url(self.args.dashboard_url) - self.server = AuthenticatingServerProxy( - xml_rpc_url, - verbose=args.verbose_xml_rpc, - allow_none=True, - use_datetime=True, - auth_backend=KeyringAuthBackend()) - - def use_non_legacy_api_if_possible(self, name='server'): - # Legacy APIs are registered in top-level object, non-legacy APIs are - # prefixed with extension name. - if "dashboard.version" in getattr(self, name).system.listMethods(): - setattr(self, name, getattr(self, name).dashboard) - - @classmethod - def register_arguments(cls, parser): - dashboard_group = parser.add_argument_group("dashboard specific arguments") - default_dashboard_url = os.getenv("DASHBOARD_URL") - if default_dashboard_url: - dashboard_group.add_argument("--dashboard-url", - metavar="URL", help="URL of your validation dashboard (currently %(default)s)", - default=default_dashboard_url) - else: - dashboard_group.add_argument("--dashboard-url", required=True, - metavar="URL", help="URL of your validation dashboard") - debug_group = parser.add_argument_group("debugging arguments") - debug_group.add_argument("--verbose-xml-rpc", - action="store_true", default=False, - help="Show XML-RPC data") - return dashboard_group - - @contextlib.contextmanager - def safety_net(self): - try: - yield - except socket.error as ex: - print >> sys.stderr, "Unable to connect to server at %s" % ( - self.args.dashboard_url,) - # It seems that some errors are reported as -errno - # while others as +errno. - ex.errno = abs(ex.errno) - if ex.errno == errno.ECONNREFUSED: - print >> sys.stderr, "Connection was refused." - parts = urlparse.urlsplit(self.args.dashboard_url) - if parts.netloc == "localhost:8000": - print >> sys.stderr, "Perhaps the server is not running?" - elif ex.errno == errno.ENOENT: - print >> sys.stderr, "Unable to resolve address" - else: - print >> sys.stderr, "Socket %d: %s" % (ex.errno, ex.strerror) - except xmlrpclib.ProtocolError as ex: - print >> sys.stderr, "Unable to exchange XML-RPC message with dashboard server" - print >> sys.stderr, "HTTP error code: %d/%s" % (ex.errcode, ex.errmsg) - except xmlrpclib.Fault as ex: - self.handle_xmlrpc_fault(ex.faultCode, ex.faultString) - except InsufficientServerVersion as ex: - print >> sys.stderr, ("This command requires at least server version " - "%s, actual server version is %s" % - (ex.required_version, ex.server_version)) - - def invoke(self): - with self.safety_net(): - self.use_non_legacy_api_if_possible() - return self.invoke_remote() - - def handle_xmlrpc_fault(self, faultCode, faultString): - if faultCode == 500: - print >> sys.stderr, "Dashboard server has experienced internal error" - print >> sys.stderr, faultString - else: - print >> sys.stderr, "XML-RPC error %d: %s" % (faultCode, faultString) - - def invoke_remote(self): - raise NotImplementedError() - - -class server_version(XMLRPCCommand): - """ - Display dashboard server version - """ - - def invoke_remote(self): - print "Dashboard server version: %s" % (self.server.version(),) - - -class put(XMLRPCCommand): - """ - Upload a bundle on the server - """ - - @classmethod - def register_arguments(cls, parser): - super(put, cls).register_arguments(parser) - parser.add_argument("LOCAL", - type=argparse.FileType("rb"), - help="pathname on the local file system") - parser.add_argument("REMOTE", - default="/anonymous/", nargs='?', - help="pathname on the server") - - def invoke_remote(self): - content = self.args.LOCAL.read() - filename = self.args.LOCAL.name - pathname = self.args.REMOTE - content_sha1 = self.server.put(content, filename, pathname) - print "Stored as bundle {0}".format(content_sha1) - - def handle_xmlrpc_fault(self, faultCode, faultString): - if faultCode == 404: - print >> sys.stderr, "Bundle stream %s does not exist" % ( - self.args.REMOTE) - elif faultCode == 409: - print >> sys.stderr, "You have already uploaded this bundle to the dashboard" - else: - super(put, self).handle_xmlrpc_fault(faultCode, faultString) - - -class get(XMLRPCCommand): - """ - Download a bundle from the server - """ - - @classmethod - def register_arguments(cls, parser): - super(get, cls).register_arguments(parser) - parser.add_argument("SHA1", - type=str, - help="SHA1 of the bundle to download") - parser.add_argument("--overwrite", - action="store_true", - help="Overwrite files on the local disk") - parser.add_argument("--output", "-o", - type=argparse.FileType("wb"), - default=None, - help="Alternate name of the output file") - - def invoke_remote(self): - response = self.server.get(self.args.SHA1) - if self.args.output is None: - filename = self.args.SHA1 - if os.path.exists(filename) and not self.args.overwrite: - print >> sys.stderr, "File {filename!r} already exists".format( - filename=filename) - print >> sys.stderr, "You may pass --overwrite to write over it" - return -1 - stream = open(filename, "wb") - else: - stream = self.args.output - filename = self.args.output.name - stream.write(response['content']) - print "Downloaded bundle {0} to file {1!r}".format( - self.args.SHA1, filename) - - def handle_xmlrpc_fault(self, faultCode, faultString): - if faultCode == 404: - print >> sys.stderr, "Bundle {sha1} does not exist".format( - sha1=self.args.SHA1) - else: - super(get, self).handle_xmlrpc_fault(faultCode, faultString) - - -class deserialize(XMLRPCCommand): - """ - Deserialize a bundle on the server - """ - - @classmethod - def register_arguments(cls, parser): - super(deserialize, cls).register_arguments(parser) - parser.add_argument("SHA1", - type=str, - help="SHA1 of the bundle to deserialize") - - def invoke_remote(self): - response = self.server.deserialize(self.args.SHA1) - print "Bundle {sha1} deserialized".format( - sha1=self.args.SHA1) - - def handle_xmlrpc_fault(self, faultCode, faultString): - if faultCode == 404: - print >> sys.stderr, "Bundle {sha1} does not exist".format( - sha1=self.args.SHA1) - elif faultCode == 409: - print >> sys.stderr, "Unable to deserialize bundle {sha1}".format( - sha1=self.args.SHA1) - print >> sys.stderr, faultString - else: - super(deserialize, self).handle_xmlrpc_fault(faultCode, faultString) - - -def _get_pretty_renderer(**kwargs): - if "separator" not in kwargs: - kwargs["separator"] = " | " - if "header_separator" not in kwargs: - kwargs["header_separator"] = True - return DataSetRenderer(**kwargs) - - -class streams(XMLRPCCommand): - """ - Show streams you have access to - """ - - renderer = _get_pretty_renderer( - order=('pathname', 'bundle_count', 'name'), - column_map={ - 'pathname': 'Pathname', - 'bundle_count': 'Number of bundles', - 'name': 'Name'}, - row_formatter={ - 'name': lambda name: name or "(not set)"}, - empty="There are no streams you can access on the server", - caption="Bundle streams") - - def invoke_remote(self): - self.renderer.render(self.server.streams()) - - -class bundles(XMLRPCCommand): - """ - Show bundles in the specified stream - """ - - renderer = _get_pretty_renderer( - column_map={ - 'uploaded_by': 'Uploader', - 'uploaded_on': 'Upload date', - 'content_filename': 'File name', - 'content_sha1': 'SHA1', - 'is_deserialized': "Deserialized?"}, - row_formatter={ - 'is_deserialized': lambda x: "yes" if x else "no", - 'uploaded_by': lambda x: x or "(anonymous)", - 'uploaded_on': lambda x: x}, - order=('content_sha1', 'content_filename', 'uploaded_by', - 'uploaded_on', 'is_deserialized'), - empty="There are no bundles in this stream", - caption="Bundles", - separator=" | ") - - @classmethod - def register_arguments(cls, parser): - super(bundles, cls).register_arguments(parser) - parser.add_argument("PATHNAME", - default="/anonymous/", nargs='?', - help="pathname on the server (defaults to %(default)s)") - - def invoke_remote(self): - self.renderer.render(self.server.bundles(self.args.PATHNAME)) - - def handle_xmlrpc_fault(self, faultCode, faultString): - if faultCode == 404: - print >> sys.stderr, "Bundle stream %s does not exist" % ( - self.args.PATHNAME) - else: - super(bundles, self).handle_xmlrpc_fault(faultCode, faultString) - - -class make_stream(XMLRPCCommand): - """ - Create a bundle stream on the server - """ - - @classmethod - def register_arguments(cls, parser): - super(make_stream, cls).register_arguments(parser) - parser.add_argument( - "pathname", - type=str, - help="Pathname of the bundle stream to create") - parser.add_argument( - "--name", - type=str, - default="", - help="Name of the bundle stream (description)") - - def invoke_remote(self): - self._check_server_version(self.server, "0.3") - pathname = self.server.make_stream(self.args.pathname, self.args.name) - print "Bundle stream {pathname} created".format(pathname=pathname) - - -class backup(XMLRPCCommand): - """ - Backup data uploaded to a dashboard instance. - - Not all data is preserved. The following data is lost: identity of the user - that uploaded each bundle, time of uploading and deserialization on the - server, name of the bundle stream that contained the data - """ - - @classmethod - def register_arguments(cls, parser): - super(backup, cls).register_arguments(parser) - parser.add_argument("BACKUP_DIR", type=str, - help="Directory to backup to") - - def invoke_remote(self): - if not os.path.exists(self.args.BACKUP_DIR): - os.mkdir(self.args.BACKUP_DIR) - for bundle_stream in self.server.streams(): - print "Processing stream %s" % bundle_stream["pathname"] - bundle_stream_dir = os.path.join(self.args.BACKUP_DIR, urllib.quote_plus(bundle_stream["pathname"])) - if not os.path.exists(bundle_stream_dir): - os.mkdir(bundle_stream_dir) - with open(os.path.join(bundle_stream_dir, "metadata.json"), "wt") as stream: - simplejson.dump({ - "pathname": bundle_stream["pathname"], - "name": bundle_stream["name"], - "user": bundle_stream["user"], - "group": bundle_stream["group"], - }, stream) - for bundle in self.server.bundles(bundle_stream["pathname"]): - print " * Backing up bundle %s" % bundle["content_sha1"] - data = self.server.get(bundle["content_sha1"]) - bundle_pathname = os.path.join(bundle_stream_dir, bundle["content_sha1"]) - # Note: we write bundles as binary data to preserve anything the user might have dumped on us - with open(bundle_pathname + ".json", "wb") as stream: - stream.write(data["content"]) - with open(bundle_pathname + ".metadata.json", "wt") as stream: - simplejson.dump({ - "uploaded_by": bundle["uploaded_by"], - "uploaded_on": datetime_extension.to_json(bundle["uploaded_on"]), - "content_filename": bundle["content_filename"], - "content_sha1": bundle["content_sha1"], - "content_size": bundle["content_size"], - }, stream) - - -class restore(XMLRPCCommand): - """ - Restore a dashboard instance from backup - """ - - @classmethod - def register_arguments(cls, parser): - super(restore, cls).register_arguments(parser) - parser.add_argument("BACKUP_DIR", type=str, - help="Directory to backup from") - - def invoke_remote(self): - self._check_server_version(self.server, "0.3") - for stream_pathname_quoted in os.listdir(self.args.BACKUP_DIR): - filesystem_stream_pathname = os.path.join(self.args.BACKUP_DIR, stream_pathname_quoted) - if not os.path.isdir(filesystem_stream_pathname): - continue - stream_pathname = urllib.unquote(stream_pathname_quoted) - if os.path.exists(os.path.join(filesystem_stream_pathname, "metadata.json")): - with open(os.path.join(filesystem_stream_pathname, "metadata.json"), "rt") as stream: - stream_metadata = simplejson.load(stream) - else: - stream_metadata = {} - print "Processing stream %s" % stream_pathname - try: - self.server.make_stream(stream_pathname, stream_metadata.get("name", "Restored from backup")) - except xmlrpclib.Fault as ex: - if ex.faultCode != 409: - raise - for content_sha1 in [item[:-len(".json")] for item in os.listdir(filesystem_stream_pathname) if item.endswith(".json") and not item.endswith(".metadata.json") and item != "metadata.json"]: - filesystem_content_filename = os.path.join(filesystem_stream_pathname, content_sha1 + ".json") - if not os.path.isfile(filesystem_content_filename): - continue - with open(os.path.join(filesystem_stream_pathname, content_sha1) + ".metadata.json", "rt") as stream: - bundle_metadata = simplejson.load(stream) - with open(filesystem_content_filename, "rb") as stream: - content = stream.read() - print " * Restoring bundle %s" % content_sha1 - try: - self.server.put(content, bundle_metadata["content_filename"], stream_pathname) - except xmlrpclib.Fault as ex: - if ex.faultCode != 409: - raise - - -class pull(ExperimentalCommandMixIn, XMLRPCCommand): - """ - Copy bundles and bundle streams from one dashboard to another. - - This command checks for two environment varialbes: - The value of DASHBOARD_URL is used as a replacement for --dashbard-url. - The value of REMOTE_DASHBOARD_URL as a replacement for FROM. - Their presence automatically makes the corresponding argument optional. - """ - - def __init__(self, parser, args): - super(pull, self).__init__(parser, args) - remote_xml_rpc_url = self._construct_xml_rpc_url(self.args.FROM) - self.remote_server = AuthenticatingServerProxy( - remote_xml_rpc_url, - verbose=args.verbose_xml_rpc, - use_datetime=True, - allow_none=True, - auth_backend=KeyringAuthBackend()) - self.use_non_legacy_api_if_possible('remote_server') - - @classmethod - def register_arguments(cls, parser): - group = super(pull, cls).register_arguments(parser) - default_remote_dashboard_url = os.getenv("REMOTE_DASHBOARD_URL") - if default_remote_dashboard_url: - group.add_argument( - "FROM", nargs="?", - help="URL of the remote validation dashboard (currently %(default)s)", - default=default_remote_dashboard_url) - else: - group.add_argument( - "FROM", - help="URL of the remote validation dashboard)") - group.add_argument("STREAM", nargs="*", help="Streams to pull from (all by default)") - - @staticmethod - def _filesizeformat(num_bytes): - """ - Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, - 102 num_bytes, etc). - """ - try: - num_bytes = float(num_bytes) - except (TypeError, ValueError, UnicodeDecodeError): - return "%(size)d byte", "%(size)d num_bytes" % {'size': 0} - - filesize_number_format = lambda value: "%0.2f" % (round(value, 1),) - - if num_bytes < 1024: - return "%(size)d bytes" % {'size': num_bytes} - if num_bytes < 1024 * 1024: - return "%s KB" % filesize_number_format(num_bytes / 1024) - if num_bytes < 1024 * 1024 * 1024: - return "%s MB" % filesize_number_format(num_bytes / (1024 * 1024)) - return "%s GB" % filesize_number_format(num_bytes / (1024 * 1024 * 1024)) - - def invoke_remote(self): - self._check_server_version(self.server, "0.3") - - print "Checking local and remote streams" - remote = self.remote_server.streams() - if self.args.STREAM: - # Check that all requested streams are available remotely - requested_set = frozenset(self.args.STREAM) - remote_set = frozenset((stream["pathname"] for stream in remote)) - unavailable_set = requested_set - remote_set - if unavailable_set: - print >> sys.stderr, "Remote stream not found: %s" % ", ".join(unavailable_set) - return -1 - # Limit to requested streams if necessary - remote = [stream for stream in remote if stream["pathname"] in requested_set] - local = self.server.streams() - missing_pathnames = set([stream["pathname"] for stream in remote]) - set([stream["pathname"] for stream in local]) - for stream in remote: - if stream["pathname"] in missing_pathnames: - self.server.make_stream(stream["pathname"], stream["name"]) - local_bundles = [] - else: - local_bundles = [bundle for bundle in self.server.bundles(stream["pathname"])] - remote_bundles = [bundle for bundle in self.remote_server.bundles(stream["pathname"])] - missing_bundles = set((bundle["content_sha1"] for bundle in remote_bundles)) - missing_bundles -= set((bundle["content_sha1"] for bundle in local_bundles)) - try: - missing_bytes = sum( - (bundle["content_size"] - for bundle in remote_bundles - if bundle["content_sha1"] in missing_bundles)) - except KeyError as ex: - # Older servers did not return content_size so this part is optional - missing_bytes = None - if missing_bytes: - print "Stream %s needs update (%s)" % (stream["pathname"], self._filesizeformat(missing_bytes)) - elif missing_bundles: - print "Stream %s needs update (no estimate available)" % (stream["pathname"],) - else: - print "Stream %s is up to date" % (stream["pathname"],) - for content_sha1 in missing_bundles: - print "Getting %s" % (content_sha1,), - sys.stdout.flush() - data = self.remote_server.get(content_sha1) - print "got %s, storing" % (self._filesizeformat(len(data["content"]))), - sys.stdout.flush() - try: - self.server.put(data["content"], data["content_filename"], stream["pathname"]) - except xmlrpclib.Fault as ex: - if ex.faultCode == 409: # duplicate - print "already present (in another stream)" - else: - raise - else: - print "done" - - -class data_views(ExperimentalCommandMixIn, XMLRPCCommand): - """ - Show data views defined on the server - """ - renderer = _get_pretty_renderer( - column_map={ - 'name': 'Name', - 'summary': 'Summary', - }, - order=('name', 'summary'), - empty="There are no data views defined yet", - caption="Data Views") - - def invoke_remote(self): - self._check_server_version(self.server, "0.4") - self.renderer.render(self.server.data_views()) - print - print "Tip: to invoke a data view try `lc-tool query-data-view`" - - -class query_data_view(ExperimentalCommandMixIn, XMLRPCCommand): - """ - Invoke a specified data view - """ - @classmethod - def register_arguments(cls, parser): - super(query_data_view, cls).register_arguments(parser) - parser.add_argument("QUERY", metavar="QUERY", nargs="...", - help="Data view name and any optional and required arguments") - - def _probe_data_views(self): - """ - Probe the server for information about data views - """ - with self.safety_net(): - self.use_non_legacy_api_if_possible() - self._check_server_version(self.server, "0.4") - return self.server.data_views() - - def reparse_arguments(self, parser, raw_args): - self.data_views = self._probe_data_views() - if self.data_views is None: - return - # Here we hack a little, the last actuin is the QUERY action added - # in register_arguments above. By removing it we make the output - # of lc-tool query-data-view NAME --help more consistent. - del parser._actions[-1] - subparsers = parser.add_subparsers( - title="Data views available on the server") - for data_view in self.data_views: - data_view_parser = subparsers.add_parser( - data_view["name"], - help=data_view["summary"], - epilog=data_view["documentation"]) - data_view_parser.set_defaults(data_view=data_view) - group = data_view_parser.add_argument_group("Data view parameters") - for argument in data_view["arguments"]: - if argument["default"] is None: - group.add_argument( - "--{name}".format(name=argument["name"].replace("_", "-")), - dest=argument["name"], - help=argument["help"], - type=str, - required=True) - else: - group.add_argument( - "--{name}".format(name=argument["name"].replace("_", "-")), - dest=argument["name"], - help=argument["help"], - type=str, - default=argument["default"]) - self.args = self.parser.parse_args(raw_args) - - def invoke(self): - # Override and _not_ call 'use_non_legacy_api_if_possible' as we - # already did this reparse_arguments - with self.safety_net(): - return self.invoke_remote() - - def invoke_remote(self): - if self.data_views is None: - return -1 - self._check_server_version(self.server, "0.4") - # Build a collection of arguments for data view - data_view_args = {} - for argument in self.args.data_view["arguments"]: - arg_name = argument["name"] - if arg_name in self.args: - data_view_args[arg_name] = getattr(self.args, arg_name) - # Invoke the data view - response = self.server.query_data_view(self.args.data_view["name"], data_view_args) - # Create a pretty-printer - renderer = _get_pretty_renderer( - caption=self.args.data_view["summary"], - order=[item["name"] for item in response["columns"]]) - # Post-process the data so that it fits the printer - data_for_renderer = [ - dict(zip( - [column["name"] for column in response["columns"]], - row)) - for row in response["rows"]] - # Print the data - renderer.render(data_for_renderer) - - -class version(Command): - """ - Show dashboard client version - """ - def invoke(self): - import versiontools - from lava_dashboard_tool import __version__ - print "Dashboard client version: {version}".format( - version=versiontools.format_version(__version__)) === removed file 'lava_dashboard_tool/main.py' --- lava_dashboard_tool/main.py 2013-04-22 17:33:58 +0000 +++ lava_dashboard_tool/main.py 1970-01-01 00:00:00 +0000 @@ -1,37 +0,0 @@ -# Copyright (C) 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# Author: Michael Hudson-Doyle -# -# This file is part of lava-dashboard-tool. -# -# lava-dashboard-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-dashboard-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-dashboard-tool. If not, see . - - -from lava_tool.dispatcher import LavaDispatcher, run_with_dispatcher_class - - -class LaunchControlDispatcher(LavaDispatcher): - - toolname = 'lava_dashboard_tool' - description = """ - Command line tool for interacting with Launch Control - """ - epilog = """ - Please report all bugs using the Launchpad bug tracker: - http://bugs.launchpad.net/lava-dashboard-tool/+filebug - """ - - -def main(): - run_with_dispatcher_class(LaunchControlDispatcher) === removed directory 'lava_dashboard_tool/tests' === removed file 'lava_dashboard_tool/tests/__init__.py' === removed file 'lava_dashboard_tool/tests/test_commands.py' --- lava_dashboard_tool/tests/test_commands.py 2013-04-22 17:33:58 +0000 +++ lava_dashboard_tool/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,44 +0,0 @@ -# Copyright (C) 2010,2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-dashboard-tool. -# -# lava-dashboard-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-dashboard-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-dashboard-tool. If not, see . - -""" -Unit tests for the launch_control.commands package -""" - -from unittest import TestCase - -from lava_dashboard_tool.commands import XMLRPCCommand - - -class XMLRPCCommandTestCase(TestCase): - - def test_construct_xml_rpc_url_preserves_path(self): - self.assertEqual( - XMLRPCCommand._construct_xml_rpc_url("http://domain/path"), - "http://domain/path/xml-rpc/") - self.assertEqual( - XMLRPCCommand._construct_xml_rpc_url("http://domain/path/"), - "http://domain/path/xml-rpc/") - - def test_construct_xml_rpc_url_adds_proper_suffix(self): - self.assertEqual( - XMLRPCCommand._construct_xml_rpc_url("http://domain/"), - "http://domain/xml-rpc/") - self.assertEqual( - XMLRPCCommand._construct_xml_rpc_url("http://domain"), - "http://domain/xml-rpc/") === removed directory 'lava_scheduler_tool' === removed file 'lava_scheduler_tool/__init__.py' --- lava_scheduler_tool/__init__.py 2013-04-22 17:23:45 +0000 +++ lava_scheduler_tool/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,19 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-scheduler-tool. -# -# lava-scheduler-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-scheduler-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-scheduler-tool. If not, see . - -__version__ = (0, 6, 0, "dev", 0) === removed file 'lava_scheduler_tool/commands.py' --- lava_scheduler_tool/commands.py 2013-07-04 09:29:28 +0000 +++ lava_scheduler_tool/commands.py 1970-01-01 00:00:00 +0000 @@ -1,157 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-scheduler-tool. -# -# lava-scheduler-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-scheduler-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-scheduler-tool. If not, see . - -import os -import sys -import argparse -import xmlrpclib - -from lava_tool.authtoken import AuthenticatingServerProxy, KeyringAuthBackend -from lava.tool.command import Command, CommandGroup -from lava.tool.errors import CommandError -from lava.tool.commands import ExperimentalCommandMixIn - - -class scheduler(CommandGroup): - """ - Interact with LAVA Scheduler - """ - - namespace = "lava.scheduler.commands" - - -class submit_job(ExperimentalCommandMixIn, Command): - """ - Submit a job to lava-scheduler - """ - - @classmethod - def register_arguments(cls, parser): - super(submit_job, cls).register_arguments(parser) - parser.add_argument("SERVER") - parser.add_argument("JSON_FILE") - - def invoke(self): - self.print_experimental_notice() - server = AuthenticatingServerProxy( - self.args.SERVER, auth_backend=KeyringAuthBackend()) - with open(self.args.JSON_FILE, 'rb') as stream: - command_text = stream.read() - try: - job_id = server.scheduler.submit_job(command_text) - except xmlrpclib.Fault, e: - raise CommandError(str(e)) - else: - print "submitted as job id:", job_id - - -class resubmit_job(ExperimentalCommandMixIn, Command): - - @classmethod - def register_arguments(self, parser): - parser.add_argument("SERVER") - parser.add_argument("JOB_ID", type=int) - - def invoke(self): - self.print_experimental_notice() - server = AuthenticatingServerProxy( - self.args.SERVER, auth_backend=KeyringAuthBackend()) - try: - job_id = server.scheduler.resubmit_job(self.args.JOB_ID) - except xmlrpclib.Fault, e: - raise CommandError(str(e)) - else: - print "resubmitted as job id:", job_id - - -class cancel_job(ExperimentalCommandMixIn, Command): - - @classmethod - def register_arguments(self, parser): - parser.add_argument("SERVER") - parser.add_argument("JOB_ID", type=int) - - def invoke(self): - self.print_experimental_notice() - server = AuthenticatingServerProxy( - self.args.SERVER, auth_backend=KeyringAuthBackend()) - server.scheduler.cancel_job(self.args.JOB_ID) - - -class job_output(Command): - """ - Get job output from the scheduler. - """ - - @classmethod - def register_arguments(cls, parser): - super(job_output, cls).register_arguments(parser) - parser.add_argument("SERVER") - parser.add_argument("JOB_ID", - type=int, - help="Job ID to download output file") - parser.add_argument("--overwrite", - action="store_true", - help="Overwrite files on the local disk") - parser.add_argument("--output", "-o", - type=argparse.FileType("wb"), - default=None, - help="Alternate name of the output file") - - def invoke(self): - if self.args.output is None: - filename = str(self.args.JOB_ID) + '_output.txt' - if os.path.exists(filename) and not self.args.overwrite: - print >> sys.stderr, "File {filename!r} already exists".format( - filename=filename) - print >> sys.stderr, "You may pass --overwrite to write over it" - return -1 - stream = open(filename, "wb") - else: - stream = self.args.output - filename = self.args.output.name - - server = AuthenticatingServerProxy( - self.args.SERVER, auth_backend=KeyringAuthBackend()) - stream.write(server.scheduler.job_output(self.args.JOB_ID).data) - - print "Downloaded job output of {0} to file {1!r}".format( - self.args.JOB_ID, filename) - - -class job_status(Command): - """ - Get job status and bundle sha1, if it existed, from the scheduler. - """ - - @classmethod - def register_arguments(cls, parser): - super(job_status, cls).register_arguments(parser) - parser.add_argument("SERVER") - parser.add_argument("JOB_ID", - type=int, - help="Job ID to check the status") - - def invoke(self): - server = AuthenticatingServerProxy( - self.args.SERVER, auth_backend=KeyringAuthBackend()) - job_status = server.scheduler.job_status(self.args.JOB_ID) - - print "Job ID: %d\nJob Status: %s\nBundle SHA1: %s" %(self.args.JOB_ID, - job_status['job_status'], job_status['bundle_sha1']) - === removed directory 'lava_tool' === removed file 'lava_tool/__init__.py' --- lava_tool/__init__.py 2012-03-13 15:40:30 +0000 +++ lava_tool/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,24 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Deprecated lava_tool package -""" - -from lava.tool import __version__ === removed file 'lava_tool/authtoken.py' --- lava_tool/authtoken.py 2013-05-22 13:45:44 +0000 +++ lava_tool/authtoken.py 1970-01-01 00:00:00 +0000 @@ -1,112 +0,0 @@ -# Copyright (C) 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -import base64 -import urllib -import urllib2 -import os -import xmlrpclib - -import keyring.core - -from lava_tool.interface import LavaCommandError - - -class AuthBackend(object): - - def add_token(self, username, endpoint_url, token): - raise NotImplementedError - - def get_token_for_endpoint(self, user, endpoint_url): - raise NotImplementedError - - -class KeyringAuthBackend(AuthBackend): - - def add_token(self, username, endpoint_url, token): - keyring.core.set_password( - "lava-tool-%s" % endpoint_url, username, token) - - def get_token_for_endpoint(self, username, endpoint_url): - return keyring.core.get_password( - "lava-tool-%s" % endpoint_url, username) - - -class MemoryAuthBackend(AuthBackend): - - def __init__(self, user_endpoint_token_list): - self._tokens = {} - for user, endpoint, token in user_endpoint_token_list: - self._tokens[(user, endpoint)] = token - - def add_token(self, username, endpoint_url, token): - self._tokens[(username, endpoint_url)] = token - - def get_token_for_endpoint(self, username, endpoint_url): - return self._tokens.get((username, endpoint_url)) - - -class XMLRPCTransport(xmlrpclib.Transport): - - def __init__(self, scheme, auth_backend): - xmlrpclib.Transport.__init__(self) - self._scheme = scheme - self.auth_backend = auth_backend - self._opener = urllib2.build_opener() - self.verbose = 0 - - def request(self, host, handler, request_body, verbose=0): - self.verbose = verbose - request = self.build_http_request(host, handler, request_body) - try: - response = self._opener.open(request) - except urllib2.HTTPError as e: - raise xmlrpclib.ProtocolError( - host + handler, e.code, e.msg, e.info()) - return self.parse_response(response) - - def build_http_request(self, host, handler, request_body): - token = None - user = None - auth, host = urllib.splituser(host) - if auth: - user, token = urllib.splitpasswd(auth) - url = self._scheme + "://" + host + handler - if user is not None and token is None: - token = self.auth_backend.get_token_for_endpoint(user, url) - if token is None: - raise LavaCommandError( - "Username provided but no token found.") - request = urllib2.Request(url, request_body) - request.add_header("Content-Type", "text/xml") - if token: - auth = base64.b64encode(urllib.unquote(user + ':' + token)) - request.add_header("Authorization", "Basic " + auth) - - return request - - -class AuthenticatingServerProxy(xmlrpclib.ServerProxy): - - def __init__(self, uri, transport=None, encoding=None, verbose=0, - allow_none=0, use_datetime=0, auth_backend=None): - if transport is None: - scheme = urllib.splittype(uri)[0] - transport = XMLRPCTransport(scheme, auth_backend=auth_backend) - xmlrpclib.ServerProxy.__init__( - self, uri, transport, encoding, verbose, allow_none, use_datetime) === removed directory 'lava_tool/commands' === removed file 'lava_tool/commands/__init__.py' --- lava_tool/commands/__init__.py 2012-03-13 18:17:45 +0000 +++ lava_tool/commands/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,24 +0,0 @@ -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Package with command line commands -""" - - -from lava.tool.commands import ExperimentalNoticeAction, ExperimentalCommandMixIn === removed file 'lava_tool/commands/auth.py' --- lava_tool/commands/auth.py 2011-07-14 12:30:52 +0000 +++ lava_tool/commands/auth.py 1970-01-01 00:00:00 +0000 @@ -1,127 +0,0 @@ -# Copyright (C) 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -import getpass -import urlparse -import xmlrpclib - -from lava_tool.authtoken import ( - AuthenticatingServerProxy, - KeyringAuthBackend, - MemoryAuthBackend, - ) -from lava_tool.interface import Command, LavaCommandError - - -def normalize_xmlrpc_url(uri): - if '://' not in uri: - uri = 'http://' + uri - if not uri.endswith('/'): - uri += '/' - if not uri.endswith('/RPC2/'): - uri += 'RPC2/' - return uri - - -class auth_add(Command): - """ - Add an authentication token. - """ - - def __init__(self, parser, args, auth_backend=None): - super(auth_add, self).__init__(parser, args) - if auth_backend is None: - auth_backend = KeyringAuthBackend() - self.auth_backend = auth_backend - - @classmethod - def register_arguments(cls, parser): - super(auth_add, cls).register_arguments(parser) - parser.add_argument( - "HOST", - help=("Endpoint to add token for, in the form " - "scheme://username@host. The username will default to " - "the currently logged in user.")) - parser.add_argument( - "--token-file", default=None, - help="Read the secret from here rather than prompting for it.") - parser.add_argument( - "--no-check", action='store_true', - help=("By default, a call to the remote server is made to check " - "that the added token works before remembering it. " - "Passing this option prevents this check.")) - - def invoke(self): - uri = normalize_xmlrpc_url(self.args.HOST) - parsed_host = urlparse.urlparse(uri) - - if parsed_host.username: - username = parsed_host.username - else: - username = getpass.getuser() - - host = parsed_host.hostname - if parsed_host.port: - host += ':' + str(parsed_host.port) - - uri = '%s://%s@%s%s' % ( - parsed_host.scheme, username, host, parsed_host.path) - - if self.args.token_file: - if parsed_host.password: - raise LavaCommandError( - "Token specified in url but --token-file also passed.") - else: - try: - token_file = open(self.args.token_file) - except IOError as ex: - raise LavaCommandError( - "opening %r failed: %s" % (self.args.token_file, ex)) - token = token_file.read().strip() - else: - if parsed_host.password: - token = parsed_host.password - else: - token = getpass.getpass("Paste token for %s: " % uri) - - userless_uri = '%s://%s%s' % ( - parsed_host.scheme, host, parsed_host.path) - - if not self.args.no_check: - sp = AuthenticatingServerProxy( - uri, auth_backend=MemoryAuthBackend( - [(username, userless_uri, token)])) - try: - token_user = sp.system.whoami() - except xmlrpclib.ProtocolError as ex: - if ex.errcode == 401: - raise LavaCommandError( - "Token rejected by server for user %s." % username) - else: - raise - except xmlrpclib.Fault as ex: - raise LavaCommandError( - "Server reported error during check: %s." % ex) - if token_user != username: - raise LavaCommandError( - "whoami() returned %s rather than expected %s -- this is " - "a bug." % (token_user, username)) - - self.auth_backend.add_token(username, userless_uri, token) - - print 'Token added successfully for user %s.' % username === removed file 'lava_tool/dispatcher.py' --- lava_tool/dispatcher.py 2012-03-22 18:03:03 +0000 +++ lava_tool/dispatcher.py 1970-01-01 00:00:00 +0000 @@ -1,49 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Module with LavaDispatcher - the command dispatcher -""" - -from lava.tool.dispatcher import Dispatcher -from lava.tool.main import LavaDispatcher as LavaNonLegacyDispatcher -from lava_tool.interface import LavaCommandError - - -class LavaDispatcher(Dispatcher): - """ - Class implementing command line interface for launch control - """ - - toolname = None - - def __init__(self): - super(LavaDispatcher, self).__init__() - prefixes = ['lava_tool'] - if self.toolname is not None: - prefixes.append(self.toolname) - for prefix in prefixes: - self.import_commands("%s.commands" % prefix) - - -def run_with_dispatcher_class(cls): - raise cls.run() - - -def main(): - LavaDispatcher.run() === removed file 'lava_tool/interface.py' --- lava_tool/interface.py 2012-03-22 18:03:03 +0000 +++ lava_tool/interface.py 1970-01-01 00:00:00 +0000 @@ -1,24 +0,0 @@ -# Copyright (C) 2010, 2011 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Interface for all lava-tool commands -""" - -from lava.tool.errors import CommandError as LavaCommandError -from lava.tool.command import Command, CommandGroup as SubCommand === removed directory 'lava_tool/tests' === removed file 'lava_tool/tests/__init__.py' --- lava_tool/tests/__init__.py 2013-07-26 08:45:05 +0000 +++ lava_tool/tests/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,73 +0,0 @@ -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Package with unit tests for lava_tool -""" - -import doctest -import unittest - - -def app_modules(): - return [ - 'lava_tool.commands', - 'lava_tool.dispatcher', - 'lava_tool.interface', - 'lava_dashboard_tool.commands', - ] - - -def test_modules(): - return [ - 'lava.device.tests.test_commands', - 'lava.device.tests.test_device', - 'lava.helper.tests.test_command', - 'lava.helper.tests.test_dispatcher', - 'lava.helper.tests.test_template', - 'lava.job.tests.test_commands', - 'lava.job.tests.test_job', - 'lava.script.tests.test_commands', - 'lava.script.tests.test_script', - 'lava.testdef.tests.test_commands', - 'lava.tests.test_commands', - 'lava.tests.test_config', - 'lava.tests.test_parameter', - 'lava_dashboard_tool.tests.test_commands', - 'lava_tool.tests.test_auth_commands', - 'lava_tool.tests.test_authtoken', - 'lava_tool.tests.test_commands', - 'lava_tool.tests.test_utils', - ] - - -def test_suite(): - """ - Build an unittest.TestSuite() object with all the tests in _modules. - Each module is harvested for both regular unittests and doctests - """ - modules = app_modules() + test_modules() - suite = unittest.TestSuite() - loader = unittest.TestLoader() - - for name in modules: - unit_suite = loader.loadTestsFromName(name) - suite.addTests(unit_suite) - doc_suite = doctest.DocTestSuite(name) - suite.addTests(doc_suite) - return suite === removed file 'lava_tool/tests/test_auth_commands.py' --- lava_tool/tests/test_auth_commands.py 2011-06-17 13:33:45 +0000 +++ lava_tool/tests/test_auth_commands.py 1970-01-01 00:00:00 +0000 @@ -1,259 +0,0 @@ -# Copyright (C) 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unit tests for the lava_tool.commands.auth package -""" - -import StringIO -import sys -import tempfile -import xmlrpclib - -from mocker import ARGS, KWARGS, CONTAINS, MockerTestCase - -from lava_tool.authtoken import MemoryAuthBackend -from lava_tool.interface import LavaCommandError -from lava_tool.commands.auth import auth_add - - -class FakeArgs: - token_file = None - no_check = False - - -class AuthAddTests(MockerTestCase): - - def setUp(self): - MockerTestCase.setUp(self) - self.saved_stdout = sys.stdout - sys.stdout = StringIO.StringIO() - self.saved_stderr = sys.stderr - sys.stderr = StringIO.StringIO() - - def tearDown(self): - MockerTestCase.tearDown(self) - sys.stdout = self.saved_stdout - sys.stderr = self.saved_stderr - - def make_command(self, auth_backend, **kwargs): - args = FakeArgs() - args.__dict__.update(kwargs) - return auth_add(None, args, auth_backend) - - def test_token_taken_from_argument(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com/RPC2/', - no_check=True) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_RPC2_implied(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=True) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_scheme_recorded(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='https://user:TOKEN@example.com/RPC2/', - no_check=True) - cmd.invoke() - self.assertEqual( - None, - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'https://example.com/RPC2/')) - - def test_path_on_server_recorded(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='https://user:TOKEN@example.com/path', - no_check=True) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'https://example.com/path/RPC2/')) - - def test_token_taken_from_getpass(self): - mocked_getpass = self.mocker.replace( - 'getpass.getpass', passthrough=False) - mocked_getpass(CONTAINS('Paste token')) - self.mocker.result("TOKEN") - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user@example.com', no_check=True) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_token_taken_from_file(self): - auth_backend = MemoryAuthBackend([]) - token_file = tempfile.NamedTemporaryFile('w') - token_file.write("TOKEN") - token_file.flush() - cmd = self.make_command( - auth_backend, HOST='http://user@example.com', no_check=True, - token_file=token_file.name) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_token_file_and_in_url_conflict(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=True, - token_file='some-file-name') - self.assertRaises(LavaCommandError, cmd.invoke) - - def test_non_existent_token_reported(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=True, - token_file='does-not-exist') - self.assertRaises(LavaCommandError, cmd.invoke) - - def test_user_taken_from_getuser(self): - mocked_getuser = self.mocker.replace( - 'getpass.getuser', passthrough=False) - mocked_getuser() - self.mocker.result("user") - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - token_file = tempfile.NamedTemporaryFile('w') - token_file.write("TOKEN") - token_file.flush() - cmd = self.make_command( - auth_backend, HOST='http://example.com', no_check=True, - token_file=token_file.name) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_port_included(self): - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, - HOST='http://user:TOKEN@example.com:1234', - no_check=True) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com:1234/RPC2/')) - - def test_check_made(self): - mocked_AuthenticatingServerProxy = self.mocker.replace( - 'lava_tool.authtoken.AuthenticatingServerProxy', passthrough=False) - mocked_sp = mocked_AuthenticatingServerProxy(ARGS, KWARGS) - # nospec() is required because of - # https://bugs.launchpad.net/mocker/+bug/794351 - self.mocker.nospec() - mocked_sp.system.whoami() - self.mocker.result('user') - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=False) - cmd.invoke() - self.assertEqual( - 'TOKEN', - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_check_auth_failure_reported_nicely(self): - mocked_AuthenticatingServerProxy = self.mocker.replace( - 'lava_tool.authtoken.AuthenticatingServerProxy', passthrough=False) - mocked_sp = mocked_AuthenticatingServerProxy(ARGS, KWARGS) - # nospec() is required because of - # https://bugs.launchpad.net/mocker/+bug/794351 - self.mocker.nospec() - mocked_sp.system.whoami() - self.mocker.throw(xmlrpclib.ProtocolError('', 401, '', [])) - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=False) - self.assertRaises(LavaCommandError, cmd.invoke) - - def test_check_fails_token_not_recorded(self): - mocked_AuthenticatingServerProxy = self.mocker.replace( - 'lava_tool.authtoken.AuthenticatingServerProxy', passthrough=False) - mocked_sp = mocked_AuthenticatingServerProxy(ARGS, KWARGS) - self.mocker.nospec() - mocked_sp.system.whoami() - self.mocker.throw(xmlrpclib.ProtocolError('', 401, '', [])) - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=False) - self.assertRaises(LavaCommandError, cmd.invoke) - self.assertEqual( - None, - auth_backend.get_token_for_endpoint( - 'user', 'http://example.com/RPC2/')) - - def test_check_other_http_failure_just_raised(self): - mocked_AuthenticatingServerProxy = self.mocker.replace( - 'lava_tool.authtoken.AuthenticatingServerProxy', passthrough=False) - mocked_sp = mocked_AuthenticatingServerProxy(ARGS, KWARGS) - # nospec() is required because of - # https://bugs.launchpad.net/mocker/+bug/794351 - self.mocker.nospec() - mocked_sp.system.whoami() - self.mocker.throw(xmlrpclib.ProtocolError('', 500, '', [])) - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=False) - self.assertRaises(xmlrpclib.ProtocolError, cmd.invoke) - - def test_fault_reported(self): - mocked_AuthenticatingServerProxy = self.mocker.replace( - 'lava_tool.authtoken.AuthenticatingServerProxy', passthrough=False) - mocked_sp = mocked_AuthenticatingServerProxy(ARGS, KWARGS) - # nospec() is required because of - # https://bugs.launchpad.net/mocker/+bug/794351 - self.mocker.nospec() - mocked_sp.system.whoami() - self.mocker.throw(xmlrpclib.Fault(100, 'faultString')) - self.mocker.replay() - auth_backend = MemoryAuthBackend([]) - cmd = self.make_command( - auth_backend, HOST='http://user:TOKEN@example.com', no_check=False) - self.assertRaises(LavaCommandError, cmd.invoke) === removed file 'lava_tool/tests/test_authtoken.py' --- lava_tool/tests/test_authtoken.py 2013-06-06 16:38:03 +0000 +++ lava_tool/tests/test_authtoken.py 1970-01-01 00:00:00 +0000 @@ -1,117 +0,0 @@ -# Copyright (C) 2011 Linaro Limited -# -# Author: Michael Hudson-Doyle -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unit tests for the lava_tool.authtoken package -""" - -import base64 -import StringIO -from unittest import TestCase -import urlparse -import xmlrpclib - -from mocker import ARGS, KWARGS, Mocker - -from lava_tool.authtoken import ( - AuthenticatingServerProxy, - XMLRPCTransport, - MemoryAuthBackend, - ) -from lava_tool.interface import LavaCommandError - -class TestAuthenticatingServerProxy(TestCase): - - def auth_headers_for_method_call_on(self, url, auth_backend): - parsed = urlparse.urlparse(url) - - mocker = Mocker() - transport = mocker.mock() - - auth_data = [] - - def intercept_request(host, handler, request_body, verbose=0): - actual_transport = XMLRPCTransport(parsed.scheme, auth_backend) - request = actual_transport.build_http_request(host, handler, request_body) - if (request.has_header('Authorization')): - auth_data.append(request.get_header('Authorization')) - - response_body = xmlrpclib.dumps((1,), methodresponse=True) - response = StringIO.StringIO(response_body) - response.status = 200 - response.__len__ = lambda: len(response_body) - - transport.request(ARGS, KWARGS) - mocker.call(intercept_request) - mocker.result(response) - - with mocker: - server_proxy = AuthenticatingServerProxy( - url, auth_backend=auth_backend, transport=transport) - server_proxy.method() - - return auth_data - - def user_and_password_from_auth_data(self, auth_data): - if len(auth_data) != 1: - self.fail("expected exactly 1 header, got %r" % len(auth_data)) - [value] = auth_data - if not value.startswith("Basic "): - self.fail("non-basic auth header found in %r" % auth_data) - auth = base64.b64decode(value[len("Basic "):]) - if ':' in auth: - return tuple(auth.split(':', 1)) - else: - return (auth, None) - - def test_no_user_no_auth(self): - auth_headers = self.auth_headers_for_method_call_on( - 'http://localhost/RPC2/', MemoryAuthBackend([])) - self.assertEqual([], auth_headers) - - def test_token_used_for_auth_http(self): - auth_headers = self.auth_headers_for_method_call_on( - 'http://user@localhost/RPC2/', - MemoryAuthBackend([('user', 'http://localhost/RPC2/', 'TOKEN')])) - self.assertEqual( - ('user', 'TOKEN'), - self.user_and_password_from_auth_data(auth_headers)) - - def test_token_used_for_auth_https(self): - auth_headers = self.auth_headers_for_method_call_on( - 'https://user@localhost/RPC2/', - MemoryAuthBackend([('user', 'https://localhost/RPC2/', 'TOKEN')])) - self.assertEqual( - ('user', 'TOKEN'), - self.user_and_password_from_auth_data(auth_headers)) - - def test_port_included(self): - auth_headers = self.auth_headers_for_method_call_on( - 'http://user@localhost:1234/RPC2/', - MemoryAuthBackend( - [('user', 'http://localhost:1234/RPC2/', 'TOKEN')])) - self.assertEqual( - ('user', 'TOKEN'), - self.user_and_password_from_auth_data(auth_headers)) - - def test_error_when_user_but_no_token(self): - self.assertRaises( - LavaCommandError, - self.auth_headers_for_method_call_on, - 'http://user@localhost/RPC2/', - MemoryAuthBackend([])) === removed file 'lava_tool/tests/test_commands.py' --- lava_tool/tests/test_commands.py 2013-05-22 13:45:13 +0000 +++ lava_tool/tests/test_commands.py 1970-01-01 00:00:00 +0000 @@ -1,137 +0,0 @@ -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -""" -Unit tests for the launch_control.commands package -""" - -from mocker import MockerTestCase, ARGS - -from lava_tool.interface import ( - Command, - LavaCommandError, - ) -from lava_tool.dispatcher import ( - LavaDispatcher, - main, - ) - - -class CommandTestCase(MockerTestCase): - - def test_register_arguments_does_nothing(self): - parser = self.mocker.mock() - self.mocker.replay() - Command.register_arguments(parser) - - def test_not_implemented(self): - self.assertRaises(NotImplementedError, Command(None, None).invoke) - - def test_get_name_uses_class_name(self): - class Foo(Command): - pass - self.assertEqual(Foo.get_name(), "Foo") - - def test_get_name_strips_leading_underscore(self): - class _Bar(Command): - pass - self.assertEqual(_Bar.get_name(), "Bar") - - def test_get_name_converts_underscore_to_dash(self): - class froz_bot(Command): - pass - self.assertEqual(froz_bot.get_name(), "froz-bot") - - def test_get_help_uses_docstring(self): - class ASDF(Command): - """ - This command was named after the lisp package management system - """ - self.assertEqual( - ASDF.get_help(), - 'This command was named after the lisp package management system') - - def test_get_help_defaults_to_None(self): - class mysterious(Command): - pass - - self.assertEqual(mysterious.get_help(), None) - - def test_get_epilog_defaults_to_None(self): - class mysterious(Command): - pass - self.assertEqual(mysterious.get_epilog(), None) - - def test_get_epilog_returns_data_after_carriage_L(self): - # The dot after 'before' is to make pep8 happy - class help_with_epilog(Command): - """ - before - . - after - """ - self.assertEqual(help_with_epilog.get_epilog(), "after") - - def test_get_help_returns_data_before_carriage_L(self): - # The dot after 'before' is to make pep8 happy - class help_with_epilog(Command): - """ - before - . - after - """ - self.assertEqual(help_with_epilog.get_help(), "before\n.") - - -class DispatcherTestCase(MockerTestCase): - - def test_main(self): - dispatcher = self.mocker.patch(LavaDispatcher) - dispatcher.dispatch(ARGS) - self.mocker.replay() - - self.assertRaises(SystemExit, main) - - def test_add_command_cls(self): - test_calls = [] - - class test(Command): - - def invoke(self): - test_calls.append(None) - - dispatcher = LavaDispatcher() - dispatcher.add_command_cls(test) - dispatcher.dispatch(raw_args=['test']) - self.assertEqual(1, len(test_calls)) - - def test_print_LavaCommandError_nicely(self): - stderr = self.mocker.replace('sys.stderr', passthrough=False) - stderr.write("ERROR: error message") - stderr.write("\n") - self.mocker.replay() - - class error(Command): - - def invoke(self): - raise LavaCommandError("error message") - - dispatcher = LavaDispatcher() - dispatcher.add_command_cls(error) - exit_code = dispatcher.dispatch(raw_args=['error']) - self.assertEquals(1, exit_code) === removed file 'lava_tool/tests/test_utils.py' --- lava_tool/tests/test_utils.py 2013-07-30 17:39:27 +0000 +++ lava_tool/tests/test_utils.py 1970-01-01 00:00:00 +0000 @@ -1,282 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -"""lava_tool.utils tests.""" - -import os -import shutil -import subprocess -import sys -import tempfile - -from unittest import TestCase -from mock import ( - MagicMock, - call, - patch, -) - -from lava.tool.errors import CommandError -from lava_tool.utils import ( - can_edit_file, - create_dir, - edit_file, - execute, - has_command, - retrieve_file, - verify_and_create_url, - verify_file_extension, -) - - -class UtilTests(TestCase): - - def setUp(self): - self.original_stdout = sys.stdout - sys.stdout = open("/dev/null", "w") - self.original_stderr = sys.stderr - sys.stderr = open("/dev/null", "w") - self.original_stdin = sys.stdin - self.temp_file = tempfile.NamedTemporaryFile(delete=False) - - def tearDown(self): - sys.stdin = self.original_stdin - sys.stdout = self.original_stdout - sys.stderr = self.original_stderr - os.unlink(self.temp_file.name) - - @patch("lava_tool.utils.subprocess.check_call") - def test_has_command_0(self, mocked_check_call): - # Make sure we raise an exception when the subprocess is called. - mocked_check_call.side_effect = subprocess.CalledProcessError(0, "") - self.assertFalse(has_command("")) - - @patch("lava_tool.utils.subprocess.check_call") - def test_has_command_1(self, mocked_check_call): - # Check that a "command" exists. The call to subprocess is mocked. - mocked_check_call.return_value = 0 - self.assertTrue(has_command("")) - - def test_verify_file_extension_with_extension(self): - extension = ".test" - supported = [extension[1:]] - try: - temp_file = tempfile.NamedTemporaryFile(suffix=extension, - delete=False) - obtained = verify_file_extension( - temp_file.name, extension[1:], supported) - self.assertEquals(temp_file.name, obtained) - finally: - if os.path.isfile(temp_file.name): - os.unlink(temp_file.name) - - def test_verify_file_extension_without_extension(self): - extension = "json" - supported = [extension] - expected = "/tmp/a_fake.json" - obtained = verify_file_extension("/tmp/a_fake", extension, supported) - self.assertEquals(expected, obtained) - - def test_verify_file_extension_with_unsupported_extension(self): - extension = "json" - supported = [extension] - expected = "/tmp/a_fake.json" - obtained = verify_file_extension( - "/tmp/a_fake.extension", extension, supported) - self.assertEquals(expected, obtained) - - @patch("os.listdir") - def test_retrieve_job_file_0(self, mocked_os_listdir): - # Make sure that exception is raised if we go through all the elements - # returned by os.listdir(). - mocked_os_listdir.return_value = ["a_file"] - self.assertRaises(CommandError, retrieve_file, - "a_path", ["ext"]) - - @patch("os.listdir") - def test_retrieve_job_file_1(self, mocked_os_listdir): - # Pass some files and directories to retrieve_file(), and make - # sure a file with .json suffix is returned. - # Pass also a hidden file. - try: - json_file = tempfile.NamedTemporaryFile(suffix=".json") - json_file_name = os.path.basename(json_file.name) - - file_name_no_suffix = tempfile.NamedTemporaryFile(delete=False) - file_name_with_suffix = tempfile.NamedTemporaryFile( - suffix=".bork", delete=False) - - temp_dir_name = "submit_command_test_tmp_dir" - temp_dir_path = os.path.join(tempfile.gettempdir(), temp_dir_name) - os.makedirs(temp_dir_path) - - hidden_file = tempfile.NamedTemporaryFile( - prefix=".tmp", delete=False) - - mocked_os_listdir.return_value = [ - temp_dir_name, file_name_no_suffix.name, - file_name_with_suffix.name, json_file_name, hidden_file.name] - - obtained = retrieve_file(tempfile.gettempdir(), ["json"]) - self.assertEqual(json_file.name, obtained) - finally: - os.removedirs(temp_dir_path) - os.unlink(file_name_no_suffix.name) - os.unlink(file_name_with_suffix.name) - os.unlink(hidden_file.name) - - def test_retrieve_job_file_2(self): - # Pass a file with the valid extension. - temp_file = tempfile.NamedTemporaryFile(suffix=".json") - obtained = retrieve_file(temp_file.name, ["json"]) - self.assertEquals(temp_file.name, obtained) - - def test_retrieve_job_file_3(self): - # Pass a file with a non-valid extension. - temp_file = tempfile.NamedTemporaryFile(suffix=".bork") - self.assertRaises( - CommandError, retrieve_file, temp_file.name, ["json"]) - - @patch("os.listdir") - def test_retrieve_job_file_4(self, mocked_os_listdir): - # Pass hidden and wrong files and make sure exception is thrown. - a_hidden_file = ".a_hidden.json" - b_hidden_file = ".b_hidden.json" - c_wrong_file = "a_wrong_file.bork" - - mocked_os_listdir.return_value = [a_hidden_file, b_hidden_file, c_wrong_file] - self.assertRaises( - CommandError, retrieve_file, tempfile.gettempdir(), ["json"]) - - @patch("lava_tool.utils.subprocess") - def test_execute_0(self, mocked_subprocess): - mocked_subprocess.check_call = MagicMock() - execute("foo") - self.assertEqual(mocked_subprocess.check_call.call_args_list, - [call(["foo"])]) - self.assertTrue(mocked_subprocess.check_call.called) - - @patch("lava_tool.utils.subprocess.check_call") - def test_execute_1(self, mocked_check_call): - mocked_check_call.side_effect = subprocess.CalledProcessError(1, "foo") - self.assertRaises(CommandError, execute, ["foo"]) - - @patch("lava_tool.utils.subprocess") - @patch("lava_tool.utils.has_command", return_value=False) - @patch("lava_tool.utils.os.environ.get", return_value=None) - @patch("lava_tool.utils.sys.exit") - def test_edit_file_0(self, mocked_sys_exit, mocked_env_get, - mocked_has_command, mocked_subprocess): - edit_file(self.temp_file.name) - self.assertTrue(mocked_sys_exit.called) - - @patch("lava_tool.utils.subprocess") - @patch("lava_tool.utils.has_command", side_effect=[True, False]) - @patch("lava_tool.utils.os.environ.get", return_value=None) - def test_edit_file_1(self, mocked_env_get, mocked_has_command, - mocked_subprocess): - mocked_subprocess.Popen = MagicMock() - edit_file(self.temp_file.name) - expected = [call(["sensible-editor", self.temp_file.name])] - self.assertEqual(expected, mocked_subprocess.Popen.call_args_list) - - @patch("lava_tool.utils.subprocess") - @patch("lava_tool.utils.has_command", side_effect=[False, True]) - @patch("lava_tool.utils.os.environ.get", return_value=None) - def test_edit_file_2(self, mocked_env_get, mocked_has_command, - mocked_subprocess): - mocked_subprocess.Popen = MagicMock() - edit_file(self.temp_file.name) - expected = [call(["xdg-open", self.temp_file.name])] - self.assertEqual(expected, mocked_subprocess.Popen.call_args_list) - - @patch("lava_tool.utils.subprocess") - @patch("lava_tool.utils.has_command", return_value=False) - @patch("lava_tool.utils.os.environ.get", return_value="vim") - def test_edit_file_3(self, mocked_env_get, mocked_has_command, - mocked_subprocess): - mocked_subprocess.Popen = MagicMock() - edit_file(self.temp_file.name) - expected = [call(["vim", self.temp_file.name])] - self.assertEqual(expected, mocked_subprocess.Popen.call_args_list) - - @patch("lava_tool.utils.subprocess") - @patch("lava_tool.utils.has_command", return_value=False) - @patch("lava_tool.utils.os.environ.get", return_value="vim") - def test_edit_file_4(self, mocked_env_get, mocked_has_command, - mocked_subprocess): - mocked_subprocess.Popen = MagicMock() - mocked_subprocess.Popen.side_effect = Exception() - self.assertRaises(CommandError, edit_file, self.temp_file.name) - - def test_can_edit_file(self): - # Tests the can_edit_file method of the config command. - # This is to make sure the device config file is not erased when - # checking if it is possible to open it. - expected = ("hostname = a_fake_panda02\nconnection_command = \n" - "device_type = panda\n") - - with open(self.temp_file.name, "w") as f: - f.write(expected) - - self.assertTrue(can_edit_file(self.temp_file.name)) - obtained = "" - with open(self.temp_file.name) as f: - obtained = f.read() - - self.assertEqual(expected, obtained) - - def test_verify_and_create_url_0(self): - expected = "https://www.example.org/" - obtained = verify_and_create_url("www.example.org") - self.assertEquals(expected, obtained) - - def test_verify_and_create_url_1(self): - expected = "http://www.example.org/" - obtained = verify_and_create_url("http://www.example.org") - self.assertEquals(expected, obtained) - - def test_verify_and_create_url_2(self): - expected = "http://www.example.org/RPC/" - obtained = verify_and_create_url("http://www.example.org/RPC") - self.assertEquals(expected, obtained) - - def test_verify_and_create_url_3(self): - expected = "https://www.example.org/RPC/" - obtained = verify_and_create_url("www.example.org/RPC") - self.assertEquals(expected, obtained) - - def test_create_dir_0(self): - try: - temp_dir = os.path.join(tempfile.gettempdir(), "a_dir") - create_dir(temp_dir) - self.assertTrue(os.path.isdir(temp_dir)) - finally: - shutil.rmtree(temp_dir) - - def test_create_dir_1(self): - try: - temp_dir = os.path.join(tempfile.gettempdir(), "a_dir") - create_dir(temp_dir, "subdir") - self.assertTrue(os.path.isdir(os.path.join(temp_dir, "subdir"))) - finally: - shutil.rmtree(temp_dir) - - def test_create_dir_2(self): - temp_dir = os.path.join("/", "a_temp_dir") - self.assertRaises(CommandError, create_dir, temp_dir) === removed file 'lava_tool/utils.py' --- lava_tool/utils.py 2013-07-30 17:39:27 +0000 +++ lava_tool/utils.py 1970-01-01 00:00:00 +0000 @@ -1,329 +0,0 @@ -# Copyright (C) 2013 Linaro Limited -# -# Author: Milo Casagrande -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -import StringIO -import base64 -import os -import tarfile -import tempfile -import types -import subprocess -import sys -import urlparse - -from lava.tool.errors import CommandError - - -def has_command(command): - """Checks that the given command is available. - - :param command: The name of the command to check availability. - """ - command_available = True - try: - subprocess.check_call(["which", command], - stdout=open(os.path.devnull, 'w')) - except subprocess.CalledProcessError: - command_available = False - return command_available - - -def to_list(value): - """Return a list from the passed value. - - :param value: The parameter to turn into a list. - """ - return_value = [] - if isinstance(value, types.StringType): - return_value = [value] - else: - return_value = list(value) - return return_value - - -def create_tar(paths): - """Creates a temporary tar file with the provided paths. - - The tar file is not deleted at the end, it has to be delete by who calls - this function. - - If just a directory is passed, it will be flattened out: its contents will - be added, but not the directory itself. - - :param paths: List of paths to be included in the tar archive. - :type list - :return The path to the temporary tar file. - """ - paths = to_list(paths) - try: - temp_tar_file = tempfile.NamedTemporaryFile(suffix=".tar", - delete=False) - with tarfile.open(temp_tar_file.name, "w") as tar_file: - for path in paths: - full_path = os.path.abspath(path) - if os.path.isfile(full_path): - arcname = os.path.basename(full_path) - tar_file.add(full_path, arcname=arcname) - elif os.path.isdir(full_path): - # If we pass a directory, flatten it out. - # List its contents, and add them as they are. - for element in os.listdir(full_path): - arcname = element - tar_file.add(os.path.join(full_path, element), - arcname=arcname) - return temp_tar_file.name - except tarfile.TarError: - raise CommandError("Error creating the temporary tar archive.") - - -def base64_encode(path): - """Encode in base64 the provided file. - - :param path: The path to a file. - :return The file content encoded in base64. - """ - if os.path.isfile(path): - encoded_content = StringIO.StringIO() - - try: - with open(path) as read_file: - base64.encode(read_file, encoded_content) - - return encoded_content.getvalue().strip() - except IOError: - raise CommandError("Cannot read file " - "'{0}'.".format(path)) - else: - raise CommandError("Provided path does not exists or is not a file: " - "{0}.".format(path)) - - -def retrieve_file(path, extensions): - """Searches for a file that has one of the supported extensions. - - The path of the first file that matches one of the supported provided - extensions will be returned. The files are examined in alphabetical - order. - - :param path: Where to look for the file. - :param extensions: A list of extensions the file to look for should - have. - :return The full path of the file. - """ - if os.path.isfile(path): - if check_valid_extension(path, extensions): - retrieved_path = path - else: - raise CommandError("The provided file '{0}' is not " - "valid: extension not supported.".format(path)) - else: - dir_listing = os.listdir(path) - dir_listing.sort() - - for element in dir_listing: - if element.startswith("."): - continue - - element_path = os.path.join(path, element) - if os.path.isdir(element_path): - continue - elif os.path.isfile(element_path): - if check_valid_extension(element_path, extensions): - retrieved_path = element_path - break - else: - raise CommandError("No suitable file found in '{0}'".format(path)) - - return retrieved_path - - -def check_valid_extension(path, extensions): - """Checks that a file has one of the supported extensions. - - :param path: The file to check. - :param extensions: A list of supported extensions. - """ - is_valid = False - - local_path, file_name = os.path.split(path) - name, full_extension = os.path.splitext(file_name) - - if full_extension: - extension = full_extension[1:].strip().lower() - if extension in extensions: - is_valid = True - return is_valid - - -def verify_file_extension(path, default, supported): - """Verifies if a file has a supported extensions. - - If the file does not have one, it will add the default extension - provided. - - :param path: The path of a file to verify. - :param default: The default extension to use. - :param supported: A list of supported extensions to check against. - :return The path of the file. - """ - full_path, file_name = os.path.split(path) - name, extension = os.path.splitext(file_name) - if not extension: - path = ".".join([path, default]) - elif extension[1:].lower() not in supported: - path = os.path.join(full_path, ".".join([name, default])) - return path - - -def verify_path_existance(path): - """Verifies if a given path exists on the file system. - - Raises a CommandError in case it exists. - - :param path: The path to verify. - """ - if os.path.exists(path): - raise CommandError("{0} already exists.".format(path)) - - -def verify_path_non_existance(path): - """Verifies if a given path does not exist on the file system. - - Raises a CommandError in case it does not exist. - - :param path: The path to verify. - """ - if not os.path.exists(path): - raise CommandError("{0} does not exists.".format(path)) - - -def write_file(path, content): - """Creates a file with the specified content. - - :param path: The path of the file to write. - :param content: What to write in the file. - """ - try: - with open(path, "w") as to_write: - to_write.write(content) - except (OSError, IOError): - raise CommandError("Error writing file '{0}'".format(path)) - - -def execute(cmd_args): - """Executes the supplied command args. - - :param cmd_args: The command, and its optional arguments, to run. - :return The command execution return code. - """ - cmd_args = to_list(cmd_args) - try: - return subprocess.check_call(cmd_args) - except subprocess.CalledProcessError: - raise CommandError("Error running the following command: " - "{0}".format(" ".join(cmd_args))) - - -def can_edit_file(path): - """Checks if a file can be opend in write mode. - - :param path: The path to the file. - :return True if it is possible to write on the file, False otherwise. - """ - can_edit = True - try: - fp = open(path, "a") - fp.close() - except IOError: - can_edit = False - return can_edit - - -def edit_file(file_to_edit): - """Opens the specified file with the default file editor. - - :param file_to_edit: The file to edit. - """ - editor = os.environ.get("EDITOR", None) - if editor is None: - if has_command("sensible-editor"): - editor = "sensible-editor" - elif has_command("xdg-open"): - editor = "xdg-open" - else: - # We really do not know how to open a file. - print >> sys.stdout, ("Cannot find an editor to open the " - "file '{0}'.".format(file_to_edit)) - print >> sys.stdout, ("Either set the 'EDITOR' environment " - "variable, or install 'sensible-editor' " - "or 'xdg-open'.") - sys.exit(-1) - try: - subprocess.Popen([editor, file_to_edit]).wait() - except Exception: - raise CommandError("Error opening the file '{0}' with the " - "following editor: {1}.".format(file_to_edit, - editor)) - - -def verify_and_create_url(endpoint): - """Checks that the provided values make a correct URL. - - If the server address does not contain a scheme, by default it will use - HTTPS. - The endpoint is then added at the URL. - - :param server: A server URL to verify. - :return A URL. - """ - url = "" - if endpoint: - scheme, netloc, path, params, query, fragment = \ - urlparse.urlparse(endpoint) - if not scheme: - scheme = "https" - if not netloc: - netloc, path = path, "" - - url = urlparse.urlunparse( - (scheme, netloc, path, params, query, fragment)) - - if url[-1:] != "/": - url += "/" - - return url - - -def create_dir(path, dir_name=None): - """Checks if a directory does not exists, and creates it. - - :param path: The path where the directory should be created. - :param dir_name: An optional name for a directory to be created at - path (dir_name will be joined with path). - :return The path of the created directory.""" - created_dir = path - if dir_name: - created_dir = os.path.join(path, dir_name) - - if not os.path.isdir(created_dir): - try: - os.makedirs(created_dir) - except OSError: - raise CommandError("Cannot create directory " - "'{0}'.".format(created_dir)) - return created_dir === removed file 'setup.cfg' --- setup.cfg 2011-01-03 17:08:25 +0000 +++ setup.cfg 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -[upload] -sign=True === removed file 'setup.py' --- setup.py 2013-07-31 10:24:54 +0000 +++ setup.py 1970-01-01 00:00:00 +0000 @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010 Linaro Limited -# -# Author: Zygmunt Krynicki -# -# This file is part of lava-tool. -# -# lava-tool is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation -# -# lava-tool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with lava-tool. If not, see . - -from setuptools import setup, find_packages -from os.path import dirname, join - -entry_points = open(join(dirname(__file__), 'entry_points.ini')).read() - -setup( - name='lava-tool', - version=":versiontools:lava.tool:__version__", - author="Zygmunt Krynicki", - author_email="zygmunt.krynicki@linaro.org", - namespace_packages=['lava'], - packages=find_packages(), - description="Command line utility for Linaro validation services", - url='https://launchpad.net/lava-tool', - test_suite='lava_tool.tests.test_suite', - license="LGPLv3", - entry_points=entry_points, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - ("License :: OSI Approved :: GNU Library or Lesser General Public" - " License (LGPL)"), - "Operating System :: OS Independent", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Topic :: Software Development :: Testing", - ], - install_requires=[ - 'PyYAML >= 3.10', - 'argparse >= 1.1', - 'argcomplete >= 0.3', - 'keyring', - 'json-schema-validator >= 2.0', - 'versiontools >= 1.3.1', - 'pyxdg == 0.25', - ], - setup_requires=['versiontools >= 1.3.1'], - tests_require=[ - 'mocker >= 1.0', - 'mock >= 0.7.2' - ], - zip_safe=True)