Merge lp:~mwhudson/lava-dispatcher/more-validation into lp:lava-dispatcher

Proposed by Michael Hudson-Doyle
Status: Merged
Merged at revision: 253
Proposed branch: lp:~mwhudson/lava-dispatcher/more-validation
Merge into: lp:lava-dispatcher
Diff against target: 439 lines (+199/-20)
11 files modified
doc/changes.rst (+2/-0)
lava-dispatch (+2/-2)
lava_dispatcher/actions/__init__.py (+17/-0)
lava_dispatcher/actions/android_deploy.py (+14/-0)
lava_dispatcher/actions/android_install_binaries.py (+4/-1)
lava_dispatcher/actions/boot_control.py (+11/-1)
lava_dispatcher/actions/deploy.py (+59/-2)
lava_dispatcher/actions/launch_control.py (+11/-0)
lava_dispatcher/actions/lava-android-test.py (+20/-0)
lava_dispatcher/actions/lava-test.py (+35/-0)
lava_dispatcher/job.py (+24/-14)
To merge this branch: bzr merge lp:~mwhudson/lava-dispatcher/more-validation
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+96507@code.launchpad.net

Description of the change

This beefs up the validation we do of the job file and moves it into a module level function so that I can call it in the scheduler and (a) give more immediate feedback about invalid job files and (b) handle the job json without having to worry about getting KeyErrors or TypeErrors all over the place.

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

+1

We should think how to validate commands better. Perhaps we could attempt constructing them and calling per-command validate()

review: Approve
242. By Michael Hudson-Doyle

allow actions to validate their parameters

243. By Michael Hudson-Doyle

add validation to all existing actions

244. By Michael Hudson-Doyle

fix --validate

245. By Michael Hudson-Doyle

oops, validate_parameters is a classmethod

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

On Thu, 08 Mar 2012 09:23:19 -0000, Zygmunt Krynicki <email address hidden> wrote:
> Review: Approve
>
> +1
>
> We should think how to validate commands better.

Yes, good idea.

> Perhaps we could attempt constructing them and calling per-command
> validate()

Have a look at what I just pushed. I worry a bit that some of the
errors will be a bit opaque.

One thing that is interesting here is that my immediate reason for doing
this is that I want to do validation in the scheduler but the scheduler
actually accepts a slightly different schema to the dispatcher -- for
example, 'target' is optional for the scheduler but not at all for the
dispatcher, and I'd like to make the 'server' parameter optional for the
submit_results command. Not sure what to do about that though...

Cheers,
mwh

246. By Michael Hudson-Doyle

merge trunk

247. By Michael Hudson-Doyle

add changes

Revision history for this message
Zygmunt Krynicki (zyga) wrote :
Download full text (3.8 KiB)

> On Thu, 08 Mar 2012 09:23:19 -0000, Zygmunt Krynicki
> <email address hidden> wrote:
> > Review: Approve
> >
> > +1
> >
> > We should think how to validate commands better.
>
> Yes, good idea.
>
> > Perhaps we could attempt constructing them and calling per-command
> > validate()
>
> Have a look at what I just pushed. I worry a bit that some of the
> errors will be a bit opaque.

I will (just started reading now). I was thinking that we could expand the schema with custom error traps. This would allow us to keep validation to the schema _and_ provide good user experience. I have not thought much about implementation but I generally think that without general-purpose expressions (turing complete) - something I _don't_ want to offer we need to focus on node-level error traps and violation-specific error messages:

Imagine the following schema:

I want to validate a "cmd_lava_test_install" action. Here's the schema for that action. Let's start with the basic schema. No error handling (apart from validation)

{
    "type": "object",
    "additionalProperties": false,
    "properties": {
        "command": {
            "type": "string",
            "enum": "lava_test_install"
        },
        "tests": {
            "type": "array",
            "items": "string",
            "uniqueItems": true,
            "minItems": 1,
        },
        "install_python": {
            "optional": true,
            "type": "array",
            "items": "string",
            "uniqueItems": true,
            "minItems": 1,
        },
        "register": {
            "optional": true,
            "type": "array",
            "items": "string",
            "uniqueItems": true,
            "minItems": 1,
        }
    }
}

