summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Kutukov <ikutukov@mirantis.com>2016-06-08 17:58:59 +0300
committerIlya Kutukov <ikutukov@mirantis.com>2016-06-22 19:46:25 +0300
commitc2d906f5aee6f64bb1074d9df3b405bc181d428d (patch)
tree76a0a72a3c8ba289a8c3d0c3c9a7521dd10769ab
parent82191ca16b40021e445e854fad37c65cd8e70b0c (diff)
Now FPB tasks schema in sync with Fuel 9.0 with 2.1 support
YAQL expressions added. Tasks 2.0 and 2.1 schema coverage improved. Change-Id: If433f29283cb4897e8137ba2b33215af14103bea Closes-Bug: #1590389
Notes
Notes (review): Verified+1: Fuel CI <fuel-ci-bot@mirantis.com> Code-Review+2: Igor Kalnitsky <ikalnitsky@mirantis.com> Code-Review+1: Roman Prykhodchenko <me@romcheg.me> Code-Review+1: Georgy Kibardin <gkibardin@mirantis.com> Code-Review+1: Bulat Gaifullin <bgaifullin@mirantis.com> Code-Review+1: Artur Svechnikov <asvechnikov@mirantis.com> Workflow+1: Igor Kalnitsky <ikalnitsky@mirantis.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Wed, 29 Jun 2016 08:53:21 +0000 Reviewed-on: https://review.openstack.org/327168 Project: openstack/fuel-plugins Branch: refs/heads/master
-rw-r--r--fuel_plugin_builder/tests/test_validator_v4.py405
-rw-r--r--fuel_plugin_builder/validators/schemas/v4.py61
2 files changed, 454 insertions, 12 deletions
diff --git a/fuel_plugin_builder/tests/test_validator_v4.py b/fuel_plugin_builder/tests/test_validator_v4.py
index 4e2a784..e456a5d 100644
--- a/fuel_plugin_builder/tests/test_validator_v4.py
+++ b/fuel_plugin_builder/tests/test_validator_v4.py
@@ -493,6 +493,411 @@ class TestValidatorV4(TestValidatorV3):
493 def test_check_tasks_schema_validation_passed(self, utils_mock, *args): 493 def test_check_tasks_schema_validation_passed(self, utils_mock, *args):
494 pass 494 pass
495 495
496 @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
497 def test_check_tasks_schema_1_0_validation_failed(self, utils_mock, *args):
498 checks = [
499 {
500 'data': {
501 'id': 'task-id',
502 'type': 'shell',
503 'parameters': {
504 'timeout': 3
505 },
506 'stage': 'post_deployment',
507 'role': '*'
508 },
509 'errorTextContains': "'cmd' is a required property, "
510 "value path '0 -> parameters'"
511 },
512 {
513 'data': {
514 'id': 'task-id',
515 'type': 'puppet',
516 'parameters': {
517 'timeout': 3
518 },
519 'stage': 'post_deployment',
520 'role': '*'
521 },
522 'errorTextContains': "'puppet_manifest' is a required property"
523 ", value path '0 -> parameters'"
524 },
525 {
526 'data': {
527 'id': 'task-id',
528 'type': 'puppet',
529 'parameters': {
530 'timeout': 3,
531 'cmd': 'xx'
532 },
533 'stage': 'post_deployment',
534 'role': '*'
535 },
536 'errorTextContains': "'puppet_manifest' is a required property"
537 ", value path '0 -> parameters'"
538 },
539 {
540 'data': {
541 'id': 'task-id',
542 'type': 'shell',
543 'parameters': {
544 'timeout': 3,
545 'puppet_manifest': 'xx',
546 'puppet_modules': 'yy',
547 },
548 'stage': 'post_deployment',
549 'role': '*'
550 },
551 'errorTextContains': "'cmd' is a required property, value path"
552 " '0 -> parameters'"
553 },
554 {
555 'data': {
556 'id': 'task-id',
557 'type': 'puppet',
558 'parameters': {
559 'timeout': 3,
560 'puppet_manifest': 'xx',
561 'puppet_modules': 'yy',
562 'retries': 'asd',
563 },
564 'stage': 'post_deployment',
565 'role': '*'
566 },
567 'errorTextContains': "'asd' is not of type 'integer', value "
568 "path '0 -> parameters -> retries'"
569 },
570 {
571 'data': {
572 'id': 'task-id',
573 'type': 'puppet',
574 'parameters': {
575 'timeout': 3,
576 'puppet_manifest': 'xx',
577 'puppet_modules': '',
578 'retries': 1,
579 },
580 'stage': 'pre_deployment',
581 'role': '*'
582 },
583 'errorTextContains': "'' is too short, value path '0 -> "
584 "parameters -> puppet_modules'"
585 },
586 {
587 'data': {
588 'id': 'task-id',
589 'type': 'puppet',
590 'parameters': {
591 'timeout': 3,
592 'puppet_manifest': '',
593 'puppet_modules': 'yy',
594 'retries': 1,
595 },
596 'stage': 'pre_deployment',
597 'role': '*'
598 },
599 'errorTextContains': "'' is too short, value path '0 -> "
600 "parameters -> puppet_manifest'"
601 }
602 ]
603
604 for check in checks:
605 utils_mock.parse_yaml.return_value = [check['data']]
606 self.assertRaisesRegexp(
607 errors.ValidationError,
608 check['errorTextContains'],
609 self.validator.check_deployment_tasks)
610
611 @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
612 def test_check_tasks_schema_1_0_validation_passed(self, utils_mock, *args):
613 data_sets = [
614 [
615 {
616 'id': 'task_id',
617 'type': 'shell',
618 'parameters': {
619 'timeout': 3,
620 'cmd': 'xx'
621 },
622 'stage': 'post_deployment',
623 'role': '*'
624 },
625 ],
626 [
627 {
628 'id': 'task_id',
629 'type': 'shell',
630 'parameters': {
631 'timeout': 3,
632 'cmd': 'xx'
633 },
634 'stage': 'post_deployment',
635 'role': '*'
636 },
637 {
638 'id': 'task_id',
639 'type': 'puppet',
640 'parameters': {
641 'timeout': 3,
642 'puppet_manifest': 'xx',
643 'puppet_modules': 'xxx'
644 },
645 'stage': 'post_deployment',
646 'role': '*'
647 },
648 ],
649 [
650 {
651 'id': 'task_id',
652 'type': 'shell',
653 'parameters': {
654 'timeout': 3,
655 'cmd': 'reboot'
656 },
657 'stage': 'post_deployment',
658 'role': '*'
659 },
660 {
661 'id': 'task_id',
662 'type': 'shell',
663 'parameters': {
664 'timeout': 3,
665 'cmd': 'xx'
666 },
667 'stage': 'post_deployment',
668 'role': '*'
669 },
670 {
671 'id': 'task_id',
672 'type': 'puppet',
673 'parameters': {
674 'timeout': 3,
675 'puppet_manifest': 'xx',
676 'puppet_modules': 'xxx'
677 },
678 'stage': 'post_deployment',
679 'role': '*'
680 }
681 ],
682 [
683 {
684 'id': 'task_id',
685 'type': 'shell',
686 'parameters': {
687 'timeout': 3,
688 'cmd': 'reboot'
689 },
690 'stage': 'post_deployment',
691 'role': '*'
692 },
693 {
694 'id': 'task_id',
695 'type': 'shell',
696 'parameters': {
697 'timeout': 3,
698 'puppet_manifest': 'xx',
699 'puppet_modules': 'yy',
700 'cmd': 'reboot'
701 },
702 'stage': 'post_deployment',
703 'role': '*'
704 },
705 {
706 'id': 'task_id',
707 'type': 'puppet',
708 'parameters': {
709 'timeout': 3,
710 'retries': 10,
711 'puppet_manifest': 'xx',
712 'puppet_modules': 'xxx'
713 },
714 'stage': 'post_deployment',
715 'role': '*'
716 },
717 {
718 'id': 'task_id',
719 'type': 'puppet',
720 'parameters': {
721 'timeout': 3,
722 'retries': 10,
723 'puppet_manifest': 'xx',
724 'puppet_modules': 'xxx'
725 },
726 'stage': 'post_deployment',
727 'role': 'master'
728 },
729 ]
730 ]
731
732 for data in data_sets:
733 utils_mock.parse_yaml.return_value = data
734 self.validator.check_deployment_tasks()
735
736 @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
737 def test_check_tasks_schema_2_0_validation_failed(self, utils_mock, *args):
738 tasks_data = [
739 {
740 'id': 'test',
741 'type': 'shell',
742 'version': '2'
743 },
744 {
745 'id': 'test',
746 'type': 'shell',
747 'cross-depends': [
748 {
749 'role': 'role_without_name'
750 }
751 ]
752 },
753 {
754 'id': 'test',
755 'type': 'shell',
756 'parameters': {
757 'strategy': 'NOSUCHSTRATEGY'
758 }
759 },
760 {
761 'id': 'test',
762 'type': 'shell',
763 'parameters': {
764 'strategy': {
765 'type': 'NOSUCHSTRATEGY'
766 }
767 }
768 }
769 ]
770
771 utils_mock.parse_yaml.return_value = tasks_data
772 self.assertRaises(errors.ValidationError,
773 self.validator.check_deployment_tasks)
774
775 @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
776 def test_check_tasks_schema_2_0_validation_passed(self, utils_mock, *args):
777 tasks_data = [
778 {
779 'id': 'task_id',
780 'type': 'puppet',
781 'version': '2.0.0',
782 'parameters': {
783 'timeout': 3,
784 'retries': 10,
785 'puppet_manifest': 'xx',
786 'puppet_modules': 'xxx'
787 },
788 'stage': 'post_deployment',
789 'roles': ['test_role'],
790 'cross-depends': [
791 {
792 'name': 'task_id2',
793 },
794 {
795 'name': 'task_id2',
796 'role': ['some_role']
797 },
798 {
799 'name': 'task_id2',
800 'role': 'some_role'
801 },
802 {
803 'name': 'task_id2',
804 'policy': 'all'
805 },
806 {
807 'name': 'task_id2',
808 'policy': 'any'
809 }
810 ],
811 'cross-depended-by': [
812 {
813 'name': 'task_id2',
814 },
815 {
816 'name': 'task_id2',
817 'role': ['some_role']
818 },
819 {
820 'name': 'task_id2',
821 'role': 'some_role'
822 },
823 {
824 'name': 'task_id2',
825 'policy': 'all'
826 },
827 {
828 'name': 'task_id2',
829 'policy': 'any'
830 }
831 ],
832 'strategy': {
833 'type': 'parallel',
834 'amount': 10
835 }
836 }
837 ]
838
839 utils_mock.parse_yaml.return_value = tasks_data
840 self.validator.check_deployment_tasks()
841
842 @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
843 def test_check_tasks_schema_2_1_validation_passed(self, utils_mock, *args):
844 # this is a slightly modified task from netconfig.yaml
845 tasks_data = [
846 {
847 "id": "netconfig",
848 "type": "puppet",
849 "version": "2.1.0",
850 "groups": [
851 "primary-controller",
852 "controller",
853 ],
854 "required_for": [
855 "deploy_end"
856 ],
857 "requires": [
858 "tools"
859 ],
860 "condition": {
861 "yaql_exp": "changedAny($.network_scheme, $.dpdk, $.get('"
862 "use_ovs'), $.get('set_rps'), $.get('set_rps')"
863 ", $.get('run_ping_checker'), $.network_scheme"
864 ".endpoints.values().where(\n $.get('gateway'"
865 ") != null).gateway)\n"
866 },
867 "parameters": {
868 "puppet_manifest": "/etc/puppet/modules/osnailyfacter/"
869 "modular/netconfig/netconfig.pp",
870 "puppet_modules": "/etc/puppet/modules",
871 "timeout": 300,
872 "strategy": {
873 "type": "parallel",
874 "amount": {
875 "yaql_exp": "switch($.get('deployed_before', {})."
876 "get('value') => 1, true => 3)\n"
877 }
878 }
879 },
880 "test_pre": {
881 "cmd": "ruby /etc/puppet/modules/osnailyfacter/modular/"
882 "netconfig/netconfig_pre.rb"
883 },
884 "test_post": {
885 "cmd": "ruby /etc/puppet/modules/osnailyfacter/modular/"
886 "netconfig/netconfig_post.rb"
887 },
888 "cross-depends": {
889 "yaql_exp": "switch( (\n $.roles.any($.matches('("
890 "primary-)?(controller|mongo)'))\n "
891 "or ($.network_metadata.get('vips',{}).get"
892 "('management') = null)\n ) => [],\n "
893 "true => [{name =>'virtual_ips'}]\n)\n"
894 }
895 }
896 ]
897
898 utils_mock.parse_yaml.return_value = tasks_data
899 self.validator.check_deployment_tasks()
900
496 @mock.patch('fuel_plugin_builder.validators.base.utils.exists') 901 @mock.patch('fuel_plugin_builder.validators.base.utils.exists')
497 def test_check_tasks_schema_validation_no_file(self, exists_mock, *args): 902 def test_check_tasks_schema_validation_no_file(self, exists_mock, *args):
498 mocked_methods = ['validate_schema'] 903 mocked_methods = ['validate_schema']
diff --git a/fuel_plugin_builder/validators/schemas/v4.py b/fuel_plugin_builder/validators/schemas/v4.py
index dbb370f..f6781d3 100644
--- a/fuel_plugin_builder/validators/schemas/v4.py
+++ b/fuel_plugin_builder/validators/schemas/v4.py
@@ -48,17 +48,42 @@ class SchemaV4(SchemaV3):
48 self.role_aliases = ROLE_ALIASES 48 self.role_aliases = ROLE_ALIASES
49 49
50 @property 50 @property
51 def _node_resolve_policy(self):
52 return {
53 'type': 'string',
54 'enum': ['all', 'any']
55 }
56
57 @property
58 def _yaql_expression(self):
59 return {
60 '$schema': 'http://json-schema.org/draft-04/schema#',
61 'type': 'object',
62 'required': ['yaql_exp'],
63 'properties': {
64 'yaql_exp': {'type': 'string'},
65 }
66 }
67
68 @property
51 def _task_relation(self): 69 def _task_relation(self):
52 return { 70 return {
71 '$schema': 'http://json-schema.org/draft-04/schema#',
53 'type': 'object', 72 'type': 'object',
54 'required': ['name'], 73 'required': ['name'],
55 'properties': { 74 'properties': {
56 'name': {'type': 'string'}, 75 'name': {
57 'role': self._task_role, 76 'oneOf': [
58 'policy': { 77 {'type': 'string'},
59 'type': 'string', 78 self._yaql_expression],
60 'enum': ['all', 'any'] 79 },
61 } 80 'role': {
81 'oneOf': [
82 {'type': 'string'},
83 {'type': 'array'},
84 self._yaql_expression]
85 },
86 'policy': self._node_resolve_policy,
62 } 87 }
63 } 88 }
64 89
@@ -83,10 +108,18 @@ class SchemaV4(SchemaV3):
83 @property 108 @property
84 def _task_strategy(self): 109 def _task_strategy(self):
85 return { 110 return {
111 '$schema': 'http://json-schema.org/draft-04/schema#',
86 'type': 'object', 112 'type': 'object',
113 'required': ['type'],
87 'properties': { 114 'properties': {
88 'type': { 115 'type': {
89 'enum': ['parallel', 'one_by_one'] 116 'type': 'string',
117 'enum': ['parallel', 'one_by_one']},
118 'amount': {
119 'oneOf': [
120 {'type': 'integer'},
121 self._yaql_expression
122 ]
90 } 123 }
91 } 124 }
92 } 125 }
@@ -149,11 +182,15 @@ class SchemaV4(SchemaV3):
149 'required_for': self.task_group, 182 'required_for': self.task_group,
150 'requires': self.task_group, 183 'requires': self.task_group,
151 'cross-depends': { 184 'cross-depends': {
152 'type': 'array', 185 'oneOf': [
153 'items': self._task_relation}, 186 {'type': 'array', 'items': self._task_relation},
187 self._yaql_expression]
188 },
154 'cross-depended-by': { 189 'cross-depended-by': {
155 'type': 'array', 190 'oneOf': [
156 'items': self._task_relation}, 191 {'type': 'array', 'items': self._task_relation},
192 self._yaql_expression]
193 },
157 'stage': self._task_stage, 194 'stage': self._task_stage,
158 'tasks': { # used only for 'group' tasks 195 'tasks': { # used only for 'group' tasks
159 'type': 'array', 196 'type': 'array',
@@ -161,7 +198,7 @@ class SchemaV4(SchemaV3):
161 'type': 'string', 198 'type': 'string',
162 'pattern': TASK_ROLE_PATTERN}}, 199 'pattern': TASK_ROLE_PATTERN}},
163 'reexecute_on': self._task_reexecute, 200 'reexecute_on': self._task_reexecute,
164 'parameters': parameters or {}, 201 'parameters': parameters,
165 }, 202 },
166 } 203 }
167 204