Merge lp:~milo/lava-tool/lava-167 into lp:~linaro-validation/lava-tool/trunk

Proposed by Milo Casagrande
Status: Merged
Merged at revision: 188
Proposed branch: lp:~milo/lava-tool/lava-167
Merge into: lp:~linaro-validation/lava-tool/trunk
Prerequisite: lp:~terceiro/lava-tool/helper
Diff against target: 972 lines (+798/-22)
15 files modified
.bzrignore (+2/-0)
.testr.conf (+3/-0)
ci-build (+8/-2)
entry_points.ini (+6/-0)
lava/config.py (+12/-1)
lava/device/__init__.py (+124/-0)
lava/device/commands.py (+250/-0)
lava/device/templates.py (+42/-0)
lava/device/tests/test_commands.py (+175/-0)
lava/device/tests/test_device.py (+123/-0)
lava/job/templates.py (+1/-5)
lava_tool/tests/__init__.py (+15/-12)
lava_tool/tests/test_authtoken.py (+0/-1)
lava_tool/utils.py (+33/-0)
setup.py (+4/-1)
To merge this branch: bzr merge lp:~milo/lava-tool/lava-167
Reviewer Review Type Date Requested Status
Antonio Terceiro Needs Fixing
Linaro Automation & Validation Pending
Review via email: mp+167727@code.launchpad.net

Description of the change

This is work in progress for Jira card LAVA-167. Early work to take a look if I'm on the right track.

To post a comment you must log in.
Revision history for this message
Milo Casagrande (milo) wrote :

Updated status to be in need review, code should be ready for general review.

Added also here are pep8 and pyflakes tests (although they give errors while running via the ci-build script).

lp:~milo/lava-tool/lava-167 updated
216. By Milo Casagrande

Added missing dependencies to run tests.

    * pep8 and pyflakes depends on testtools, since it gives a nicer
      output on errors.

Revision history for this message
Milo Casagrande (milo) wrote :

With commit #216 even the ci-build script now works. pep8 and pyflakes tests depend on testtools package since it provides a better error output on test runs.

lp:~milo/lava-tool/lava-167 updated
217. By Milo Casagrande

Fixed config command test.

218. By Milo Casagrande

Refactored command tests: defined device name on setup.

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

9 +versiontools-1.9.1-py2.7.egg

You probably want to ignore *.egg

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

All the stuff around testing. You'd better just depend on py.test or something like that, it has that exact stuff integrated and ready. You may also find that just blindly enabling pyflakes and pep8 will throw lots of errors at you (unless lava is perfect, I doubt that). The better approach would be to use a diff mode and only run that at merge request time. Then you at least don't introduce _new_ issues.

lp:~milo/lava-tool/lava-167 updated
219. By Milo Casagrande

Fixed ignored .egg pattern.

Revision history for this message
Milo Casagrande (milo) wrote :

On Mon, Jun 10, 2013 at 3:00 PM, Zygmunt Krynicki
<email address hidden> wrote:
> All the stuff around testing. You'd better just depend on py.test or something like that, it has that exact stuff integrated and ready.

Never heard about that, will give it a look. Thanks for the pointer!
Thanks also for the *.egg in bzrignore.

> You may also find that just blindly enabling pyflakes and pep8 will throw lots of errors at you (unless lava is perfect, I doubt that). The better approach would be to use a diff mode and only run that at merge request time. Then you at least don't introduce _new_ issues.

I agree, I'm not that happy with that too, I was thinking about a
git/bzr pre-[commit|push] hook, but at least for now (and locally,
testing only the lava-tool) I prefer having those error thrown at me.
For lava-tool they shouldn't even be too hard to fix.

--
Milo Casagrande | Automation Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

lp:~milo/lava-tool/lava-167 updated
220. By Milo Casagrande

Removed pep8 and pyflakes tests.

    * At this stage better have them as part of a pre-commit hook.

Revision history for this message
Milo Casagrande (milo) wrote :

I removed the pep8 and pyflakes tests from the project. Fixing them would not take too much, but there are some, in particular doctest ones, that might be a trickier to fix.

At this stage it makes more sense to provide pre-commit or pre-push hooks for git/bzr to run pep8/pyflakes/pylint/...

Revision history for this message
Antonio Terceiro (terceiro) wrote :
Download full text (13.2 KiB)

Hi Milo,

Very nice progress here! I have some comments below to help improving it.

 review needs-fixing

Also I wonder whether we should merge my branch into trunk already so
you don't keep developing off the official places ... it's not like
there will be a release before this work is done with.

> === modified file '.bzrignore'
> --- .bzrignore 2013-06-10 15:38:27 +0000
> +++ .bzrignore 2013-06-10 15:38:27 +0000
> @@ -3,3 +3,6 @@
> ./build
> ./dist
> /tags
> +.testrepository
> +*.egg
> +ci-build-venv

you won't need the last one anymore.

> === added file '.testr.conf'
> --- .testr.conf 1970-01-01 00:00:00 +0000
> +++ .testr.conf 2013-06-10 15:38:27 +0000
> @@ -0,0 +1,3 @@
> +[DEFAULT]
> +test_command=python -m subunit.run $IDLIST
> +test_id_list_default=lava_tool.tests.test_suite
>
> === modified file 'ci-build'
> --- ci-build 2013-06-10 15:38:27 +0000
> +++ ci-build 2013-06-10 15:38:27 +0000
> @@ -1,11 +1,13 @@
> #!/bin/sh
>
> +VENV_DIR="/tmp/ci-build-venv"
> +
> set -e
>
> if test -z "$VIRTUAL_ENV"; then
> set -x
> - virtualenv ci-build-venv
> - . ci-build-venv/bin/activate
> + virtualenv $VENV_DIR
> + . $VENV_DIR/bin/activate
> python setup.py develop
> fi
>
> @@ -21,6 +23,22 @@
> pip install mocker
> fi
>
> +if ! pip show pep8 | grep -q pep8; then
> + pip install pep8
> +fi
> +
> +if ! pip show pyflakes | grep -q pyflakes; then
> + pip install pyflakes
> +fi
> +
> +if ! pip show testtools | grep -q testtools; then
> + pip install testtools
> +fi
> +
> +if ! pip show mock | grep -q mock; then
> + pip install mock
> +fi
> +
> export LAVACONFIG=/dev/null
>
> if test -z "$DISPLAY"; then
>

if you dropped pyflakes/pep8 tests there is no point in installing them
here.

