Describe in schema validation all task types
Add to validation schema task types such as: copy_files, sync, upload_file, stage and reboot Change-Id: I2010635979b5db9b48677cbf27431240edd5a9d4 Closes-Bug: #1493347
This commit is contained in:
parent
be46bdc335
commit
5f1b8c094c
|
@ -217,18 +217,17 @@ class TestValidatorV3(BaseValidator):
|
|||
)
|
||||
|
||||
for fuel_version in fuel_version_checks:
|
||||
version_in_msg = fuel_version[0]
|
||||
utils_mock.parse_yaml.return_value = {
|
||||
mock_data = {
|
||||
'fuel_version': fuel_version,
|
||||
'package_version': '3.0.0'}
|
||||
err_msg = 'Current plugin format 3.0.0 is not compatible with ' \
|
||||
'{0} Fuel release. Fuel version must be 7.0 or higher.' \
|
||||
' Please remove {0} version from metadata.yaml file or' \
|
||||
' downgrade package_version.'.format(fuel_version[0])
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
'Current plugin format 3.0.0 is not compatible with {0}'
|
||||
' Fuel release. Fuel version must be 7.0 or higher.'
|
||||
' Please remove {0} version from metadata.yaml file or'
|
||||
' downgrade package_version.'.format(version_in_msg)):
|
||||
self.validator.check_compatibility()
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_compatibility)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_compatibility_passed(self, utils_mock):
|
||||
|
@ -238,33 +237,142 @@ class TestValidatorV3(BaseValidator):
|
|||
self.validator.check_compatibility()
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_group_type_deployment_task(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'group',
|
||||
'groups': ['plugin_name']}]
|
||||
def test_role_attribute_is_required_for_deployment_task_types(
|
||||
self, utils_mock):
|
||||
deployment_task_types = [
|
||||
'group', 'shell', 'copy_files', 'sync', 'upload_file']
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/deployment_tasks.yaml', "
|
||||
"'role' is a required property, value path '0'"):
|
||||
self.validator.check_deployment_tasks()
|
||||
for task_type in deployment_task_types:
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': task_type}]
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'role' is a required property, value path '0'"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_puppet_type_deployment_task(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'puppet'}]
|
||||
def test_parameters_attribute_is_required_for_deployment_task_types(
|
||||
self, utils_mock):
|
||||
deployment_task_types = ['copy_files', 'sync', 'upload_file']
|
||||
|
||||
self.validator.check_deployment_tasks()
|
||||
for task_type in deployment_task_types:
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': task_type,
|
||||
'role': '*'}]
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'parameters' is a required property, value path '0'"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_skipped_type_deployment_task(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
def test_files_attribute_is_required_for_copy_files_task_type(
|
||||
self, utils_mock):
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'skipped'}]
|
||||
'type': 'copy_files',
|
||||
'role': '*',
|
||||
'parameters': {}}]
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'files' is a required property, value path '0 " \
|
||||
"-> parameters'"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
self.validator.check_deployment_tasks()
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_files_should_contain_at_least_one_item_for_copy_files_task_type(
|
||||
self, utils_mock):
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'copy_files',
|
||||
'role': '*',
|
||||
'parameters': {'files': []}}]
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"\[\] is too short, value path '0 -> parameters -> files'"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_src_and_dst_attributes_are_required_for_copy_files_task_type(
|
||||
self, utils_mock):
|
||||
data_to_check = [
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'copy_files',
|
||||
'role': '*',
|
||||
'parameters': {
|
||||
'files': [{}]}
|
||||
}], 'src'),
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'copy_files',
|
||||
'role': '*',
|
||||
'parameters': {
|
||||
'files': [{'src': 'some_source'}]}
|
||||
}], 'dst')]
|
||||
|
||||
for mock_data, key in data_to_check:
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'{0}' is a required property, value path '0 " \
|
||||
"-> parameters -> files -> 0'".format(key)
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_src_and_dst_attributes_are_required_for_sync_task_type(
|
||||
self, utils_mock):
|
||||
data_to_check = [
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'sync',
|
||||
'role': '*',
|
||||
'parameters': {}
|
||||
}], 'src'),
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'sync',
|
||||
'role': '*',
|
||||
'parameters': {'src': 'some_source'}
|
||||
}], 'dst')]
|
||||
|
||||
for mock_data, key in data_to_check:
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'{0}' is a required property, value path '0 " \
|
||||
"-> parameters'".format(key)
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_path_and_data_attributes_are_required_for_upload_file_task_type(
|
||||
self, utils_mock):
|
||||
data_to_check = [
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'upload_file',
|
||||
'role': '*',
|
||||
'parameters': {}
|
||||
}], 'path'),
|
||||
([{
|
||||
'id': 'plugin_name',
|
||||
'type': 'upload_file',
|
||||
'role': '*',
|
||||
'parameters': {'path': 'some_path'}
|
||||
}], 'data')]
|
||||
|
||||
for mock_data, key in data_to_check:
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml', " \
|
||||
"'{0}' is a required property, value path '0 " \
|
||||
"-> parameters'".format(key)
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_group_type_deployment_task_does_not_contain_manifests(
|
||||
|
@ -279,16 +387,15 @@ class TestValidatorV3(BaseValidator):
|
|||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_deployment_task_role_failed(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'group',
|
||||
'role': ['plugin_n@me']}]
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/deployment_tasks.yaml',"
|
||||
" 'plugin_n@me' does not match"):
|
||||
self.validator.check_deployment_tasks()
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml'," \
|
||||
" 'plugin_n@me' does not match"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.validator_v3.utils')
|
||||
def test_check_deployment_task_role(self, utils_mock):
|
||||
|
@ -302,25 +409,46 @@ class TestValidatorV3(BaseValidator):
|
|||
{'id': 'plugin_name', 'type': 'shell', 'role': []},
|
||||
{'id': 'plugin_name', 'type': 'shell', 'role': ['a', 'b']},
|
||||
{'id': 'plugin_name', 'type': 'shell', 'role': '*'},
|
||||
{'id': 'plugin_name', 'type': 'skipped', 'role': []},
|
||||
{'id': 'plugin_name', 'type': 'skipped', 'role': ['a', 'b']},
|
||||
{'id': 'plugin_name', 'type': 'skipped', 'role': '*'},
|
||||
{'id': 'plugin_name', 'type': 'skipped'},
|
||||
{'id': 'plugin_name', 'type': 'stage'},
|
||||
{'id': 'plugin_name', 'type': 'reboot'},
|
||||
{
|
||||
'id': 'plugin_name',
|
||||
'type': 'copy_files',
|
||||
'role': '*',
|
||||
'parameters': {
|
||||
'files': [
|
||||
{'src': 'some_source', 'dst': 'some_destination'}]}
|
||||
},
|
||||
{
|
||||
'id': 'plugin_name',
|
||||
'type': 'sync',
|
||||
'role': '*',
|
||||
'parameters': {
|
||||
'src': 'some_source', 'dst': 'some_destination'}
|
||||
},
|
||||
{
|
||||
'id': 'plugin_name',
|
||||
'type': 'upload_file',
|
||||
'role': '*',
|
||||
'parameters': {
|
||||
'path': 'some_path', 'data': 'some_data'}
|
||||
},
|
||||
]
|
||||
|
||||
self.validator.check_deployment_tasks()
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_deployment_task_id(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
mock_data = [{
|
||||
'id': 'plugin_n@me',
|
||||
'type': 'group',
|
||||
'role': ['plugin_name']}]
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/deployment_tasks.yaml',"
|
||||
" 'plugin_n@me' does not match"):
|
||||
self.validator.check_deployment_tasks_schema()
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml'," \
|
||||
" 'plugin_n@me' does not match"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_deployment_task_valid_dependencies(self, utils_mock):
|
||||
|
@ -334,42 +462,39 @@ class TestValidatorV3(BaseValidator):
|
|||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_deployment_task_invalid_dependencies(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
mock_data = [{
|
||||
'id': 'plugin_name',
|
||||
'type': 'group',
|
||||
'role': ['plugin_name'],
|
||||
'requires': ['dependency_1', 'dependency_#']}]
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/deployment_tasks.yaml',"
|
||||
" 'dependency_#' does not match"):
|
||||
self.validator.check_deployment_tasks_schema()
|
||||
err_msg = "File '/tmp/plugin_path/deployment_tasks.yaml'," \
|
||||
" 'dependency_#' does not match"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_deployment_tasks_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_node_roles_have_correct_name(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = {
|
||||
mock_data = {
|
||||
'plug$n_n@me': {
|
||||
'name': 'test_plugin',
|
||||
'description': 'test plugin'}}
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/node_roles.yaml', Additional"
|
||||
" properties are not allowed"):
|
||||
self.validator.check_node_roles_schema()
|
||||
err_msg = "File '/tmp/plugin_path/node_roles.yaml', Additional" \
|
||||
" properties are not allowed"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_node_roles_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_node_role_should_has_name(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = {
|
||||
mock_data = {
|
||||
'plugin_name': {
|
||||
'description': 'test plugin'}}
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/node_roles.yaml', 'name' is"
|
||||
" a required property, value path 'plugin_name'"):
|
||||
self.validator.check_node_roles_schema()
|
||||
err_msg = "File '/tmp/plugin_path/node_roles.yaml', 'name' is" \
|
||||
" a required property, value path 'plugin_name'"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_node_roles_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_valid_volumes_roles_mapping_name(self, utils_mock):
|
||||
|
@ -382,16 +507,15 @@ class TestValidatorV3(BaseValidator):
|
|||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_invalid_volumes_roles_mapping_name(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = {
|
||||
mock_data = {
|
||||
'volumes_roles_mapping': {
|
||||
'm@pping_name': [{'allocate_size': 'min', 'id': 'test'}]},
|
||||
'volumes': []}
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/volumes.yaml', Additional"
|
||||
" properties are not allowed"):
|
||||
self.validator.check_volumes_schema()
|
||||
err_msg = "File '/tmp/plugin_path/volumes.yaml', Additional" \
|
||||
" properties are not allowed"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_volumes_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_valid_network_roles(self, utils_mock):
|
||||
|
@ -409,7 +533,7 @@ class TestValidatorV3(BaseValidator):
|
|||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_network_roles_vip_have_invalid_name(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
mock_data = [{
|
||||
"id": "example_net_role",
|
||||
"default_mapping": "public",
|
||||
"properties": {
|
||||
|
@ -418,16 +542,15 @@ class TestValidatorV3(BaseValidator):
|
|||
"vip": [{
|
||||
"name": "vip@name",
|
||||
"namespace": "haproxy"}]}}]
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/network_roles.yaml',"
|
||||
" 'vip@name' does not match"):
|
||||
self.validator.check_network_roles_schema()
|
||||
err_msg = "File '/tmp/plugin_path/network_roles.yaml'," \
|
||||
" 'vip@name' does not match"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_network_roles_schema)
|
||||
|
||||
@mock.patch('fuel_plugin_builder.validators.base.utils')
|
||||
def test_check_network_roles_vip_have_invalid_namespace(self, utils_mock):
|
||||
utils_mock.parse_yaml.return_value = [{
|
||||
mock_data = [{
|
||||
"id": "example_net_role",
|
||||
"default_mapping": "public",
|
||||
"properties": {
|
||||
|
@ -436,9 +559,16 @@ class TestValidatorV3(BaseValidator):
|
|||
"vip": [{
|
||||
"name": "vip_name",
|
||||
"namespace": "hap roxy"}]}}]
|
||||
err_msg = "File '/tmp/plugin_path/network_roles.yaml'," \
|
||||
" 'hap roxy' does not match"
|
||||
self._check_raised_exception(
|
||||
utils_mock, mock_data,
|
||||
err_msg, self.validator.check_network_roles_schema)
|
||||
|
||||
with self.assertRaisesRegexp(
|
||||
errors.ValidationError,
|
||||
"File '/tmp/plugin_path/network_roles.yaml',"
|
||||
" 'hap roxy' does not match"):
|
||||
self.validator.check_network_roles_schema()
|
||||
def _check_raised_exception(self, mock_obj, mock_data,
|
||||
err_msg, executed_method,
|
||||
err_type=errors.ValidationError):
|
||||
mock_obj.parse_yaml.return_value = mock_data
|
||||
|
||||
with self.assertRaisesRegexp(err_type, err_msg):
|
||||
executed_method()
|
||||
|
|
|
@ -19,6 +19,7 @@ from fuel_plugin_builder.validators.schemas import SchemaV2
|
|||
|
||||
TASK_NAME_PATTERN = '^[0-9a-zA-Z_-]+$'
|
||||
NETWORK_ROLE_PATTERN = '^[0-9a-z_-]+$'
|
||||
FILE_PERMISSIONS_PATTERN = '^[0-7]{4}$'
|
||||
|
||||
|
||||
class SchemaV3(SchemaV2):
|
||||
|
@ -160,6 +161,88 @@ class SchemaV3(SchemaV2):
|
|||
'type': {'enum': ['skipped']}}
|
||||
}
|
||||
|
||||
@property
|
||||
def copy_files(self):
|
||||
return {
|
||||
'type': 'object',
|
||||
'required': ['role', 'parameters'],
|
||||
'properties': {
|
||||
'type': {'enum': ['copy_files']},
|
||||
'role': self.task_role,
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'required': ['files'],
|
||||
'properties': {
|
||||
'files': {
|
||||
'type': 'array',
|
||||
'minItems': 1,
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'required': ['src', 'dst'],
|
||||
'properties': {
|
||||
'src': {'type': 'string'},
|
||||
'dst': {'type': 'string'}}}},
|
||||
'permissions': {
|
||||
'type': 'string',
|
||||
'pattern': FILE_PERMISSIONS_PATTERN},
|
||||
'dir_permissions': {
|
||||
'type': 'string',
|
||||
'pattern': FILE_PERMISSIONS_PATTERN}}}}
|
||||
}
|
||||
|
||||
@property
|
||||
def sync(self):
|
||||
return {
|
||||
'type': 'object',
|
||||
'required': ['role', 'parameters'],
|
||||
'properties': {
|
||||
'type': {'enum': ['sync']},
|
||||
'role': self.task_role,
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'required': ['src', 'dst'],
|
||||
'properties': {
|
||||
'src': {'type': 'string'},
|
||||
'dst': {'type': 'string'},
|
||||
'timeout': {'type': 'integer'}}}}
|
||||
}
|
||||
|
||||
@property
|
||||
def upload_file(self):
|
||||
return {
|
||||
'type': 'object',
|
||||
'required': ['role', 'parameters'],
|
||||
'properties': {
|
||||
'type': {'enum': ['upload_file']},
|
||||
'role': self.task_role,
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'required': ['path', 'data'],
|
||||
'properties': {
|
||||
'path': {'type': 'string'},
|
||||
'data': {'type': 'string'}}}}
|
||||
}
|
||||
|
||||
@property
|
||||
def stage(self):
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': {'enum': ['stage']}}
|
||||
}
|
||||
|
||||
@property
|
||||
def reboot(self):
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': {'enum': ['reboot']},
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'timeout': {'type': 'integer'}}}}
|
||||
}
|
||||
|
||||
@property
|
||||
def deployment_task_schema(self):
|
||||
return {
|
||||
|
@ -172,7 +255,17 @@ class SchemaV3(SchemaV2):
|
|||
'id': {
|
||||
'type': 'string',
|
||||
'pattern': TASK_NAME_PATTERN},
|
||||
'type': {'enum': ['puppet', 'shell', 'group', 'skipped']},
|
||||
'type': {
|
||||
'enum': [
|
||||
'puppet',
|
||||
'shell',
|
||||
'group',
|
||||
'skipped',
|
||||
'copy_files',
|
||||
'sync',
|
||||
'upload_file',
|
||||
'stage',
|
||||
'reboot']},
|
||||
'required_for': self.task_group,
|
||||
'requires': self.task_group}}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,12 @@ class ValidatorV3(ValidatorV2):
|
|||
'puppet': self.schema.puppet_task,
|
||||
'shell': self.schema.shell_task,
|
||||
'group': self.schema.group_task,
|
||||
'skipped': self.schema.skipped_task}
|
||||
'skipped': self.schema.skipped_task,
|
||||
'copy_files': self.schema.copy_files,
|
||||
'sync': self.schema.sync,
|
||||
'upload_file': self.schema.upload_file,
|
||||
'stage': self.schema.stage,
|
||||
'reboot': self.schema.reboot}
|
||||
|
||||
for idx, deployment_task in enumerate(deployment_tasks):
|
||||
self.validate_schema(
|
||||
|
|
Loading…
Reference in New Issue