'File exists' validator and Utils class for validators

Change-Id: I43451ae5a79618cd7d8b60a98e9904052b242276
This commit is contained in:
astaroverov 2017-06-13 13:36:31 +03:00 committed by chenhb
parent d32b2d5f2b
commit 4925b5692e
13 changed files with 127 additions and 55 deletions

View File

@ -67,7 +67,7 @@
MuranoPackages.import_and_list_packages: MuranoPackages.import_and_list_packages:
- -
args: args:
package: "/home/jenkins/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/" package: "~/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/"
runner: runner:
type: "constant" type: "constant"
times: 10 times: 10
@ -81,7 +81,7 @@
max: 0 max: 0
- -
args: args:
package: "/home/jenkins/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter.zip" package: "~/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter.zip"
runner: runner:
type: "constant" type: "constant"
times: 1 times: 1
@ -97,7 +97,7 @@
MuranoPackages.import_and_delete_package: MuranoPackages.import_and_delete_package:
- -
args: args:
package: "/home/jenkins/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/" package: "~/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/"
runner: runner:
type: "constant" type: "constant"
times: 10 times: 10
@ -113,7 +113,7 @@
MuranoPackages.import_and_filter_applications: MuranoPackages.import_and_filter_applications:
- -
args: args:
package: "/home/jenkins/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/" package: "~/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/"
filter_query: {"category" : "Web"} filter_query: {"category" : "Web"}
runner: runner:
type: "constant" type: "constant"
@ -130,7 +130,7 @@
MuranoPackages.package_lifecycle: MuranoPackages.package_lifecycle:
- -
args: args:
package: "/home/jenkins/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/" package: "~/.rally/extra/murano/applications/HelloReporter/io.murano.apps.HelloReporter/"
body: {"categories": ["Web"]} body: {"categories": ["Web"]}
operation: "add" operation: "add"
runner: runner:

View File

@ -737,7 +737,7 @@
context: context:
image_command_customizer: image_command_customizer:
command: command:
local_path: "/home/jenkins/.rally/extra/install_benchmark.sh" local_path: "~/.rally/extra/install_benchmark.sh"
remote_path: "./install_benchmark.sh" remote_path: "./install_benchmark.sh"
flavor: flavor:
name: "m1.tiny" name: "m1.tiny"

View File

@ -255,7 +255,7 @@
hooks: hooks:
- name: sys_call - name: sys_call
description: Run script description: Run script
args: sh /home/jenkins/.rally/extra/hook_example_script.sh args: sh ~/.rally/extra/hook_example_script.sh
trigger: trigger:
name: event name: event
args: args:

View File

@ -1,4 +1,3 @@
# Copyright 2017: Mirantis Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -14,6 +13,7 @@
# under the License. # under the License.
import inspect import inspect
import os
import jsonschema import jsonschema
import six import six
@ -24,6 +24,23 @@ from rally.common import validation
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class ValidatorUtils(object):
@staticmethod
def _file_access_ok(filename, mode, param_name, required=True):
if not filename:
return validation.ValidationResult(
not required,
"Parameter %s required" % param_name)
if not os.access(os.path.expanduser(filename), mode):
return validation.ValidationResult(
False, "Could not open %(filename)s with mode %(mode)s "
"for parameter %(param_name)s"
% {"filename": filename, "mode": mode,
"param_name": param_name})
return validation.ValidationResult(True)
@validation.configure(name="jsonschema") @validation.configure(name="jsonschema")
class JsonSchemaValidator(validation.Validator): class JsonSchemaValidator(validation.Validator):
"""JSON schema validator""" """JSON schema validator"""
@ -294,8 +311,7 @@ class RequiredContextsValidator(validation.Validator):
return self.fail(msg) return self.fail(msg)
@validation.configure(name="required_param_or_context", @validation.configure(name="required_param_or_context")
namespace="openstack")
class RequiredParamOrContextValidator(validation.Validator): class RequiredParamOrContextValidator(validation.Validator):
def __init__(self, param_name, ctx_name): def __init__(self, param_name, ctx_name):
@ -318,3 +334,36 @@ class RequiredParamOrContextValidator(validation.Validator):
if self.param_name in config.get("args", {}): if self.param_name in config.get("args", {}):
return return
return self.fail(msg) return self.fail(msg)
@validation.configure(name="file_exists")
class FileExistsValidator(validation.Validator):
def __init__(self, param_name, mode=os.R_OK, required=True):
"""Validator checks parameter is proper path to file with proper mode.
Ensure a file exists and can be accessed with the specified mode.
Note that path to file will be expanded before access checking.
:param param_name: Name of parameter to validate
:param mode: Access mode to test for. This should be one of:
* os.F_OK (file exists)
* os.R_OK (file is readable)
* os.W_OK (file is writable)
* os.X_OK (file is executable)
If multiple modes are required they can be added, eg:
mode=os.R_OK+os.W_OK
:param required: Boolean indicating whether this argument is required.
"""
super(FileExistsValidator, self).__init__()
self.param_name = param_name
self.mode = mode
self.required = required
def validate(self, config, credentials, plugin_cls, plugin_cfg):
return ValidatorUtils._file_access_ok(
config.get("args", {}).get(self.param_name),
self.mode, self.param_name, self.required)

