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
1=== modified file 'doc/changes.rst'
2--- doc/changes.rst 2012-03-09 01:18:19 +0000
3+++ doc/changes.rst 2012-03-11 23:40:23 +0000
4@@ -10,6 +10,8 @@
5 issue where the server gets busy, and doesn't connect quickly enough
6 for getting the tarballs
7 * Stop reading the long-obsolete 'image_type' field from the job json.
8+* Make the validation of the job file that happens before a job starts
9+ more rigorous.
10
11 .. _version_0_5_6:
12
13
14=== modified file 'lava-dispatch'
15--- lava-dispatch 2012-03-01 17:10:04 +0000
16+++ lava-dispatch 2012-03-11 23:40:23 +0000
17@@ -26,7 +26,7 @@
18
19 from json_schema_validator.errors import ValidationError
20
21-from lava_dispatcher.job import LavaTestJob
22+from lava_dispatcher.job import LavaTestJob, validate_job_data
23 from lava_dispatcher.config import get_config
24
25 parser = optparse.OptionParser('%prog: lava-dispatch <json job file>')
26@@ -83,7 +83,7 @@
27 #FIXME Return status
28 if options.validate:
29 try:
30- job.validate()
31+ validate_job_data(job.job_data)
32 except ValidationError as e:
33 print e
34 else:
35
36=== modified file 'lava_dispatcher/actions/__init__.py'
37--- lava_dispatcher/actions/__init__.py 2012-02-24 01:36:44 +0000
38+++ lava_dispatcher/actions/__init__.py 2012-03-11 23:40:23 +0000
39@@ -24,6 +24,15 @@
40 import imp
41 import os
42
43+from json_schema_validator.schema import Schema
44+from json_schema_validator.validator import Validator
45+
46+
47+null_or_empty_schema = {
48+ 'type': ['object', 'null'],
49+ 'additionalProperties': False,
50+ }
51+
52
53 class classproperty(object):
54 """Like the builtin @property, but binds to the class not instances."""
55@@ -56,6 +65,14 @@
56 def test_name(self, **params):
57 return self.command_name
58
59+ param_schema = None
60+
61+ @classmethod
62+ def validate_parameters(cls, params):
63+ if cls.parameters_schema:
64+ schema = Schema(cls.parameters_schema)
65+ Validator.validate(schema, params)
66+
67
68 def _find_commands(module):
69 cmds = {}
70
71=== modified file 'lava_dispatcher/actions/android_deploy.py'
72--- lava_dispatcher/actions/android_deploy.py 2011-12-19 02:23:53 +0000
73+++ lava_dispatcher/actions/android_deploy.py 2012-03-11 23:40:23 +0000
74@@ -23,5 +23,19 @@
75
76
77 class cmd_deploy_linaro_android_image(BaseAction):
78+
79+ parameters_schema = {
80+ 'type': 'object',
81+ 'properties': {
82+ 'boot': {'type': 'string'},
83+ 'system': {'type': 'string'},
84+ 'data': {'type': 'string'},
85+ 'pkg': {'type': 'string', 'optional': True},
86+ 'use_cache': {'type': 'bool', 'optional': True, 'default': True},
87+ 'rootfstype': {'type': 'string', 'optional': True, 'default': 'ext4'},
88+ },
89+ 'additionalProperties': False,
90+ }
91+
92 def run(self, boot, system, data, pkg=None, use_cache=True, rootfstype='ext4'):
93 self.client.deploy_linaro_android(boot, system, data, pkg, use_cache, rootfstype)
94
95=== modified file 'lava_dispatcher/actions/android_install_binaries.py'
96--- lava_dispatcher/actions/android_install_binaries.py 2012-01-27 03:10:08 +0000
97+++ lava_dispatcher/actions/android_install_binaries.py 2012-03-11 23:40:23 +0000
98@@ -19,11 +19,14 @@
99
100 import ConfigParser
101 import logging
102-from lava_dispatcher.actions import BaseAction
103+from lava_dispatcher.actions import BaseAction, null_or_empty_schema
104 from lava_dispatcher.client.master import _deploy_tarball_to_board
105
106
107 class cmd_android_install_binaries(BaseAction):
108+
109+ parameters_schema = null_or_empty_schema
110+
111 def run(self):
112 try:
113 driver_tarball = self.client.device_option(
114
115=== modified file 'lava_dispatcher/actions/boot_control.py'
116--- lava_dispatcher/actions/boot_control.py 2012-01-26 10:20:09 +0000
117+++ lava_dispatcher/actions/boot_control.py 2012-03-11 23:40:23 +0000
118@@ -22,12 +22,16 @@
119
120 import logging
121
122-from lava_dispatcher.actions import BaseAction
123+from lava_dispatcher.actions import BaseAction, null_or_empty_schema
124 from lava_dispatcher.client.base import CriticalError
125
126+
127 class cmd_boot_linaro_android_image(BaseAction):
128 """ Call client code to boot to the master image
129 """
130+
131+ parameters_schema = null_or_empty_schema
132+
133 def run(self):
134 client = self.client
135 try:
136@@ -39,6 +43,9 @@
137 class cmd_boot_linaro_image(BaseAction):
138 """ Call client code to boot to the test image
139 """
140+
141+ parameters_schema = null_or_empty_schema
142+
143 def run(self):
144 client = self.client
145 status = 'pass'
146@@ -55,6 +62,9 @@
147 class cmd_boot_master_image(BaseAction):
148 """ Call client code to boot to the master image
149 """
150+
151+ parameters_schema = null_or_empty_schema
152+
153 def run(self):
154 client = self.client
155 client.boot_master_image()
156
157=== modified file 'lava_dispatcher/actions/deploy.py'
158--- lava_dispatcher/actions/deploy.py 2012-01-31 01:20:47 +0000
159+++ lava_dispatcher/actions/deploy.py 2012-03-11 23:40:23 +0000
160@@ -21,6 +21,63 @@
161
162
163 class cmd_deploy_linaro_image(BaseAction):
164- def run(self, hwpack=None, rootfs=None, image=None, kernel_matrix=None, use_cache=True, rootfstype='ext3'):
165+
166+ # This is how the schema for parameters should look, but there are bugs in
167+ # json_schema_validation that means it doesn't work (see
168+ # https://github.com/zyga/json-schema-validator/pull/6).
169+
170+ ## parameters_schema = {
171+ ## 'type': [
172+ ## {
173+ ## 'type': 'object',
174+ ## 'properties': {
175+ ## 'image': {'type': 'string'},
176+ ## },
177+ ## 'additionalProperties': False,
178+ ## },
179+ ## {
180+ ## 'type': 'object',
181+ ## 'properties': {
182+ ## 'hwpack': {'type': 'string'},
183+ ## 'rootfs': {'type': 'string'},
184+ ## 'kernel_matrix': {'type': 'string', 'optional': True},
185+ ## 'use_cache': {'type': 'bool', 'optional': True, 'default': True},
186+ ## 'rootfstype': {'type': 'string', 'optional': True, 'default': 'ext3'},
187+ ## },
188+ ## 'additionalProperties': False,
189+ ## },
190+ ## ],
191+ ## }
192+
193+ parameters_schema = {
194+ 'type': 'object',
195+ 'properties': {
196+ 'hwpack': {'type': 'string', 'optional': True},
197+ 'rootfs': {'type': 'string', 'optional': True},
198+ 'image': {'type': 'string', 'optional': True},
199+ 'kernel_matrix': {'type': 'string', 'optional': True},
200+ 'use_cache': {'type': 'bool', 'optional': True},
201+ 'rootfstype': {'type': 'string', 'optional': True},
202+ },
203+ 'additionalProperties': False,
204+ }
205+
206+ @classmethod
207+ def validate_parameters(cls, parameters):
208+ super(cmd_deploy_linaro_image, cls).validate_parameters(parameters)
209+ if 'hwpack' in parameters:
210+ if 'rootfs' not in parameters:
211+ raise ValueError('must specify rootfs when specifying hwpack')
212+ if 'image' in parameters:
213+ raise ValueError('cannot specify image and hwpack')
214+ elif 'image' not in parameters:
215+ raise ValueError('must specify image if not specifying a hwpack')
216+ elif 'kernel_matrix' in parameters:
217+ raise ValueError('cannot specify kernel_matrix with an image')
218+
219+ def run(self, hwpack=None, rootfs=None, image=None, kernel_matrix=None,
220+ use_cache=True, rootfstype='ext3'):
221 self.client.deploy_linaro(
222- hwpack=hwpack, rootfs=rootfs, image=image, kernel_matrix=kernel_matrix, use_cache=use_cache, rootfstype=rootfstype)
223+ hwpack=hwpack, rootfs=rootfs, image=image,
224+ kernel_matrix=kernel_matrix, use_cache=use_cache,
225+ rootfstype=rootfstype)
226
227=== modified file 'lava_dispatcher/actions/launch_control.py'
228--- lava_dispatcher/actions/launch_control.py 2012-02-24 02:13:45 +0000
229+++ lava_dispatcher/actions/launch_control.py 2012-03-11 23:40:23 +0000
230@@ -77,6 +77,17 @@
231
232 class cmd_submit_results(BaseAction):
233
234+ parameters_schema = {
235+ 'type': 'object',
236+ 'properties': {
237+ 'server': {'type': 'string'},
238+ 'stream': {'type': 'string'},
239+ 'result_disk': {'type': 'string', 'optional': True},
240+ 'token': {'type': 'string', 'optional': True},
241+ },
242+ 'additionalProperties': False,
243+ }
244+
245 def _get_bundles_from_device(self, result_disk):
246 err_msg = ''
247 status = 'fail'
248
249=== modified file 'lava_dispatcher/actions/lava-android-test.py'
250--- lava_dispatcher/actions/lava-android-test.py 2011-11-24 03:00:54 +0000
251+++ lava_dispatcher/actions/lava-android-test.py 2012-03-11 23:40:23 +0000
252@@ -36,6 +36,15 @@
253
254 class cmd_lava_android_test_run(AndroidTestAction):
255
256+ parameters_schema = {
257+ 'type': 'object',
258+ 'properties': {
259+ 'test_name': {'type': 'string'},
260+ 'timeout': {'type': 'integer', 'optional': True},
261+ },
262+ 'additionalProperties': False,
263+ }
264+
265 def test_name(self, test_name, timeout=-1):
266 return super(cmd_lava_android_test_run, self).test_name() + \
267 ' (%s)' % test_name
268@@ -61,6 +70,17 @@
269 """
270 lava-test deployment to test image rootfs by chroot
271 """
272+
273+ parameters_schema = {
274+ 'type': 'object',
275+ 'properties': {
276+ 'tests': {'type': 'array', 'items': {'type': 'string'}},
277+ 'option': {'type': 'string', 'optional': True},
278+ 'timeout': {'type': 'integer', 'optional': True},
279+ },
280+ 'additionalProperties': False,
281+ }
282+
283 def run(self, tests, option=None, timeout=2400):
284 self.check_lava_android_test_installed()
285 with self.client.android_tester_session() as session:
286
287=== modified file 'lava_dispatcher/actions/lava-test.py'
288--- lava_dispatcher/actions/lava-test.py 2012-03-08 21:52:44 +0000
289+++ lava_dispatcher/actions/lava-test.py 2012-03-11 23:40:23 +0000
290@@ -49,6 +49,16 @@
291
292 class cmd_lava_test_run(BaseAction):
293
294+ parameters_schema = {
295+ 'type': 'object',
296+ 'properties': {
297+ 'test_name': {'type': 'string'},
298+ 'test_options': {'type': 'string', 'optional': True},
299+ 'timeout': {'type': 'integer', 'optional': True},
300+ },
301+ 'additionalProperties': False,
302+ }
303+
304 def test_name(self, test_name, test_options = "", timeout=-1):
305 return super(cmd_lava_test_run, self).test_name() + ' (%s)' % test_name
306
307@@ -85,6 +95,22 @@
308 """
309 lava-test deployment to test image rootfs by chroot
310 """
311+
312+ parameters_schema = {
313+ 'type': 'object',
314+ 'properties': {
315+ 'tests': {'type': 'array', 'items': {'type': 'string'}},
316+ 'install_python': {
317+ 'type': 'array', 'items': {'type': 'string'}, 'optional': True
318+ },
319+ 'register': {
320+ 'type': 'array', 'items': {'type': 'string'}, 'optional': True
321+ },
322+ 'timeout': {'type': 'integer', 'optional': True},
323+ },
324+ 'additionalProperties': False,
325+ }
326+
327 def run(self, tests, install_python = None, register = None, timeout=2400):
328 logging.info("Executing lava_test_install (%s) command" % ",".join(tests))
329
330@@ -112,6 +138,15 @@
331 add apt repository to test image rootfs by chroot
332 arg could be 'deb uri distribution [component1] [component2][...]' or ppa:<ppa_name>
333 """
334+
335+ parameters_schema = {
336+ 'type': 'object',
337+ 'properties': {
338+ 'arg': {'type': 'string'},
339+ },
340+ 'additionalProperties': False,
341+ }
342+
343 def run(self, arg):
344 with self.client.reliable_session() as session:
345
346
347=== modified file 'lava_dispatcher/job.py'
348--- lava_dispatcher/job.py 2012-03-09 01:18:19 +0000
349+++ lava_dispatcher/job.py 2012-03-11 23:40:23 +0000
350@@ -28,8 +28,7 @@
351
352 from lava_dispatcher.actions import get_all_cmds
353 from lava_dispatcher.client.base import CriticalError, GeneralError
354-from lava_dispatcher.config import get_config
355-from lava_dispatcher.context import LavaContext
356+from lava_dispatcher.context import LavaContext
357
358
359 job_schema = {
360@@ -38,12 +37,15 @@
361 'properties': {
362 'actions': {
363 'items': {
364+ 'type': 'object',
365 'properties': {
366 'command': {
367 'optional': False,
368+ 'type': 'string',
369 },
370 'parameters': {
371 'optional': True,
372+ 'type': 'object',
373 },
374 'metadata': {
375 'optional': True,
376@@ -53,12 +55,15 @@
377 },
378 },
379 'device_type': {
380+ 'type': 'string',
381 'optional': True,
382 },
383 'job_name': {
384+ 'type': 'string',
385 'optional': True,
386 },
387 'target': {
388+ 'type': 'string',
389 'optional': True,
390 },
391 'timeout': {
392@@ -66,11 +71,27 @@
393 'optional': False,
394 },
395 'logging_level': {
396+ 'type': 'string',
397+ 'enum': ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
398 'optional': True,
399 },
400 },
401 }
402
403+
404+def validate_job_data(job_data):
405+ schema = Schema(job_schema)
406+ validator = Validator()
407+ validator.validate(schema, job_data)
408+ lava_commands = get_all_cmds()
409+ for action in job_data['actions']:
410+ command_name = action['command']
411+ command = lava_commands.get(command_name)
412+ if command is None:
413+ raise ValueError("action %r not known" % command_name)
414+ command.validate_parameters(action.get('parameters'))
415+
416+
417 class LavaTestJob(object):
418 def __init__(self, job_json, oob_file, config):
419 self.job_status = 'pass'
420@@ -92,19 +113,8 @@
421 except :
422 return None
423
424- def validate(self):
425- schema = Schema(job_schema)
426- validator = Validator()
427- validator.validate(schema, self.job_data)
428-
429- lava_commands = get_all_cmds()
430- for action in self.job_data['actions']:
431- command_name = action['command']
432- if command_name not in lava_commands:
433- raise CriticalError("action %r not known" % command_name)
434-
435 def run(self):
436- self.validate()
437+ validate_job_data(self.job_data)
438 self._set_logging_level()
439 lava_commands = get_all_cmds()
440

Subscribers

People subscribed via source and target branches