> === added directory 'lava/device'
> === added file 'lava/device/__init__.py'
> --- lava/device/__init__.py 1970-01-01 00:00:00 +0000
> +++ lava/device/__init__.py 2013-06-10 15:38:27 +0000
> @@ -0,0 +1,112 @@
> +# Copyright (C) 2013 Linaro Limited
> +#
> +# Author: Milo Casagrande <email address hidden>
> +#
> +# 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 <http://www.gnu.org/licenses/>.
> +
> +import re
> +
> +from lava.device.templates import DEFAULT_TEMPLATE
> +from lava.tool.errors import CommandError
> +
> +
> +class Device(object):
> + """A generic device."""
> + def __init__(self, name):
> + super(Device, self).__init__()

I don't think object has anything useful in its constructor.

> + self.device_type = None
> + self.hostname = name
> + self.template = DEFAULT_TEMPLATE

In _update you are changing this object, you might want to ...

review: Needs Fixing
lp:~milo/lava-tool/lava-167 updated
221. By Milo Casagrande

Removed unnecessary ignore rule.

222. By Milo Casagrande

Cleaned up dependencies.

223. By Milo Casagrande

Renamed variable name.

224. By Milo Casagrande

Reworked the Device class.

    * Removed PandaDevice class, fixed tests.
    * Added couple more tests.
    * Added a panda template, a vexpress one and refactored how the
      known devices dictionary is built.
    * Some PEP8 and pylint cleaning.

225. By Milo Casagrande

Fixed subprocess call.

Revision history for this message
Milo Casagrande (milo) wrote :
Download full text (3.9 KiB)

Hi Antonio!

Thanks for the review.

On Tue, Jun 11, 2013 at 2:31 AM, Antonio Terceiro
<email address hidden> wrote:
> Review: Needs Fixing
>
> Hi Milo,
>
> Very nice progress here! I have some comments below to help improving it.
>
> review needs-fixing
>
> Also I wonder whether we should merge my branch into trunk already so
> you don't keep developing off the official places ... it's not like
> there will be a release before this work is done with.

As you prefer, I based my branch on yours so the diff would make sense here.
As soon as we merge that we can switch to trunk.

> if you dropped pyflakes/pep8 tests there is no point in installing them
> here.

Forgot to clean up those dependencies. Cleared them all out, and
cleaned also the bzrignore file.

> I don't think object has anything useful in its constructor.

Default code snippet/autocompletion when I create classes. I usually
do not even pay attention to that.
Cleared out.

> In _update you are changing this object, you might want to make a copy instead.

Makes sense, indeed.

>> +
>> + def write(self, where):
>> + """Writes the object to file.
>> +
>> + :param where: The full path of the file where to write."""
>> + with open(where, 'w') as f:
>> + f.write(self.__str__())
>
> renaming `where` to `file` will make the code a lot more obvious.

I used `conf_file`, I do not like using the same name as the built-in
"file" for a variable even if it is as simple as in this case (plus
there is the description just above/below).

>> +# Dictionary with key the name of a know device, and value a tuple composed of
>> +# a matcher used to guess the device type, and its associated Device class.
>> +known_devices = {
>> + 'panda': (re.compile('^.*panda.*', re.I), PandaDevice),
>> +}
>
> you might create this dynamically from a list of device type names. I don't
> see us needing one class per device type, so perhaps the last element in the
> value tuple should be a template, which could be looked up by device type name
> in lava.device.templates, or a default if the device type name does not bring
> anything.

I cleaned up this part, removing the specialized PandaDevice class and
creating "custom" templates.
Take a look of what I did was how you were thinking about.

The regex matcher is also create "on-the-fly".

> I think you cannot always assume an instance here. If you have lava-tool
> installed with Debian packages, then you don't have a local instance. The
> problem is that the dispatcher does not expose a list of configuration
> directories that we can reference. I will work on this tomorrow, and will let
> you know.

Yeah, I was looking into getting as much info as possible out of LAVA,
but for what I needed I didn't have much luck.
Indeed this part here needs a better logic, but I wanted to have the
code out there for a first review.

> I think we could keep a dictionary of templates, indexed by device type (see
> also my comments above wrt lava.device.__init__.py)

This should have been addressed. Please take a look and let me know.

> CalledProcessError will never be raised here. It's only raised by check_call
> and check_output.
>
> try th...

Read more...

lp:~milo/lava-tool/lava-167 updated
226. By Milo Casagrande

Reworked docstring for command line help.

227. By Milo Casagrande

Reworked how new templates are created.

228. By Milo Casagrande

Used dict copy instead of importing copy module.

229. By Milo Casagrande

Removed rm from the entry points: it showed up as remove in the command list.

230. By Milo Casagrande

Refactored string formatting to be more Python3 compliant.

231. By Milo Casagrande

Refactoring and tests fixed.

    * Added function to get from lava-dispatcher the config paths.
    * Some cleanups and refactoring.

232. By Milo Casagrande

Made the user choose the correct path to use.

    * Added logic to handle user input.
    * Fixed tests.

233. By Milo Casagrande

Added new test.

Revision history for this message
Antonio Terceiro (terceiro) wrote :
Download full text (5.5 KiB)

> == added file 'lava/device/commands.py'
> -- lava/device/commands.py 1970-01-01 00:00:00 +0000
> ++ lava/device/commands.py 2013-06-12 13:21:33 +0000
> @ -0,0 +1,237 @@
[snip]
> # Default lava-dispatcher paths.
> DEFAULT_DISPATCHER_PATH = os.path.join("etc", "lava-dispatcher")
> USER_DISPATCHER_PATH = os.path.join(os.path.expanduser("~"), ".config",
> "lava-dispatcher")

It would be better to

  from lava_dispatcher.import user_config_path, system_config_path

and use those instead, so you don't need to duplicate the logic here. We don't
want to make the dispatcher a hard dependency of lava-tool, but if we don't
have the dispatcher available, just fail the entire thing.

> # Default devices path, has to be joined with the dispatcher path.
> DEFAULT_DEVICES_PATH = "devices"
> DEVICE_FILE_SUFFIX = "conf"
>
>
> class device(CommandGroup):
> """LAVA devices handling."""
>
> namespace = "lava.device.commands"
>
>
> class BaseCommand(Command):
> """Base command for device commands."""
> def __init__(self, parser, args):
> super(BaseCommand, self).__init__(parser, args)
> self.config = InteractiveConfig(
> force_interactive=self.args.interactive)
>
> @classmethod
> def register_arguments(cls, parser):
> super(BaseCommand, cls).register_arguments(parser)
> parser.add_argument("-i", "--interactive",
> action='store_true',
> help=("Forces asking for input parameters even if "
> "we already have them cached."))
>
> @classmethod
> def _get_dispatcher_paths(cls):
> """Tries to get the dispatcher from lava-dispatcher."""
> global_paths = []
> try:
> from lava_dispatcher.config import search_path
>
> global_paths += search_path()
> except ImportError:
> print >> sys.stderr, "Cannot import lava_dispatcher."

