Merge "update installer under scenario"
[releng.git] / utils / test / testapi / opnfv_testapi / resources / scenario_handlers.py
1 import functools
2
3 from opnfv_testapi.common import message
4 from opnfv_testapi.common import raises
5 from opnfv_testapi.resources import handlers
6 import opnfv_testapi.resources.scenario_models as models
7 from opnfv_testapi.tornado_swagger import swagger
8
9
10 class GenericScenarioHandler(handlers.GenericApiHandler):
11     def __init__(self, application, request, **kwargs):
12         super(GenericScenarioHandler, self).__init__(application,
13                                                      request,
14                                                      **kwargs)
15         self.table = self.db_scenarios
16         self.table_cls = models.Scenario
17
18     def set_query(self, locators):
19         query = dict()
20         elem_query = dict()
21         for k, v in locators.iteritems():
22             if k == 'scenario':
23                 query['name'] = v
24             elif k == 'installer':
25                 elem_query["installer"] = v
26             elif k == 'version':
27                 elem_query["versions.version"] = v
28             elif k == 'project':
29                 elem_query["versions.projects.project"] = v
30             else:
31                 query[k] = v
32         if elem_query:
33             query['installers'] = {'$elemMatch': elem_query}
34         return query
35
36
37 class ScenariosCLHandler(GenericScenarioHandler):
38     @swagger.operation(nickname="queryScenarios")
39     def get(self):
40         """
41             @description: Retrieve scenario(s).
42             @notes: Retrieve scenario(s)
43                 Available filters for this request are :
44                  - name : scenario name
45
46                 GET /scenarios?name=scenario_1
47             @param name: scenario name
48             @type name: L{string}
49             @in name: query
50             @required name: False
51             @param installer: installer type
52             @type installer: L{string}
53             @in installer: query
54             @required installer: False
55             @param version: version
56             @type version: L{string}
57             @in version: query
58             @required version: False
59             @param project: project name
60             @type project: L{string}
61             @in project: query
62             @required project: False
63             @return 200: all scenarios satisfy queries,
64                          empty list if no scenario is found
65             @rtype: L{Scenarios}
66         """
67
68         def _set_query():
69             query = dict()
70             elem_query = dict()
71             for k in self.request.query_arguments.keys():
72                 v = self.get_query_argument(k)
73                 if k == 'installer':
74                     elem_query["installer"] = v
75                 elif k == 'version':
76                     elem_query["versions.version"] = v
77                 elif k == 'project':
78                     elem_query["versions.projects.project"] = v
79                 else:
80                     query[k] = v
81             if elem_query:
82                 query['installers'] = {'$elemMatch': elem_query}
83             return query
84
85         self._list(query=_set_query())
86
87     @swagger.operation(nickname="createScenario")
88     def post(self):
89         """
90             @description: create a new scenario by name
91             @param body: scenario to be created
92             @type body: L{ScenarioCreateRequest}
93             @in body: body
94             @rtype: L{CreateResponse}
95             @return 200: scenario is created.
96             @raise 403: scenario already exists
97             @raise 400:  body or name not provided
98         """
99         def query():
100             return {'name': self.json_args.get('name')}
101         miss_fields = ['name']
102         self._create(miss_fields=miss_fields, query=query)
103
104
105 class ScenarioGURHandler(GenericScenarioHandler):
106     @swagger.operation(nickname='getScenarioByName')
107     def get(self, name):
108         """
109             @description: get a single scenario by name
110             @rtype: L{Scenario}
111             @return 200: scenario exist
112             @raise 404: scenario not exist
113         """
114         self._get_one(query={'name': name})
115         pass
116
117     def put(self, name):
118         pass
119
120     @swagger.operation(nickname="deleteScenarioByName")
121     def delete(self, name):
122         """
123         @description: delete a scenario by name
124         @return 200: delete success
125         @raise 404: scenario not exist:
126         """
127         self._delete(query={'name': name})
128
129
130 class ScenarioUpdater(object):
131     def __init__(self, data, body=None,
132                  installer=None, version=None, project=None):
133         self.data = data
134         self.body = body
135         self.installer = installer
136         self.version = version
137         self.project = project
138
139     def update(self, item, action):
140         updates = {
141             ('scores', 'post'): self._update_requests_add_score,
142             ('trust_indicators', 'post'): self._update_requests_add_ti,
143             ('customs', 'post'): self._update_requests_add_customs,
144             ('customs', 'put'): self._update_requests_update_customs,
145             ('customs', 'delete'): self._update_requests_delete_customs,
146             ('projects', 'post'): self._update_requests_add_projects,
147             ('projects', 'put'): self._update_requests_update_projects,
148             ('projects', 'delete'): self._update_requests_delete_projects,
149             ('owner', 'put'): self._update_requests_change_owner,
150             ('versions', 'post'): self._update_requests_add_versions,
151             ('versions', 'put'): self._update_requests_update_versions,
152             ('versions', 'delete'): self._update_requests_delete_versions,
153             ('installers', 'post'): self._update_requests_add_installers,
154             ('installers', 'put'): self._update_requests_update_installers,
155             ('installers', 'delete'): self._update_requests_delete_installers,
156         }
157         updates[(item, action)](self.data)
158
159         return self.data.format()
160
161     def iter_installers(xstep):
162         @functools.wraps(xstep)
163         def magic(self, data):
164             [xstep(self, installer)
165              for installer in self._filter_installers(data.installers)]
166         return magic
167
168     def iter_versions(xstep):
169         @functools.wraps(xstep)
170         def magic(self, installer):
171             [xstep(self, version)
172              for version in (self._filter_versions(installer.versions))]
173         return magic
174
175     def iter_projects(xstep):
176         @functools.wraps(xstep)
177         def magic(self, version):
178             [xstep(self, project)
179              for project in (self._filter_projects(version.projects))]
180         return magic
181
182     @iter_installers
183     @iter_versions
184     @iter_projects
185     def _update_requests_add_score(self, project):
186         project.scores.append(
187             models.ScenarioScore.from_dict(self.body))
188
189     @iter_installers
190     @iter_versions
191     @iter_projects
192     def _update_requests_add_ti(self, project):
193         project.trust_indicators.append(
194             models.ScenarioTI.from_dict(self.body))
195
196     @iter_installers
197     @iter_versions
198     @iter_projects
199     def _update_requests_add_customs(self, project):
200         project.customs = list(set(project.customs + self.body))
201
202     @iter_installers
203     @iter_versions
204     @iter_projects
205     def _update_requests_update_customs(self, project):
206         project.customs = list(set(self.body))
207
208     @iter_installers
209     @iter_versions
210     @iter_projects
211     def _update_requests_delete_customs(self, project):
212         project.customs = filter(
213             lambda f: f not in self.body,
214             project.customs)
215
216     @iter_installers
217     @iter_versions
218     def _update_requests_add_projects(self, version):
219         version.projects = self._update_with_body(models.ScenarioProject,
220                                                   'project',
221                                                   version.projects)
222
223     @iter_installers
224     @iter_versions
225     def _update_requests_update_projects(self, version):
226         version.projects = self._update_with_body(models.ScenarioProject,
227                                                   'project',
228                                                   list())
229
230     @iter_installers
231     @iter_versions
232     def _update_requests_delete_projects(self, version):
233         version.projects = self._remove_projects(version.projects)
234
235     @iter_installers
236     @iter_versions
237     def _update_requests_change_owner(self, version):
238         version.owner = self.body.get('owner')
239
240     @iter_installers
241     def _update_requests_add_versions(self, installer):
242         installer.versions = self._update_with_body(models.ScenarioVersion,
243                                                     'version',
244                                                     installer.versions)
245
246     @iter_installers
247     def _update_requests_update_versions(self, installer):
248         installer.versions = self._update_with_body(models.ScenarioVersion,
249                                                     'version',
250                                                     list())
251
252     @iter_installers
253     def _update_requests_delete_versions(self, installer):
254         installer.versions = self._remove_versions(installer.versions)
255
256     def _update_requests_add_installers(self, scenario):
257         scenario.installers = self._update_with_body(models.ScenarioInstaller,
258                                                      'installer',
259                                                      scenario.installers)
260
261     def _update_requests_update_installers(self, scenario):
262         scenario.installers = self._update_with_body(models.ScenarioInstaller,
263                                                      'installer',
264                                                      list())
265
266     def _update_requests_delete_installers(self, scenario):
267         scenario.installers = self._remove_installers(scenario.installers)
268
269     def _update_with_body(self, clazz, field, withs):
270         exists = list()
271         malformat = list()
272         for new in self.body:
273             try:
274                 format_new = clazz.from_dict_with_raise(new)
275                 new_name = getattr(format_new, field)
276                 if not any(getattr(o, field) == new_name for o in withs):
277                     withs.append(format_new)
278                 else:
279                     exists.append(new_name)
280             except Exception as error:
281                 malformat.append(error.message)
282         if malformat:
283             raises.BadRequest(message.bad_format(malformat))
284         elif exists:
285             raises.Conflict(message.exist('{}s'.format(field), exists))
286         return withs
287
288     def _filter_installers(self, installers):
289         return self._filter('installer', installers)
290
291     def _remove_installers(self, installers):
292         return self._remove('installer', installers)
293
294     def _filter_versions(self, versions):
295         return self._filter('version', versions)
296
297     def _remove_versions(self, versions):
298         return self._remove('version', versions)
299
300     def _filter_projects(self, projects):
301         return self._filter('project', projects)
302
303     def _remove_projects(self, projects):
304         return self._remove('project', projects)
305
306     def _filter(self, item, items):
307         return filter(
308             lambda f: getattr(f, item) == getattr(self, item),
309             items)
310
311     def _remove(self, field, fields):
312         return filter(
313             lambda f: getattr(f, field) not in self.body,
314             fields)
315
316
317 class GenericScenarioUpdateHandler(GenericScenarioHandler):
318     def __init__(self, application, request, **kwargs):
319         super(GenericScenarioUpdateHandler, self).__init__(application,
320                                                            request,
321                                                            **kwargs)
322         self.installer = None
323         self.version = None
324         self.project = None
325         self.item = None
326         self.action = None
327
328     def do_update(self, item, action, locators):
329         self.item = item
330         self.action = action
331         for k, v in locators.iteritems():
332             if not v:
333                 v = self.get_query_argument(k)
334                 setattr(self, k, v)
335                 locators[k] = v
336         self.pure_update(query=self.set_query(locators=locators))
337
338     def _update_requests(self, data):
339         return ScenarioUpdater(data,
340                                self.json_args,
341                                self.installer,
342                                self.version,
343                                self.project).update(self.item, self.action)
344
345
346 class ScenarioScoresHandler(GenericScenarioUpdateHandler):
347     @swagger.operation(nickname="addScoreRecord")
348     def post(self, scenario):
349         """
350         @description: add a new score record
351         @notes: add a new score record to a project
352             POST /api/v1/scenarios/<scenario_name>/scores? \
353                 installer=<installer_name>& \
354                 version=<version_name>& \
355                 project=<project_name>
356         @param body: score to be added
357         @type body: L{ScenarioScore}
358         @in body: body
359         @param installer: installer type
360         @type installer: L{string}
361         @in installer: query
362         @required installer: True
363         @param version: version
364         @type version: L{string}
365         @in version: query
366         @required version: True
367         @param project: project name
368         @type project: L{string}
369         @in project: query
370         @required project: True
371         @return 200: score is created.
372         @raise 404:  scenario/installer/version/project not existed
373         """
374         self.do_update('scores',
375                        'post',
376                        locators={'scenario': scenario,
377                                  'installer': None,
378                                  'version': None,
379                                  'project': None})
380
381
382 class ScenarioTIsHandler(GenericScenarioUpdateHandler):
383     @swagger.operation(nickname="addTrustIndicatorRecord")
384     def post(self, scenario):
385         """
386         @description: add a new trust indicator record
387         @notes: add a new trust indicator record to a project
388             POST /api/v1/scenarios/<scenario_name>/trust_indicators? \
389                 installer=<installer_name>& \
390                 version=<version_name>& \
391                 project=<project_name>
392         @param body: trust indicator to be added
393         @type body: L{ScenarioTI}
394         @in body: body
395         @param installer: installer type
396         @type installer: L{string}
397         @in installer: query
398         @required installer: True
399         @param version: version
400         @type version: L{string}
401         @in version: query
402         @required version: True
403         @param project: project name
404         @type project: L{string}
405         @in project: query
406         @required project: True
407         @return 200: trust indicator is added.
408         @raise 404:  scenario/installer/version/project not existed
409         """
410         self.do_update('trust_indicators',
411                        'post',
412                        locators={'scenario': scenario,
413                                  'installer': None,
414                                  'version': None,
415                                  'project': None})
416
417
418 class ScenarioCustomsHandler(GenericScenarioUpdateHandler):
419     @swagger.operation(nickname="addCustomizedTestCases")
420     def post(self, scenario):
421         """
422         @description: add customized test cases
423         @notes: add several test cases to a project
424             POST /api/v1/scenarios/<scenario_name>/customs? \
425                 installer=<installer_name>& \
426                 version=<version_name>& \
427                 project=<project_name>
428         @param body: test cases to be added
429         @type body: C{list} of L{string}
430         @in body: body
431         @param installer: installer type
432         @type installer: L{string}
433         @in installer: query
434         @required installer: True
435         @param version: version
436         @type version: L{string}
437         @in version: query
438         @required version: True
439         @param project: project name
440         @type project: L{string}
441         @in project: query
442         @required project: True
443         @return 200: test cases are added.
444         @raise 404:  scenario/installer/version/project not existed
445         """
446         self.do_update('customs',
447                        'post',
448                        locators={'scenario': scenario,
449                                  'installer': None,
450                                  'version': None,
451                                  'project': None})
452
453     @swagger.operation(nickname="updateCustomizedTestCases")
454     def put(self, scenario):
455         """
456         @description: update customized test cases
457         @notes: substitute all the customized test cases
458             PUT /api/v1/scenarios/<scenario_name>/customs? \
459                 installer=<installer_name>& \
460                 version=<version_name>& \
461                 project=<project_name>
462         @param body: new supported test cases
463         @type body: C{list} of L{string}
464         @in body: body
465         @param installer: installer type
466         @type installer: L{string}
467         @in installer: query
468         @required installer: True
469         @param version: version
470         @type version: L{string}
471         @in version: query
472         @required version: True
473         @param project: project name
474         @type project: L{string}
475         @in project: query
476         @required project: True
477         @return 200: substitute test cases success.
478         @raise 404:  scenario/installer/version/project not existed
479         """
480         self.do_update('customs',
481                        'put',
482                        locators={'scenario': scenario,
483                                  'installer': None,
484                                  'version': None,
485                                  'project': None})
486
487     @swagger.operation(nickname="deleteCustomizedTestCases")
488     def delete(self, scenario):
489         """
490         @description: delete one or several customized test cases
491         @notes: delete one or some customized test cases
492             DELETE /api/v1/scenarios/<scenario_name>/customs? \
493                 installer=<installer_name>& \
494                 version=<version_name>& \
495                 project=<project_name>
496         @param body: test case(s) to be deleted
497         @type body: C{list} of L{string}
498         @in body: body
499         @param installer: installer type
500         @type installer: L{string}
501         @in installer: query
502         @required installer: True
503         @param version: version
504         @type version: L{string}
505         @in version: query
506         @required version: True
507         @param project: project name
508         @type project: L{string}
509         @in project: query
510         @required project: True
511         @return 200: delete test case(s) success.
512         @raise 404:  scenario/installer/version/project not existed
513         """
514         self.do_update('customs',
515                        'delete',
516                        locators={'scenario': scenario,
517                                  'installer': None,
518                                  'version': None,
519                                  'project': None})
520
521
522 class ScenarioProjectsHandler(GenericScenarioUpdateHandler):
523     @swagger.operation(nickname="addProjectsUnderScenario")
524     def post(self, scenario):
525         """
526         @description: add projects to scenario
527         @notes: add one or multiple projects
528             POST /api/v1/scenarios/<scenario_name>/projects? \
529                 installer=<installer_name>& \
530                 version=<version_name>
531         @param body: projects to be added
532         @type body: C{list} of L{ScenarioProject}
533         @in body: body
534         @param installer: installer type
535         @type installer: L{string}
536         @in installer: query
537         @required installer: True
538         @param version: version
539         @type version: L{string}
540         @in version: query
541         @required version: True
542         @return 200: projects are added.
543         @raise 400: bad schema
544         @raise 409: conflict, project already exists
545         @raise 404:  scenario/installer/version not existed
546         """
547         self.do_update('projects',
548                        'post',
549                        locators={'scenario': scenario,
550                                  'installer': None,
551                                  'version': None})
552
553     @swagger.operation(nickname="updateScenarioProjects")
554     def put(self, scenario):
555         """
556         @description: replace all projects
557         @notes: substitute all projects, delete existed ones with new provides
558             PUT /api/v1/scenarios/<scenario_name>/projects? \
559                 installer=<installer_name>& \
560                 version=<version_name>
561         @param body: new projects
562         @type body: C{list} of L{ScenarioProject}
563         @in body: body
564         @param installer: installer type
565         @type installer: L{string}
566         @in installer: query
567         @required installer: True
568         @param version: version
569         @type version: L{string}
570         @in version: query
571         @required version: True
572         @return 200: replace projects success.
573         @raise 400: bad schema
574         @raise 404:  scenario/installer/version not existed
575         """
576         self.do_update('projects',
577                        'put',
578                        locators={'scenario': scenario,
579                                  'installer': None,
580                                  'version': None})
581
582     @swagger.operation(nickname="deleteProjectsUnderScenario")
583     def delete(self, scenario):
584         """
585         @description: delete one or multiple projects
586         @notes: delete one or multiple projects
587             DELETE /api/v1/scenarios/<scenario_name>/projects? \
588                 installer=<installer_name>& \
589                 version=<version_name>
590         @param body: projects(names) to be deleted
591         @type body: C{list} of L{string}
592         @in body: body
593         @param installer: installer type
594         @type installer: L{string}
595         @in installer: query
596         @required installer: True
597         @param version: version
598         @type version: L{string}
599         @in version: query
600         @required version: True
601         @return 200: delete project(s) success.
602         @raise 404:  scenario/installer/version not existed
603         """
604         self.do_update('projects',
605                        'delete',
606                        locators={'scenario': scenario,
607                                  'installer': None,
608                                  'version': None})
609
610
611 class ScenarioOwnerHandler(GenericScenarioUpdateHandler):
612     @swagger.operation(nickname="changeScenarioOwner")
613     def put(self, scenario):
614         """
615         @description: change scenario owner
616         @notes: substitute all projects, delete existed ones with new provides
617             PUT /api/v1/scenarios/<scenario_name>/owner? \
618                 installer=<installer_name>& \
619                 version=<version_name>
620         @param body: new owner
621         @type body: L{ScenarioChangeOwnerRequest}
622         @in body: body
623         @param installer: installer type
624         @type installer: L{string}
625         @in installer: query
626         @required installer: True
627         @param version: version
628         @type version: L{string}
629         @in version: query
630         @required version: True
631         @return 200: change owner success.
632         @raise 404:  scenario/installer/version not existed
633         """
634         self.do_update('owner',
635                        'put',
636                        locators={'scenario': scenario,
637                                  'installer': None,
638                                  'version': None})
639
640
641 class ScenarioVersionsHandler(GenericScenarioUpdateHandler):
642     @swagger.operation(nickname="addVersionsUnderScenario")
643     def post(self, scenario):
644         """
645         @description: add versions to scenario
646         @notes: add one or multiple versions
647             POST /api/v1/scenarios/<scenario_name>/versions? \
648                 installer=<installer_name>
649         @param body: versions to be added
650         @type body: C{list} of L{ScenarioVersion}
651         @in body: body
652         @param installer: installer type
653         @type installer: L{string}
654         @in installer: query
655         @required installer: True
656         @return 200: versions are added.
657         @raise 400: bad schema
658         @raise 409: conflict, version already exists
659         @raise 404:  scenario/installer not exist
660         """
661         self.do_update('versions',
662                        'post',
663                        locators={'scenario': scenario,
664                                  'installer': None})
665
666     @swagger.operation(nickname="updateVersionsUnderScenario")
667     def put(self, scenario):
668         """
669         @description: replace all versions
670         @notes: substitute all versions as a totality
671             PUT /api/v1/scenarios/<scenario_name>/versions? \
672                 installer=<installer_name>
673         @param body: new versions
674         @type body: C{list} of L{ScenarioVersion}
675         @in body: body
676         @param installer: installer type
677         @type installer: L{string}
678         @in installer: query
679         @required installer: True
680         @return 200: replace versions success.
681         @raise 400: bad schema
682         @raise 404:  scenario/installer not exist
683         """
684         self.do_update('versions',
685                        'put',
686                        locators={'scenario': scenario,
687                                  'installer': None})
688
689     @swagger.operation(nickname="deleteVersionsUnderScenario")
690     def delete(self, scenario):
691         """
692         @description: delete one or multiple versions
693         @notes: delete one or multiple versions
694             DELETE /api/v1/scenarios/<scenario_name>/versions? \
695                 installer=<installer_name>
696         @param body: versions(names) to be deleted
697         @type body: C{list} of L{string}
698         @in body: body
699         @param installer: installer type
700         @type installer: L{string}
701         @in installer: query
702         @required installer: True
703         @return 200: delete versions success.
704         @raise 404:  scenario/installer not exist
705         """
706         self.do_update('versions',
707                        'delete',
708                        locators={'scenario': scenario,
709                                  'installer': None})
710
711
712 class ScenarioInstallersHandler(GenericScenarioUpdateHandler):
713     @swagger.operation(nickname="addInstallersUnderScenario")
714     def post(self, scenario):
715         """
716         @description: add installers to scenario
717         @notes: add one or multiple installers
718             POST /api/v1/scenarios/<scenario_name>/installers
719         @param body: installers to be added
720         @type body: C{list} of L{ScenarioInstaller}
721         @in body: body
722         @return 200: installers are added.
723         @raise 400: bad schema
724         @raise 409: conflict, installer already exists
725         @raise 404:  scenario not exist
726         """
727         self.do_update('installers',
728                        'post',
729                        locators={'scenario': scenario})
730
731     @swagger.operation(nickname="updateInstallersUnderScenario")
732     def put(self, scenario):
733         """
734         @description: replace all installers
735         @notes: substitute all installers as a totality
736             PUT /api/v1/scenarios/<scenario_name>/installers
737         @param body: new installers
738         @type body: C{list} of L{ScenarioInstaller}
739         @in body: body
740         @return 200: replace versions success.
741         @raise 400: bad schema
742         @raise 404:  scenario/installer not exist
743         """
744         self.do_update('installers',
745                        'put',
746                        locators={'scenario': scenario})
747
748     @swagger.operation(nickname="deleteInstallersUnderScenario")
749     def delete(self, scenario):
750         """
751         @description: delete one or multiple installers
752         @notes: delete one or multiple installers
753             DELETE /api/v1/scenarios/<scenario_name>/installers
754         @param body: installers(names) to be deleted
755         @type body: C{list} of L{string}
756         @in body: body
757         @return 200: delete versions success.
758         @raise 404:  scenario/installer not exist
759         """
760         self.do_update('installers',
761                        'delete',
762                        locators={'scenario': scenario})