From 1f26d590a47d083720f29e736195b08183cd3a24 Mon Sep 17 00:00:00 2001 From: Sheel Rana Date: Sat, 14 May 2016 16:56:23 +0530 Subject: [PATCH] Added common utilities This patchset contains some common utilities like tox pep8 fast environment, context generate/check, yaml load. Change-Id: I855e40a3562b9c4fa015dd13f31642882097833e --- higgins/common/context.py | 114 ++++++++++++++++++++++++++++++++++++ higgins/common/short_id.py | 63 ++++++++++++++++++++ higgins/common/yamlutils.py | 39 ++++++++++++ higgins/version.py | 17 ++++++ requirements.txt | 6 +- tools/fast8.sh | 16 +++++ tox.ini | 8 +++ 7 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 higgins/common/context.py create mode 100644 higgins/common/short_id.py create mode 100644 higgins/common/yamlutils.py create mode 100644 higgins/version.py create mode 100644 tools/fast8.sh diff --git a/higgins/common/context.py b/higgins/common/context.py new file mode 100644 index 000000000..41b72ca65 --- /dev/null +++ b/higgins/common/context.py @@ -0,0 +1,114 @@ +# 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. + +from eventlet.green import threading +from oslo_context import context + + +class RequestContext(context.RequestContext): + """Extends security contexts from the OpenStack common library.""" + + def __init__(self, auth_token=None, auth_url=None, domain_id=None, + domain_name=None, user_name=None, user_id=None, + project_name=None, project_id=None, roles=None, + is_admin=False, read_only=False, show_deleted=False, + request_id=None, trust_id=None, auth_token_info=None, + all_tenants=False, **kwargs): + """Stores several additional request parameters: + + :param domain_id: The ID of the domain. + :param domain_name: The name of the domain. + """ + super(RequestContext, self).__init__(auth_token=auth_token, + user=user_name, + tenant=project_name, + is_admin=is_admin, + read_only=read_only, + show_deleted=show_deleted, + request_id=request_id) + + self.user_name = user_name + self.user_id = user_id + self.project_name = project_name + self.project_id = project_id + self.domain_id = domain_id + self.domain_name = domain_name + self.roles = roles + self.auth_url = auth_url + self.auth_token_info = auth_token_info + self.trust_id = trust_id + self.all_tenants = all_tenants + + def to_dict(self): + value = super(RequestContext, self).to_dict() + value.update({'auth_token': self.auth_token, + 'auth_url': self.auth_url, + 'domain_id': self.domain_id, + 'domain_name': self.domain_name, + 'user_name': self.user_name, + 'user_id': self.user_id, + 'project_name': self.project_name, + 'project_id': self.project_id, + 'is_admin': self.is_admin, + 'read_only': self.read_only, + 'roles': self.roles, + 'show_deleted': self.show_deleted, + 'request_id': self.request_id, + 'trust_id': self.trust_id, + 'auth_token_info': self.auth_token_info, + 'all_tenants': self.all_tenants}) + return value + + @classmethod + def from_dict(cls, values): + return cls(**values) + + +def make_context(*args, **kwargs): + return RequestContext(*args, **kwargs) + + +def make_admin_context(show_deleted=False, all_tenants=False): + """Create an administrator context. + + :param show_deleted: if True, will show deleted items when query db + """ + + context = RequestContext(user_id=None, + project=None, + is_admin=True, + show_deleted=show_deleted, + all_tenants=all_tenants) + return context + + +_CTX_STORE = threading.local() +_CTX_KEY = 'current_ctx' + + +def has_ctx(): + return hasattr(_CTX_STORE, _CTX_KEY) + + +def ctx(): + return getattr(_CTX_STORE, _CTX_KEY) + + +def set_ctx(new_ctx): + if not new_ctx and has_ctx(): + delattr(_CTX_STORE, _CTX_KEY) + if hasattr(context._request_store, 'context'): + delattr(context._request_store, 'context') + + if new_ctx: + setattr(_CTX_STORE, _CTX_KEY, new_ctx) + setattr(context._request_store, 'context', new_ctx) diff --git a/higgins/common/short_id.py b/higgins/common/short_id.py new file mode 100644 index 000000000..720c44a7c --- /dev/null +++ b/higgins/common/short_id.py @@ -0,0 +1,63 @@ +# +# 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. + +"""Utilities for creating short ID strings based on a random UUID. +The IDs each comprise 12 (lower-case) alphanumeric characters. +""" + +import base64 +import uuid + +import six + +from higgins.common.i18n import _ + + +def _to_byte_string(value, num_bits): + """Convert an integer to a big-endian string of bytes with padding. + + Padding is added at the end (i.e. after the least-significant bit) if + required. + """ + + shifts = six.moves.xrange(num_bits - 8, -8, -8) + byte_at = lambda off: (value >> off if off >= 0 else value << -off) & 0xff + return ''.join(chr(byte_at(offset)) for offset in shifts) + + +def get_id(source_uuid): + """Derive a short (12 character) id from a random UUID. + + The supplied UUID must be a version 4 UUID object. + """ + + if isinstance(source_uuid, six.string_types): + source_uuid = uuid.UUID(source_uuid) + if source_uuid.version != 4: + raise ValueError(_('Invalid UUID version (%d)') % source_uuid.version) + + # The "time" field of a v4 UUID contains 60 random bits + # (see RFC4122, Section 4.4) + random_bytes = _to_byte_string(source_uuid.time, 60) + # The first 12 bytes (= 60 bits) of base32-encoded output is our data + encoded = base64.b32encode(six.b(random_bytes))[:12] + + if six.PY3: + return encoded.lower().decode('utf-8') + else: + return encoded.lower() + + +def generate_id(): + """Generate a short (12 character), random id.""" + return get_id(uuid.uuid4()) diff --git a/higgins/common/yamlutils.py b/higgins/common/yamlutils.py new file mode 100644 index 000000000..8eac2a7ab --- /dev/null +++ b/higgins/common/yamlutils.py @@ -0,0 +1,39 @@ +# 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. + +import yaml + + +if hasattr(yaml, 'CSafeDumper'): + yaml_dumper = yaml.CSafeDumper +else: + yaml_dumper = yaml.SafeDumper + + +def load(s): + try: + yml_dict = yaml.safe_load(s) + except yaml.YAMLError as exc: + msg = 'An error occurred during YAML parsing.' + if hasattr(exc, 'problem_mark'): + msg += ' Error position: (%s:%s)' % (exc.problem_mark.line + 1, + exc.problem_mark.column + 1) + raise ValueError(msg) + if not isinstance(yml_dict, dict) and not isinstance(yml_dict, list): + raise ValueError('The source is not a YAML mapping or list.') + if isinstance(yml_dict, dict) and len(yml_dict) < 1: + raise ValueError('Could not find any element in your YAML mapping.') + return yml_dict + + +def dump(s): + return yaml.dump(s, Dumper=yaml_dumper) diff --git a/higgins/version.py b/higgins/version.py new file mode 100644 index 000000000..46581f9f5 --- /dev/null +++ b/higgins/version.py @@ -0,0 +1,17 @@ +# +# 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. + +from pbr import version as pbr_version + +version_info = pbr_version.VersionInfo('higgins') +version_string = version_info.version_string diff --git a/requirements.txt b/requirements.txt index cd5574039..f9b8e5639 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,11 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. +PyYAML>=3.1.0 # MIT +eventlet!=0.18.3,>=0.18.2 # MIT +greenlet>=0.3.2 # MIT pbr>=1.6 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 pecan>=1.0.0 # BSD +oslo.i18n>=2.1.0 # Apache-2.0 +six>=1.9.0 # MIT WebOb>=1.2.3 # MIT diff --git a/tools/fast8.sh b/tools/fast8.sh new file mode 100644 index 000000000..3ee00a010 --- /dev/null +++ b/tools/fast8.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +cd $(dirname "$0")/.. +CHANGED=$(git diff --name-only HEAD~1 | tr '\n' ' ') + +# Skip files that don't exist +# (have been git rm'd) +CHECK="" +for FILE in $CHANGED; do + if [ -f "$FILE" ]; then + CHECK="$CHECK $FILE" + fi +done + +diff -u --from-file /dev/null $CHECK | flake8 --diff + diff --git a/tox.ini b/tox.ini index 12e9d257e..f755015e3 100644 --- a/tox.ini +++ b/tox.ini @@ -23,6 +23,14 @@ commands = flake8 {posargs} install_command = {[testenv:common-constraints]install_command} commands = flake8 {posargs} + +[testenv:fast8] +# Use same environment directory as pep8 env to save space and install time + +envdir = {toxworkdir}/pep8 +commands = + {toxinidir}/tools/fast8.sh + [testenv:venv] commands = {posargs}