Now let's suppose we want to provide nice error message to all kinds of errors related to the "tests" property:

"lava-errors": {
    "optional": "lava_test_install action must define which tests to install using thest 'tests' property",
    "type": "lava_test_install 'tests' property must be an array of tests to install",
    "items": "lava_test_install 'tests' property must be an array of strings (where each string is a name of test to install)",
    "uniqueItems": "lava_test_install 'tests' property cannot contain duplicates",
    "minItems": "lava_test_install 'tests' property cannot be empty. If you don't want to install any tests then simply omit this action",
}

Each of those items would be used when an error would have been reported by the validator. Notice how this also shows the logical bug in the definition of the schema above. If I just want to install python/json tests then I cannot do it with tests.minItems = 1. Perhaps tests should be optional as well.

Anyway I think this can be very powerful. Remember that we can construct type unions (type may be an alternative of types) and that "dependencies" (http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.8) and "disallow" (http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.25) are super-powerful to fine tune everything. We could use it to provide conditional validation.

> One thing that is interesting here is that my immediate reason for doing
> ...

Read more...

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

8 +* Make the validation of the job file that happens before a job starts
9 + more rigorous.

Yay for changelogs!

108 +
109 + parameters_schema = null_or_trivial_schema
110 +
111 def run(self):

Won't this break?

If I say:

params: {"foo": 1, "bar": 2}

Then the code will call

run(foo=1, bar=2)

But run() will raise a TypeError then.

82 + 'boot': {'type': 'string'},
83 + 'system': {'type': 'string'},
84 + 'data': {'type': 'string'},

I smell URL validation.

85 + 'pkg': {'type': 'string', 'optional': True},

?

86 + 'use_cache': {'type': 'bool', 'optional': True},

If you have optional then also specify default. This is super nice when you have json-document (and we will soon in the dispatcher). Then you can just say: params.use_cache and the default will be magically there. DRY (no need to keep the default on the python function).

87 + 'rootfstype': {'type': 'string', 'optional': True},

What was this? Is this an enum?

182 + if 'hwpack' in parameters:
183 + if 'rootfs' not in parameters:
184 + raise ValueError('must specify rootfs when specifying hwpack')

You can specify that via the schema:

"hwpack": {
   "requires": "rootfs" // 3rd draft renames this to "dependencies"
}

"rootfs" {
   "requires": "hwpack" // 3rd draft renames this to "dependencies"
}

185 + if 'image' in parameters:
186 + raise ValueError('cannot specify image and hwpack')

This tells me that the schema is a type union. You should list all valid types as complete alternatives and don't resort to optional:

{
"type": [schema_rootfs_plus_hwpack, schema_image]
}

189 + elif 'kernel_matrix' in parameters:
190 + raise ValueError('cannot specify kernel_matrix with an image')

I don't get this. What was kernel_matrix again?

212 + 'result_disk': {'type': 'string', 'optional': True},

What is this?
Hint: let's start using "title" and "description". I'll pull them in json-document pydoc support.

233 + 'timeout': {'type': 'integer', 'optional': True},

Let's add validation against ranges (0 -> something). I'll implement them in the validator.

Otherwise +1

review: Approve
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (3.1 KiB)

On Fri, 09 Mar 2012 10:16:20 -0000, Zygmunt Krynicki <email address hidden> wrote:
> Review: Approve
>
> 8 +* Make the validation of the job file that happens before a job starts
> 9 + more rigorous.
>
> Yay for changelogs!

:)

> 108 +
> 109 + parameters_schema = null_or_trivial_schema
> 110 +
> 111 def run(self):
>
> Won't this break?
>
> If I say:
>
> params: {"foo": 1, "bar": 2}
>
> Then the code will call
>
> run(foo=1, bar=2)
>
> But run() will raise a TypeError then.

Ah, maybe null_or_trivial_schema is mis-named. null_or_trivial_schema
matches {} or None/null -- nothing else. I'll change it to
null_or_empty_schema.