View File

@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_config import cfg from oslo_config import cfg
import requests import requests

View File

@ -51,7 +51,7 @@ class ListExecutions(utils.MistralScenario):
sort_keys=sort_keys, sort_dirs=sort_dirs) sort_keys=sort_keys, sort_dirs=sort_dirs)
@validation.file_exists("definition") @validation.add("file_exists", param_name="definition")
@types.convert(definition={"type": "file"}) @types.convert(definition={"type": "file"})
@types.convert(params={"type": "file"}) @types.convert(params={"type": "file"})
@types.convert(wf_input={"type": "file"}) @types.convert(wf_input={"type": "file"})

View File

@ -38,7 +38,7 @@ class ListWorkbooks(utils.MistralScenario):
self._list_workbooks() self._list_workbooks()
@validation.file_exists("definition") @validation.add("file_exists", param_name="definition")
@types.convert(definition={"type": "file"}) @types.convert(definition={"type": "file"})
@validation.add("required_platform", platform="openstack", users=True) @validation.add("required_platform", platform="openstack", users=True)
@validation.add("required_services", @validation.add("required_services",

View File

@ -25,7 +25,7 @@ from rally.task import validation
"""Scenarios for Murano packages.""" """Scenarios for Murano packages."""
@validation.file_exists(param_name="package", mode=os.F_OK) @validation.add("file_exists", param_name="package", mode=os.F_OK)
@validation.add("required_services", services=[consts.Service.MURANO]) @validation.add("required_services", services=[consts.Service.MURANO])
@validation.add("required_platform", platform="openstack", users=True) @validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(context={"cleanup": ["murano.packages"]}, @scenario.configure(context={"cleanup": ["murano.packages"]},
@ -55,7 +55,7 @@ class ImportAndListPackages(utils.MuranoScenario):
os.remove(package_path) os.remove(package_path)
@validation.file_exists(param_name="package", mode=os.F_OK) @validation.add("file_exists", param_name="package", mode=os.F_OK)
@validation.add("required_services", services=[consts.Service.MURANO]) @validation.add("required_services", services=[consts.Service.MURANO])
@validation.add("required_platform", platform="openstack", users=True) @validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(context={"cleanup": ["murano.packages"]}, @scenario.configure(context={"cleanup": ["murano.packages"]},
@ -82,7 +82,7 @@ class ImportAndDeletePackage(utils.MuranoScenario):
os.remove(package_path) os.remove(package_path)
@validation.file_exists(param_name="package", mode=os.F_OK) @validation.add("file_exists", param_name="package", mode=os.F_OK)
@validation.add("required_services", services=[consts.Service.MURANO]) @validation.add("required_services", services=[consts.Service.MURANO])
@validation.add("required_platform", platform="openstack", users=True) @validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(context={"cleanup": ["murano.packages"]}, @scenario.configure(context={"cleanup": ["murano.packages"]},
@ -118,7 +118,7 @@ class PackageLifecycle(utils.MuranoScenario):
os.remove(package_path) os.remove(package_path)
@validation.file_exists(param_name="package", mode=os.F_OK) @validation.add("file_exists", param_name="package", mode=os.F_OK)
@validation.add("required_services", services=[consts.Service.MURANO]) @validation.add("required_services", services=[consts.Service.MURANO])
@validation.add("required_platform", platform="openstack", users=True) @validation.add("required_platform", platform="openstack", users=True)
@scenario.configure(context={"cleanup": ["murano.packages"]}, @scenario.configure(context={"cleanup": ["murano.packages"]},

View File

@ -124,8 +124,9 @@ class VMScenario(nova_utils.NovaScenario):
"or list type") "or list type")
cmd.extend(remote_path) cmd.extend(remote_path)
if command.get("local_path"): if command.get("local_path"):
ssh.put_file(command["local_path"], remote_path[-1], ssh.put_file(os.path.expanduser(
mode=self.USER_RWX_OTHERS_RX_ACCESS_MODE) command["local_path"]), remote_path[-1],
mode=self.USER_RWX_OTHERS_RX_ACCESS_MODE)
if command.get("script_file"): if command.get("script_file"):
stdin = open(os.path.expanduser(command["script_file"]), "rb") stdin = open(os.path.expanduser(command["script_file"]), "rb")

View File

@ -115,30 +115,6 @@ def _file_access_ok(filename, mode, param_name, required=True):
return ValidationResult(True) return ValidationResult(True)
@validator
def file_exists(config, clients, deployment, param_name, mode=os.R_OK,
required=True):
"""Validator checks parameter is proper path to file with proper mode.
Ensure a file exists and can be accessed with the specified mode.
Note that path to file will be expanded before access checking.
:param param_name: Name of parameter to validate
:param mode: Access mode to test for. This should be one of:
* os.F_OK (file exists)
* os.R_OK (file is readable)
* os.W_OK (file is writable)
* os.X_OK (file is executable)
If multiple modes are required they can be added, eg:
mode=os.R_OK+os.W_OK
:param required: Boolean indicating whether this argument is required.
"""
return _file_access_ok(config.get("args", {}).get(param_name), mode,
param_name, required)
def check_command_dict(command): def check_command_dict(command):
"""Check command-specifying dict `command', raise ValueError on error.""" """Check command-specifying dict `command', raise ValueError on error."""
@ -409,3 +385,5 @@ required_param_or_context = deprecated_validator("required_param_or_context",
volume_type_exists = deprecated_validator("volume_type_exists", volume_type_exists = deprecated_validator("volume_type_exists",
"volume_type_exists", "volume_type_exists",
"0.10.0") "0.10.0")
file_exists = deprecated_validator("file_exists", "file_exists", "0.10.0")

View File

@ -1,4 +1,3 @@
# Copyright 2017: Mirantis Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -15,7 +14,11 @@
import ddt import ddt
import mock import mock
import os
import shutil
import tempfile
import rally
from rally.common.plugin import plugin from rally.common.plugin import plugin
from rally.common import validation from rally.common import validation
from rally.plugins.common import validators from rally.plugins.common import validators
@ -340,3 +343,38 @@ class RequiredParamOrContextValidatorTestCase(test.TestCase):
self.assertEqual(err_msg, result.msg) self.assertEqual(err_msg, result.msg)
else: else:
self.assertIsNone(result) self.assertIsNone(result)
class FileExistsValidatorTestCase(test.TestCase):
rally_jobs_path = os.path.join(
os.path.dirname(rally.__file__), "..", "rally-jobs")
def setUp(self):
super(FileExistsValidatorTestCase, self).setUp()
self.validator = validators.FileExistsValidator(param_name="p",
required=False)
self.credentials = dict(openstack={"admin": mock.MagicMock(),
"users": [mock.MagicMock()], })
self.tmp_dir = tempfile.mkdtemp()
os.makedirs(os.path.join(self.tmp_dir, ".rally"))
shutil.copytree(os.path.join(self.rally_jobs_path, "extra"),
os.path.join(self.tmp_dir, ".rally", "extra"))
self.original_home = os.environ["HOME"]
os.environ["HOME"] = self.tmp_dir
def return_home():
os.environ["HOME"] = self.original_home
self.addCleanup(shutil.rmtree, self.tmp_dir)
self.addCleanup(return_home)
@mock.patch("rally.plugins.common.validators."
"ValidatorUtils._file_access_ok")
def test_file_exists(self, mock__file_access_ok):
mock__file_access_ok.return_value = "foobar"
result = self.validator.validate({"args": {"p": "test_file"}},
self.credentials, None, None)
self.assertEqual("foobar", result)
mock__file_access_ok.assert_called_once_with(
"test_file", os.R_OK, "p", False)

View File

@ -13,6 +13,8 @@
# under the License. # under the License.
import os import os
import shutil
import tempfile
import traceback import traceback
import mock import mock
@ -30,6 +32,22 @@ class RallyJobsTestCase(test.TestCase):
rally_jobs_path = os.path.join( rally_jobs_path = os.path.join(
os.path.dirname(rally.__file__), "..", "rally-jobs") os.path.dirname(rally.__file__), "..", "rally-jobs")
def setUp(self):
super(RallyJobsTestCase, self).setUp()
self.tmp_dir = tempfile.mkdtemp()
os.makedirs(os.path.join(self.tmp_dir, ".rally"))
shutil.copytree(os.path.join(self.rally_jobs_path, "extra"),
os.path.join(self.tmp_dir, ".rally", "extra"))
self.original_home = os.environ["HOME"]
os.environ["HOME"] = self.tmp_dir
def return_home():
os.environ["HOME"] = self.original_home
self.addCleanup(shutil.rmtree, self.tmp_dir)
self.addCleanup(return_home)
def test_schema_is_valid(self): def test_schema_is_valid(self):
discover.load_plugins(os.path.join(self.rally_jobs_path, "plugins")) discover.load_plugins(os.path.join(self.rally_jobs_path, "plugins"))

View File

@ -192,17 +192,6 @@ class ValidatorsTestCase(test.TestCase):
"foobar", os.R_OK, "p", False) "foobar", os.R_OK, "p", False)
self.assertFalse(result.is_valid, result.msg) self.assertFalse(result.is_valid, result.msg)
@mock.patch(MODULE + "_file_access_ok")
def test_file_exists(self, mock__file_access_ok):
mock__file_access_ok.return_value = "foobar"
validator = self._unwrap_validator(validation.file_exists,
param_name="p",
required=False)
result = validator({"args": {"p": "test_file"}}, None, None)
self.assertEqual("foobar", result)
mock__file_access_ok.assert_called_once_with(
"test_file", os.R_OK, "p", False)
@ddt.data({"raises_message": "Command must be a dictionary"}, @ddt.data({"raises_message": "Command must be a dictionary"},
{"command": "foo", {"command": "foo",
"raises_message": "Command must be a dictionary"}, "raises_message": "Command must be a dictionary"},