diff --git a/CHANGELOG b/CHANGELOG index 790c07eb4..8466f1c9e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,23 @@ +3.0.2 + + * CLI+API: Simplified object mask reformatting and added support for more complex masks. + + * CLI: Fixed the sl bmc create --network argument. + + * CLI+API: Improved output of the message queue feature and fixed some minor bugs. + + * CLI: Fixed an error when using --test and ordering a non-private subnet. + + * API: Fix to prevent double counting results in summary_by_datacenter(). + + * CLI+API: Added IPMI IP address to hardware details. + + * CLI: Added support for ordering multiple disks when creating a CCI. + + * API: Added flag to disable compression on HTTP requests. + + * CLI: Added CIDR information to subnet displays. + 3.0.1 * CLI: Fixed an error message about pricing information that appeared when ordering a new private subnet. diff --git a/README.rst b/README.rst index 8999279b8..bebe6ad06 100644 --- a/README.rst +++ b/README.rst @@ -2,12 +2,15 @@ SoftLayer API Python Client =========================== .. image:: https://api.travis-ci.org/softlayer/softlayer-api-python-client.png :target: https://travis-ci.org/softlayer/softlayer-api-python-client + +.. image:: https://landscape.io/github/softlayer/softlayer-api-python-client/master/landscape.png + :target: https://landscape.io/github/softlayer/softlayer-api-python-client/master .. image:: https://badge.fury.io/py/SoftLayer.png :target: http://badge.fury.io/py/SoftLayer .. image:: https://pypip.in/d/SoftLayer/badge.png - :target: https://crate.io/packages/SoftLayer + :target: https://crate.io/packages/SoftLayer SoftLayer API bindings for Python. For use with `SoftLayer's API `_. diff --git a/SoftLayer/API.py b/SoftLayer/API.py index 5f2b4175a..9b7dcc46f 100644 --- a/SoftLayer/API.py +++ b/SoftLayer/API.py @@ -6,7 +6,7 @@ :copyright: (c) 2013, SoftLayer Technologies, Inc. All rights reserved. :license: MIT, see LICENSE for more details. """ -import datetime +import time from consts import API_PUBLIC_ENDPOINT, API_PRIVATE_ENDPOINT, USER_AGENT from transports import make_xml_rpc_api_call @@ -22,6 +22,7 @@ 'mask', 'filter', 'headers', + 'compress', 'raw_headers', 'limit', 'offset', @@ -140,6 +141,7 @@ def call(self, service, method, *args, **kwargs): objectmask = kwargs.get('mask') objectfilter = kwargs.get('filter') headers = kwargs.get('headers', {}) + compress = kwargs.get('compress', True) raw_headers = kwargs.get('raw_headers') limit = kwargs.get('limit') offset = kwargs.get('offset', 0) @@ -166,6 +168,11 @@ def call(self, service, method, *args, **kwargs): 'User-Agent': USER_AGENT, 'Content-Type': 'application/xml', } + + if compress: + http_headers['Accept'] = '*/*' + http_headers['Accept-Encoding'] = 'gzip, deflate, compress' + if raw_headers: http_headers.update(raw_headers) @@ -265,14 +272,12 @@ class TimedClient(Client): def call(self, service, method, *args, **kwargs): """ See Client.call for documentation. """ - start_time = datetime.datetime.utcnow() + start_time = time.time() result = super(TimedClient, self).call(service, method, *args, **kwargs) - end_time = datetime.datetime.utcnow() + end_time = time.time() diff = end_time - start_time - self.last_calls.append((service + '.' + method, - start_time.strftime('%s'), - diff.total_seconds())) + self.last_calls.append((service + '.' + method, start_time, diff)) return result def get_last_calls(self): @@ -300,6 +305,7 @@ def call(self, name, *args, **kwargs): :param mask: (optional) object mask :param dict filter: (optional) filter dict :param dict headers: (optional) optional XML-RPC headers + :param boolean compress: (optional) Enable/Disable HTTP compression :param dict raw_headers: (optional) HTTP transport headers :param int limit: (optional) return at most this many results :param int offset: (optional) offset results by this many diff --git a/SoftLayer/CLI/core.py b/SoftLayer/CLI/core.py index 1ab546509..eb9ee50fe 100644 --- a/SoftLayer/CLI/core.py +++ b/SoftLayer/CLI/core.py @@ -17,13 +17,14 @@ sshkey Manage SSH keys on your account Networking: - dns Domain Name System - firewall Firewall rule and security management - globalip Global IP address management - rwhois RWhoIs operations - ssl Manages SSL - subnet Subnet ordering and management - vlan Manage VLANs on your account + dns Domain Name System + firewall Firewall rule and security management + globalip Global IP address management + messaging Message Queue Service + rwhois RWhoIs operations + ssl Manages SSL + subnet Subnet ordering and management + vlan Manage VLANs on your account Storage: iscsi View iSCSI details @@ -50,8 +51,7 @@ from SoftLayer import Client, TimedClient, SoftLayerError, SoftLayerAPIError from SoftLayer.consts import VERSION from helpers import CLIAbort, ArgumentError, format_output, KeyValueTable -from environment import ( - Environment, CLIRunnableType, InvalidCommand, InvalidModule) +from environment import Environment, InvalidCommand, InvalidModule DEBUG_LOGGING_MAP = { @@ -120,8 +120,7 @@ def parse_module_args(self, module_name, args): version=VERSION, argv=[module_name] + args, options_first=True) - module = self.env.load_module(module_name) - return module, arguments + return arguments def parse_command_args(self, module_name, command_name, args): command = self.env.get_command(module_name, command_name) @@ -135,8 +134,7 @@ def parse(self, args): module_name = main_args[''] # handle `sl ...` - module, module_args = self.parse_module_args( - module_name, main_args['']) + module_args = self.parse_module_args(module_name, main_args['']) # get the command argument command_name = module_args.get('') @@ -153,7 +151,6 @@ def main(args=sys.argv[1:], env=Environment()): Entry point for the command-line client. """ # Parse Top-Level Arguments - CLIRunnableType.env = env exit_status = 0 resolver = CommandParser(env) try: @@ -173,24 +170,25 @@ def main(args=sys.argv[1:], env=Environment()): client = Client(config_file=command_args.get('--config')) # Do the thing - data = command.execute(client, command_args) + runnable = command(client=client, env=env) + data = runnable.execute(command_args) if data: - format = command_args.get('--format', 'table') - if format not in VALID_FORMATS: - raise ArgumentError('Invalid format "%s"' % format) - s = format_output(data, fmt=format) + out_format = command_args.get('--format', 'table') + if out_format not in VALID_FORMATS: + raise ArgumentError('Invalid format "%s"' % out_format) + s = format_output(data, fmt=out_format) if s: env.out(s) if command_args.get('--timings'): - format = command_args.get('--format', 'table') + out_format = command_args.get('--format', 'table') api_calls = client.get_last_calls() t = KeyValueTable(['call', 'time']) - for call, initiated, duration in api_calls: + for call, _, duration in api_calls: t.add_row([call, duration]) - env.err(format_output(t, fmt=format)) + env.err(format_output(t, fmt=out_format)) except InvalidCommand as e: env.err(resolver.get_module_help(e.module_name)) diff --git a/SoftLayer/CLI/environment.py b/SoftLayer/CLI/environment.py index 3da51f654..b16877ab4 100644 --- a/SoftLayer/CLI/environment.py +++ b/SoftLayer/CLI/environment.py @@ -6,11 +6,12 @@ :copyright: (c) 2013, SoftLayer Technologies, Inc. All rights reserved. :license: MIT, see LICENSE for more details. """ -import sys import getpass from importlib import import_module +import inspect import os import os.path +import sys from SoftLayer.CLI.modules import get_module_list from SoftLayer import SoftLayerError @@ -62,7 +63,11 @@ def get_module_name(self, module_name): def load_module(self, module_name): # pragma: no cover try: - return import_module('SoftLayer.CLI.modules.%s' % module_name) + module = import_module('SoftLayer.CLI.modules.%s' % module_name) + for _, obj in inspect.getmembers(module): + if inspect.isclass(obj) and issubclass(obj, CLIRunnable): + self.add_plugin(obj) + return module except ImportError: raise InvalidModule(module_name) @@ -95,25 +100,13 @@ def exit(self, code=0): sys.exit(code) -class CLIRunnableType(type): - - env = Environment() - - def __init__(cls, name, bases, attrs): - super(CLIRunnableType, cls).__init__(name, bases, attrs) - if cls.env and name != 'CLIRunnable': - cls.env.add_plugin(cls) - - class CLIRunnable(object): - __metaclass__ = CLIRunnableType - options = [] - action = None + options = [] # set by subclass + action = None # set by subclass - @staticmethod - def add_additional_args(parser): - pass + def __init__(self, client=None, env=None): + self.client = client + self.env = env - @staticmethod - def execute(client, args): + def execute(self, args): pass diff --git a/SoftLayer/CLI/exceptions.py b/SoftLayer/CLI/exceptions.py index 510f9859c..104ae90f3 100644 --- a/SoftLayer/CLI/exceptions.py +++ b/SoftLayer/CLI/exceptions.py @@ -22,5 +22,5 @@ def __init__(self, msg, *args): class ArgumentError(CLIAbort): def __init__(self, msg, *args): - super(CLIAbort, self).__init__(code=2, *args) + super(ArgumentError, self).__init__(msg, *args) self.message = "Argument Error: %s" % msg diff --git a/SoftLayer/CLI/modules/bmc.py b/SoftLayer/CLI/modules/bmc.py index 5aeac8629..7dd2195f6 100644 --- a/SoftLayer/CLI/modules/bmc.py +++ b/SoftLayer/CLI/modules/bmc.py @@ -43,19 +43,18 @@ class BMCCreateOptions(CLIRunnable): action = 'create-options' options = ['datacenter', 'cpu', 'memory', 'os', 'disk', 'nic'] - @classmethod - def execute(cls, client, args): + def execute(self, args): t = KeyValueTable(['Name', 'Value']) t.align['Name'] = 'r' t.align['Value'] = 'l' show_all = True - for opt_name in cls.options: + for opt_name in self.options: if args.get("--" + opt_name): show_all = False break - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) bmi_options = mgr.get_bare_metal_create_options() @@ -63,12 +62,12 @@ def execute(cls, client, args): show_all = True if args['--datacenter'] or show_all: - results = cls.get_create_options(bmi_options, 'datacenter')[0] + results = self.get_create_options(bmi_options, 'datacenter')[0] t.add_row([results[0], listing(sorted(results[1]))]) if args['--cpu'] or args['--memory'] or show_all: - results = cls.get_create_options(bmi_options, 'cpu') + results = self.get_create_options(bmi_options, 'cpu') memory_cpu_table = Table(['memory', 'cpu']) for result in results: memory_cpu_table.add_row([ @@ -80,7 +79,7 @@ def execute(cls, client, args): t.add_row(['memory/cpu', memory_cpu_table]) if args['--os'] or show_all: - results = cls.get_create_options(bmi_options, 'os') + results = self.get_create_options(bmi_options, 'os') for result in results: t.add_row([ @@ -91,13 +90,13 @@ def execute(cls, client, args): )]) if args['--disk'] or show_all: - results = cls.get_create_options(bmi_options, 'disk')[0] + results = self.get_create_options(bmi_options, 'disk')[0] t.add_row([results[0], listing( [item[0] for item in sorted(results[1])])]) if args['--nic'] or show_all: - results = cls.get_create_options(bmi_options, 'nic') + results = self.get_create_options(bmi_options, 'nic') for result in results: t.add_row([result[0], listing( @@ -105,8 +104,7 @@ def execute(cls, client, args): return t - @classmethod - def get_create_options(cls, bmi_options, section, pretty=True): + def get_create_options(self, bmi_options, section, pretty=True): """ This method can be used to parse the bare metal instance creation options into different sections. This can be useful for data validation as well as printing the options on a help screen. @@ -257,7 +255,8 @@ def _generate_windows_code(description): dual.append((str(int(item['capacity'])) + '_DUAL', item['price_id'])) else: - single.append((int(item['capacity']), item['price_id'])) + single.append((str(int(item['capacity'])), + item['price_id'])) return [('single nic', single), ('dual nic', dual)] @@ -266,7 +265,7 @@ def _generate_windows_code(description): class CreateBMCInstance(CLIRunnable): """ -usage: sl bmc create [--disk=DISK...] [--key=KEY...] [options] +usage: sl bmc create [--disk=SIZE...] [--key=KEY...] [options] Order/create a bare metal instance. See 'sl bmc create-options' for valid options @@ -289,13 +288,14 @@ class CreateBMCInstance(CLIRunnable): to the first available datacenter --dry-run, --test Do not create the instance, just get a quote --export=FILE Exports options to a template file + -d, --disk=SIZE... Disks. Can be specified multiple times -k KEY, --key=KEY SSH keys to assign to the root user. Can be specified multiple times. -n MBPS, --network=MBPS Network port speed in Mbps - --vlan_public=VLAN The ID of the public VLAN on which you want the CCI - placed. - --vlan_private=VLAN The ID of the private VLAN on which you want the CCI - placed. + --vlan_public=VLAN The ID of the public VLAN on which you want the + hardware placed. + --vlan_private=VLAN The ID of the private VLAN on which you want the + hardware placed. -t, --template=FILE A template file that defaults the command-line options using the long name in INI format @@ -304,10 +304,9 @@ class CreateBMCInstance(CLIRunnable): options = ['confirm'] required_params = ['--hostname', '--domain', '--cpu', '--memory', '--os'] - @classmethod - def execute(cls, client, args): + def execute(self, args): update_with_template_args(args) - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) # Disks will be a comma-separated list. Let's make it a real list. if isinstance(args.get('--disk'), str): @@ -317,7 +316,7 @@ def execute(cls, client, args): if isinstance(args.get('--key'), str): args['--key'] = args.get('--key').split(',') - cls._validate_args(args) + self._validate_args(args) bmi_options = mgr.get_bare_metal_create_options() @@ -328,9 +327,9 @@ def execute(cls, client, args): } # Validate the CPU/Memory combination and get the price ID - server_core = cls._get_cpu_and_memory_price_ids(bmi_options, - args['--cpu'], - args['--memory']) + server_core = self._get_cpu_and_memory_price_ids(bmi_options, + args['--cpu'], + args['--memory']) if server_core: order['server'] = server_core @@ -340,8 +339,8 @@ def execute(cls, client, args): order['hourly'] = args['--hourly'] # Convert the OS code back into a price ID - os_price = cls._get_price_id_from_options(bmi_options, 'os', - args['--os']) + os_price = self._get_price_id_from_options(bmi_options, 'os', + args['--os']) if os_price: order['os'] = os_price @@ -353,22 +352,22 @@ def execute(cls, client, args): # Set the disk size disk_prices = [] for disk in args.get('--disk'): - disk_price = cls._get_price_id_from_options(bmi_options, 'disk', - disk) + disk_price = self._get_price_id_from_options(bmi_options, 'disk', + disk) if disk_price: disk_prices.append(disk_price) if not disk_prices: - disk_prices.append(cls._get_default_value(bmi_options, 'disk0')) + disk_prices.append(self._get_default_value(bmi_options, 'disk0')) order['disks'] = disk_prices # Set the port speed port_speed = args.get('--network') or 10 - nic_price = cls._get_price_id_from_options(bmi_options, 'nic', - port_speed) + nic_price = self._get_price_id_from_options(bmi_options, 'nic', + port_speed) if nic_price: order['port_speed'] = nic_price @@ -379,8 +378,8 @@ def execute(cls, client, args): if args.get('--key'): keys = [] for key in args.get('--key'): - key_id = resolve_id(SshKeyManager(client).resolve_ids, key, - 'SshKey') + key_id = resolve_id(SshKeyManager(self.client).resolve_ids, + key, 'SshKey') keys.append(key_id) order['ssh_keys'] = keys @@ -441,9 +440,8 @@ def execute(cls, client, args): return output - @classmethod - def _validate_args(cls, args): - invalid_args = [k for k in cls.required_params if args.get(k) is None] + def _validate_args(self, args): + invalid_args = [k for k in self.required_params if args.get(k) is None] if invalid_args: raise ArgumentError('Missing required options: %s' % ','.join(invalid_args)) @@ -460,8 +458,7 @@ def _validate_args(cls, args): if not any([args['--hourly'], args['--monthly']]): raise ArgumentError('One of [--hourly | --monthly] is required') - @classmethod - def _get_cpu_and_memory_price_ids(cls, bmi_options, cpu_value, + def _get_cpu_and_memory_price_ids(self, bmi_options, cpu_value, memory_value): bmi_obj = BMCCreateOptions() price_id = None @@ -477,8 +474,7 @@ def _get_cpu_and_memory_price_ids(cls, bmi_options, cpu_value, return price_id - @classmethod - def _get_default_value(cls, bmi_options, option): + def _get_default_value(self, bmi_options, option): if option not in bmi_options['categories']: return @@ -492,12 +488,11 @@ def _get_default_value(cls, bmi_options, option): ]): return item['price_id'] - @classmethod - def _get_price_id_from_options(cls, bmi_options, option, value): + def _get_price_id_from_options(self, bmi_options, option, value): bmi_obj = BMCCreateOptions() price_id = None - for k, v in bmi_obj.get_create_options(bmi_options, option, False): + for _, v in bmi_obj.get_create_options(bmi_options, option, False): for item_options in v: if item_options[0] == value: price_id = item_options[1] @@ -519,9 +514,8 @@ class CancelInstance(CLIRunnable): action = 'cancel' options = ['confirm'] - @staticmethod - def execute(client, args): - hw = HardwareManager(client) + def execute(self, args): + hw = HardwareManager(self.client) hw_id = resolve_id( hw.resolve_ids, args.get(''), 'hardware') diff --git a/SoftLayer/CLI/modules/cci.py b/SoftLayer/CLI/modules/cci.py index 3af2c684e..b189b69f1 100755 --- a/SoftLayer/CLI/modules/cci.py +++ b/SoftLayer/CLI/modules/cci.py @@ -30,7 +30,7 @@ from os import linesep import os.path -from SoftLayer import CCIManager, SshKeyManager, DNSManager +from SoftLayer import CCIManager, SshKeyManager, DNSManager, DNSZoneNotFound from SoftLayer.utils import lookup from SoftLayer.CLI import ( CLIRunnable, Table, no_going_back, confirm, mb_to_gb, listing, @@ -74,9 +74,8 @@ class ListCCIs(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) tags = None if args.get('--tags'): @@ -104,7 +103,7 @@ def execute(client, args): guest = NestedDict(guest) t.add_row([ guest['id'], - guest['datacenter']['name'], + guest['datacenter']['name'] or blank(), guest['fullyQualifiedDomainName'], guest['maxCpu'], mb_to_gb(guest['maxMemory']), @@ -128,9 +127,8 @@ class CCIDetails(CLIRunnable): """ action = 'detail' - @staticmethod - def execute(client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) t = KeyValueTable(['Name', 'Value']) t.align['Name'] = 'r' t.align['Value'] = 'l' @@ -150,16 +148,16 @@ def execute(client, args): lookup(result, 'powerState', 'keyName'), lookup(result, 'powerState', 'name'), )]) - t.add_row(['datacenter', result['datacenter']['name']]) + t.add_row(['datacenter', result['datacenter']['name'] or blank()]) operating_system = lookup(result, 'operatingSystem', 'softwareLicense', - 'softwareDescription') + 'softwareDescription') or {} t.add_row([ 'os', FormattedItem( - operating_system['version'] or blank(), - operating_system['name'] or blank() + operating_system.get('version') or blank(), + operating_system.get('name') or blank() )]) t.add_row(['os_version', operating_system['version'] or blank()]) t.add_row(['cores', result['maxCpu']]) @@ -197,7 +195,7 @@ def execute(client, args): t.add_row(['tags', listing(tag_row, separator=',')]) if not result['privateNetworkOnlyFlag']: - ptr_domains = client['Virtual_Guest'].\ + ptr_domains = self.client['Virtual_Guest'].\ getReverseDomainRecords(id=cci_id) for ptr_domain in ptr_domains: @@ -225,13 +223,12 @@ class CreateOptionsCCI(CLIRunnable): action = 'create-options' options = ['datacenter', 'cpu', 'nic', 'disk', 'os', 'memory'] - @classmethod - def execute(cls, client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) result = cci.get_create_options() show_all = True - for opt_name in cls.options: + for opt_name in self.options: if args.get("--" + opt_name): show_all = False break @@ -338,7 +335,7 @@ def block_rows(blocks, name): class CreateCCI(CLIRunnable): """ -usage: sl cci create [--key=KEY...] [options] +usage: sl cci create [--disk=SIZE...] [--key=KEY...] [options] Order/create a CCI. See 'sl cci create-options' for valid options @@ -368,6 +365,7 @@ class CreateCCI(CLIRunnable): multiple times --like=IDENTIFIER Use the configuration from an existing CCI -n, --network=MBPS Network port speed in Mbps + -d, --disk=SIZE... Disks. Can be specified multiple times --private Forces the CCI to only have access the private network -t, --template=FILE A template file that defaults the command-line @@ -384,17 +382,20 @@ class CreateCCI(CLIRunnable): options = ['confirm'] required_params = ['--hostname', '--domain', '--cpu', '--memory'] - @classmethod - def execute(cls, client, args): + def execute(self, args): update_with_template_args(args) - cci = CCIManager(client) - cls._update_with_like_args(cci, args) + cci = CCIManager(self.client) + self._update_with_like_args(args) + + # Disks will be a comma-separated list. Let's make it a real list. + if isinstance(args.get('--disk'), str): + args['--disk'] = args.get('--disk').split(',') # SSH keys may be a comma-separated list. Let's make it a real list. if isinstance(args.get('--key'), str): args['--key'] = args.get('--key').split(',') - cls._validate_args(args) + self._validate_args(args) # Do not create CCI with --test or --export do_create = not (args['--export'] or args['--test']) @@ -402,7 +403,7 @@ def execute(cls, client, args): t = Table(['Item', 'cost']) t.align['Item'] = 'r' t.align['cost'] = 'r' - data = cls._parse_create_args(client, args) + data = self._parse_create_args(args) output = [] if args.get('--test'): @@ -468,9 +469,8 @@ def execute(cls, client, args): return output - @classmethod - def _validate_args(cls, args): - invalid_args = [k for k in cls.required_params if args.get(k) is None] + def _validate_args(self, args): + invalid_args = [k for k in self.required_params if args.get(k) is None] if invalid_args: raise ArgumentError('Missing required options: %s' % ','.join(invalid_args)) @@ -504,14 +504,14 @@ def _validate_args(cls, args): 'File does not exist [-u | --userfile] = %s' % args['--userfile']) - @staticmethod - def _update_with_like_args(cci, args): + def _update_with_like_args(self, args): """ Update arguments with options taken from a currently running CCI. :param CCIManager args: A CCIManager :param dict args: CLI arguments """ if args['--like']: + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.pop('--like'), 'CCI') like_details = cci.get_instance(cci_id) like_args = { @@ -554,8 +554,7 @@ def _update_with_like_args(cci, args): if args.get(key) in [None, False]: args[key] = value - @staticmethod - def _parse_create_args(client, args): + def _parse_create_args(self, args): """ Converts CLI arguments to arguments that can be passed into CCIManager.create_instance. @@ -568,6 +567,7 @@ def _parse_create_args(client, args): "hostname": args['--hostname'], "private": args['--private'], "dedicated": args['--dedicated'], + "disks": args['--disk'], "local_disk": True, } @@ -616,8 +616,8 @@ def _parse_create_args(client, args): if args.get('--key'): keys = [] for key in args.get('--key'): - key_id = resolve_id(SshKeyManager(client).resolve_ids, key, - 'SshKey') + key_id = resolve_id(SshKeyManager(self.client).resolve_ids, + key, 'SshKey') keys.append(key_id) data['ssh_keys'] = keys @@ -642,9 +642,8 @@ class ReadyCCI(CLIRunnable): """ action = 'ready' - @staticmethod - def execute(client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') ready = cci.wait_for_transaction(cci_id, int(args.get('--wait') or 0)) @@ -671,15 +670,14 @@ class ReloadCCI(CLIRunnable): action = 'reload' options = ['confirm'] - @staticmethod - def execute(client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') keys = [] if args.get('--key'): for key in args.get('--key'): - key_id = resolve_id(SshKeyManager(client).resolve_ids, key, - 'SshKey') + key_id = resolve_id(SshKeyManager(self.client).resolve_ids, + key, 'SshKey') keys.append(key_id) if args['--really'] or no_going_back(cci_id): cci.reload_instance(cci_id, args['--postinstall'], keys) @@ -697,9 +695,8 @@ class CancelCCI(CLIRunnable): action = 'cancel' options = ['confirm'] - @staticmethod - def execute(client, args): - cci = CCIManager(client) + def execute(self, args): + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') if args['--really'] or no_going_back(cci_id): cci.cancel_instance(cci_id) @@ -719,10 +716,9 @@ class CCIPowerOff(CLIRunnable): action = 'power-off' options = ['confirm'] - @classmethod - def execute(cls, client, args): - vg = client['Virtual_Guest'] - cci = CCIManager(client) + def execute(self, args): + vg = self.client['Virtual_Guest'] + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') if args['--really'] or confirm('This will power off the CCI with id ' '%s. Continue?' % cci_id): @@ -747,10 +743,9 @@ class CCIReboot(CLIRunnable): action = 'reboot' options = ['confirm'] - @classmethod - def execute(cls, client, args): - vg = client['Virtual_Guest'] - cci = CCIManager(client) + def execute(self, args): + vg = self.client['Virtual_Guest'] + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') if args['--really'] or confirm('This will reboot the CCI with id ' '%s. Continue?' % cci_id): @@ -772,10 +767,9 @@ class CCIPowerOn(CLIRunnable): """ action = 'power-on' - @classmethod - def execute(cls, client, args): - vg = client['Virtual_Guest'] - cci = CCIManager(client) + def execute(self, args): + vg = self.client['Virtual_Guest'] + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') vg.powerOn(id=cci_id) @@ -789,10 +783,9 @@ class CCIPause(CLIRunnable): action = 'pause' options = ['confirm'] - @classmethod - def execute(cls, client, args): - vg = client['Virtual_Guest'] - cci = CCIManager(client) + def execute(self, args): + vg = self.client['Virtual_Guest'] + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') if args['--really'] or confirm('This will pause the CCI with id ' @@ -810,10 +803,9 @@ class CCIResume(CLIRunnable): """ action = 'resume' - @classmethod - def execute(cls, client, args): - vg = client['Virtual_Guest'] - cci = CCIManager(client) + def execute(self, args): + vg = self.client['Virtual_Guest'] + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') vg.resume(id=cci_id) @@ -830,11 +822,10 @@ class NicEditCCI(CLIRunnable): """ action = 'nic-edit' - @classmethod - def execute(cls, client, args): + def execute(self, args): public = args['public'] - cci = CCIManager(client) + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') cci.change_port_speed(cci_id, public, args['--speed']) @@ -857,17 +848,14 @@ class CCIDNS(CLIRunnable): action = 'dns' options = ['confirm'] - @classmethod - def execute(cls, client, args): - args['--ttl'] = args['--ttl'] or DNSManager.DEFAULT_TTL + def execute(self, args): + args['--ttl'] = args['--ttl'] or 7200 if args['sync']: - return cls.dns_sync(client, args) + return self.dns_sync(args) - @staticmethod - def dns_sync(client, args): - from SoftLayer import DNSManager, DNSZoneNotFound - dns = DNSManager(client) - cci = CCIManager(client) + def dns_sync(self, args): + dns = DNSManager(self.client) + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') instance = cci.get_instance(cci_id) @@ -899,7 +887,7 @@ def sync_a_record(): def sync_ptr_record(): host_rec = instance['primaryIpAddress'].split('.')[-1] - ptr_domains = client['Virtual_Guest'].\ + ptr_domains = self.client['Virtual_Guest'].\ getReverseDomainRecords(id=instance['id'])[0] edit_ptr = None for ptr in ptr_domains['resourceRecords']: @@ -960,8 +948,7 @@ class EditCCI(CLIRunnable): """ action = 'edit' - @staticmethod - def execute(client, args): + def execute(self, args): data = {} if args['--userdata'] and args['--userfile']: @@ -985,7 +972,7 @@ def execute(client, args): data['hostname'] = args.get('--hostname') data['domain'] = args.get('--domain') - cci = CCIManager(client) + cci = CCIManager(self.client) cci_id = resolve_id(cci.resolve_ids, args.get(''), 'CCI') if not cci.edit(cci_id, **data): raise CLIAbort("Failed to update CCI") diff --git a/SoftLayer/CLI/modules/config.py b/SoftLayer/CLI/modules/config.py index e5ade3cd3..c40d24d8f 100644 --- a/SoftLayer/CLI/modules/config.py +++ b/SoftLayer/CLI/modules/config.py @@ -86,14 +86,13 @@ class Setup(CLIRunnable): """ action = 'setup' - @classmethod - def execute(cls, client, args): - settings = get_settings_from_client(client) + def execute(self, args): + settings = get_settings_from_client(self.client) # User Input # Ask for username while True: - username = cls.env.input( + username = self.env.input( 'Username [%s]: ' % settings['username']) \ or settings['username'] if username: @@ -101,7 +100,7 @@ def execute(cls, client, args): # Ask for 'secret' which can be api_key or their password while True: - secret = cls.env.getpass( + secret = self.env.getpass( 'API Key or Password [%s]: ' % settings['api_key']) \ or settings['api_key'] if secret: @@ -109,7 +108,8 @@ def execute(cls, client, args): # Ask for which endpoint they want to use while True: - endpoint_type = cls.env.input('Endpoint (public|private|custom): ') + endpoint_type = self.env.input( + 'Endpoint (public|private|custom): ') endpoint_type = endpoint_type.lower() if not endpoint_type: endpoint_url = API_PUBLIC_ENDPOINT @@ -121,7 +121,7 @@ def execute(cls, client, args): endpoint_url = API_PRIVATE_ENDPOINT break elif endpoint_type == 'custom': - endpoint_url = cls.env.input( + endpoint_url = self.env.input( 'Endpoint URL [%s]: ' % settings['endpoint_url'] ) or settings['endpoint_url'] break @@ -137,7 +137,7 @@ def execute(cls, client, args): path = args.get('--config') config_path = os.path.expanduser(path) - cls.env.out(format_output(config_table(settings))) + self.env.out(format_output(config_table(settings))) if not confirm('Are you sure you want to write settings to "%s"?' % config_path, default=True): @@ -174,7 +174,6 @@ class Show(CLIRunnable): """ action = 'show' - @classmethod - def execute(cls, client, args): - settings = get_settings_from_client(client) + def execute(self, args): + settings = get_settings_from_client(self.client) return config_table(settings) diff --git a/SoftLayer/CLI/modules/dns.py b/SoftLayer/CLI/modules/dns.py index d8a2e852d..e4b352d91 100755 --- a/SoftLayer/CLI/modules/dns.py +++ b/SoftLayer/CLI/modules/dns.py @@ -33,9 +33,8 @@ class DumpZone(CLIRunnable): """ action = "print" - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) zone_id = resolve_id(manager.resolve_ids, args[''], name='zone') try: return manager.dump_zone(zone_id) @@ -54,9 +53,8 @@ class CreateZone(CLIRunnable): """ action = 'create' - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) manager.create_zone(args['']) @@ -72,9 +70,8 @@ class DeleteZone(CLIRunnable): action = 'delete' options = ['confirm'] - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) zone_id = resolve_id(manager.resolve_ids, args[''], name='zone') if args['--really'] or no_going_back(args['']): @@ -97,16 +94,14 @@ class ListZones(CLIRunnable): """ action = 'list' - @classmethod - def execute(cls, client, args): + def execute(self, args): if args['']: - return cls.list_zone(client, args[''], args) + return self.list_zone(args) - return cls.list_all_zones(client) + return self.list_all_zones() - @staticmethod - def list_zone(client, zone, args): - manager = DNSManager(client) + def list_zone(self, args): + manager = DNSManager(self.client) t = Table([ "record", "type", @@ -123,7 +118,7 @@ def list_zone(client, zone, args): try: records = manager.get_records( zone_id, - type=args.get('--type'), + record_type=args.get('--type'), host=args.get('--record'), ttl=args.get('--ttl'), data=args.get('--data'), @@ -141,9 +136,8 @@ def list_zone(client, zone, args): return t - @staticmethod - def list_all_zones(client): - manager = DNSManager(client) + def list_all_zones(self): + manager = DNSManager(self.client) zones = manager.list_zones() t = Table([ "id", @@ -183,12 +177,11 @@ class AddRecord(CLIRunnable): """ action = 'add' - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) zone_id = resolve_id(manager.resolve_ids, args[''], name='zone') - args['--ttl'] = args['--ttl'] or DNSManager.DEFAULT_TTL + args['--ttl'] = args['--ttl'] or 7200 manager.create_record( zone_id, @@ -216,9 +209,8 @@ class EditRecord(CLIRunnable): """ action = 'edit' - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) zone_id = resolve_id(manager.resolve_ids, args[''], name='zone') try: @@ -252,9 +244,8 @@ class RecordRemove(CLIRunnable): action = 'remove' options = ['confirm'] - @staticmethod - def execute(client, args): - manager = DNSManager(client) + def execute(self, args): + manager = DNSManager(self.client) zone_id = resolve_id(manager.resolve_ids, args[''], name='zone') if args['--id']: diff --git a/SoftLayer/CLI/modules/firewall.py b/SoftLayer/CLI/modules/firewall.py index 0ab7b16a3..11a2b5c6a 100755 --- a/SoftLayer/CLI/modules/firewall.py +++ b/SoftLayer/CLI/modules/firewall.py @@ -22,9 +22,8 @@ class FWList(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - f = FirewallManager(client) + def execute(self, args): + f = FirewallManager(self.client) fwvlans = f.get_firewalls() t = Table(['vlan', 'type', 'features']) diff --git a/SoftLayer/CLI/modules/globalip.py b/SoftLayer/CLI/modules/globalip.py index 7eae77dc0..7873f90c6 100644 --- a/SoftLayer/CLI/modules/globalip.py +++ b/SoftLayer/CLI/modules/globalip.py @@ -31,15 +31,14 @@ class GlobalIpAssign(CLIRunnable): """ action = 'assign' - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) - id = mgr.resolve_global_ip_ids(args.get('')) - if not id: + global_ip_id = mgr.resolve_global_ip_ids(args.get('')) + if not global_ip_id: raise CLIAbort("Unable to find global IP record for " + args['']) - mgr.assign_global_ip(id, args['']) + mgr.assign_global_ip(global_ip_id, args['']) class GlobalIpCancel(CLIRunnable): @@ -52,13 +51,12 @@ class GlobalIpCancel(CLIRunnable): action = 'cancel' options = ['confirm'] - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) - id = mgr.resolve_global_ip_ids(args.get('')) + def execute(self, args): + mgr = NetworkManager(self.client) + global_ip_id = mgr.resolve_global_ip_ids(args.get('')) - if args['--really'] or no_going_back(id): - mgr.cancel_global_ip(id) + if args['--really'] or no_going_back(global_ip_id): + mgr.cancel_global_ip(global_ip_id) else: CLIAbort('Aborted') @@ -77,9 +75,8 @@ class GlobalIpCreate(CLIRunnable): action = 'create' options = ['confirm'] - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) version = 4 if args.get('--v6'): @@ -126,9 +123,8 @@ class GlobalIpList(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) t = Table([ 'id', 'ip', 'assigned', 'target' @@ -174,12 +170,11 @@ class GlobalIpUnassign(CLIRunnable): """ action = 'unassign' - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) - id = mgr.resolve_global_ip_ids(args.get('')) - if not id: + global_ip_id = mgr.resolve_global_ip_ids(args.get('')) + if not global_ip_id: raise CLIAbort("Unable to find global IP record for " + args['']) - mgr.unassign_global_ip(id) + mgr.unassign_global_ip(global_ip_id) diff --git a/SoftLayer/CLI/modules/help.py b/SoftLayer/CLI/modules/help.py index 11137a4a1..f490a76de 100644 --- a/SoftLayer/CLI/modules/help.py +++ b/SoftLayer/CLI/modules/help.py @@ -17,10 +17,9 @@ class Show(CLIRunnable): __doc__ = __doc__ action = None - @classmethod - def execute(cls, client, args): - parser = CommandParser(cls.env) - cls.env.load_module(args['']) + def execute(self, args): + parser = CommandParser(self.env) + self.env.load_module(args['']) if args['']: return parser.get_command_help(args[''], args['']) elif args['']: diff --git a/SoftLayer/CLI/modules/image.py b/SoftLayer/CLI/modules/image.py index dae619a1e..49b610ee0 100644 --- a/SoftLayer/CLI/modules/image.py +++ b/SoftLayer/CLI/modules/image.py @@ -25,22 +25,21 @@ class ListImages(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - account = client['Account'] + def execute(self, args): + account = self.client['Account'] neither = not any([args['--private'], args['--public']]) results = [] if args['--private'] or neither: - account = client['Account'] + account = self.client['Account'] mask = 'id,accountId,name,globalIdentifier,blockDevices,parentId' r = account.getPrivateBlockDeviceTemplateGroups(mask=mask) results.append(r) if args['--public'] or neither: - vgbd = client['Virtual_Guest_Block_Device_Template_Group'] + vgbd = self.client['Virtual_Guest_Block_Device_Template_Group'] r = vgbd.getPublicImages() results.append(r) diff --git a/SoftLayer/CLI/modules/iscsi.py b/SoftLayer/CLI/modules/iscsi.py index 216adbd60..04c1bf238 100644 --- a/SoftLayer/CLI/modules/iscsi.py +++ b/SoftLayer/CLI/modules/iscsi.py @@ -21,9 +21,8 @@ class ListISCSI(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - account = client['Account'] + def execute(self, args): + account = self.client['Account'] iscsi = account.getIscsiNetworkStorage( mask='eventCount,serviceResource[datacenter.name]') diff --git a/SoftLayer/CLI/modules/messaging.py b/SoftLayer/CLI/modules/messaging.py index b50fa1f31..ae2964c21 100644 --- a/SoftLayer/CLI/modules/messaging.py +++ b/SoftLayer/CLI/modules/messaging.py @@ -50,15 +50,17 @@ class ListAccounts(CLIRunnable): """ action = 'accounts-list' - @staticmethod - def execute(client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) accounts = manager.list_accounts() t = Table([ 'id', 'name', 'status' ]) for account in accounts: + if not account['nodes']: + continue + t.add_row([ account['nodes'][0]['accountName'], account['name'], @@ -77,9 +79,8 @@ class ListEndpoints(CLIRunnable): """ action = 'endpoints-list' - @staticmethod - def execute(client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) regions = manager.get_endpoints() t = Table([ @@ -104,9 +105,8 @@ class Ping(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'ping' - @staticmethod - def execute(client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) ok = manager.ping( datacenter=args['--datacenter'], network=args['--network']) if ok: @@ -173,9 +173,8 @@ class QueueList(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-list' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) queues = mq_client.get_queues()['items'] @@ -201,9 +200,8 @@ class QueueDetail(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-detail' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) queue = mq_client.get_queue(args['']) return queue_table(queue) @@ -224,9 +222,8 @@ class QueueCreate(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-add' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) tags = None if args.get('--tags'): @@ -256,9 +253,8 @@ class QueueModify(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-edit' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) tags = None if args.get('--tags'): @@ -286,9 +282,8 @@ class QueueDelete(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-remove' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) if args['']: @@ -311,9 +306,8 @@ class QueuePush(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-push' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) body = '' if args[''] is not None: @@ -337,9 +331,8 @@ class QueuePop(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'queue-pop' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) messages = mq_client.pop_message( @@ -366,9 +359,8 @@ class TopicList(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-list' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) topics = mq_client.get_topics()['items'] @@ -387,9 +379,8 @@ class TopicDetail(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-detail' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) topic = mq_client.get_topic(args['']) subscriptions = mq_client.get_subscriptions(args['']) @@ -408,9 +399,8 @@ class TopicCreate(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-add' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) tags = None if args.get('--tags'): @@ -438,9 +428,8 @@ class TopicDelete(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-remove' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) mq_client.delete_topic(args[''], args.get('--force')) @@ -461,9 +450,8 @@ class TopicSubscribe(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-subscribe' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) if args['--type'] == 'queue': subscription = mq_client.create_subscription( @@ -495,9 +483,8 @@ class TopicUnsubscribe(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-unsubscribe' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) mq_client.delete_subscription( @@ -515,9 +502,8 @@ class TopicPush(CLIRunnable): """ + COMMON_MESSAGING_ARGS action = 'topic-push' - @classmethod - def execute(cls, client, args): - manager = MessagingManager(client) + def execute(self, args): + manager = MessagingManager(self.client) mq_client = manager.get_connection(args['']) # the message body comes from the positional argument or stdin diff --git a/SoftLayer/CLI/modules/metadata.py b/SoftLayer/CLI/modules/metadata.py index f6b5727dd..93a081030 100644 --- a/SoftLayer/CLI/modules/metadata.py +++ b/SoftLayer/CLI/modules/metadata.py @@ -23,11 +23,25 @@ # :copyright: (c) 2013, SoftLayer Technologies, Inc. All rights reserved. # :license: MIT, see LICENSE for more details. -from SoftLayer import MetadataManager +from SoftLayer import MetadataManager, TransportError from SoftLayer.CLI import CLIRunnable, KeyValueTable, listing, CLIAbort -class BackendMacAddresses(CLIRunnable): +class MetaRunnable(CLIRunnable): + def execute(self, args): + try: + return self._execute(args) + except TransportError: + raise CLIAbort( + 'Cannot connect to the backend service address. Make sure ' + 'this command is being ran from a device on the backend ' + 'network.') + + def _execute(self, _): + pass + + +class BackendMacAddresses(MetaRunnable): """ usage: sl metadata backend_mac [options] @@ -35,12 +49,11 @@ class BackendMacAddresses(CLIRunnable): """ action = 'backend_mac' - @staticmethod - def execute(client, args): + def _execute(self, _): return listing(MetadataManager().get('backend_mac'), separator=',') -class Datacenter(CLIRunnable): +class Datacenter(MetaRunnable): """ usage: sl metadata datacenter [options] @@ -48,12 +61,11 @@ class Datacenter(CLIRunnable): """ action = 'datacenter' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('datacenter') -class DatacenterId(CLIRunnable): +class DatacenterId(MetaRunnable): """ usage: sl metadata datacenter_id [options] @@ -61,12 +73,11 @@ class DatacenterId(CLIRunnable): """ action = 'datacenter_id' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('datacenter_id') -class FrontendMacAddresses(CLIRunnable): +class FrontendMacAddresses(MetaRunnable): """ usage: sl metadata frontend_mac [options] @@ -74,12 +85,11 @@ class FrontendMacAddresses(CLIRunnable): """ action = 'frontend_mac' - @staticmethod - def execute(client, args): + def _execute(self, _): return listing(MetadataManager().get('frontend_mac'), separator=',') -class FullyQualifiedDomainName(CLIRunnable): +class FullyQualifiedDomainName(MetaRunnable): """ usage: sl metadata fqdn [options] @@ -87,12 +97,11 @@ class FullyQualifiedDomainName(CLIRunnable): """ action = 'fqdn' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('fqdn') -class Hostname(CLIRunnable): +class Hostname(MetaRunnable): """ usage: sl metadata hostname [options] @@ -100,12 +109,11 @@ class Hostname(CLIRunnable): """ action = 'hostname' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('hostname') -class Id(CLIRunnable): +class Id(MetaRunnable): """ usage: sl metadata id @@ -113,12 +121,11 @@ class Id(CLIRunnable): """ action = 'id' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('id') -class PrimaryBackendIpAddress(CLIRunnable): +class PrimaryBackendIpAddress(MetaRunnable): """ usage: sl metadata backend_ip [options] @@ -126,12 +133,11 @@ class PrimaryBackendIpAddress(CLIRunnable): """ action = 'backend_ip' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('primary_backend_ip') -class PrimaryIpAddress(CLIRunnable): +class PrimaryIpAddress(MetaRunnable): """ usage: sl metadata ip [options] @@ -139,12 +145,11 @@ class PrimaryIpAddress(CLIRunnable): """ action = 'ip' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('primary_ip') -class ProvisionState(CLIRunnable): +class ProvisionState(MetaRunnable): """ usage: sl metadata provision_state [options] @@ -152,12 +157,11 @@ class ProvisionState(CLIRunnable): """ action = 'provision_state' - @staticmethod - def execute(client, args): + def _execute(self, _): return MetadataManager().get('provision_state') -class Tags(CLIRunnable): +class Tags(MetaRunnable): """ usage: sl metadata tags [options] @@ -165,8 +169,7 @@ class Tags(CLIRunnable): """ action = 'tags' - @staticmethod - def execute(client, args): + def _execute(self, _): return listing(MetadataManager().get('tags'), separator=',') @@ -178,8 +181,7 @@ class UserMetadata(CLIRunnable): """ action = 'user_data' - @staticmethod - def execute(client, args): + def _execute(self, _): userdata = MetadataManager().get('user_data') if userdata: return userdata @@ -187,17 +189,15 @@ def execute(client, args): raise CLIAbort("No user metadata.") -class Network(CLIRunnable): +class Network(MetaRunnable): """ usage: sl metadata network ( | ) [options] Get details about the public or private network """ - """ details about either the public or private network """ action = 'network' - @staticmethod - def execute(client, args): + def _execute(self, args): meta = MetadataManager() if args['']: t = KeyValueTable(['Name', 'Value']) diff --git a/SoftLayer/CLI/modules/nas.py b/SoftLayer/CLI/modules/nas.py index 0bdf06dc8..0d0fe8f5c 100644 --- a/SoftLayer/CLI/modules/nas.py +++ b/SoftLayer/CLI/modules/nas.py @@ -23,9 +23,8 @@ class ListNAS(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - account = client['Account'] + def execute(self, args): + account = self.client['Account'] nas = account.getNasNetworkStorage( mask='eventCount,serviceResource[datacenter.name]') diff --git a/SoftLayer/CLI/modules/rwhois.py b/SoftLayer/CLI/modules/rwhois.py index 0cf43ea86..fa840c607 100644 --- a/SoftLayer/CLI/modules/rwhois.py +++ b/SoftLayer/CLI/modules/rwhois.py @@ -39,9 +39,8 @@ class RWhoisEdit(CLIRunnable): """ action = 'edit' - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) update = { 'abuse_email': args.get('--abuse'), @@ -75,9 +74,8 @@ class RWhoisShow(CLIRunnable): """ action = 'show' - @staticmethod - def execute(client, args): - mgr = NetworkManager(client) + def execute(self, args): + mgr = NetworkManager(self.client) result = mgr.get_rwhois() t = KeyValueTable(['Name', 'Value']) diff --git a/SoftLayer/CLI/modules/server.py b/SoftLayer/CLI/modules/server.py index 116fc7fe2..49f77d2da 100644 --- a/SoftLayer/CLI/modules/server.py +++ b/SoftLayer/CLI/modules/server.py @@ -63,9 +63,8 @@ class ListServers(CLIRunnable): """ action = 'list' - @staticmethod - def execute(client, args): - manager = HardwareManager(client) + def execute(self, args): + manager = HardwareManager(self.client) tags = None if args.get('--tags'): @@ -96,7 +95,7 @@ def execute(client, args): server = NestedDict(server) t.add_row([ server['id'], - server['datacenter']['name'], + server['datacenter']['name'] or blank(), server['fullyQualifiedDomainName'], server['processorPhysicalCoreAmount'], gb(server['memoryCapacity']), @@ -120,9 +119,8 @@ class ServerDetails(CLIRunnable): """ action = 'detail' - @staticmethod - def execute(client, args): - hardware = HardwareManager(client) + def execute(self, args): + hardware = HardwareManager(self.client) t = KeyValueTable(['Name', 'Value']) t.align['Name'] = 'r' @@ -136,12 +134,14 @@ def execute(client, args): t.add_row(['id', result['id']]) t.add_row(['hostname', result['fullyQualifiedDomainName']]) t.add_row(['status', result['hardwareStatus']['status']]) - t.add_row(['datacenter', result['datacenter']['name']]) + t.add_row(['datacenter', result['datacenter']['name'] or blank()]) t.add_row(['cores', result['processorPhysicalCoreAmount']]) t.add_row(['memory', gb(result['memoryCapacity'])]) t.add_row(['public_ip', result['primaryIpAddress'] or blank()]) t.add_row( ['private_ip', result['primaryBackendIpAddress'] or blank()]) + t.add_row(['ipmi_ip', + result['networkManagementIpAddress'] or blank()]) t.add_row([ 'os', FormattedItem( @@ -179,8 +179,8 @@ def execute(client, args): t.add_row(['tags', listing(tag_row, separator=',')]) if not result['privateNetworkOnlyFlag']: - ptr_domains = client['Hardware_Server'].getReverseDomainRecords( - id=hardware_id) + ptr_domains = self.client['Hardware_Server']\ + .getReverseDomainRecords(id=hardware_id) for ptr_domain in ptr_domains: for ptr in ptr_domain['resourceRecords']: @@ -205,16 +205,15 @@ class ServerReload(CLIRunnable): action = 'reload' options = ['confirm'] - @staticmethod - def execute(client, args): - hardware = HardwareManager(client) + def execute(self, args): + hardware = HardwareManager(self.client) hardware_id = resolve_id( hardware.resolve_ids, args.get(''), 'hardware') keys = [] if args.get('--key'): for key in args.get('--key'): - key_id = resolve_id(SshKeyManager(client).resolve_ids, key, - 'SshKey') + key_id = resolve_id(SshKeyManager(self.client).resolve_ids, + key, 'SshKey') keys.append(key_id) if args['--really'] or no_going_back(hardware_id): hardware.reload(hardware_id, args['--postinstall'], keys) @@ -237,16 +236,15 @@ class CancelServer(CLIRunnable): action = 'cancel' options = ['confirm'] - @classmethod - def execute(cls, client, args): - hw = HardwareManager(client) + def execute(self, args): + hw = HardwareManager(self.client) hw_id = resolve_id( hw.resolve_ids, args.get(''), 'hardware') comment = args.get('--comment') if not comment and not args['--really']: - comment = cls.env.input("(Optional) Add a cancellation comment:") + comment = self.env.input("(Optional) Add a cancellation comment:") reason = args.get('--reason') @@ -265,13 +263,12 @@ class ServerCancelReasons(CLIRunnable): action = 'cancel-reasons' - @staticmethod - def execute(client, args): + def execute(self, args): t = Table(['Code', 'Reason']) t.align['Code'] = 'r' t.align['Reason'] = 'l' - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) reasons = mgr.get_cancellation_reasons().iteritems() for code, reason in reasons: @@ -289,10 +286,9 @@ class ServerPowerOff(CLIRunnable): action = 'power-off' options = ['confirm'] - @classmethod - def execute(cls, client, args): - hw = client['Hardware_Server'] - mgr = HardwareManager(client) + def execute(self, args): + hw = self.client['Hardware_Server'] + mgr = HardwareManager(self.client) hw_id = resolve_id(mgr.resolve_ids, args.get(''), 'hardware') if args['--really'] or confirm('This will power off the server with ' @@ -315,10 +311,9 @@ class ServerReboot(CLIRunnable): action = 'reboot' options = ['confirm'] - @classmethod - def execute(cls, client, args): - hw = client['Hardware_Server'] - mgr = HardwareManager(client) + def execute(self, args): + hw = self.client['Hardware_Server'] + mgr = HardwareManager(self.client) hw_id = resolve_id(mgr.resolve_ids, args.get(''), 'hardware') if args['--really'] or confirm('This will power off the server with ' @@ -341,10 +336,9 @@ class ServerPowerOn(CLIRunnable): """ action = 'power-on' - @classmethod - def execute(cls, client, args): - hw = client['Hardware_Server'] - mgr = HardwareManager(client) + def execute(self, args): + hw = self.client['Hardware_Server'] + mgr = HardwareManager(self.client) hw_id = resolve_id(mgr.resolve_ids, args.get(''), 'hardware') hw.powerOn(id=hw_id) @@ -359,10 +353,9 @@ class ServerPowerCycle(CLIRunnable): action = 'power-cycle' options = ['confirm'] - @classmethod - def execute(cls, client, args): - hw = client['Hardware_Server'] - mgr = HardwareManager(client) + def execute(self, args): + hw = self.client['Hardware_Server'] + mgr = HardwareManager(self.client) hw_id = resolve_id(mgr.resolve_ids, args.get(''), 'hardware') @@ -386,11 +379,10 @@ class NicEditServer(CLIRunnable): """ action = 'nic-edit' - @classmethod - def execute(cls, client, args): + def execute(self, args): public = args['public'] - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) hw_id = resolve_id(mgr.resolve_ids, args.get(''), 'hardware') @@ -405,13 +397,12 @@ class ListChassisServer(CLIRunnable): """ action = 'list-chassis' - @staticmethod - def execute(client, args): + def execute(self, args): t = Table(['Code', 'Chassis']) t.align['Code'] = 'r' t.align['Chassis'] = 'l' - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) chassis = mgr.get_available_dedicated_server_packages() for chassis in chassis: @@ -442,9 +433,8 @@ class ServerCreateOptions(CLIRunnable): options = ['datacenter', 'cpu', 'memory', 'os', 'disk', 'nic', 'controller'] - @classmethod - def execute(cls, client, args): - mgr = HardwareManager(client) + def execute(self, args): + mgr = HardwareManager(self.client) t = KeyValueTable(['Name', 'Value']) t.align['Name'] = 'r' @@ -455,7 +445,7 @@ def execute(cls, client, args): ds_options = mgr.get_dedicated_server_create_options(chassis_id) show_all = True - for opt_name in cls.options: + for opt_name in self.options: if args.get("--" + opt_name): show_all = False break @@ -464,12 +454,12 @@ def execute(cls, client, args): show_all = True if args['--datacenter'] or show_all: - results = cls.get_create_options(ds_options, 'datacenter')[0] + results = self.get_create_options(ds_options, 'datacenter')[0] t.add_row([results[0], listing(sorted(results[1]))]) if args['--cpu'] or show_all: - results = cls.get_create_options(ds_options, 'cpu') + results = self.get_create_options(ds_options, 'cpu') cpu_table = Table(['id', 'description']) for result in sorted(results): @@ -477,13 +467,13 @@ def execute(cls, client, args): t.add_row(['cpu', cpu_table]) if args['--memory'] or show_all: - results = cls.get_create_options(ds_options, 'memory')[0] + results = self.get_create_options(ds_options, 'memory')[0] t.add_row([results[0], listing( item[0] for item in sorted(results[1]))]) if args['--os'] or show_all: - results = cls.get_create_options(ds_options, 'os') + results = self.get_create_options(ds_options, 'os') for result in results: t.add_row([ @@ -494,7 +484,7 @@ def execute(cls, client, args): )]) if args['--disk'] or show_all: - results = cls.get_create_options(ds_options, 'disk')[0] + results = self.get_create_options(ds_options, 'disk')[0] t.add_row([ results[0], @@ -504,22 +494,21 @@ def execute(cls, client, args): )]) if args['--nic'] or show_all: - results = cls.get_create_options(ds_options, 'nic') + results = self.get_create_options(ds_options, 'nic') for result in results: t.add_row([result[0], listing( item[0] for item in sorted(result[1],))]) if args['--controller'] or show_all: - results = cls.get_create_options(ds_options, 'disk_controller')[0] + results = self.get_create_options(ds_options, 'disk_controller')[0] t.add_row([results[0], listing( item[0] for item in sorted(results[1],))]) return t - @classmethod - def get_create_options(cls, ds_options, section, pretty=True): + def get_create_options(self, ds_options, section, pretty=True): """ This method can be used to parse the bare metal instance creation options into different sections. This can be useful for data validation as well as printing the options on a help screen. @@ -695,8 +684,7 @@ class CreateServer(CLIRunnable): usage: sl server create [--disk=SIZE...] [--key=KEY...] [options] Order/create a dedicated server. See 'sl server list-chassis' and -'sl server create-options' for valid options. --disk can be repeated to -order multiple disks. +'sl server create-options' for valid options. Required: -H --hostname=HOST Host portion of the FQDN. example: server @@ -718,10 +706,10 @@ class CreateServer(CLIRunnable): -k KEY, --key=KEY SSH keys to assign to the root user. Can be specified multiple times. --dry-run, --test Do not create the server, just get a quote - --vlan_public=VLAN The ID of the public VLAN on which you want the CCI - placed. - --vlan_private=VLAN The ID of the private VLAN on which you want the CCI - placed. + --vlan_public=VLAN The ID of the public VLAN on which you want the hardware + placed + --vlan_private=VLAN The ID of the private VLAN on which you want the + hardware placed -t, --template=FILE A template file that defaults the command-line options using the long name in INI format --export=FILE Exports options to a template file @@ -731,10 +719,9 @@ class CreateServer(CLIRunnable): required_params = ['--hostname', '--domain', '--chassis', '--cpu', '--memory', '--os'] - @classmethod - def execute(cls, client, args): + def execute(self, args): update_with_template_args(args) - mgr = HardwareManager(client) + mgr = HardwareManager(self.client) # Disks will be a comma-separated list. Let's make it a real list. if isinstance(args.get('--disk'), str): @@ -744,7 +731,7 @@ def execute(cls, client, args): if isinstance(args.get('--key'), str): args['--key'] = args.get('--key').split(',') - cls._validate_args(args) + self._validate_args(args) ds_options = mgr.get_dedicated_server_create_options(args['--chassis']) @@ -756,8 +743,8 @@ def execute(cls, client, args): } # Convert the OS code back into a price ID - os_price = cls._get_price_id_from_options(ds_options, 'os', - args['--os']) + os_price = self._get_price_id_from_options(ds_options, 'os', + args['--os']) if os_price: order['os'] = os_price @@ -766,39 +753,38 @@ def execute(cls, client, args): order['location'] = args['--datacenter'] or 'FIRST_AVAILABLE' order['server'] = args['--cpu'] - order['ram'] = cls._get_price_id_from_options(ds_options, 'memory', - int(args['--memory'])) + order['ram'] = self._get_price_id_from_options(ds_options, 'memory', + int(args['--memory'])) # Set the disk sizes disk_prices = [] disk_number = 0 for disk in args.get('--disk'): - disk_price = cls._get_disk_price(ds_options, disk, disk_number) + disk_price = self._get_disk_price(ds_options, disk, disk_number) disk_number += 1 if disk_price: disk_prices.append(disk_price) if not disk_prices: - disk_prices.append(cls._get_default_value(ds_options, 'disk0')) + disk_prices.append(self._get_default_value(ds_options, 'disk0')) order['disks'] = disk_prices # Set the disk controller price if args.get('--controller'): - dc_price = cls._get_price_id_from_options(ds_options, - 'disk_controller', - args.get('--controller')) + dc_price = self._get_price_id_from_options( + ds_options, 'disk_controller', args.get('--controller')) else: - dc_price = cls._get_price_id_from_options(ds_options, - 'disk_controller', - 'None') + dc_price = self._get_price_id_from_options(ds_options, + 'disk_controller', + 'None') order['disk_controller'] = dc_price # Set the port speed port_speed = args.get('--network') or '100' - nic_price = cls._get_price_id_from_options(ds_options, 'nic', - port_speed) + nic_price = self._get_price_id_from_options(ds_options, 'nic', + port_speed) if nic_price: order['port_speed'] = nic_price @@ -809,8 +795,8 @@ def execute(cls, client, args): if args.get('--key'): keys = [] for key in args.get('--key'): - key_id = resolve_id(SshKeyManager(client).resolve_ids, key, - 'SshKey') + key_id = resolve_id(SshKeyManager(self.client).resolve_ids, + key, 'SshKey') keys.append(key_id) order['ssh_keys'] = keys @@ -869,15 +855,13 @@ def execute(cls, client, args): return output - @classmethod - def _validate_args(cls, args): - invalid_args = [k for k in cls.required_params if args.get(k) is None] + def _validate_args(self, args): + invalid_args = [k for k in self.required_params if args.get(k) is None] if invalid_args: raise ArgumentError('Missing required options: %s' % ','.join(invalid_args)) - @classmethod - def _get_default_value(cls, ds_options, option): + def _get_default_value(self, ds_options, option): if option not in ds_options['categories']: return @@ -891,26 +875,24 @@ def _get_default_value(cls, ds_options, option): ]): return item['price_id'] - @classmethod - def _get_disk_price(cls, ds_options, value, number): + def _get_disk_price(self, ds_options, value, number): if not number: - return cls._get_price_id_from_options(ds_options, 'disk', value) + return self._get_price_id_from_options(ds_options, 'disk', value) # This will get the item ID for the matching identifier string, which # we can then use to get the price ID for our specific disk - item_id = cls._get_price_id_from_options(ds_options, 'disk', - value, True) + item_id = self._get_price_id_from_options(ds_options, 'disk', + value, True) key = 'disk' + str(number) if key in ds_options['categories']: for item in ds_options['categories'][key]['items']: if item['id'] == item_id: return item['price_id'] - @classmethod - def _get_price_id_from_options(cls, ds_options, option, value, + def _get_price_id_from_options(self, ds_options, option, value, item_id=False): ds_obj = ServerCreateOptions() - for k, v in ds_obj.get_create_options(ds_options, option, False): + for _, v in ds_obj.get_create_options(ds_options, option, False): for item_options in v: if item_options[0] == value: if not item_id: @@ -932,8 +914,7 @@ class EditServer(CLIRunnable): """ action = 'edit' - @staticmethod - def execute(client, args): + def execute(self, args): data = {} if args['--userdata'] and args['--userfile']: @@ -957,7 +938,7 @@ def execute(client, args): data['hostname'] = args.get('--hostname') data['domain'] = args.get('--domain') - hw = HardwareManager(client) + hw = HardwareManager(self.client) hw_id = resolve_id(hw.resolve_ids, args.get(''), 'hardware') if not hw.edit(hw_id, **data): diff --git a/SoftLayer/CLI/modules/sshkey.py b/SoftLayer/CLI/modules/sshkey.py index bd392c35b..b630b5449 100644 --- a/SoftLayer/CLI/modules/sshkey.py +++ b/SoftLayer/CLI/modules/sshkey.py @@ -41,8 +41,7 @@ class AddSshKey(CLIRunnable): """ action = 'add' - @staticmethod - def execute(client, args): + def execute(self, args): if args.get('--key'): key = args['--key'] else: @@ -50,7 +49,7 @@ def execute(client, args): key = f.read().strip() f.close() - mgr = SshKeyManager(client) + mgr = SshKeyManager(self.client) result = mgr.add_key(key, args['