Merge "Add Python 3.4 as supported interpreters"
This commit is contained in:
commit
ec14f4ef08
@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
# NOTE: The script assumes that you have the following
|
# NOTE: The script assumes that you have the following
|
||||||
# programs already installed:
|
# programs already installed:
|
||||||
# -> Python 2.6 or Python 2.7
|
# -> Python 2.6, Python 2.7 or Python 3.4
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import itertools
|
|||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from rally.common import costilius
|
||||||
from rally.common import log as logging
|
from rally.common import log as logging
|
||||||
from rally.common import utils
|
from rally.common import utils
|
||||||
from rally import consts
|
from rally import consts
|
||||||
@ -58,7 +59,7 @@ class Scenario(object):
|
|||||||
self._admin_clients = admin_clients
|
self._admin_clients = admin_clients
|
||||||
self._clients = clients
|
self._clients = clients
|
||||||
self._idle_duration = 0
|
self._idle_duration = 0
|
||||||
self._atomic_actions = {}
|
self._atomic_actions = costilius.OrderedDict()
|
||||||
|
|
||||||
# TODO(amaretskiy): consider about prefix part of benchmark uuid
|
# TODO(amaretskiy): consider about prefix part of benchmark uuid
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -323,12 +323,12 @@ def _add_command_parsers(categories, subparsers):
|
|||||||
|
|
||||||
def validate_deprecated_args(argv, fn):
|
def validate_deprecated_args(argv, fn):
|
||||||
if (len(argv) > 3
|
if (len(argv) > 3
|
||||||
and (argv[2] == fn.func_name)
|
and (argv[2] == fn.__name__)
|
||||||
and getattr(fn, "deprecated_args", None)):
|
and getattr(fn, "deprecated_args", None)):
|
||||||
for item in fn.deprecated_args:
|
for item in fn.deprecated_args:
|
||||||
if item in argv[3:]:
|
if item in argv[3:]:
|
||||||
LOG.warning("Deprecated argument %s for %s." % (item,
|
LOG.warning("Deprecated argument %s for %s." % (item,
|
||||||
fn.func_name))
|
fn.__name__))
|
||||||
|
|
||||||
|
|
||||||
def run(argv, categories):
|
def run(argv, categories):
|
||||||
@ -379,14 +379,15 @@ def run(argv, categories):
|
|||||||
return(0)
|
return(0)
|
||||||
|
|
||||||
fn = CONF.category.action_fn
|
fn = CONF.category.action_fn
|
||||||
fn_args = [arg.decode("utf-8") for arg in CONF.category.action_args]
|
fn_args = [encodeutils.safe_decode(arg)
|
||||||
|
for arg in CONF.category.action_args]
|
||||||
fn_kwargs = {}
|
fn_kwargs = {}
|
||||||
for k in CONF.category.action_kwargs:
|
for k in CONF.category.action_kwargs:
|
||||||
v = getattr(CONF.category, "action_kwarg_" + k)
|
v = getattr(CONF.category, "action_kwarg_" + k)
|
||||||
if v is None:
|
if v is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(v, six.string_types):
|
if isinstance(v, six.string_types):
|
||||||
v = v.decode("utf-8")
|
v = encodeutils.safe_decode(v)
|
||||||
fn_kwargs[k] = v
|
fn_kwargs[k] = v
|
||||||
|
|
||||||
# call the action with the remaining arguments
|
# call the action with the remaining arguments
|
||||||
|
@ -608,7 +608,8 @@ class TaskCommands(object):
|
|||||||
STATUS_FAIL = "FAIL"
|
STATUS_FAIL = "FAIL"
|
||||||
for result in results:
|
for result in results:
|
||||||
key = result["key"]
|
key = result["key"]
|
||||||
for sla in result["data"]["sla"]:
|
for sla in sorted(result["data"]["sla"],
|
||||||
|
key=lambda x: x["criterion"]):
|
||||||
success = sla.pop("success")
|
success = sla.pop("success")
|
||||||
sla["status"] = success and STATUS_PASS or STATUS_FAIL
|
sla["status"] = success and STATUS_PASS or STATUS_FAIL
|
||||||
sla["benchmark"] = key["name"]
|
sla["benchmark"] = key["name"]
|
||||||
@ -616,7 +617,7 @@ class TaskCommands(object):
|
|||||||
failed_criteria += int(not success)
|
failed_criteria += int(not success)
|
||||||
data.append(sla if tojson else rutils.Struct(**sla))
|
data.append(sla if tojson else rutils.Struct(**sla))
|
||||||
if tojson:
|
if tojson:
|
||||||
print(json.dumps(data))
|
print(json.dumps(data, sort_keys=False))
|
||||||
else:
|
else:
|
||||||
cliutils.print_list(data, ("benchmark", "pos", "criterion",
|
cliutils.print_list(data, ("benchmark", "pos", "criterion",
|
||||||
"status", "detail"))
|
"status", "detail"))
|
||||||
|
51
rally/common/costilius.py
Normal file
51
rally/common/costilius.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module is a storage for different types of workarounds.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections import OrderedDict # noqa
|
||||||
|
except ImportError:
|
||||||
|
# NOTE(andreykurilin): Python 2.6 issue. OrderedDict is not
|
||||||
|
# present in `collections` library.
|
||||||
|
from ordereddict import OrderedDict # noqa
|
||||||
|
|
||||||
|
|
||||||
|
def is_py26():
|
||||||
|
return sys.version_info[:2] == (2, 6)
|
||||||
|
|
||||||
|
|
||||||
|
if is_py26():
|
||||||
|
import simplejson as json
|
||||||
|
else:
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def json_loads(*args, **kwargs):
|
||||||
|
"""Deserialize a str or unicode instance to a Python object.
|
||||||
|
|
||||||
|
'simplejson' is used in Python 2.6 environment, because standard 'json'
|
||||||
|
library not include several important features(for example
|
||||||
|
'object_pairs_hook', which allows to deserialize input object to
|
||||||
|
OrderedDict)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return json.loads(*args, **kwargs)
|
@ -19,6 +19,8 @@ from sqlalchemy.dialects import mysql as mysql_types
|
|||||||
from sqlalchemy.ext import mutable
|
from sqlalchemy.ext import mutable
|
||||||
from sqlalchemy import types as sa_types
|
from sqlalchemy import types as sa_types
|
||||||
|
|
||||||
|
from rally.common import costilius
|
||||||
|
|
||||||
|
|
||||||
class JSONEncodedDict(sa_types.TypeDecorator):
|
class JSONEncodedDict(sa_types.TypeDecorator):
|
||||||
"""Represents an immutable structure as a json-encoded string."""
|
"""Represents an immutable structure as a json-encoded string."""
|
||||||
@ -27,12 +29,13 @@ class JSONEncodedDict(sa_types.TypeDecorator):
|
|||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
def process_bind_param(self, value, dialect):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = json.dumps(value)
|
value = json.dumps(value, sort_keys=False)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
def process_result_value(self, value, dialect):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = json.loads(value)
|
value = costilius.json_loads(
|
||||||
|
value, object_pairs_hook=costilius.OrderedDict)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,3 +37,7 @@ requests>=2.2.0,!=2.4.0
|
|||||||
SQLAlchemy>=0.9.7,<=0.9.99
|
SQLAlchemy>=0.9.7,<=0.9.99
|
||||||
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
|
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
|
||||||
six>=1.9.0
|
six>=1.9.0
|
||||||
|
|
||||||
|
# Python 2.6 related packages(see rally.common.costilius for more details)
|
||||||
|
ordereddict
|
||||||
|
simplejson>=2.2.0
|
||||||
|
@ -17,6 +17,8 @@ classifier =
|
|||||||
Programming Language :: Python :: 2
|
Programming Language :: Python :: 2
|
||||||
Programming Language :: Python :: 2.6
|
Programming Language :: Python :: 2.6
|
||||||
Programming Language :: Python :: 2.7
|
Programming Language :: Python :: 2.7
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.4
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
|
@ -535,11 +535,11 @@ class SLATestCase(unittest.TestCase):
|
|||||||
rally("task sla_check")
|
rally("task sla_check")
|
||||||
expected = [
|
expected = [
|
||||||
{"benchmark": "KeystoneBasic.create_and_list_users",
|
{"benchmark": "KeystoneBasic.create_and_list_users",
|
||||||
"criterion": "max_seconds_per_iteration",
|
"criterion": "failure_rate",
|
||||||
"detail": mock.ANY,
|
"detail": mock.ANY,
|
||||||
"pos": 0, "status": "PASS"},
|
"pos": 0, "status": "PASS"},
|
||||||
{"benchmark": "KeystoneBasic.create_and_list_users",
|
{"benchmark": "KeystoneBasic.create_and_list_users",
|
||||||
"criterion": "failure_rate",
|
"criterion": "max_seconds_per_iteration",
|
||||||
"detail": mock.ANY,
|
"detail": mock.ANY,
|
||||||
"pos": 0, "status": "PASS"}
|
"pos": 0, "status": "PASS"}
|
||||||
]
|
]
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# 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_utils import encodeutils
|
||||||
from six.moves import configparser
|
from six.moves import configparser
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
@ -37,7 +38,7 @@ class RallyCmdError(Exception):
|
|||||||
|
|
||||||
def __init__(self, code, output):
|
def __init__(self, code, output):
|
||||||
self.code = code
|
self.code = code
|
||||||
self.output = output
|
self.output = encodeutils.safe_decode(output)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Code: %d Output: %s\n" % (self.code, self.output)
|
return "Code: %d Output: %s\n" % (self.code, self.output)
|
||||||
@ -50,7 +51,7 @@ class TaskConfig(object):
|
|||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
config_file = tempfile.NamedTemporaryFile(delete=False)
|
config_file = tempfile.NamedTemporaryFile(delete=False)
|
||||||
config_file.write(json.dumps(config).encode("utf-8"))
|
config_file.write(encodeutils.safe_encode(json.dumps(config)))
|
||||||
config_file.close()
|
config_file.close()
|
||||||
self.filename = config_file.name
|
self.filename = config_file.name
|
||||||
|
|
||||||
@ -159,7 +160,7 @@ class Rally(object):
|
|||||||
:param getjson: in cases, when rally prints JSON, you can catch output
|
:param getjson: in cases, when rally prints JSON, you can catch output
|
||||||
deserialized
|
deserialized
|
||||||
:param report_path: if present, rally command and its output will be
|
:param report_path: if present, rally command and its output will be
|
||||||
wretten to file with passed file name
|
written to file with passed file name
|
||||||
:param raw: don't write command itself to report file. Only output
|
:param raw: don't write command itself to report file. Only output
|
||||||
will be written
|
will be written
|
||||||
"""
|
"""
|
||||||
@ -167,8 +168,8 @@ class Rally(object):
|
|||||||
if not isinstance(cmd, list):
|
if not isinstance(cmd, list):
|
||||||
cmd = cmd.split(" ")
|
cmd = cmd.split(" ")
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(self.args + cmd,
|
output = encodeutils.safe_decode(subprocess.check_output(
|
||||||
stderr=subprocess.STDOUT)
|
self.args + cmd, stderr=subprocess.STDOUT))
|
||||||
|
|
||||||
if write_report:
|
if write_report:
|
||||||
if not report_path:
|
if not report_path:
|
||||||
@ -181,6 +182,6 @@ class Rally(object):
|
|||||||
|
|
||||||
if getjson:
|
if getjson:
|
||||||
return json.loads(output)
|
return json.loads(output)
|
||||||
return output.decode("utf-8")
|
return output
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise RallyCmdError(e.returncode, e.output)
|
raise RallyCmdError(e.returncode, e.output)
|
||||||
|
Loading…
Reference in New Issue
Block a user