Right here. We should baild out if the dispatcher is not available for device
management. After all without a local dispatcher there is no point in managing
devices.

> return global_paths
>
> @classmethod
> def _choose_dispatcher_path(cls, paths):
> """Lets the user choose the path to use.
>
> :param paths: A list of paths.
> :return The path at the user selected index.
> """
> print >> sys.stdout, ("Multiple dispatcher paths found. Please "
> "select one between:\n")
> out_list = []
> len_paths = range(1, len(paths) + 1)
> for index, dispatcher_path in zip(len_paths, paths):
> out_list.append("\t{0}. {1}\n".format(index, dispatcher_path))
> print >> sys.stdout, "".join(out_list)
> while True:
> try:
> choice = raw_input("Dispatcher path to use: ").strip()
> if choice in [str(x) for x in len_paths]:
> return paths[int(choice) - 1]
> else:
> continue
> except KeyboardInterrupt:
> sys.exit(-1)

I think we should not g...

Read more...

lp:~milo/lava-tool/lava-167 updated
234. By Milo Casagrande

Fixed templates.

235. By Milo Casagrande

Fixed command logic to use lava_dispatcher.

    * We now soft depend on lava_dispatcher: if it is not installed, execution
      will not continue.
    * Fixed tests.

Revision history for this message
Milo Casagrande (milo) wrote :
Download full text (3.5 KiB)

On Thu, Jun 13, 2013 at 2:46 AM, Antonio Terceiro
<email address hidden> wrote:
>> == added file 'lava/device/commands.py'
>> -- lava/device/commands.py 1970-01-01 00:00:00 +0000
>> ++ lava/device/commands.py 2013-06-12 13:21:33 +0000
>> @ -0,0 +1,237 @@
> [snip]
>> # Default lava-dispatcher paths.
>> DEFAULT_DISPATCHER_PATH = os.path.join("etc", "lava-dispatcher")
>> USER_DISPATCHER_PATH = os.path.join(os.path.expanduser("~"), ".config",
>> "lava-dispatcher")
>
> It would be better to
>
> from lava_dispatcher.import user_config_path, system_config_path
>
> and use those instead, so you don't need to duplicate the logic here. We don't
> want to make the dispatcher a hard dependency of lava-tool, but if we don't
> have the dispatcher available, just fail the entire thing.

Modifying the logic in the _get_dispatcher_paths() and the other
methods that deal with picking the correct path, I think it is not
necessary to import "user_config_path" and "system_config_path" from
the dispatcher.

Since we get the paths via lava_dispatcher.config.search_path(), it
will return those paths already, and if we cannot import it we will
fail.

> Right here. We should baild out if the dispatcher is not available for device
> management. After all without a local dispatcher there is no point in managing
> devices.

Sounds cool.
I was trying to be a little bit more "gentle" and fallback to let
users create devices anyway. :-)

> I think we should not give too many options here: if the user is capable of
> choosing the right directory to write to, he doesn't need lava-tool to do it
> for him; he would just create a file with $EDITOR there.
>
> Instead of this, I think you should assume that dispatcher branch of mine, and
> use the following strategy:
>
> 1) finding where to create a new configurarion file:
>
> the first of [system_dispatcher_path, user_config_path] that is writable
>
> 2) finding an existing configuration file to edit:
>
> from lava_dispatcher.config import get_config_file
> [...]
>
> conf_file = get_config_file("devices/%s.conf", device_name)
> if conf_file:
> # edit
> else:
> print("No device %s found" % device_name)
> exit(-1)
>
> I think that doing this will simplify a lot things in this file.

OK, I reworked this part, probably in a slighter different way.
This is how it will work now for the add command:

- We check if a device with that config file name already exists: this
is now done with lava_dispatcher.config.get_config_file. If we cannot
import the function, we fail.
- If the device already exists, we exit printing to use "lava device
config DEVICE" (as was before)
- Otherwise, get the paths from the dispatcher using
lava_dispatcher.config.search_path() (again, if it gives errors we
fail)
- Check if one of those paths is writable and pick the first one that
is, otherwise fail
- At this stage we are sure that the device doesn't exists and we can create it.

There might be another check we want to do though: if
lava_dispatcher.config.get_config_file() returns a path to an existing
file that is not writable by the user (in particular for the "config"
command).

> I think something l...

Read more...

lp:~milo/lava-tool/lava-167 updated
236. By Milo Casagrande

Fixed print statements, added one on remove command.

237. By Milo Casagrande

Minor fixes on printing statements.

238. By Milo Casagrande

Minor fixes on printing statements: fixed typos.

239. By Milo Casagrande

Fixed two tests.

240. By Milo Casagrande

Fixed one test.

241. By Milo Casagrande

Fixed logic when choosing devices path.

242. By Milo Casagrande

Better output on add command.

Revision history for this message
Antonio Terceiro (terceiro) wrote :

On Thu, Jun 13, 2013 at 10:35:00AM -0000, Milo Casagrande wrote:
> > I think we should not give too many options here: if the user is capable of
> > choosing the right directory to write to, he doesn't need lava-tool to do it
> > for him; he would just create a file with $EDITOR there.
> >
> > Instead of this, I think you should assume that dispatcher branch of mine, and
> > use the following strategy:
> >
> > 1) finding where to create a new configurarion file:
> >
> > the first of [system_dispatcher_path, user_config_path] that is writable
> >
> > 2) finding an existing configuration file to edit:
> >
> > from lava_dispatcher.config import get_config_file
> > [...]
> >
> > conf_file = get_config_file("devices/%s.conf", device_name)
> > if conf_file:
> > # edit
> > else:
> > print("No device %s found" % device_name)
> > exit(-1)
> >
> > I think that doing this will simplify a lot things in this file.
>
> OK, I reworked this part, probably in a slighter different way.
> This is how it will work now for the add command:
>
> - We check if a device with that config file name already exists: this
> is now done with lava_dispatcher.config.get_config_file. If we cannot
> import the function, we fail.
> - If the device already exists, we exit printing to use "lava device
> config DEVICE" (as was before)
> - Otherwise, get the paths from the dispatcher using
> lava_dispatcher.config.search_path() (again, if it gives errors we
> fail)
> - Check if one of those paths is writable and pick the first one that
> is, otherwise fail

