Merge lp:~mwhudson/lava-dispatcher/more-validation into lp:lava-dispatcher
- more-validation
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zygmunt Krynicki (community) | Approve | ||
Review via email: mp+96507@code.launchpad.net |
Commit message
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.
- 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
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
Zygmunt Krynicki (zyga) 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 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_
{
"type": "object",
"additional
"properties": {
"command": {
"type": "string",
"enum": "lava_test_install"
},
"tests": {
"type": "array",
},
"type": "array",
},
"register": {
"type": "array",
}
}
}
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://
> One thing that is interesting here is that my immediate reason for doing
> ...
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_
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_
}
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
Michael Hudson-Doyle (mwhudson) wrote : | # |
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_
> 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_
matches {} or None/null -- nothing else. I'll change it to
null_or_
> 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_
> }
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...
- 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
Michael Hudson-Doyle (mwhudson) wrote : | # |
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://
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_
> for that action. Let's start with the basic schema. No error handling
> (apart from validation)
>
> {
> "type": "object",
> "additionalProp
> "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...
Preview Diff
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 |
+1
We should think how to validate commands better. Perhaps we could attempt constructing them and calling per-command validate()