File: core.py

package info (click to toggle)
python-softlayer 6.2.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,508 kB
  • sloc: python: 57,195; makefile: 133; xml: 97; sh: 59
file content (211 lines) | stat: -rw-r--r-- 7,555 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""
    SoftLayer.CLI.core
    ~~~~~~~~~~~~~~~~~~
    Core for the SoftLayer CLI

    :license: MIT, see LICENSE for more details.
"""
import logging
import os
import sys
import time
import traceback

import click
import requests
from rich.markup import escape

import SoftLayer
from SoftLayer.CLI.command import CommandLoader
from SoftLayer.CLI import environment
from SoftLayer.CLI import exceptions
from SoftLayer.CLI import formatting
from SoftLayer import consts

# pylint: disable=too-many-public-methods, broad-except, unused-argument
# pylint: disable=redefined-builtin, super-init-not-called, arguments-differ

START_TIME = time.time()
DEBUG_LOGGING_MAP = {
    0: logging.CRITICAL,
    1: logging.WARNING,
    2: logging.INFO,
    3: logging.DEBUG
}

PROG_NAME = "slcli (SoftLayer Command-line)"
VALID_FORMATS = ['table', 'raw', 'json', 'jsonraw', 'csv']
DEFAULT_FORMAT = 'raw'

if sys.stdout.isatty():
    DEFAULT_FORMAT = 'table'


def get_latest_version():
    """Gets the latest version of the Softlayer library."""
    try:
        result = requests.get('https://pypi.org/pypi/SoftLayer/json',  timeout=60)
        json_result = result.json()
        latest = f"v{json_result['info']['version']}"
    except Exception:
        latest = "Unable to get version from pypi."
    return latest


CONTEXT_SETTINGS = {
    "help_option_names": ['--help', '-h'],
    "auto_envvar_prefix": 'SLCLI',
    "max_content_width": 999
}


def get_version_message(ctx, param, value):
    """Gets current and latest release versions message."""
    if not value or ctx.resilient_parsing:
        return
    current = SoftLayer.consts.VERSION
    latest = get_latest_version()
    click.secho(f"Current: {PROG_NAME} {current}\nLatest:  {PROG_NAME} {latest}")
    ctx.exit()


@click.group(help="SoftLayer Command-line Client",
             epilog="""To use most commands your SoftLayer username and api_key need to be configured.
The easiest way to do that is to use: 'slcli setup'""",
             cls=CommandLoader,
             context_settings=CONTEXT_SETTINGS)
@click.option('--format', default=DEFAULT_FORMAT, show_default=True,
              help="Output format",
              type=click.Choice(VALID_FORMATS))
@click.option('-C', '--config', required=False, show_default=True,
              default=click.get_app_dir('softlayer', force_posix=True),
              help="Config file location",
              type=click.Path(resolve_path=True))
@click.option('--verbose', '-v',
              help="Sets the debug noise level, specify multiple times for more verbosity.",
              type=click.IntRange(0, 3, clamp=True),
              count=True)
@click.option('--proxy', required=False,
              help="HTTPS or HTTP proxy to be use to make API calls")
@click.option('--really / --not-really', '-y', is_flag=True, required=False,
              help="Confirm all prompt actions")
@click.option('--demo / --no-demo', is_flag=True, required=False,
              help="Use demo data instead of actually making API calls")
@click.option('--version', is_flag=True, expose_value=False, is_eager=True, callback=get_version_message,
              help="Show version information.",  allow_from_autoenv=False,)
@click.option('--account', '-a', help="Account Id, only needed for some API calls.")
@click.option('--internal', '-i', is_flag=True, required=False,
              help="Use the Employee Client instead of the Customer Client.")
@environment.pass_env
def cli(env,
        format='table',
        config=None,
        verbose=0,
        proxy=None,
        really=False,
        demo=False,
        account=None,
        internal=False,
        **kwargs):
    """Main click CLI entry-point."""

    # Populate environment with client and set it as the context object
    env.skip_confirmations = really
    env.config_file = config
    env.format = format
    env.set_env_theme(config_file=config)
    if internal:
        env.ensure_emp_client(config_file=config, is_demo=demo, proxy=proxy)
    else:
        env.ensure_client(config_file=config, is_demo=demo, proxy=proxy)
    env.vars['_start'] = time.time()
    logger = logging.getLogger()

    if demo is False:
        logger.addHandler(logging.StreamHandler())
    else:
        # This section is for running CLI tests.
        logging.getLogger("urllib3").setLevel(logging.WARNING)
        logger.addHandler(logging.NullHandler())

    logger.setLevel(DEBUG_LOGGING_MAP.get(verbose, logging.DEBUG))
    env.vars['_timings'] = SoftLayer.DebugTransport(env.client.transport)
    env.vars['verbose'] = verbose
    env.client.transport = env.vars['_timings']
    env.client.account_id = account


@cli.result_callback()
@environment.pass_env
def output_diagnostics(env, result, verbose=0, **kwargs):
    """Output diagnostic information."""

    if verbose > 0:
        diagnostic_table = formatting.Table(['name', 'value'])
        diagnostic_table.add_row(['execution_time', '%fs' % (time.time() - START_TIME)])

        api_call_value = formatting.Table(['API Calls'], title=None, align="left")
        for call in env.client.transport.get_last_calls():
            api_call_value.add_row(["%s::%s (%fs)" % (call.service, call.method, call.end_time - call.start_time)])

        diagnostic_table.add_row(['api_calls', api_call_value])
        diagnostic_table.add_row(['version', consts.USER_AGENT])
        diagnostic_table.add_row(['python_version', sys.version])
        diagnostic_table.add_row(['library_location', os.path.dirname(SoftLayer.__file__)])

        env.err(env.fmt(diagnostic_table))

    if verbose > 1:
        for call in env.client.transport.get_last_calls():
            call_table = formatting.Table(['', f'{call.service}::{call.method}'], align="left")
            nice_mask = ''
            if call.mask is not None:
                nice_mask = call.mask
            call_table.add_row(['id', call.identifier])
            #  Need to escape this so Rich doesn't think the mask is a tag and hide it.
            call_table.add_row(['mask', escape(nice_mask)])
            call_table.add_row(['filter', call.filter])
            call_table.add_row(['limit', call.limit])
            call_table.add_row(['offset', call.offset])
            env.err(env.fmt(call_table))

    if verbose > 2:
        for call in env.client.transport.get_last_calls():
            env.err(env.client.transport.print_reproduceable(call))


def main(reraise_exceptions=False, **kwargs):
    """Main program. Catches several common errors and displays them nicely."""
    exit_status = 0

    try:
        cli.main(**kwargs)
    except SoftLayer.SoftLayerAPIError as ex:
        if 'invalid api token' in ex.faultString.lower():
            print("Authentication Failed: To update your credentials, use 'slcli config setup'")
            exit_status = 1
        else:
            print(str(ex))
            exit_status = 1
    except SoftLayer.SoftLayerError as ex:
        print(str(ex))
        exit_status = 1
    except exceptions.CLIAbort as ex:
        print(str(ex.message))
        exit_status = ex.code
    except Exception:
        if reraise_exceptions:
            raise

        print("An unexpected error has occured:")
        print(str(traceback.format_exc()))
        print("Feel free to report this error as it is likely a bug:")
        print("    https://github.com/softlayer/softlayer-python/issues")
        print("The following snippet should be able to reproduce the error")
        exit_status = 1

    sys.exit(exit_status)


if __name__ == '__main__':
    main()