It cannot be the first of search_path. Say you write to user_config_path
(~user/.config/...). For some(most?) devices, at the moment you need to
run the dispatcher as root. The dispatcher will then look at
~root/config/... , and will not find anything the device.

Also, picking some path in search_path involves the (small, but
non-zero) risk of writing to the default_config_path, what we don't want.

Maybe we could just export [system_config_path, user_config_path] as
lava_dispatcher.config.write_search_path, then, keep your logic of find
the first?

> - At this stage we are sure that the device doesn't exists and we can create it.
>
> There might be another check we want to do though: if
> lava_dispatcher.config.get_config_file() returns a path to an existing
> file that is not writable by the user (in particular for the "config"
> command).

Yep.

--
Antonio Terceiro
Software Engineer - Linaro
http://www.linaro.org

lp:~milo/lava-tool/lava-167 updated
243. By Milo Casagrande

Fixed use of lava_dispatcher function to retrieve config write path.

244. By Milo Casagrande

Added test for editing device file.

Revision history for this message
Milo Casagrande (milo) wrote :

On Thu, Jun 13, 2013 at 4:24 PM, Antonio Terceiro
<email address hidden> wrote:
>
> It cannot be the first of search_path. Say you write to user_config_path
> (~user/.config/...). For some(most?) devices, at the moment you need to
> run the dispatcher as root. The dispatcher will then look at
> ~root/config/... , and will not find anything the device.
>
> Also, picking some path in search_path involves the (small, but
> non-zero) risk of writing to the default_config_path, what we don't want.
>
> Maybe we could just export [system_config_path, user_config_path] as
> lava_dispatcher.config.write_search_path, then, keep your logic of find
> the first?

I fixed this using your latest changes in the dispatcher.
I also added a test when editing the config file to check if it is
writable or not.

--
Milo Casagrande | Automation Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

lp:~milo/lava-tool/lava-167 updated
245. By Milo Casagrande

Fixed method name error.

246. By Milo Casagrande

Added another test.

247. By Milo Casagrande

Fixed opening of file: opening with 'w' ereases the file.

248. By Milo Casagrande

Use append mode also in another case for file opening.

249. By Milo Casagrande

Fixed output.

250. By Milo Casagrande

Fixed tests.

251. By Milo Casagrande

Added a new test.

252. By Milo Casagrande

Fixed the can_edit_file test.

253. By Milo Casagrande

