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