# 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_log import log as logging from oslo_utils import encodeutils from oslo_utils import importutils import requests from glareclient import exc LOG = logging.getLogger(__name__) 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.blob_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, deflate". # 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) self.verify_md5 = verify_md5 def __iter__(self): return self def next(self): try: data = self.iter.next() if self.verify_md5: self.blob_md5.update(data) return data except StopIteration: if self.verify_md5 and self.blob_md5.hexdigest() != self.hash_md5: raise IOError(errno.EPIPE, 'Checksum mismatch: %s (expected %s)' % (self.blob_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() def get_artifact_id(client, parsed_args): if parsed_args.id: return parsed_args.name try: return client.artifacts.get_by_name( parsed_args.name, version=parsed_args.artifact_version, type_name=parsed_args.type_name)['id'] except exc.BadRequest as e: exit(msg=e.details) def get_system_ca_file(): """Return path to system default CA file.""" # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, # Suse, FreeBSD/OpenBSD, MacOSX, and the bundled ca ca_path = ['/etc/ssl/certs/ca-certificates.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/etc/ssl/cert.pem', '/System/Library/OpenSSL/certs/cacert.pem', requests.certs.where()] for ca in ca_path: LOG.debug("Looking for ca file %s", ca) if os.path.exists(ca): LOG.debug("Using ca file %s", ca) return ca LOG.warning("System ca file could not be found.")