Merged from trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2013-05-24 17:37:52 +0000
+++ .bzrignore 2013-06-19 06:51:26 +0000
@@ -3,3 +3,5 @@
3./build3./build
4./dist4./dist
5/tags5/tags
6.testrepository
7*.egg
68
=== added file '.testr.conf'
--- .testr.conf 1970-01-01 00:00:00 +0000
+++ .testr.conf 2013-06-19 06:51:26 +0000
@@ -0,0 +1,3 @@
1[DEFAULT]
2test_command=python -m subunit.run $IDLIST
3test_id_list_default=lava_tool.tests.test_suite
04
=== modified file 'ci-build'
--- ci-build 2013-06-03 20:56:10 +0000
+++ ci-build 2013-06-19 06:51:26 +0000
@@ -1,11 +1,13 @@
1#!/bin/sh1#!/bin/sh
22
3VENV_DIR="/tmp/ci-build-venv"
4
3set -e5set -e
46
5if test -z "$VIRTUAL_ENV"; then7if test -z "$VIRTUAL_ENV"; then
6 set -x8 set -x
7 virtualenv ci-build-venv9 virtualenv $VENV_DIR
8 . ci-build-venv/bin/activate10 . $VENV_DIR/bin/activate
9 python setup.py develop11 python setup.py develop
10fi12fi
1113
@@ -21,6 +23,10 @@
21 pip install mocker23 pip install mocker
22fi24fi
2325
26if ! pip show mock | grep -q mock; then
27 pip install mock
28fi
29
24export LAVACONFIG=/dev/null30export LAVACONFIG=/dev/null
2531
26if test -z "$DISPLAY"; then32if test -z "$DISPLAY"; then
2733
=== modified file 'entry_points.ini'
--- entry_points.ini 2013-05-27 20:51:39 +0000
+++ entry_points.ini 2013-06-19 06:51:26 +0000
@@ -8,6 +8,7 @@
8scheduler = lava_scheduler_tool.commands:scheduler8scheduler = lava_scheduler_tool.commands:scheduler
9dashboard = lava_dashboard_tool.commands:dashboard9dashboard = lava_dashboard_tool.commands:dashboard
10job = lava.job.commands:job10job = lava.job.commands:job
11device = lava.device.commands:device
1112
12[lava_tool.commands]13[lava_tool.commands]
13help = lava.tool.commands.help:help14help = lava.tool.commands.help:help
@@ -70,3 +71,8 @@
70new = lava.job.commands:new71new = lava.job.commands:new
71submit = lava.job.commands:submit72submit = lava.job.commands:submit
72run = lava.job.commands:run73run = lava.job.commands:run
74
75[lava.device.commands]
76add = lava.device.commands:add
77remove = lava.device.commands:remove
78config = lava.device.commands:config
7379
=== modified file 'lava/config.py'
--- lava/config.py 2013-05-28 22:08:12 +0000
+++ lava/config.py 2013-06-19 06:51:26 +0000
@@ -30,14 +30,25 @@
30 pass30 pass
31atexit.register(readline.write_history_file, history)31atexit.register(readline.write_history_file, history)
3232
33config_file = os.environ.get('LAVACONFIG') or os.path.join(os.path.expanduser('~'), '.lavaconfig')33config_file = (os.environ.get('LAVACONFIG') or
34 os.path.join(os.path.expanduser('~'), '.lavaconfig'))
34config_backend = ConfigParser()35config_backend = ConfigParser()
35config_backend.read([config_file])36config_backend.read([config_file])
37
38
36def save_config():39def save_config():
37 with open(config_file, 'w') as f:40 with open(config_file, 'w') as f:
38 config_backend.write(f)41 config_backend.write(f)
39atexit.register(save_config)42atexit.register(save_config)
4043
44
45class Parameter(object):
46
47 def __init__(self, id, depends=None):
48 self.id = id
49 self.depends = depends
50
51
41class InteractiveConfig(object):52class InteractiveConfig(object):
4253
43 def __init__(self, force_interactive=False):54 def __init__(self, force_interactive=False):
4455
=== added directory 'lava/device'
=== added file 'lava/device/__init__.py'
--- lava/device/__init__.py 1970-01-01 00:00:00 +0000
+++ lava/device/__init__.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,124 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19import re
20
21from lava.device.templates import (
22 KNOWN_TEMPLATES,
23 DEFAULT_TEMPLATE,
24)
25from lava.tool.errors import CommandError
26
27
28def __re_compile(name):
29 """Creates a generic regex for the specified device name.
30
31 :param name: The name of the device.
32 :return A Pattern object.
33 """
34 return re.compile('^.*{0}.*'.format(name), re.I)
35
36
37# Dictionary of know devices.
38# Keys are the general device name taken from lava.device.templates, values
39# are tuples of: a regex matcher to match the device, and the device associated
40# template.
41KNOWN_DEVICES = dict([(device, (__re_compile(device), template))
42 for device, template in KNOWN_TEMPLATES.iteritems()])
43
44
45class Device(object):
46 """A generic device."""
47 def __init__(self, hostname, template):
48 self.device_type = None
49 self.hostname = hostname
50 self.template = template.copy()
51
52 def write(self, conf_file):
53 """Writes the object to file.
54
55 :param conf_file: The full path of the file where to write."""
56 with open(conf_file, 'w') as write_file:
57 write_file.write(self.__str__())
58
59 def _update(self):
60 """Updates the template with the values specified for this class.
61
62 Subclasses need to override this when they add more specific
63 attributes.
64 """
65 # This is needed for the 'default' behavior. If we matched a known
66 # device, we do not need to update its device_type, since its already
67 # defined in the template.
68 if self.device_type:
69 self.template.update(hostname=self.hostname,
70 device_type=self.device_type)
71 else:
72 self.template.update(hostname=self.hostname)
73
74 def __str__(self):
75 self._update()
76 string_list = []
77 for key, value in self.template.iteritems():
78 if not value:
79 value = ''
80 string_list.append("{0} = {1}\n".format(str(key), str(value)))
81 return "".join(string_list)
82
83 def __repr__(self):
84 self._update()
85 return str(self.template)
86
87
88def _get_device_type_from_user():
89 """Makes the user write what kind of device this is.
90
91 If something goes wrong, raises CommandError.
92 """
93 try:
94 dev_type = raw_input("Please specify the device type: ").strip()
95 except (EOFError, KeyboardInterrupt):
96 dev_type = None
97 if not dev_type:
98 raise CommandError("DEVICE name not specified or not correct.")
99 return dev_type
100
101
102def get_known_device(name):
103 """Tries to match a device name with a known device type.
104
105 :param name: The name of the device we want matched to a real device.
106 :return A Device instance.
107 """
108 instance = None
109 for known_dev, (matcher, dev_template) in KNOWN_DEVICES.iteritems():
110 if matcher.match(name):
111 instance = Device(name, dev_template)
112 if not instance:
113 dev_type = _get_device_type_from_user()
114 known_dev = KNOWN_DEVICES.get(dev_type, None)
115 if known_dev:
116 instance = Device(name, known_dev[1])
117 else:
118 print ("Device '{0}' does not match a known "
119 "device.".format(dev_type))
120 instance = Device(name, DEFAULT_TEMPLATE)
121 # Not stricly necessary, users can fill up the field later.
122 instance.device_type = dev_type
123
124 return instance
0125
=== added file 'lava/device/commands.py'
--- lava/device/commands.py 1970-01-01 00:00:00 +0000
+++ lava/device/commands.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,250 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19"""
20Device specific commands class.
21"""
22
23import os
24import subprocess
25import sys
26import random
27import string
28
29from lava.config import InteractiveConfig
30from lava.device import get_known_device
31from lava.tool.command import Command, CommandGroup
32from lava.tool.errors import CommandError
33from lava_tool.utils import has_command
34
35# Default lava-dispatcher paths.
36DEFAULT_DISPATCHER_PATH = os.path.join("etc", "lava-dispatcher")
37USER_DISPATCHER_PATH = os.path.join(os.path.expanduser("~"), ".config",
38 "lava-dispatcher")
39# Default devices path, has to be joined with the dispatcher path.
40DEFAULT_DEVICES_PATH = "devices"
41DEVICE_FILE_SUFFIX = "conf"
42
43
44class device(CommandGroup):
45 """LAVA devices handling."""
46
47 namespace = "lava.device.commands"
48
49
50class BaseCommand(Command):
51 """Base command for device commands."""
52 def __init__(self, parser, args):
53 super(BaseCommand, self).__init__(parser, args)
54 self.config = InteractiveConfig(
55 force_interactive=self.args.interactive)
56
57 @classmethod
58 def register_arguments(cls, parser):
59 super(BaseCommand, cls).register_arguments(parser)
60 parser.add_argument("-i", "--interactive",
61 action='store_true',
62 help=("Forces asking for input parameters even if "
63 "we already have them cached."))
64
65 @classmethod
66 def _get_dispatcher_paths(cls):
67 """Tries to get the dispatcher from lava-dispatcher."""
68 try:
69 from lava_dispatcher.config import write_path
70 return write_path()
71 except ImportError:
72 raise CommandError("Cannot find lava-dispatcher installation.")
73
74 @classmethod
75 def _choose_devices_path(cls, paths):
76 """Picks the first path that is writable by the user.
77
78 :param paths: A list of paths.
79 :return The first path where it is possible to write.
80 """
81 valid_path = None
82 for path in paths:
83 path = os.path.join(path, DEFAULT_DEVICES_PATH)
84 if os.path.exists(path):
85 name = "".join(random.choice(string.ascii_letters)
86 for x in range(6))
87 test_file = os.path.join(path, name)
88 try:
89 fp = open(test_file, 'a')
90 fp.close()
91 except IOError:
92 # Cannot write here.
93 continue
94 else:
95 valid_path = path
96 if os.path.isfile(test_file):
97 os.unlink(test_file)
98 break
99 else:
100 try:
101 os.makedirs(path)
102 except OSError:
103 # Cannot write here either.
104 continue
105 else:
106 valid_path = path
107 break
108 else:
109 raise CommandError("Insufficient permissions to create new "
110 "devices.")
111 return valid_path
112
113 @classmethod
114 def _get_devices_path(cls):
115 """Gets the path to the devices in the LAVA dispatcher."""
116 dispatcher_paths = cls._get_dispatcher_paths()
117 return cls._choose_devices_path(dispatcher_paths)
118
119 @classmethod
120 def edit_config_file(cls, config_file):
121 """Opens the specified file with the default file editor.
122
123 :param config_file: The file to edit.
124 """
125 editor = os.environ.get("EDITOR", None)
126 if editor is None:
127 if has_command("sensible-editor"):
128 editor = "sensible-editor"
129 elif has_command("xdg-open"):
130 editor = "xdg-open"
131 else:
132 # We really do not know how to open a file.
133 print >> sys.stdout, ("Cannot find an editor to open the "
134 "file '{0}'.".format(config_file))
135 print >> sys.stdout, ("Either set the 'EDITOR' environment "
136 "variable, or install 'sensible-editor' "
137 "or 'xdg-open'.")
138 sys.exit(-1)
139
140 try:
141 subprocess.Popen([editor, config_file]).wait()
142 except Exception:
143 raise CommandError("Error opening the file '{0}' with the "
144 "following editor: {1}.".format(config_file,
145 editor))
146
147 @classmethod
148 def _get_device_file(cls, file_name):
149 """Retrieves the config file name specified, if it exists.
150
151 :param file_name: The config file name to search.
152 :return The path to the file, or None if it does not exist.
153 """
154 try:
155 from lava_dispatcher.config import get_config_file
156
157 return get_config_file(os.path.join(DEFAULT_DEVICES_PATH,
158 file_name))
159 except ImportError:
160 raise CommandError("Cannot find lava-dispatcher installation.")
161
162
163class add(BaseCommand):
164 """Adds a new device."""
165
166 @classmethod
167 def register_arguments(cls, parser):
168 super(add, cls).register_arguments(parser)
169 parser.add_argument("DEVICE", help="The name of the device to add.")
170
171 def invoke(self):
172 real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX])
173
174 if self._get_device_file(real_file_name):
175 print >> sys.stdout, ("A device configuration file named '{0}' "
176 "already exists.".format(real_file_name))
177 print >> sys.stdout, ("Use 'lava device config {0}' to edit "
178 "it.".format(self.args.DEVICE))
179 sys.exit(-1)
180
181 devices_path = self._get_devices_path()
182 device_conf_file = os.path.abspath(os.path.join(devices_path,
183 real_file_name))
184
185 device = get_known_device(self.args.DEVICE)
186 device.write(device_conf_file)
187
188 print >> sys.stdout, ("Created device file '{0}' in: {1}".format(
189 real_file_name, devices_path))
190 self.edit_config_file(device_conf_file)
191
192
193class remove(BaseCommand):
194 """Removes the specified device."""
195
196 @classmethod
197 def register_arguments(cls, parser):
198 super(remove, cls).register_arguments(parser)
199 parser.add_argument("DEVICE",
200 help="The name of the device to remove.")
201
202 def invoke(self):
203 real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX])
204 device_conf = self._get_device_file(real_file_name)
205
206 if device_conf:
207 try:
208 os.remove(device_conf)
209 print >> sys.stdout, ("Device configuration file '{0}' "
210 "removed.".format(real_file_name))
211 except OSError:
212 raise CommandError("Cannot remove file '{0}' at: {1}.".format(
213 real_file_name, os.path.dirname(device_conf)))
214 else:
215 print >> sys.stdout, ("No device configuration file '{0}' "
216 "found.".format(real_file_name))
217
218
219class config(BaseCommand):
220 """Opens the specified device config file."""
221 @classmethod
222 def register_arguments(cls, parser):
223 super(config, cls).register_arguments(parser)
224 parser.add_argument("DEVICE",
225 help="The name of the device to edit.")
226
227 @classmethod
228 def can_edit_file(cls, conf_file):
229 """Checks if a file can be opend in write mode.
230
231 :param conf_file: The path to the file.
232 :return True if it is possible to write on the file, False otherwise.
233 """
234 can_edit = True
235 try:
236 fp = open(conf_file, "a")
237 fp.close()
238 except IOError:
239 can_edit = False
240 return can_edit
241
242 def invoke(self):
243 real_file_name = ".".join([self.args.DEVICE, DEVICE_FILE_SUFFIX])
244
245 device_conf = self._get_device_file(real_file_name)
246 if device_conf and self.can_edit_file(device_conf):
247 self.edit_config_file(device_conf)
248 else:
249 raise CommandError("Cannot edit file '{0}' at: "
250 "{1}.".format(real_file_name, device_conf))
0251
=== added file 'lava/device/templates.py'
--- lava/device/templates.py 1970-01-01 00:00:00 +0000
+++ lava/device/templates.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,42 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19"""
20This is just a place where to store a template like dictionary that
21will be used to serialize a Device object.
22"""
23
24DEFAULT_TEMPLATE = {
25 'device_type': None,
26 'hostname': None,
27 'connection_command': None,
28}
29
30# Dictionary with templates of known devices.
31KNOWN_TEMPLATES = {
32 'panda': {
33 'device_type': 'panda',
34 'hostname': None,
35 'connection_command': None,
36 },
37 'vexpress': {
38 'device_type': 'vexpress',
39 'hostname': None,
40 'connection_command': None,
41 },
42}
043
=== added directory 'lava/device/tests'
=== added file 'lava/device/tests/__init__.py'
=== added file 'lava/device/tests/test_commands.py'
--- lava/device/tests/test_commands.py 1970-01-01 00:00:00 +0000
+++ lava/device/tests/test_commands.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,175 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19"""
20Commands class unit tests.
21"""
22
23import os
24import shutil
25import sys
26import tempfile
27
28from mock import MagicMock
29from unittest import TestCase
30
31from lava.device.commands import (
32 BaseCommand,
33 add,
34 config,
35 remove,
36)
37
38from lava.tool.errors import CommandError
39
40
41class CommandsTest(TestCase):
42 def setUp(self):
43 # Fake the stdout.
44 self.original_stdout = sys.stdout
45 sys.stdout = open("/dev/null", "w")
46 self.original_stderr = sys.stderr
47 sys.stderr = open("/dev/null", "w")
48 self.original_stdin = sys.stdin
49
50 self.device = "a_fake_panda02"
51
52 self.tempdir = tempfile.mkdtemp()
53 self.parser = MagicMock()
54 self.args = MagicMock()
55 self.args.interactive = MagicMock(return_value=False)
56 self.args.DEVICE = self.device
57
58 self.config = MagicMock()
59 self.config.get = MagicMock(return_value=self.tempdir)
60
61 def tearDown(self):
62 sys.stdin = self.original_stdin
63 sys.stdout = self.original_stdout
64 sys.stderr = self.original_stderr
65 shutil.rmtree(self.tempdir)
66
67 def test_get_devices_path_0(self):
68 # Tests that the correct devices path is returned and created.
69 base_command = BaseCommand(self.parser, self.args)
70 base_command.config = self.config
71 BaseCommand._get_dispatcher_paths = MagicMock(return_value=[
72 self.tempdir])
73 obtained = base_command._get_devices_path()
74 expected_path = os.path.join(self.tempdir, "devices")
75 self.assertEqual(expected_path, obtained)
76 self.assertTrue(os.path.isdir(expected_path))
77
78 def test_get_devices_path_1(self):
79 # Tests that when passing a path that is not writable, CommandError
80 # is raised.
81 base_command = BaseCommand(self.parser, self.args)
82 base_command.config = self.config
83 BaseCommand._get_dispatcher_paths = MagicMock(
84 return_value=["/", "/root", "/root/tmpdir"])
85 self.assertRaises(CommandError, base_command._get_devices_path)
86
87 def test_choose_devices_path(self):
88 # Tests that when passing more than one path, the first writable one
89 # is returned.
90 base_command = BaseCommand(self.parser, self.args)
91 base_command.config = self.config
92 obtained = base_command._choose_devices_path(
93 ["/", "/root", self.tempdir, os.path.expanduser("~")])
94 expected = os.path.join(self.tempdir, "devices")
95 self.assertEqual(expected, obtained)
96
97 def test_add_invoke(self):
98 # Tests invocation of the add command. Verifies that the conf file is
99 # written to disk.
100 add_command = add(self.parser, self.args)
101 add_command.edit_config_file = MagicMock()
102 add_command._get_device_file = MagicMock(return_value=None)
103 add_command._get_devices_path = MagicMock(return_value=self.tempdir)
104 add_command.invoke()
105
106 expected_path = os.path.join(self.tempdir,
107 ".".join([self.device, "conf"]))
108 self.assertTrue(os.path.isfile(expected_path))
109
110 def test_remove_invoke(self):
111 # Tests invocation of the remove command. Verifies that the conf file
112 # has been correctly removed.
113 # First we add a new conf file, then we remove it.
114 add_command = add(self.parser, self.args)
115 add_command.edit_config_file = MagicMock()
116 add_command._get_device_file = MagicMock(return_value=None)
117 add_command._get_devices_path = MagicMock(return_value=self.tempdir)
118 add_command.invoke()
119
120 expected_path = os.path.join(self.tempdir,
121 ".".join([self.device, "conf"]))
122
123 remove_command = remove(self.parser, self.args)
124 remove_command._get_device_file = MagicMock(return_value=expected_path)
125 remove_command._get_devices_path = MagicMock(return_value=self.tempdir)
126 remove_command.invoke()
127
128 self.assertFalse(os.path.isfile(expected_path))
129
130 def test_remove_invoke_raises(self):
131 # Tests invocation of the remove command, with a non existent device
132 # configuration file.
133 remove_command = remove(self.parser, self.args)
134 remove_command._get_device_file = MagicMock(return_value="/root")
135
136 self.assertRaises(CommandError, remove_command.invoke)
137
138 def test_config_invoke_raises_0(self):
139 # Tests invocation of the config command, with a non existent device
140 # configuration file.
141 config_command = config(self.parser, self.args)
142 config_command._get_device_file = MagicMock(return_value=None)
143
144 self.assertRaises(CommandError, config_command.invoke)
145
146 def test_config_invoke_raises_1(self):
147 # Tests invocation of the config command, with a non writable file.
148 # Hopefully tests are not run as root.
149 config_command = config(self.parser, self.args)
150 config_command._get_device_file = MagicMock(return_value="/etc/passwd")
151
152 self.assertRaises(CommandError, config_command.invoke)
153
154 def test_can_edit_file(self):
155 # Tests the can_edit_file method of the config command.
156 # This is to make sure the device config file is not erased when
157 # checking if it is possible to open it.
158 expected = ("hostname = a_fake_panda02\nconnection_command = \n"
159 "device_type = panda\n")
160
161 config_command = config(self.parser, self.args)
162 try:
163 conf_file = tempfile.NamedTemporaryFile(delete=False)
164
165 with open(conf_file.name, "w") as f:
166 f.write(expected)
167
168 self.assertTrue(config_command.can_edit_file(conf_file.name))
169 obtained = ""
170 with open(conf_file.name) as f:
171 obtained = f.read()
172
173 self.assertEqual(expected, obtained)
174 finally:
175 os.unlink(conf_file.name)
0176
=== added file 'lava/device/tests/test_device.py'
--- lava/device/tests/test_device.py 1970-01-01 00:00:00 +0000
+++ lava/device/tests/test_device.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,123 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19"""
20Device class unit tests.
21"""
22
23import os
24import sys
25import tempfile
26
27from StringIO import StringIO
28from unittest import TestCase
29
30from lava.device import (
31 Device,
32 get_known_device,
33)
34from lava.tool.errors import CommandError
35
36
37class DeviceTest(TestCase):
38
39 def setUp(self):
40 # Fake the stdin and the stdout.
41 self.original_stdout = sys.stdout
42 sys.stdout = open("/dev/null", "w")
43 self.original_stdin = sys.stdin
44 sys.stdin = StringIO()
45 self.temp_file = tempfile.NamedTemporaryFile(delete=False)
46
47 def tearDown(self):
48 sys.stdout = self.original_stdout
49 sys.stdin = self.original_stdin
50 os.unlink(self.temp_file.name)
51
52 def test_get_known_device_panda_0(self):
53 # User creates a new device with a guessable name for a device.
54 instance = get_known_device('panda_new_01')
55 self.assertIsInstance(instance, Device)
56 self.assertEqual(instance.template['device_type'], 'panda')
57 self.assertIsNone(instance.device_type)
58
59 def test_get_known_device_panda_1(self):
60 # User creates a new device with a guessable name for a device.
61 # Name passed has capital letters.
62 instance = get_known_device('new_PanDa_02')
63 self.assertIsInstance(instance, Device)
64 self.assertEqual(instance.template['device_type'], 'panda')
65 self.assertIsNone(instance.device_type)
66
67 def test_get_known_device_vexpress_0(self):
68 # User creates a new device with a guessable name for a device.
69 # Name passed has capital letters.
70 instance = get_known_device('a_VexPress_Device')
71 self.assertIsInstance(instance, Device)
72 self.assertEqual(instance.template['device_type'], 'vexpress')
73 self.assertIsNone(instance.device_type)
74
75 def test_get_known_device_vexpress_1(self):
76 # User creates a new device with a guessable name for a device.
77 instance = get_known_device('another-vexpress')
78 self.assertIsInstance(instance, Device)
79 self.assertEqual(instance.template['device_type'], 'vexpress')
80 self.assertIsNone(instance.device_type)
81
82 def test_instance_update(self):
83 # Tests that when calling the _update() function with an known device
84 # it does not update the device_type instance attribute, and that the
85 # template contains the correct name.
86 instance = get_known_device('Another_PanDa_device')
87 instance._update()
88 self.assertIsInstance(instance, Device)
89 self.assertEqual(instance.template['device_type'], 'panda')
90 self.assertIsNone(instance.device_type)
91
92 def test_get_known_device_unknown(self):
93 # User tries to create a new device with an unknown device type. She
94 # is asked to insert the device type and types 'a_fake_device'.
95 sys.stdin = StringIO('a_fake_device')
96 instance = get_known_device('a_fake_device')
97 self.assertIsInstance(instance, Device)
98 self.assertEqual(instance.device_type, 'a_fake_device')
99
100 def test_get_known_device_known(self):
101 # User tries to create a new device with a not recognizable name.
102 # She is asked to insert the device type and types 'panda'.
103 sys.stdin = StringIO("panda")
104 instance = get_known_device("another_fake_device")
105 self.assertIsInstance(instance, Device)
106 self.assertEqual(instance.template["device_type"], "panda")
107
108 def test_get_known_device_raises(self):
109 # User tries to create a new device, but in some way nothing is passed
110 # on the command line when asked.
111 self.assertRaises(CommandError, get_known_device, 'a_fake_device')
112
113 def test_device_write(self):
114 # User tries to create a new panda device. The conf file is written
115 # and contains the expected results.
116 expected = ("hostname = panda02\nconnection_command = \n"
117 "device_type = panda\n")
118 instance = get_known_device("panda02")
119 instance.write(self.temp_file.name)
120 obtained = ""
121 with open(self.temp_file.name) as f:
122 obtained = f.read()
123 self.assertEqual(expected, obtained)
0124
=== modified file 'lava/job/templates.py'
--- lava/job/templates.py 2013-05-28 22:08:12 +0000
+++ lava/job/templates.py 2013-06-19 06:51:26 +0000
@@ -16,11 +16,7 @@
16# You should have received a copy of the GNU Lesser General Public License16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
1818
19class Parameter(object):19from lava.config import Parameter
20
21 def __init__(self, id, depends=None):
22 self.id = id
23 self.depends = depends
2420
25device_type = Parameter("device_type")21device_type = Parameter("device_type")
26prebuilt_image = Parameter("prebuilt_image", depends=device_type)22prebuilt_image = Parameter("prebuilt_image", depends=device_type)
2723
=== modified file 'lava_tool/tests/__init__.py'
--- lava_tool/tests/__init__.py 2013-05-27 20:51:39 +0000
+++ lava_tool/tests/__init__.py 2013-06-19 06:51:26 +0000
@@ -26,22 +26,24 @@
2626
27def app_modules():27def app_modules():
28 return [28 return [
29 'lava_tool.commands',29 'lava_tool.commands',
30 'lava_tool.dispatcher',30 'lava_tool.dispatcher',
31 'lava_tool.interface',31 'lava_tool.interface',
32 'lava_dashboard_tool.commands',32 'lava_dashboard_tool.commands',
33 ]33 ]
3434
3535
36def test_modules():36def test_modules():
37 return [37 return [
38 'lava_tool.tests.test_authtoken',38 'lava_tool.tests.test_authtoken',
39 'lava_tool.tests.test_auth_commands',39 'lava_tool.tests.test_auth_commands',
40 'lava_tool.tests.test_commands',40 'lava_tool.tests.test_commands',
41 'lava_dashboard_tool.tests.test_commands',41 'lava_dashboard_tool.tests.test_commands',
42 'lava.job.tests.test_job',42 'lava.job.tests.test_job',
43 'lava.job.tests.test_commands',43 'lava.job.tests.test_commands',
44 ]44 'lava.device.tests.test_device',
45 'lava.device.tests.test_commands',
46 ]
4547
4648
47def test_suite():49def test_suite():
@@ -52,6 +54,7 @@
52 modules = app_modules() + test_modules()54 modules = app_modules() + test_modules()
53 suite = unittest.TestSuite()55 suite = unittest.TestSuite()
54 loader = unittest.TestLoader()56 loader = unittest.TestLoader()
57
55 for name in modules:58 for name in modules:
56 unit_suite = loader.loadTestsFromName(name)59 unit_suite = loader.loadTestsFromName(name)
57 suite.addTests(unit_suite)60 suite.addTests(unit_suite)
5861
=== modified file 'lava_tool/tests/test_authtoken.py'
--- lava_tool/tests/test_authtoken.py 2013-05-22 13:45:44 +0000
+++ lava_tool/tests/test_authtoken.py 2013-06-19 06:51:26 +0000
@@ -24,7 +24,6 @@
24import StringIO24import StringIO
25from unittest import TestCase25from unittest import TestCase
26import urlparse26import urlparse
27import sys
28import xmlrpclib27import xmlrpclib
2928
30from mocker import ARGS, KWARGS, Mocker29from mocker import ARGS, KWARGS, Mocker
3130
=== added file 'lava_tool/utils.py'
--- lava_tool/utils.py 1970-01-01 00:00:00 +0000
+++ lava_tool/utils.py 2013-06-19 06:51:26 +0000
@@ -0,0 +1,33 @@
1# Copyright (C) 2013 Linaro Limited
2#
3# Author: Milo Casagrande <milo.casagrande@linaro.org>
4#
5# This file is part of lava-tool.
6#
7# lava-tool is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License version 3
9# as published by the Free Software Foundation
10#
11# lava-tool is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with lava-tool. If not, see <http://www.gnu.org/licenses/>.
18
19import subprocess
20
21
22def has_command(command):
23 """Checks that the given command is available.
24
25 :param command: The name of the command to check availability.
26 """
27 command_available = True
28 try:
29 subprocess.check_call(["which", command],
30 stdout=open('/dev/null', 'w'))
31 except subprocess.CalledProcessError:
32 command_available = False
33 return command_available
034
=== modified file 'setup.py'
--- setup.py 2013-05-17 19:21:51 +0000
+++ setup.py 2013-06-19 06:51:26 +0000
@@ -53,5 +53,8 @@
53 'versiontools >= 1.3.1'53 'versiontools >= 1.3.1'
54 ],54 ],
55 setup_requires=['versiontools >= 1.3.1'],55 setup_requires=['versiontools >= 1.3.1'],
56 tests_require=['mocker >= 1.0'],56 tests_require=[
57 'mocker >= 1.0',
58 'mock >= 0.7.2'
59 ],
57 zip_safe=True)60 zip_safe=True)

Subscribers

People subscribed via source and target branches