Library `requests` can handle files, pipes, dictionaries and iterators as `data` argument. Use 'json' argument to send json requests. Rewrite some unittests using mock. Change-Id: I95b71eb2716dc57708ed105659ffece376bd8344
		
			
				
	
	
		
			160 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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 sys
 | 
						|
 | 
						|
if os.name == 'nt':
 | 
						|
    import msvcrt
 | 
						|
else:
 | 
						|
    msvcrt = None
 | 
						|
 | 
						|
from oslo_utils import encodeutils
 | 
						|
from oslo_utils import importutils
 | 
						|
import requests
 | 
						|
 | 
						|
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)
 | 
						|
 | 
						|
 | 
						|
class ResponseBlobWrapper(object):
 | 
						|
    """Represent HTTP response as iterator with known length."""
 | 
						|
 | 
						|
    def __init__(self, resp, verify_md5=True):
 | 
						|
        self.hash_md5 = resp.headers.get("Content-MD5")
 | 
						|
        self.check_md5 = hashlib.md5()
 | 
						|
        if 301 <= resp.status_code <= 302:
 | 
						|
            # NOTE(sskripnick): handle redirect manually to prevent sending
 | 
						|
            # auth token to external resource.
 | 
						|
            # Use stream=True to prevent reading whole response into memory.
 | 
						|
            # Set Accept-Encoding explicitly to "identity" because setting
 | 
						|
            # stream=True forces Accept-Encoding to be "gzip, defalate".
 | 
						|
            # It should be "identity" because we should know Content-Length.
 | 
						|
            resp = requests.get(resp.headers.get("Location"),
 | 
						|
                                headers={"Accept-Encoding": "identity"})
 | 
						|
        self.len = resp.headers.get("Content-Length", 0)
 | 
						|
        self.iter = resp.iter_content(65536)
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return self
 | 
						|
 | 
						|
    def next(self):
 | 
						|
        try:
 | 
						|
            data = self.iter.next()
 | 
						|
            self.check_md5.update(data)
 | 
						|
            return data
 | 
						|
        except StopIteration:
 | 
						|
            if self.check_md5.hexdigest() != self.hash_md5:
 | 
						|
                raise IOError(errno.EPIPE,
 | 
						|
                              'Checksum mismatch: %s (expected %s)' %
 | 
						|
                              (self.check_md5.hexdigest(), self.hash_md5))
 | 
						|
            raise
 | 
						|
 | 
						|
    __next__ = next
 | 
						|
 | 
						|
 | 
						|
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()
 |