> 82 + 'boot': {'type': 'string'},
> 83 + 'system': {'type': 'string'},
> 84 + 'data': {'type': 'string'},
>
> I smell URL validation.

Heh yes.

> 85 + 'pkg': {'type': 'string', 'optional': True},
>
> ?
>
> 86 + 'use_cache': {'type': 'bool', 'optional': True},
>
> If you have optional then also specify default. This is super nice when you have json-document (and we will soon in the dispatcher). Then you can just say: params.use_cache and the default will be magically there. DRY (no need to keep the default on the python function).

Ah yes, that would be nice. I'll add the defaults.
`
Also would be nice to not have parameters as arguments to the run
function, I like the sound of params.use_cache instead...

> 87 + 'rootfstype': {'type': 'string', 'optional': True},
>
> What was this? Is this an enum?

I ... guess it's an enum. I don't know if we want to encoode all the
filesystems supported for mkfs in our master images in the schema
though? I'll leave it as string for now.

> 182 + if 'hwpack' in parameters:
> 183 + if 'rootfs' not in parameters:
> 184 + raise ValueError('must specify rootfs when specifying hwpack')
>
> You can specify that via the schema:
>
> "hwpack": {
> "requires": "rootfs" // 3rd draft renames this to "dependencies"
> }
>
> "rootfs" {
> "requires": "hwpack" // 3rd draft renames this to "dependencies"
> }

Nice. Although your next suggestion is actually better...

> 185 + if 'image' in parameters:
> 186 + raise ValueError('cannot specify image and hwpack')
>
> This tells me that the schema is a type union. You should list all valid types as complete alternatives and don't resort to optional:
>
> {
> "type": [schema_rootfs_plus_hwpack, schema_image]
> }

Ah, I didn't know you could do that.

> 189 + elif 'kernel_matrix' in parameters:
> 190 + raise ValueError('cannot specify kernel_matrix with an image')
>
> I don't get this. What was kernel_matrix again?

I don't remember entirely either. Let's not try to solve all the
problems of the world at once :-)

> 212 + 'result_disk': {'type': 'string', 'optional': True},
>
> What is this?

I don't remember. I'm pretty sure it's useless, but didn't want to do
the legwork to check if anyone has used it...

> Hint: let's start using "title" and "description". I'll pull them in json-document pydoc support.
>
>
> 233 + 'timeout': {'type': 'integer', 'optional': True},
>
> Let's add validation against ranges (0 -> something). I'll implement them in the validator.

What is the syntax for ranges?

>
> Otherwise +1

T...

Read more...

248. By Michael Hudson-Doyle

merge trunk

249. By Michael Hudson-Doyle

null_or_trivial_schema -> null_or_empty_schema

250. By Michael Hudson-Doyle

attempt to use more sophisticated schema features
i failed, but left the attempt in a comment for when the validator bugs get fixed

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (5.4 KiB)

On Fri, 09 Mar 2012 10:04:18 -0000, Zygmunt Krynicki <email address hidden> wrote:
> > On Thu, 08 Mar 2012 09:23:19 -0000, Zygmunt Krynicki
> > <email address hidden> wrote:
> > > Review: Approve
> > >
> > > +1
> > >
> > > We should think how to validate commands better.
> >
> > Yes, good idea.
> >
> > > Perhaps we could attempt constructing them and calling per-command
> > > validate()
> >
> > Have a look at what I just pushed. I worry a bit that some of the
> > errors will be a bit opaque.
>
> I will (just started reading now). I was thinking that we could expand
> the schema with custom error traps. This would allow us to keep
> validation to the schema _and_ provide good user experience. I have
> not thought much about implementation but I generally think that
> without general-purpose expressions (turing complete) - something I
> _don't_ want to offer

Yes, having a turing complete schema would more or less totally defeat
the point of the whole excercise :-)

> we need to focus on node-level error traps and violation-specific
> error messages:

When I thought about this, I actually had a different, orthogonoal idea.
How about, when you submit an invalid job to LAVA, you get back a terse
message and a link you can visit for further details? I think the
difficulty in interpreting the messages comes partly from the
difficulty in locating where the problem comes from, so just showing the
message in a helpful way like so:

http://people.linaro.org/~mwh/err.html

will probably help a lot (it helps that job definitions are usually
pretty short of course, this perhaps wouldn't work so well for bundles).

If we implement what you suggest, we could have the best of both worlds
I guess.

> Imagine the following schema:
>
> I want to validate a "cmd_lava_test_install" action. Here's the schema
> for that action. Let's start with the basic schema. No error handling
> (apart from validation)
>
> {
> "type": "object",
> "additionalProperties": false,
> "properties": {
> "command": {
> "type": "string",
> "enum": "lava_test_install"
> },
> "tests": {
> "type": "array",
> "items": "string",
> "uniqueItems": true,
> "minItems": 1,
> },
> "install_python": {
> "optional": true,
> "type": "array",
> "items": "string",
> "uniqueItems": true,
> "minItems": 1,
> },
> "register": {
> "optional": true,
> "type": "array",
> "items": "string",
> "uniqueItems": true,
> "minItems": 1,
> }
> }
> }
>
> Now let's suppose we want to provide nice error message to all kinds
> of errors related to the "tests" property:
>
> "lava-errors": {
> "optional": "lava_test_install action must define which tests to install using thest 'tests' property",
> "type": "lava_test_install 'tests' property must be an array of tests to install",
> "items": "lava_test_install 'tests' property must be an array of strings (where each string is a name of test to install)",
> "uniqueI...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'doc/changes.rst'
--- doc/changes.rst 2012-03-09 01:18:19 +0000
+++ doc/changes.rst 2012-03-11 23:40:23 +0000
@@ -10,6 +10,8 @@
10 issue where the server gets busy, and doesn't connect quickly enough10 issue where the server gets busy, and doesn't connect quickly enough
11 for getting the tarballs11 for getting the tarballs
12* Stop reading the long-obsolete 'image_type' field from the job json.12* Stop reading the long-obsolete 'image_type' field from the job json.
13* Make the validation of the job file that happens before a job starts
14 more rigorous.
1315
14.. _version_0_5_6:16.. _version_0_5_6:
1517
1618
=== modified file 'lava-dispatch'
--- lava-dispatch 2012-03-01 17:10:04 +0000
+++ lava-dispatch 2012-03-11 23:40:23 +0000
@@ -26,7 +26,7 @@
2626
27from json_schema_validator.errors import ValidationError27from json_schema_validator.errors import ValidationError
2828
29from lava_dispatcher.job import LavaTestJob29from lava_dispatcher.job import LavaTestJob, validate_job_data
30from lava_dispatcher.config import get_config30from lava_dispatcher.config import get_config
3131
32parser = optparse.OptionParser('%prog: lava-dispatch <json job file>')32parser = optparse.OptionParser('%prog: lava-dispatch <json job file>')
@@ -83,7 +83,7 @@
83#FIXME Return status83#FIXME Return status
84if options.validate:84if options.validate:
85 try:85 try:
86 job.validate()86 validate_job_data(job.job_data)
87 except ValidationError as e:87 except ValidationError as e:
88 print e88 print e
89else:89else:
9090
=== modified file 'lava_dispatcher/actions/__init__.py'
--- lava_dispatcher/actions/__init__.py 2012-02-24 01:36:44 +0000
+++ lava_dispatcher/actions/__init__.py 2012-03-11 23:40:23 +0000
@@ -24,6 +24,15 @@
24import imp24import imp
25import os25import os
2626
27from json_schema_validator.schema import Schema
28from json_schema_validator.validator import Validator
29
30
31null_or_empty_schema = {
32 'type': ['object', 'null'],
33 'additionalProperties': False,
34 }
35
2736
28class classproperty(object):37class classproperty(object):
29 """Like the builtin @property, but binds to the class not instances."""38 """Like the builtin @property, but binds to the class not instances."""
@@ -56,6 +65,14 @@
56 def test_name(self, **params):65 def test_name(self, **params):
57 return self.command_name66 return self.command_name
5867
68 param_schema = None
69
70 @classmethod
71 def validate_parameters(cls, params):
72 if cls.parameters_schema:
73 schema = Schema(cls.parameters_schema)
74 Validator.validate(schema, params)
75
5976
60def _find_commands(module):77def _find_commands(module):
61 cmds = {}78 cmds = {}
6279
=== modified file 'lava_dispatcher/actions/android_deploy.py'
--- lava_dispatcher/actions/android_deploy.py 2011-12-19 02:23:53 +0000
+++ lava_dispatcher/actions/android_deploy.py 2012-03-11 23:40:23 +0000
@@ -23,5 +23,19 @@
2323
2424
25class cmd_deploy_linaro_android_image(BaseAction):25class cmd_deploy_linaro_android_image(BaseAction):
26
27 parameters_schema = {
28 'type': 'object',
29 'properties': {
30 'boot': {'type': 'string'},
31 'system': {'type': 'string'},
32 'data': {'type': 'string'},
33 'pkg': {'type': 'string', 'optional': True},
34 'use_cache': {'type': 'bool', 'optional': True, 'default': True},
35 'rootfstype': {'type': 'string', 'optional': True, 'default': 'ext4'},
36 },
37 'additionalProperties': False,
38 }
39
26 def run(self, boot, system, data, pkg=None, use_cache=True, rootfstype='ext4'):40 def run(self, boot, system, data, pkg=None, use_cache=True, rootfstype='ext4'):
27 self.client.deploy_linaro_android(boot, system, data, pkg, use_cache, rootfstype)41 self.client.deploy_linaro_android(boot, system, data, pkg, use_cache, rootfstype)
2842
=== modified file 'lava_dispatcher/actions/android_install_binaries.py'
--- lava_dispatcher/actions/android_install_binaries.py 2012-01-27 03:10:08 +0000
+++ lava_dispatcher/actions/android_install_binaries.py 2012-03-11 23:40:23 +0000
@@ -19,11 +19,14 @@
1919
20import ConfigParser20import ConfigParser
21import logging21import logging
22from lava_dispatcher.actions import BaseAction22from lava_dispatcher.actions import BaseAction, null_or_empty_schema
23from lava_dispatcher.client.master import _deploy_tarball_to_board23from lava_dispatcher.client.master import _deploy_tarball_to_board
2424
2525
26class cmd_android_install_binaries(BaseAction):26class cmd_android_install_binaries(BaseAction):
27
28 parameters_schema = null_or_empty_schema
29
27 def run(self):30 def run(self):
28 try:31 try:
29 driver_tarball = self.client.device_option(32 driver_tarball = self.client.device_option(
3033
=== modified file 'lava_dispatcher/actions/boot_control.py'
--- lava_dispatcher/actions/boot_control.py 2012-01-26 10:20:09 +0000
+++ lava_dispatcher/actions/boot_control.py 2012-03-11 23:40:23 +0000
@@ -22,12 +22,16 @@
2222
23import logging23import logging
2424
25from lava_dispatcher.actions import BaseAction25from lava_dispatcher.actions import BaseAction, null_or_empty_schema
26from lava_dispatcher.client.base import CriticalError26from lava_dispatcher.client.base import CriticalError
2727
28
28class cmd_boot_linaro_android_image(BaseAction):29class cmd_boot_linaro_android_image(BaseAction):
29 """ Call client code to boot to the master image30 """ Call client code to boot to the master image
30 """31 """
32
33 parameters_schema = null_or_empty_schema
34
31 def run(self):35 def run(self):
32 client = self.client36 client = self.client
33 try:37 try:
@@ -39,6 +43,9 @@
39class cmd_boot_linaro_image(BaseAction):43class cmd_boot_linaro_image(BaseAction):
40 """ Call client code to boot to the test image44 """ Call client code to boot to the test image
41 """45 """
46
47 parameters_schema = null_or_empty_schema
48
42 def run(self):49 def run(self):
43 client = self.client50 client = self.client
44 status = 'pass'51 status = 'pass'
@@ -55,6 +62,9 @@
55class cmd_boot_master_image(BaseAction):62class cmd_boot_master_image(BaseAction):
56 """ Call client code to boot to the master image63 """ Call client code to boot to the master image
57 """64 """
65
66 parameters_schema = null_or_empty_schema
67
58 def run(self):68 def run(self):
59 client = self.client69 client = self.client
60 client.boot_master_image()70 client.boot_master_image()
6171
=== modified file 'lava_dispatcher/actions/deploy.py'
--- lava_dispatcher/actions/deploy.py 2012-01-31 01:20:47 +0000
+++ lava_dispatcher/actions/deploy.py 2012-03-11 23:40:23 +0000
@@ -21,6 +21,63 @@
2121
2222
23class cmd_deploy_linaro_image(BaseAction):23class cmd_deploy_linaro_image(BaseAction):
24 def run(self, hwpack=None, rootfs=None, image=None, kernel_matrix=None, use_cache=True, rootfstype='ext3'):24
25 # This is how the schema for parameters should look, but there are bugs in
26 # json_schema_validation that means it doesn't work (see
27 # https://github.com/zyga/json-schema-validator/pull/6).
28
29 ## parameters_schema = {
30 ## 'type': [
31 ## {
32 ## 'type': 'object',
33 ## 'properties': {
34 ## 'image': {'type': 'string'},
35 ## },
36 ## 'additionalProperties': False,
37 ## },
38 ## {
39 ## 'type': 'object',
40 ## 'properties': {
41 ## 'hwpack': {'type': 'string'},
42 ## 'rootfs': {'type': 'string'},
43 ## 'kernel_matrix': {'type': 'string', 'optional': True},
44 ## 'use_cache': {'type': 'bool', 'optional': True, 'default': True},
45 ## 'rootfstype': {'type': 'string', 'optional': True, 'default': 'ext3'},
46 ## },
47 ## 'additionalProperties': False,
48 ## },
49 ## ],
50 ## }
51
52 parameters_schema = {
53 'type': 'object',
54 'properties': {
55 'hwpack': {'type': 'string', 'optional': True},
56 'rootfs': {'type': 'string', 'optional': True},
57 'image': {'type': 'string', 'optional': True},
58 'kernel_matrix': {'type': 'string', 'optional': True},
59 'use_cache': {'type': 'bool', 'optional': True},
60 'rootfstype': {'type': 'string', 'optional': True},
61 },
62 'additionalProperties': False,
63 }
64
65 @classmethod
66 def validate_parameters(cls, parameters):
67 super(cmd_deploy_linaro_image, cls).validate_parameters(parameters)
68 if 'hwpack' in parameters:
69 if 'rootfs' not in parameters:
70 raise ValueError('must specify rootfs when specifying hwpack')
71 if 'image' in parameters:
72 raise ValueError('cannot specify image and hwpack')
73 elif 'image' not in parameters:
74 raise ValueError('must specify image if not specifying a hwpack')
75 elif 'kernel_matrix' in parameters:
76 raise ValueError('cannot specify kernel_matrix with an image')
77
78 def run(self, hwpack=None, rootfs=None, image=None, kernel_matrix=None,
79 use_cache=True, rootfstype='ext3'):
25 self.client.deploy_linaro(80 self.client.deploy_linaro(
26 hwpack=hwpack, rootfs=rootfs, image=image, kernel_matrix=kernel_matrix, use_cache=use_cache, rootfstype=rootfstype)81 hwpack=hwpack, rootfs=rootfs, image=image,
82 kernel_matrix=kernel_matrix, use_cache=use_cache,
83 rootfstype=rootfstype)
2784
=== modified file 'lava_dispatcher/actions/launch_control.py'
--- lava_dispatcher/actions/launch_control.py 2012-02-24 02:13:45 +0000
+++ lava_dispatcher/actions/launch_control.py 2012-03-11 23:40:23 +0000
@@ -77,6 +77,17 @@
7777
78class cmd_submit_results(BaseAction):78class cmd_submit_results(BaseAction):
7979
80 parameters_schema = {
81 'type': 'object',
82 'properties': {
83 'server': {'type': 'string'},
84 'stream': {'type': 'string'},
85 'result_disk': {'type': 'string', 'optional': True},
86 'token': {'type': 'string', 'optional': True},
87 },
88 'additionalProperties': False,
89 }
90
80 def _get_bundles_from_device(self, result_disk):91 def _get_bundles_from_device(self, result_disk):
81 err_msg = ''92 err_msg = ''
82 status = 'fail'93 status = 'fail'
8394
=== modified file 'lava_dispatcher/actions/lava-android-test.py'
--- lava_dispatcher/actions/lava-android-test.py 2011-11-24 03:00:54 +0000
+++ lava_dispatcher/actions/lava-android-test.py 2012-03-11 23:40:23 +0000
@@ -36,6 +36,15 @@
3636
37class cmd_lava_android_test_run(AndroidTestAction):37class cmd_lava_android_test_run(AndroidTestAction):
3838
39 parameters_schema = {
40 'type': 'object',
41 'properties': {
42 'test_name': {'type': 'string'},
43 'timeout': {'type': 'integer', 'optional': True},
44 },
45 'additionalProperties': False,
46 }
47
39 def test_name(self, test_name, timeout=-1):48 def test_name(self, test_name, timeout=-1):
40 return super(cmd_lava_android_test_run, self).test_name() + \49 return super(cmd_lava_android_test_run, self).test_name() + \
41 ' (%s)' % test_name50 ' (%s)' % test_name
@@ -61,6 +70,17 @@
61 """70 """
62 lava-test deployment to test image rootfs by chroot71 lava-test deployment to test image rootfs by chroot
63 """72 """
73
74 parameters_schema = {
75 'type': 'object',
76 'properties': {
77 'tests': {'type': 'array', 'items': {'type': 'string'}},
78 'option': {'type': 'string', 'optional': True},
79 'timeout': {'type': 'integer', 'optional': True},
80 },
81 'additionalProperties': False,
82 }
83
64 def run(self, tests, option=None, timeout=2400):84 def run(self, tests, option=None, timeout=2400):
65 self.check_lava_android_test_installed()85 self.check_lava_android_test_installed()
66 with self.client.android_tester_session() as session:86 with self.client.android_tester_session() as session:
6787
=== modified file 'lava_dispatcher/actions/lava-test.py'
--- lava_dispatcher/actions/lava-test.py 2012-03-08 21:52:44 +0000
+++ lava_dispatcher/actions/lava-test.py 2012-03-11 23:40:23 +0000
@@ -49,6 +49,16 @@
4949
50class cmd_lava_test_run(BaseAction):50class cmd_lava_test_run(BaseAction):
5151
52 parameters_schema = {
53 'type': 'object',
54 'properties': {
55 'test_name': {'type': 'string'},
56 'test_options': {'type': 'string', 'optional': True},
57 'timeout': {'type': 'integer', 'optional': True},
58 },
59 'additionalProperties': False,
60 }
61
52 def test_name(self, test_name, test_options = "", timeout=-1):62 def test_name(self, test_name, test_options = "", timeout=-1):
53 return super(cmd_lava_test_run, self).test_name() + ' (%s)' % test_name63 return super(cmd_lava_test_run, self).test_name() + ' (%s)' % test_name
5464
@@ -85,6 +95,22 @@
85 """95 """
86 lava-test deployment to test image rootfs by chroot96 lava-test deployment to test image rootfs by chroot
87 """97 """
98
99 parameters_schema = {
100 'type': 'object',
101 'properties': {
102 'tests': {'type': 'array', 'items': {'type': 'string'}},
103 'install_python': {
104 'type': 'array', 'items': {'type': 'string'}, 'optional': True
105 },
106 'register': {
107 'type': 'array', 'items': {'type': 'string'}, 'optional': True
108 },
109 'timeout': {'type': 'integer', 'optional': True},
110 },
111 'additionalProperties': False,
112 }
113
88 def run(self, tests, install_python = None, register = None, timeout=2400):114 def run(self, tests, install_python = None, register = None, timeout=2400):
89 logging.info("Executing lava_test_install (%s) command" % ",".join(tests))115 logging.info("Executing lava_test_install (%s) command" % ",".join(tests))
90116
@@ -112,6 +138,15 @@
112 add apt repository to test image rootfs by chroot138 add apt repository to test image rootfs by chroot
113 arg could be 'deb uri distribution [component1] [component2][...]' or ppa:<ppa_name>139 arg could be 'deb uri distribution [component1] [component2][...]' or ppa:<ppa_name>
114 """140 """
141
142 parameters_schema = {
143 'type': 'object',
144 'properties': {
145 'arg': {'type': 'string'},
146 },
147 'additionalProperties': False,
148 }
149
115 def run(self, arg):150 def run(self, arg):
116 with self.client.reliable_session() as session:151 with self.client.reliable_session() as session:
117152
118153
=== modified file 'lava_dispatcher/job.py'
--- lava_dispatcher/job.py 2012-03-09 01:18:19 +0000
+++ lava_dispatcher/job.py 2012-03-11 23:40:23 +0000
@@ -28,8 +28,7 @@
2828
29from lava_dispatcher.actions import get_all_cmds29from lava_dispatcher.actions import get_all_cmds
30from lava_dispatcher.client.base import CriticalError, GeneralError30from lava_dispatcher.client.base import CriticalError, GeneralError
31from lava_dispatcher.config import get_config31from lava_dispatcher.context import LavaContext
32from lava_dispatcher.context import LavaContext
3332
3433
35job_schema = {34job_schema = {
@@ -38,12 +37,15 @@
38 'properties': {37 'properties': {
39 'actions': {38 'actions': {
40 'items': {39 'items': {
40 'type': 'object',
41 'properties': {41 'properties': {
42 'command': {42 'command': {
43 'optional': False,43 'optional': False,
44 'type': 'string',
44 },45 },
45 'parameters': {46 'parameters': {
46 'optional': True,47 'optional': True,
48 'type': 'object',
47 },49 },
48 'metadata': {50 'metadata': {
49 'optional': True,51 'optional': True,
@@ -53,12 +55,15 @@
53 },55 },
54 },56 },
55 'device_type': {57 'device_type': {
58 'type': 'string',
56 'optional': True,59 'optional': True,
57 },60 },
58 'job_name': {61 'job_name': {
62 'type': 'string',
59 'optional': True,63 'optional': True,
60 },64 },
61 'target': {65 'target': {
66 'type': 'string',
62 'optional': True,67 'optional': True,
63 },68 },
64 'timeout': {69 'timeout': {
@@ -66,11 +71,27 @@
66 'optional': False,71 'optional': False,
67 },72 },
68 'logging_level': {73 'logging_level': {
74 'type': 'string',
75 'enum': ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
69 'optional': True,76 'optional': True,
70 },77 },
71 },78 },
72 }79 }
7380
81
82def validate_job_data(job_data):
83 schema = Schema(job_schema)
84 validator = Validator()
85 validator.validate(schema, job_data)
86 lava_commands = get_all_cmds()
87 for action in job_data['actions']:
88 command_name = action['command']
89 command = lava_commands.get(command_name)
90 if command is None:
91 raise ValueError("action %r not known" % command_name)
92 command.validate_parameters(action.get('parameters'))
93
94
74class LavaTestJob(object):95class LavaTestJob(object):
75 def __init__(self, job_json, oob_file, config):96 def __init__(self, job_json, oob_file, config):
76 self.job_status = 'pass'97 self.job_status = 'pass'
@@ -92,19 +113,8 @@
92 except :113 except :
93 return None114 return None
94115
95 def validate(self):
96 schema = Schema(job_schema)
97 validator = Validator()
98 validator.validate(schema, self.job_data)
99
100 lava_commands = get_all_cmds()
101 for action in self.job_data['actions']:
102 command_name = action['command']
103 if command_name not in lava_commands:
104 raise CriticalError("action %r not known" % command_name)
105
106 def run(self):116 def run(self):
107 self.validate()117 validate_job_data(self.job_data)
108 self._set_logging_level()118 self._set_logging_level()
109 lava_commands = get_all_cmds()119 lava_commands = get_all_cmds()
110120

Subscribers

People subscribed via source and target branches