218 lines
6.0 KiB
Python
Raw Normal View History

# Copyright 2012 OpenStack Foundation
# 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.
from __future__ import print_function
import errno
import hashlib
import os
import six
import sys
if os.name == 'nt':
import msvcrt
else:
msvcrt = None
from oslo_utils import encodeutils
from oslo_utils import importutils
SENSITIVE_HEADERS = ('X-Auth-Token', )
def env(*vars, **kwargs):
"""Search for the first defined of possibly many env vars.
Returns the first environment variable defined in vars, or
returns the default defined in kwargs.
"""
for v in vars:
value = os.environ.get(v, None)
if value:
return value
return kwargs.get('default', '')
def import_versioned_module(version, submodule=None):
module = 'glareclient.v%s' % version
if submodule:
module = '.'.join((module, submodule))
return importutils.import_module(module)
def exit(msg='', exit_code=1):
if msg:
print(encodeutils.safe_decode(msg), file=sys.stderr)
sys.exit(exit_code)
def integrity_iter(iter, checksum):
"""Check blob integrity.
:raises: IOError
"""
md5sum = hashlib.md5()
for chunk in iter:
yield chunk
if isinstance(chunk, six.string_types):
chunk = six.b(chunk)
md5sum.update(chunk)
md5sum = md5sum.hexdigest()
if md5sum != checksum:
raise IOError(errno.EPIPE,
'Corrupt blob download. Checksum was %s expected %s' %
(md5sum, checksum))
class IterableWithLength(object):
def __init__(self, iterable, length):
self.iterable = iterable
self.length = length
def __iter__(self):
try:
for chunk in self.iterable:
yield chunk
finally:
self.iterable.close()
def next(self):
return next(self.iterable)
def __len__(self):
return self.length
def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
"""Return a tuple containing the item properties.
:param item: a single item resource (e.g. Server, Project, etc)
:param fields: tuple of strings with the desired field names
:param mixed_case_fields: tuple of field names to preserve case
:param formatters: dictionary mapping field names to callables
to format the values
"""
if mixed_case_fields is None:
mixed_case_fields = []
if formatters is None:
formatters = {}
row = []
for field in fields:
if field in mixed_case_fields:
field_name = field.replace(' ', '_')
else:
field_name = field.lower().replace(' ', '_')
data = item[field_name]
if field in formatters:
row.append(formatters[field](data))
else:
row.append(data)
return tuple(row)
def make_size_human_readable(size):
suffix = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB']
base = 1024.0
index = 0
if size is None:
size = 0
while size >= base:
index = index + 1
size = size / base
padded = '%.1f' % size
stripped = padded.rstrip('0').rstrip('.')
return '%s%s' % (stripped, suffix[index])
def save_blob(data, path):
"""Save a blob to the specified path.
:param data: blob of the artifact
:param path: path to save the blob to
"""
if path is None:
blob = getattr(sys.stdout, 'buffer',
sys.stdout)
else:
blob = open(path, 'wb')
try:
for chunk in data:
blob.write(chunk)
finally:
if path is not None:
blob.close()
def get_data_file(blob):
if blob:
return open(blob, 'rb')
else:
# distinguish cases where:
# (1) stdin is not valid (as in cron jobs):
# glare ... <&-
# (2) blob is provided through standard input:
# glare ... < /tmp/file or cat /tmp/file | glare ...
# (3) no blob provided:
# glare ...
try:
os.fstat(0)
except OSError:
# (1) stdin is not valid (closed...)
return None
if not sys.stdin.isatty():
# (2) blob data is provided through standard input
blob_data = sys.stdin
if hasattr(sys.stdin, 'buffer'):
blob_data = sys.stdin.buffer
if msvcrt:
msvcrt.setmode(blob_data.fileno(), os.O_BINARY)
return blob_data
else:
# (3) no blob data provided
return None
def get_file_size(file_obj):
"""Analyze file-like object and attempt to determine its size.
:param file_obj: file-like object.
:retval The file's size or None if it cannot be determined.
"""
if (hasattr(file_obj, 'seek') and hasattr(file_obj, 'tell') and
(six.PY2 or six.PY3 and file_obj.seekable())):
try:
curr = file_obj.tell()
file_obj.seek(0, os.SEEK_END)
size = file_obj.tell()
file_obj.seek(curr)
return size
except IOError as e:
if e.errno == errno.ESPIPE:
# Illegal seek. This means the file object
# is a pipe (e.g. the user is trying
# to pipe blob to the client,
# echo testdata | bin/glare add blah...), or
# that file object is empty, or that a file-like
# object which doesn't support 'seek/tell' has
# been supplied.
return
else:
raise