1# Licensed to the Apache Software Foundation (ASF) under one or more
2# contributor license agreements.  See the NOTICE file distributed with
3# this work for additional information regarding copyright ownership.
4# The ASF licenses this file to You under the Apache License, Version 2.0
5# (the "License"); you may not use this file except in compliance with
6# the License.  You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17libcloud provides a unified interface to the cloud computing resources.
18
19:var __version__: Current version of libcloud
20"""
21
22import os
23import codecs
24import atexit
25
26from libcloud.base import DriverType  # NOQA
27from libcloud.base import DriverTypeFactoryMap  # NOQA
28from libcloud.base import get_driver  # NOQA
29
30try:
31    # TODO: This import is slow and adds overhead in situations when no
32    # requests are made but it's necessary for detecting bad version of
33    # requests
34    import requests  # NOQA
35    have_requests = True
36except ImportError:
37    have_requests = False
38
39__all__ = [
40    '__version__',
41    'enable_debug'
42]
43
44__version__ = '3.4.1'
45
46
47def enable_debug(fo):
48    """
49    Enable library wide debugging to a file-like object.
50
51    :param fo: Where to append debugging information
52    :type fo: File like object, only write operations are used.
53    """
54    from libcloud.common.base import Connection
55    from libcloud.utils.loggingconnection import LoggingConnection
56
57    LoggingConnection.log = fo
58    Connection.conn_class = LoggingConnection
59
60    # Ensure the file handle is closed on exit
61    def close_file(fd):
62        try:
63            fd.close()
64        except Exception:
65            pass
66
67    atexit.register(close_file, fo)
68
69
70def _init_once():
71    """
72    Utility function that is ran once on Library import.
73
74    This checks for the LIBCLOUD_DEBUG environment variable, which if it exists
75    is where we will log debug information about the provider transports.
76
77    This also checks for known environment/dependency incompatibilities.
78    """
79    path = os.getenv('LIBCLOUD_DEBUG')
80
81    if path:
82        mode = 'a'
83
84        # Special case for /dev/stderr and /dev/stdout on Python 3.
85        from libcloud.utils.py3 import PY3
86
87        # Opening those files in append mode will throw "illegal seek"
88        # exception there.
89        # Late import to avoid setup.py related side affects
90        if path in ['/dev/stderr', '/dev/stdout'] and PY3:
91            mode = 'w'
92
93        fo = codecs.open(path, mode, encoding='utf8')
94        enable_debug(fo)
95
96        # NOTE: We use lazy import to avoid unnecessary import time overhead
97        try:
98            import paramiko  # NOQA
99            have_paramiko = True
100        except ImportError:
101            have_paramiko = False
102
103        if have_paramiko and hasattr(paramiko.util, 'log_to_file'):
104            import logging
105
106            # paramiko always tries to open file path in append mode which
107            # won't work with /dev/{stdout, stderr} so we just ignore those
108            # errors
109            try:
110                paramiko.util.log_to_file(filename=path, level=logging.DEBUG)
111            except OSError as e:
112                if "illegal seek" not in str(e).lower():
113                    raise e
114
115    # check for broken `yum install python-requests`
116    if have_requests and requests.__version__ == '2.6.0':
117        chardet_version = requests.packages.chardet.__version__
118        required_chardet_version = '2.3.0'
119        assert chardet_version == required_chardet_version, (
120            'Known bad version of requests detected! This can happen when '
121            'requests was installed from a source other than PyPI, e.g. via '
122            'a package manager such as yum. Please either install requests '
123            'from PyPI or run `pip install chardet==%s` to resolve this '
124            'issue.' % required_chardet_version
125        )
126
127
128_init_once()
129