From 83266f6f9645eb2f9f9bf81dfbcbb117526e463d Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Wed, 8 Jul 2020 17:22:26 -0500 Subject: [PATCH 1/4] #828 refactored hardware create to use the ordering manager primarily instead of doing price lookups on its own. Added option to specify a particular networking keyname instead of just a speed --- SoftLayer/CLI/formatting.py | 9 +- SoftLayer/CLI/hardware/create.py | 67 ++---- SoftLayer/CLI/hardware/create_options.py | 28 ++- SoftLayer/managers/hardware.py | 211 +++++++---------- tests/CLI/modules/server_tests.py | 32 +-- tests/managers/hardware_tests.py | 289 +++++------------------ 6 files changed, 193 insertions(+), 443 deletions(-) diff --git a/SoftLayer/CLI/formatting.py b/SoftLayer/CLI/formatting.py index b88fe056b..d02ceb1a1 100644 --- a/SoftLayer/CLI/formatting.py +++ b/SoftLayer/CLI/formatting.py @@ -301,8 +301,13 @@ def prettytable(self): else: msg = "Column (%s) doesn't exist to sort by" % self.sortby raise exceptions.CLIAbort(msg) - for a_col, alignment in self.align.items(): - table.align[a_col] = alignment + + if isinstance(self.align, str): + table.align = self.align + else: + # Required because PrettyTable has a strict setter function for alignment + for a_col, alignment in self.align.items(): + table.align[a_col] = alignment if self.title: table.title = self.title diff --git a/SoftLayer/CLI/hardware/create.py b/SoftLayer/CLI/hardware/create.py index 472cec2e2..eb83e6664 100644 --- a/SoftLayer/CLI/hardware/create.py +++ b/SoftLayer/CLI/hardware/create.py @@ -12,56 +12,40 @@ @click.command(epilog="See 'slcli server create-options' for valid options.") -@click.option('--hostname', '-H', - help="Host portion of the FQDN", - required=True, - prompt=True) -@click.option('--domain', '-D', - help="Domain portion of the FQDN", - required=True, - prompt=True) -@click.option('--size', '-s', - help="Hardware size", - required=True, - prompt=True) -@click.option('--os', '-o', help="OS install code", - required=True, - prompt=True) -@click.option('--datacenter', '-d', help="Datacenter shortname", - required=True, - prompt=True) -@click.option('--port-speed', - type=click.INT, - help="Port speeds", - required=True, - prompt=True) -@click.option('--billing', +@click.option('--hostname', '-H', required=True, prompt=True, + help="Host portion of the FQDN") +@click.option('--domain', '-D', required=True, prompt=True, + help="Domain portion of the FQDN") +@click.option('--size', '-s', required=True, prompt=True, + help="Hardware size") +@click.option('--os', '-o', required=True, prompt=True, + help="OS Key value") +@click.option('--datacenter', '-d', required=True, prompt=True, + help="Datacenter shortname") +@click.option('--port-speed', type=click.INT, required=True, prompt=True, + help="Port speeds. DEPRECATED, use --network") +@click.option('--no-public', is_flag=True, + help="Private network only. DEPRECATED, use --network.") +@click.option('--network', + help="Network Option Key.") +@click.option('--billing', default='hourly', show_default=True, type=click.Choice(['hourly', 'monthly']), - default='hourly', - show_default=True, help="Billing rate") -@click.option('--postinstall', '-i', help="Post-install script to download") -@helpers.multi_option('--key', '-k', - help="SSH keys to add to the root user") -@click.option('--no-public', - is_flag=True, - help="Private network only") -@helpers.multi_option('--extra', '-e', help="Extra options") -@click.option('--test', - is_flag=True, +@click.option('--postinstall', '-i', + help="Post-install script. Should be a HTTPS URL.") +@click.option('--test', is_flag=True, help="Do not actually create the server") -@click.option('--template', '-t', - is_eager=True, +@click.option('--template', '-t', is_eager=True, callback=template.TemplateCallback(list_args=['key']), help="A template file that defaults the command-line options", type=click.Path(exists=True, readable=True, resolve_path=True)) -@click.option('--export', - type=click.Path(writable=True, resolve_path=True), +@click.option('--export', type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") -@click.option('--wait', - type=click.INT, +@click.option('--wait', type=click.INT, help="Wait until the server is finished provisioning for up to " "X seconds before returning") +@helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") +@helpers.multi_option('--extra', '-e', help="Extra option Key Names") @environment.pass_env def cli(env, **args): """Order/create a dedicated server.""" @@ -86,6 +70,7 @@ def cli(env, **args): 'port_speed': args.get('port_speed'), 'no_public': args.get('no_public') or False, 'extras': args.get('extra'), + 'network': args.get('network') } # Do not create hardware server with --test or --export diff --git a/SoftLayer/CLI/hardware/create_options.py b/SoftLayer/CLI/hardware/create_options.py index 4eba88c84..2e9949d09 100644 --- a/SoftLayer/CLI/hardware/create_options.py +++ b/SoftLayer/CLI/hardware/create_options.py @@ -19,36 +19,42 @@ def cli(env): tables = [] # Datacenters - dc_table = formatting.Table(['datacenter', 'value']) - dc_table.sortby = 'value' + dc_table = formatting.Table(['Datacenter', 'Value'], title="Datacenters") + dc_table.sortby = 'Value' + dc_table.align = 'l' for location in options['locations']: dc_table.add_row([location['name'], location['key']]) tables.append(dc_table) # Presets - preset_table = formatting.Table(['size', 'value']) - preset_table.sortby = 'value' + preset_table = formatting.Table(['Size', 'Value'], title="Sizes") + preset_table.sortby = 'Value' + preset_table.align = 'l' for size in options['sizes']: preset_table.add_row([size['name'], size['key']]) tables.append(preset_table) # Operating systems - os_table = formatting.Table(['operating_system', 'value', 'operatingSystemReferenceCode ']) - os_table.sortby = 'value' + os_table = formatting.Table(['OS', 'Key', 'Reference Code'], title="Operating Systems") + os_table.sortby = 'Key' + os_table.align = 'l' for operating_system in options['operating_systems']: os_table.add_row([operating_system['name'], operating_system['key'], operating_system['referenceCode']]) tables.append(os_table) # Port speed - port_speed_table = formatting.Table(['port_speed', 'value']) - port_speed_table.sortby = 'value' + port_speed_table = formatting.Table(['Network', 'Speed', 'Key'], title="Network Options") + port_speed_table.sortby = 'Speed' + port_speed_table.align = 'l' + for speed in options['port_speeds']: - port_speed_table.add_row([speed['name'], speed['key']]) + port_speed_table.add_row([speed['name'], speed['speed'], speed['key']]) tables.append(port_speed_table) # Extras - extras_table = formatting.Table(['extras', 'value']) - extras_table.sortby = 'value' + extras_table = formatting.Table(['Extra Option', 'Value'], title="Extras") + extras_table.sortby = 'Value' + extras_table.align = 'l' for extra in options['extras']: extras_table.add_row([extra['name'], extra['key']]) tables.append(extras_table) diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py index b531910e4..cccf6b29f 100644 --- a/SoftLayer/managers/hardware.py +++ b/SoftLayer/managers/hardware.py @@ -22,7 +22,9 @@ EXTRA_CATEGORIES = ['pri_ipv6_addresses', 'static_ipv6_addresses', - 'sec_ip_addresses'] + 'sec_ip_addresses', + 'trusted_platform_module', + 'software_guard_extensions'] class HardwareManager(utils.IdentifierMixin, object): @@ -53,6 +55,7 @@ def __init__(self, client, ordering_manager=None): self.hardware = self.client['Hardware_Server'] self.account = self.client['Account'] self.resolvers = [self._get_ids_from_ip, self._get_ids_from_hostname] + self.package_keyname = 'BARE_METAL_SERVER' if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) else: @@ -337,16 +340,14 @@ def place_order(self, **kwargs): :param string os: operating system name :param int port_speed: Port speed in Mbps :param list ssh_keys: list of ssh key ids - :param string post_uri: The URI of the post-install script to run - after reload - :param boolean hourly: True if using hourly pricing (default). - False for monthly. - :param boolean no_public: True if this server should only have private - interfaces + :param string post_uri: The URI of the post-install script to run after reload + :param boolean hourly: True if using hourly pricing (default). False for monthly. + :param boolean no_public: True if this server should only have private interfaces :param list extras: List of extra feature names """ create_options = self._generate_create_dict(**kwargs) - return self.client['Product_Order'].placeOrder(create_options) + return self.ordering_manager.place_order(**create_options) + # return self.client['Product_Order'].placeOrder(create_options) def verify_order(self, **kwargs): """Verifies an order for a piece of hardware. @@ -354,7 +355,7 @@ def verify_order(self, **kwargs): See :func:`place_order` for a list of available options. """ create_options = self._generate_create_dict(**kwargs) - return self.client['Product_Order'].verifyOrder(create_options) + return self.ordering_manager.verify_order(**create_options) def get_cancellation_reasons(self): """Returns a dictionary of valid cancellation reasons. @@ -397,33 +398,27 @@ def get_create_options(self): 'key': preset['keyName'] }) - # Operating systems operating_systems = [] + port_speeds = [] + extras = [] for item in package['items']: - if item['itemCategory']['categoryCode'] == 'os': + category = item['itemCategory']['categoryCode'] + # Operating systems + if category == 'os': operating_systems.append({ 'name': item['softwareDescription']['longDescription'], 'key': item['keyName'], 'referenceCode': item['softwareDescription']['referenceCode'] }) - - # Port speeds - port_speeds = [] - for item in package['items']: - if all([item['itemCategory']['categoryCode'] == 'port_speed', - # Hide private options - not _is_private_port_speed_item(item), - # Hide unbonded options - _is_bonded(item)]): + # Port speeds + elif category == 'port_speed': port_speeds.append({ 'name': item['description'], - 'key': item['capacity'], + 'speed': item['capacity'], + 'key': item['keyName'] }) - - # Extras - extras = [] - for item in package['items']: - if item['itemCategory']['categoryCode'] in EXTRA_CATEGORIES: + # Extras + elif category in EXTRA_CATEGORIES: extras.append({ 'name': item['description'], 'key': item['keyName'] @@ -454,9 +449,7 @@ def _get_package(self): accountRestrictedActivePresets, regions[location[location[priceGroups]]] ''' - - package_keyname = 'BARE_METAL_SERVER' - package = self.ordering_manager.get_package_by_key(package_keyname, mask=mask) + package = self.ordering_manager.get_package_by_key(self.package_keyname, mask=mask) return package def _generate_create_dict(self, @@ -470,59 +463,66 @@ def _generate_create_dict(self, post_uri=None, hourly=True, no_public=False, - extras=None): + extras=None, + network=None): """Translates arguments into a dictionary for creating a server.""" extras = extras or [] package = self._get_package() - location = _get_location(package, location) - - prices = [] - for category in ['pri_ip_addresses', - 'vpn_management', - 'remote_management']: - prices.append(_get_default_price_id(package['items'], - option=category, - hourly=hourly, - location=location)) - - prices.append(_get_os_price_id(package['items'], os, - location=location)) - prices.append(_get_bandwidth_price_id(package['items'], - hourly=hourly, - no_public=no_public, - location=location)) - prices.append(_get_port_speed_price_id(package['items'], - port_speed, - no_public, - location=location)) + items = package.get('items', {}) + location_id = _get_location(package, location) + + key_names = [ + '1_IP_ADDRESS', + 'UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT', + 'REBOOT_KVM_OVER_IP' + ] + + # Operating System + key_names.append(os) + + # Bandwidth Options + key_names.append( + _get_bandwidth_key(items, hourly=hourly, no_public=no_public, location=location_id) + ) + + # Port Speed Options + # New option in v5.9.0 + if network: + key_names.append(network) + # Legacy Option, doesn't support bonded/redundant + else: + key_names.append( + _get_port_speed_key(items, port_speed, no_public, location=location_id) + ) + # Extras for extra in extras: - prices.append(_get_extra_price_id(package['items'], - extra, hourly, - location=location)) + key_names.append(extra) - hardware = { - 'hostname': hostname, - 'domain': domain, + extras = { + 'hardware': [{ + 'hostname': hostname, + 'domain': domain, + }] } - - order = { - 'hardware': [hardware], - 'location': location['keyname'], - 'prices': [{'id': price} for price in prices], - 'packageId': package['id'], - 'presetId': _get_preset_id(package, size), - 'useHourlyPricing': hourly, - } - if post_uri: - order['provisionScripts'] = [post_uri] + extras['provisionScripts'] = [post_uri] if ssh_keys: - order['sshKeys'] = [{'sshKeyIds': ssh_keys}] + extras['sshKeys'] = [{'sshKeyIds': ssh_keys}] + order = { + 'package_keyname': self.package_keyname, + 'location': location, + 'item_keynames': key_names, + 'complex_type': 'SoftLayer_Container_Product_Order_Hardware_Server', + 'hourly': hourly, + 'preset_keyname': size, + 'extras': extras, + 'quantity': 1, + } return order def _get_ids_from_hostname(self, hostname): @@ -745,78 +745,38 @@ def _get_extra_price_id(items, key_name, hourly, location): return price['id'] - raise SoftLayerError( - "Could not find valid price for extra option, '%s'" % key_name) - + raise SoftLayerError("Could not find valid price for extra option, '%s'" % key_name) -def _get_default_price_id(items, option, hourly, location): - """Returns a 'free' price id given an option.""" - for item in items: - if utils.lookup(item, 'itemCategory', 'categoryCode') != option: - continue - - for price in item['prices']: - if all([float(price.get('hourlyRecurringFee', 0)) == 0.0, - float(price.get('recurringFee', 0)) == 0.0, - _matches_billing(price, hourly), - _matches_location(price, location)]): - return price['id'] - - raise SoftLayerError( - "Could not find valid price for '%s' option" % option) - - -def _get_bandwidth_price_id(items, - hourly=True, - no_public=False, - location=None): - """Choose a valid price id for bandwidth.""" +def _get_bandwidth_key(items, hourly=True, no_public=False, location=None): + """Picks a valid Bandwidth Item, returns the KeyName""" + keyName = None # Prefer pay-for-use data transfer with hourly for item in items: capacity = float(item.get('capacity', 0)) # Hourly and private only do pay-as-you-go bandwidth - if any([utils.lookup(item, - 'itemCategory', - 'categoryCode') != 'bandwidth', + if any([utils.lookup(item, 'itemCategory', 'categoryCode') != 'bandwidth', (hourly or no_public) and capacity != 0.0, not (hourly or no_public) and capacity == 0.0]): continue + keyName = item['keyName'] for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue + return keyName - return price['id'] - - raise SoftLayerError( - "Could not find valid price for bandwidth option") - - -def _get_os_price_id(items, os, location): - """Returns the price id matching.""" - - for item in items: - if any([utils.lookup(item, 'itemCategory', 'categoryCode') != 'os', - utils.lookup(item, 'keyName') != os]): - continue - - for price in item['prices']: - if not _matches_location(price, location): - continue - - return price['id'] + raise SoftLayerError("Could not find valid price for bandwidth option") - raise SoftLayerError("Could not find valid price for os: '%s'" % os) - -def _get_port_speed_price_id(items, port_speed, no_public, location): +def _get_port_speed_key(items, port_speed, no_public, location): """Choose a valid price id for port speed.""" + keyName = None for item in items: if utils.lookup(item, 'itemCategory', 'categoryCode') != 'port_speed': continue @@ -826,12 +786,12 @@ def _get_port_speed_price_id(items, port_speed, no_public, location): _is_private_port_speed_item(item) != no_public, not _is_bonded(item)]): continue - + keyName = item['keyName'] for price in item['prices']: if not _matches_location(price, location): continue - return price['id'] + return keyName raise SoftLayerError( "Could not find valid price for port speed: '%s'" % port_speed) @@ -883,12 +843,3 @@ def _get_location(package, location): return region raise SoftLayerError("Could not find valid location for: '%s'" % location) - - -def _get_preset_id(package, size): - """Get the preset id given the keyName of the preset.""" - for preset in package['activePresets'] + package['accountRestrictedActivePresets']: - if preset['keyName'] == size or preset['id'] == size: - return preset['id'] - - raise SoftLayerError("Could not find valid size for: '%s'" % size) diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py index 605bd32bf..d016c7730 100644 --- a/tests/CLI/modules/server_tests.py +++ b/tests/CLI/modules/server_tests.py @@ -355,19 +355,10 @@ def test_create_options(self): result = self.run_command(['server', 'create-options']) self.assert_no_fail(result) - expected = [ - [{'datacenter': 'Washington 1', 'value': 'wdc01'}], - [{'size': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID', - 'value': 'S1270_8GB_2X1TBSATA_NORAID'}, - {'size': 'Dual Xeon Gold, 384GB Ram, 4x960GB SSD, RAID 10', - 'value': 'DGOLD_6140_384GB_4X960GB_SSD_SED_RAID_10'}], - [{'operating_system': 'Ubuntu / 14.04-64', - 'value': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', - 'operatingSystemReferenceCode ': 'UBUNTU_14_64'}], - [{'port_speed': '10 Mbps Public & Private Network Uplinks', - 'value': '10'}], - [{'extras': '1 IPv6 Address', 'value': '1_IPV6_ADDRESS'}]] - self.assertEqual(json.loads(result.output), expected) + output = json.loads(result.output) + self.assertEqual(output[0][0]['Value'], 'wdc01') + self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') + @mock.patch('SoftLayer.HardwareManager.place_order') def test_create_server(self, order_mock): @@ -391,21 +382,6 @@ def test_create_server(self, order_mock): self.assertEqual(json.loads(result.output), {'id': 98765, 'created': '2013-08-02 15:23:47'}) - def test_create_server_missing_required(self): - - # This is missing a required argument - result = self.run_command(['server', 'create', - # Note: no chassis id - '--hostname=test', - '--domain=example.com', - '--datacenter=TEST00', - '--network=100', - '--os=UBUNTU_12_64_MINIMAL', - ]) - - self.assertEqual(result.exit_code, 2) - self.assertIsInstance(result.exception, SystemExit) - @mock.patch('SoftLayer.CLI.template.export_to_template') def test_create_server_with_export(self, export_mock): if (sys.platform.startswith("win")): diff --git a/tests/managers/hardware_tests.py b/tests/managers/hardware_tests.py index f6a2445d1..2e3d7b3a5 100644 --- a/tests/managers/hardware_tests.py +++ b/tests/managers/hardware_tests.py @@ -7,6 +7,7 @@ import copy import mock +from pprint import pprint as pp import SoftLayer from SoftLayer import fixtures @@ -117,29 +118,29 @@ def test_reload(self): def test_get_create_options(self): options = self.hardware.get_create_options() - expected = { - 'extras': [{'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'}], - 'locations': [{'key': 'wdc01', 'name': 'Washington 1'}], - 'operating_systems': [{'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', - 'name': 'Ubuntu / 14.04-64', - 'referenceCode': 'UBUNTU_14_64'}], - 'port_speeds': [{ - 'key': '10', - 'name': '10 Mbps Public & Private Network Uplinks' - }], - 'sizes': [ - { - 'key': 'S1270_8GB_2X1TBSATA_NORAID', - 'name': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID' - }, - { - 'key': 'DGOLD_6140_384GB_4X960GB_SSD_SED_RAID_10', - 'name': 'Dual Xeon Gold, 384GB Ram, 4x960GB SSD, RAID 10' - } - ] + extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'} + locations = {'key': 'wdc01', 'name': 'Washington 1'} + operating_systems = { + 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', + 'name': 'Ubuntu / 14.04-64', + 'referenceCode': 'UBUNTU_14_64' + } + + port_speeds = { + 'key': '10', + 'name': '10 Mbps Public & Private Network Uplinks' + } + sizes = { + 'key': 'S1270_8GB_2X1TBSATA_NORAID', + 'name': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID' } - self.assertEqual(options, expected) + self.assertEqual(options['extras'][0], extras) + self.assertEqual(options['locations'][0], locations) + self.assertEqual(options['operating_systems'][0], operating_systems) + self.assertEqual(options['port_speeds'][0]['name'], port_speeds['name']) + self.assertEqual(options['sizes'][0], sizes) + def test_get_create_options_package_missing(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') @@ -148,18 +149,6 @@ def test_get_create_options_package_missing(self): ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware.get_create_options) self.assertEqual("Package BARE_METAL_SERVER does not exist", str(ex)) - def test_generate_create_dict_no_items(self): - packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') - packages_copy = copy.deepcopy( - fixtures.SoftLayer_Product_Package.getAllObjects) - packages_copy[0]['items'] = [] - packages.return_value = packages_copy - - ex = self.assertRaises(SoftLayer.SoftLayerError, - self.hardware._generate_create_dict, - location="wdc01") - self.assertIn("Could not find valid price", str(ex)) - def test_generate_create_dict_no_regions(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages_copy = copy.deepcopy( @@ -172,20 +161,6 @@ def test_generate_create_dict_no_regions(self): **MINIMAL_TEST_CREATE_ARGS) self.assertIn("Could not find valid location for: 'wdc01'", str(ex)) - def test_generate_create_dict_invalid_size(self): - args = { - 'size': 'UNKNOWN_SIZE', - 'hostname': 'unicorn', - 'domain': 'giggles.woo', - 'location': 'wdc01', - 'os': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', - 'port_speed': 10, - } - - ex = self.assertRaises(SoftLayer.SoftLayerError, - self.hardware._generate_create_dict, **args) - self.assertIn("Could not find valid size for: 'UNKNOWN_SIZE'", str(ex)) - def test_generate_create_dict(self): args = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', @@ -199,51 +174,59 @@ def test_generate_create_dict(self): 'post_uri': 'http://example.com/script.php', 'ssh_keys': [10], } - - expected = { + + package = 'BARE_METAL_SERVER' + location = 'wdc01' + item_keynames = [ + '1_IP_ADDRESS', + 'UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT', + 'REBOOT_KVM_OVER_IP', + 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', + 'BANDWIDTH_0_GB_2', + '10_MBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS', + '1_IPV6_ADDRESS' + ] + hourly = True + preset_keyname = 'S1270_8GB_2X1TBSATA_NORAID' + extras = { 'hardware': [{ 'domain': 'giggles.woo', 'hostname': 'unicorn', }], - 'location': 'WASHINGTON_DC', - 'packageId': 200, - 'presetId': 64, - 'prices': [{'id': 21}, - {'id': 420}, - {'id': 906}, - {'id': 37650}, - {'id': 1800}, - {'id': 272}, - {'id': 17129}], - 'useHourlyPricing': True, 'provisionScripts': ['http://example.com/script.php'], - 'sshKeys': [{'sshKeyIds': [10]}], + 'sshKeys' : [{'sshKeyIds': [10]}] } data = self.hardware._generate_create_dict(**args) - self.assertEqual(expected, data) + self.assertEqual(package, data['package_keyname']) + self.assertEqual(location, data['location']) + for keyname in item_keynames: + self.assertIn(keyname, data['item_keynames']) + self.assertEqual(extras, data['extras']) - @mock.patch('SoftLayer.managers.hardware.HardwareManager' - '._generate_create_dict') - def test_verify_order(self, create_dict): + + @mock.patch('SoftLayer.managers.ordering.OrderingManager.verify_order') + @mock.patch('SoftLayer.managers.hardware.HardwareManager._generate_create_dict') + def test_verify_order(self, create_dict, verify_order): create_dict.return_value = {'test': 1, 'verify': 1} + verify_order.return_value = {'test': 2} - self.hardware.verify_order(test=1, verify=1) + result = self.hardware.verify_order(test=1, verify=1) + self.assertEqual(2, result['test']) create_dict.assert_called_once_with(test=1, verify=1) - self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', - args=({'test': 1, 'verify': 1},)) + verify_order.assert_called_once_with(test=1, verify=1) - @mock.patch('SoftLayer.managers.hardware.HardwareManager' - '._generate_create_dict') - def test_place_order(self, create_dict): + @mock.patch('SoftLayer.managers.ordering.OrderingManager.place_order') + @mock.patch('SoftLayer.managers.hardware.HardwareManager._generate_create_dict') + def test_place_order(self, create_dict, place_order): create_dict.return_value = {'test': 1, 'verify': 1} - self.hardware.place_order(test=1, verify=1) - + place_order.return_value = {'test': 1} + result = self.hardware.place_order(test=1, verify=1) + self.assertEqual(1, result['test']) create_dict.assert_called_once_with(test=1, verify=1) - self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', - args=({'test': 1, 'verify': 1},)) + place_order.assert_called_once_with(test=1, verify=1) def test_cancel_hardware_without_reason(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') @@ -629,162 +612,6 @@ def test_get_hard_drive_empty(self): class HardwareHelperTests(testing.TestCase): - def test_get_extra_price_id_no_items(self): - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_extra_price_id, - [], 'test', True, None) - self.assertEqual("Could not find valid price for extra option, 'test'", str(ex)) - - def test_get_extra_price_mismatched(self): - items = [ - {'keyName': 'TEST', 'prices': [{'id': 1, 'locationGroupId': None, 'recurringFee': 99}]}, - {'keyName': 'TEST', 'prices': [{'id': 2, 'locationGroupId': 55, 'hourlyRecurringFee': 99}]}, - {'keyName': 'TEST', 'prices': [{'id': 3, 'locationGroupId': None, 'hourlyRecurringFee': 99}]}, - ] - location = { - 'location': { - 'location': { - 'priceGroups': [ - {'id': 50}, - {'id': 51} - ] - } - } - } - result = managers.hardware._get_extra_price_id(items, 'TEST', True, location) - self.assertEqual(3, result) - - def test_get_bandwidth_price_mismatched(self): - items = [ - {'itemCategory': {'categoryCode': 'bandwidth'}, - 'capacity': 100, - 'prices': [{'id': 1, 'locationGroupId': None, 'hourlyRecurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'bandwidth'}, - 'capacity': 100, - 'prices': [{'id': 2, 'locationGroupId': 55, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'bandwidth'}, - 'capacity': 100, - 'prices': [{'id': 3, 'locationGroupId': None, 'recurringFee': 99}] - }, - ] - location = { - 'location': { - 'location': { - 'priceGroups': [ - {'id': 50}, - {'id': 51} - ] - } - } - } - result = managers.hardware._get_bandwidth_price_id(items, False, False, location) - self.assertEqual(3, result) - - def test_get_os_price_mismatched(self): - items = [ - {'itemCategory': {'categoryCode': 'os'}, - 'keyName': 'OS_TEST', - 'prices': [{'id': 2, 'locationGroupId': 55, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'os'}, - 'keyName': 'OS_TEST', - 'prices': [{'id': 3, 'locationGroupId': None, 'recurringFee': 99}] - }, - ] - location = { - 'location': { - 'location': { - 'priceGroups': [ - {'id': 50}, - {'id': 51} - ] - } - } - } - result = managers.hardware._get_os_price_id(items, 'OS_TEST', location) - self.assertEqual(3, result) - - def test_get_default_price_id_item_not_first(self): - items = [{ - 'itemCategory': {'categoryCode': 'unknown', 'id': 325}, - 'keyName': 'UNKNOWN', - 'prices': [{'accountRestrictions': [], - 'currentPriceFlag': '', - 'hourlyRecurringFee': '10.0', - 'id': 1245172, - 'recurringFee': '1.0'}], - }] - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_default_price_id, - items, 'unknown', True, None) - self.assertEqual("Could not find valid price for 'unknown' option", str(ex)) - - def test_get_default_price_id_no_items(self): - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_default_price_id, - [], 'test', True, None) - self.assertEqual("Could not find valid price for 'test' option", str(ex)) - - def test_get_bandwidth_price_id_no_items(self): - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_bandwidth_price_id, - [], hourly=True, no_public=False) - self.assertEqual("Could not find valid price for bandwidth option", str(ex)) - - def test_get_os_price_id_no_items(self): - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_os_price_id, - [], 'UBUNTU_14_64', None) - self.assertEqual("Could not find valid price for os: 'UBUNTU_14_64'", str(ex)) - - def test_get_port_speed_price_id_no_items(self): - ex = self.assertRaises(SoftLayer.SoftLayerError, - managers.hardware._get_port_speed_price_id, - [], 10, True, None) - self.assertEqual("Could not find valid price for port speed: '10'", str(ex)) - - def test_get_port_speed_price_id_mismatch(self): - items = [ - {'itemCategory': {'categoryCode': 'port_speed'}, - 'capacity': 101, - 'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}], - 'prices': [{'id': 1, 'locationGroupId': None, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'port_speed'}, - 'capacity': 100, - 'attributes': [{'attributeTypeKeyName': 'IS_NOT_PRIVATE_NETWORK_ONLY'}], - 'prices': [{'id': 2, 'locationGroupId': 55, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'port_speed'}, - 'capacity': 100, - 'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}, {'attributeTypeKeyName': 'NON_LACP'}], - 'prices': [{'id': 3, 'locationGroupId': 55, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'port_speed'}, - 'capacity': 100, - 'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}], - 'prices': [{'id': 4, 'locationGroupId': 12, 'recurringFee': 99}] - }, - {'itemCategory': {'categoryCode': 'port_speed'}, - 'capacity': 100, - 'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}], - 'prices': [{'id': 5, 'locationGroupId': None, 'recurringFee': 99}] - }, - ] - location = { - 'location': { - 'location': { - 'priceGroups': [ - {'id': 50}, - {'id': 51} - ] - } - } - } - result = managers.hardware._get_port_speed_price_id(items, 100, True, location) - self.assertEqual(5, result) def test_matches_location(self): price = {'id': 1, 'locationGroupId': 51, 'recurringFee': 99} From b6933ebc0365a7ddb559e6989d916d0978fe5832 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Thu, 9 Jul 2020 17:26:12 -0500 Subject: [PATCH 2/4] #828 full unit test coverage --- SoftLayer/CLI/hardware/create.py | 47 ++++------- SoftLayer/managers/hardware.py | 27 +++--- tests/CLI/modules/server_tests.py | 25 +++++- tests/managers/hardware_tests.py | 132 ++++++++++++++++++++++++++---- 4 files changed, 168 insertions(+), 63 deletions(-) diff --git a/SoftLayer/CLI/hardware/create.py b/SoftLayer/CLI/hardware/create.py index eb83e6664..40fa871bc 100644 --- a/SoftLayer/CLI/hardware/create.py +++ b/SoftLayer/CLI/hardware/create.py @@ -12,38 +12,25 @@ @click.command(epilog="See 'slcli server create-options' for valid options.") -@click.option('--hostname', '-H', required=True, prompt=True, - help="Host portion of the FQDN") -@click.option('--domain', '-D', required=True, prompt=True, - help="Domain portion of the FQDN") -@click.option('--size', '-s', required=True, prompt=True, - help="Hardware size") -@click.option('--os', '-o', required=True, prompt=True, - help="OS Key value") -@click.option('--datacenter', '-d', required=True, prompt=True, - help="Datacenter shortname") -@click.option('--port-speed', type=click.INT, required=True, prompt=True, - help="Port speeds. DEPRECATED, use --network") -@click.option('--no-public', is_flag=True, - help="Private network only. DEPRECATED, use --network.") -@click.option('--network', - help="Network Option Key.") -@click.option('--billing', default='hourly', show_default=True, - type=click.Choice(['hourly', 'monthly']), +@click.option('--hostname', '-H', required=True, prompt=True, help="Host portion of the FQDN") +@click.option('--domain', '-D', required=True, prompt=True, help="Domain portion of the FQDN") +@click.option('--size', '-s', required=True, prompt=True, help="Hardware size") +@click.option('--os', '-o', required=True, prompt=True, help="OS Key value") +@click.option('--datacenter', '-d', required=True, prompt=True, help="Datacenter shortname") +@click.option('--port-speed', type=click.INT, help="Port speeds. DEPRECATED, use --network") +@click.option('--no-public', is_flag=True, help="Private network only. DEPRECATED, use --network.") +@click.option('--network', help="Network Option Key. Use instead of port-speed option") +@click.option('--billing', default='hourly', show_default=True, type=click.Choice(['hourly', 'monthly']), help="Billing rate") -@click.option('--postinstall', '-i', - help="Post-install script. Should be a HTTPS URL.") -@click.option('--test', is_flag=True, - help="Do not actually create the server") -@click.option('--template', '-t', is_eager=True, +@click.option('--postinstall', '-i', help="Post-install script. Should be a HTTPS URL.") +@click.option('--test', is_flag=True, help="Do not actually create the server") +@click.option('--template', '-t', is_eager=True, type=click.Path(exists=True, readable=True, resolve_path=True), callback=template.TemplateCallback(list_args=['key']), - help="A template file that defaults the command-line options", - type=click.Path(exists=True, readable=True, resolve_path=True)) + help="A template file that defaults the command-line options") @click.option('--export', type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") @click.option('--wait', type=click.INT, - help="Wait until the server is finished provisioning for up to " - "X seconds before returning") + help="Wait until the server is finished provisioning for up to X seconds before returning") @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @helpers.multi_option('--extra', '-e', help="Extra option Key Names") @environment.pass_env @@ -101,15 +88,13 @@ def cli(env, **args): if args['export']: export_file = args.pop('export') - template.export_to_template(export_file, args, - exclude=['wait', 'test']) + template.export_to_template(export_file, args, exclude=['wait', 'test']) env.fout('Successfully exported options to a template file.') return if do_create: if not (env.skip_confirmations or formatting.confirm( - "This action will incur charges on your account. " - "Continue?")): + "This action will incur charges on your account. Continue?")): raise exceptions.CLIAbort('Aborting dedicated server order.') result = mgr.place_order(**order) diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py index cccf6b29f..136b22c0a 100644 --- a/SoftLayer/managers/hardware.py +++ b/SoftLayer/managers/hardware.py @@ -729,23 +729,23 @@ def get_hard_drives(self, instance_id): return self.hardware.getHardDrives(id=instance_id) -def _get_extra_price_id(items, key_name, hourly, location): - """Returns a price id attached to item with the given key_name.""" +# def _get_extra_price_id(items, key_name, hourly, location): +# """Returns a price id attached to item with the given key_name.""" - for item in items: - if utils.lookup(item, 'keyName') != key_name: - continue +# for item in items: +# if utils.lookup(item, 'keyName') != key_name: +# continue - for price in item['prices']: - if not _matches_billing(price, hourly): - continue +# for price in item['prices']: +# if not _matches_billing(price, hourly): +# continue - if not _matches_location(price, location): - continue +# if not _matches_location(price, location): +# continue - return price['id'] +# return price['id'] - raise SoftLayerError("Could not find valid price for extra option, '%s'" % key_name) +# raise SoftLayerError("Could not find valid price for extra option, '%s'" % key_name) def _get_bandwidth_key(items, hourly=True, no_public=False, location=None): @@ -793,8 +793,7 @@ def _get_port_speed_key(items, port_speed, no_public, location): return keyName - raise SoftLayerError( - "Could not find valid price for port speed: '%s'" % port_speed) + raise SoftLayerError("Could not find valid price for port speed: '%s'" % port_speed) def _matches_billing(price, hourly): diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py index d016c7730..388fc310d 100644 --- a/tests/CLI/modules/server_tests.py +++ b/tests/CLI/modules/server_tests.py @@ -359,7 +359,6 @@ def test_create_options(self): self.assertEqual(output[0][0]['Value'], 'wdc01') self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') - @mock.patch('SoftLayer.HardwareManager.place_order') def test_create_server(self, order_mock): order_mock.return_value = { @@ -860,3 +859,27 @@ def test_billing(self): } self.assert_no_fail(result) self.assertEqual(json.loads(result.output), billing_json) + + def test_create_hw_export(self): + if(sys.platform.startswith("win")): + self.skipTest("Temp files do not work properly in Windows.") + with tempfile.NamedTemporaryFile() as config_file: + result = self.run_command(['hw', 'create', '--hostname=test', '--export', config_file.name, + '--domain=example.com', '--datacenter=TEST00', + '--network=TEST_NETWORK', '--os=UBUNTU_12_64', + '--size=S1270_8GB_2X1TBSATA_NORAID']) + self.assert_no_fail(result) + self.assertTrue('Successfully exported options to a template file.' in result.output) + contents = config_file.read().decode("utf-8") + self.assertIn('hostname=TEST', contents) + self.assertIn('size=S1270_8GB_2X1TBSATA_NORAID', contents) + + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_hw_no_confirm(self, confirm_mock): + confirm_mock.return_value = False + + result = self.run_command(['hw', 'create', '--hostname=test', '--size=S1270_8GB_2X1TBSATA_NORAID', + '--domain=example.com', '--datacenter=TEST00', + '--network=TEST_NETWORK', '--os=UBUNTU_12_64']) + + self.assertEqual(result.exit_code, 2) diff --git a/tests/managers/hardware_tests.py b/tests/managers/hardware_tests.py index 2e3d7b3a5..a61aeece0 100644 --- a/tests/managers/hardware_tests.py +++ b/tests/managers/hardware_tests.py @@ -7,7 +7,6 @@ import copy import mock -from pprint import pprint as pp import SoftLayer from SoftLayer import fixtures @@ -118,7 +117,7 @@ def test_reload(self): def test_get_create_options(self): options = self.hardware.get_create_options() - extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'} + extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'} locations = {'key': 'wdc01', 'name': 'Washington 1'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', @@ -141,7 +140,6 @@ def test_get_create_options(self): self.assertEqual(options['port_speeds'][0]['name'], port_speeds['name']) self.assertEqual(options['sizes'][0], sizes) - def test_get_create_options_package_missing(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages.return_value = [] @@ -174,7 +172,7 @@ def test_generate_create_dict(self): 'post_uri': 'http://example.com/script.php', 'ssh_keys': [10], } - + package = 'BARE_METAL_SERVER' location = 'wdc01' item_keynames = [ @@ -194,7 +192,7 @@ def test_generate_create_dict(self): 'hostname': 'unicorn', }], 'provisionScripts': ['http://example.com/script.php'], - 'sshKeys' : [{'sshKeyIds': [10]}] + 'sshKeys': [{'sshKeyIds': [10]}] } data = self.hardware._generate_create_dict(**args) @@ -204,7 +202,25 @@ def test_generate_create_dict(self): for keyname in item_keynames: self.assertIn(keyname, data['item_keynames']) self.assertEqual(extras, data['extras']) + self.assertEqual(preset_keyname, data['preset_keyname']) + self.assertEqual(hourly, data['hourly']) + def test_generate_create_dict_network_key(self): + args = { + 'size': 'S1270_8GB_2X1TBSATA_NORAID', + 'hostname': 'test1', + 'domain': 'test.com', + 'location': 'wdc01', + 'os': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', + 'network': 'NETWORKING', + 'hourly': True, + 'extras': ['1_IPV6_ADDRESS'], + 'post_uri': 'http://example.com/script.php', + 'ssh_keys': [10], + } + + data = self.hardware._generate_create_dict(**args) + self.assertIn('NETWORKING', data['item_keynames']) @mock.patch('SoftLayer.managers.ordering.OrderingManager.verify_order') @mock.patch('SoftLayer.managers.hardware.HardwareManager._generate_create_dict') @@ -613,17 +629,99 @@ def test_get_hard_drive_empty(self): class HardwareHelperTests(testing.TestCase): + def set_up(self): + self.items = [ + { + "itemCategory": {"categoryCode": "port_speed"}, + "capacity": 100, + "attributes": [ + {'attributeTypeKeyName': 'NON_LACP'}, + {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} + ], + "keyName": "ITEM_1", + "prices": [{"id": 1, "locationGroupId": 100}] + }, + { + "itemCategory": {"categoryCode": "port_speed"}, + "capacity": 200, + "attributes": [ + {'attributeTypeKeyName': 'YES_LACP'}, + {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} + ], + "keyName": "ITEM_2", + "prices": [{"id": 1, "locationGroupId": 151}] + }, + { + "itemCategory": {"categoryCode": "port_speed"}, + "capacity": 200, + "attributes": [ + {'attributeTypeKeyName': 'YES_LACP'}, + {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} + ], + "keyName": "ITEM_3", + "prices": [{"id": 1, "locationGroupId": 51}] + }, + { + "itemCategory": {"categoryCode": "bandwidth"}, + "capacity": 0.0, + "attributes": [], + "keyName": "HOURLY_BANDWIDTH_1", + "prices": [{"id": 1, "locationGroupId": 51, "hourlyRecurringFee": 1.0, "recurringFee": 1.0}] + }, + { + "itemCategory": {"categoryCode": "bandwidth"}, + "capacity": 10.0, + "attributes": [], + "keyName": "MONTHLY_BANDWIDTH_1", + "prices": [{"id": 1, "locationGroupId": 151, "recurringFee": 1.0}] + }, + { + "itemCategory": {"categoryCode": "bandwidth"}, + "capacity": 10.0, + "attributes": [], + "keyName": "MONTHLY_BANDWIDTH_2", + "prices": [{"id": 1, "locationGroupId": 51, "recurringFee": 1.0}] + }, + ] + self.location = {'location': {'location': {'priceGroups': [{'id': 50}, {'id': 51}]}}} + + def test_bandwidth_key(self): + result = managers.hardware._get_bandwidth_key(self.items, True, False, self.location) + self.assertEqual('HOURLY_BANDWIDTH_1', result) + result = managers.hardware._get_bandwidth_key(self.items, False, True, self.location) + self.assertEqual('HOURLY_BANDWIDTH_1', result) + result = managers.hardware._get_bandwidth_key(self.items, False, False, self.location) + self.assertEqual('MONTHLY_BANDWIDTH_2', result) + ex = self.assertRaises(SoftLayer.SoftLayerError, + managers.hardware._get_bandwidth_key, [], True, False, self.location) + self.assertEqual("Could not find valid price for bandwidth option", str(ex)) + + def test_port_speed_key(self): + result = managers.hardware._get_port_speed_key(self.items, 200, True, self.location) + self.assertEqual("ITEM_3", result) + + def test_port_speed_key_exception(self): + items = [] + location = {} + ex = self.assertRaises(SoftLayer.SoftLayerError, + managers.hardware._get_port_speed_key, items, 999, False, location) + self.assertEqual("Could not find valid price for port speed: '999'", str(ex)) + def test_matches_location(self): price = {'id': 1, 'locationGroupId': 51, 'recurringFee': 99} - location = { - 'location': { - 'location': { - 'priceGroups': [ - {'id': 50}, - {'id': 51} - ] - } - } - } - result = managers.hardware._matches_location(price, location) - self.assertTrue(result) + + self.assertTrue(managers.hardware._matches_location(price, self.location)) + price['locationGroupId'] = 99999 + self.assertFalse(managers.hardware._matches_location(price, self.location)) + + def test_is_bonded(self): + item_non_lacp = {'attributes': [{'attributeTypeKeyName': 'NON_LACP'}]} + item_lacp = {'attributes': [{'attributeTypeKeyName': 'YES_LACP'}]} + self.assertFalse(managers.hardware._is_bonded(item_non_lacp)) + self.assertTrue(managers.hardware._is_bonded(item_lacp)) + + def test_is_private(self): + item_private = {'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}]} + item_public = {'attributes': [{'attributeTypeKeyName': 'NOT_PRIVATE_NETWORK_ONLY'}]} + self.assertTrue(managers.hardware._is_private_port_speed_item(item_private)) + self.assertFalse(managers.hardware._is_private_port_speed_item(item_public)) From 6dd7041e24ae4cc15631443ae6dd471680e018c5 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Fri, 10 Jul 2020 16:26:30 -0500 Subject: [PATCH 3/4] #828 code cleanup --- SoftLayer/managers/hardware.py | 19 ------------------- docs/cli/hardware.rst | 2 ++ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py index 136b22c0a..956d33e3a 100644 --- a/SoftLayer/managers/hardware.py +++ b/SoftLayer/managers/hardware.py @@ -729,25 +729,6 @@ def get_hard_drives(self, instance_id): return self.hardware.getHardDrives(id=instance_id) -# def _get_extra_price_id(items, key_name, hourly, location): -# """Returns a price id attached to item with the given key_name.""" - -# for item in items: -# if utils.lookup(item, 'keyName') != key_name: -# continue - -# for price in item['prices']: -# if not _matches_billing(price, hourly): -# continue - -# if not _matches_location(price, location): -# continue - -# return price['id'] - -# raise SoftLayerError("Could not find valid price for extra option, '%s'" % key_name) - - def _get_bandwidth_key(items, hourly=True, no_public=False, location=None): """Picks a valid Bandwidth Item, returns the KeyName""" diff --git a/docs/cli/hardware.rst b/docs/cli/hardware.rst index 0cce23042..5ca325cb7 100644 --- a/docs/cli/hardware.rst +++ b/docs/cli/hardware.rst @@ -27,6 +27,8 @@ Interacting with Hardware Provides some basic functionality to order a server. `slcli order` has a more full featured method of ordering servers. This command only supports the FAST_PROVISION type. +As of v5.9.0 please use the `--network` option for specifying port speed, as that allows a bit more granularity for choosing your networking type. + .. click:: SoftLayer.CLI.hardware.credentials:cli :prog: hardware credentials :show-nested: From f4c256593ca8066b6f58040c7a542d96751d3ee6 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Fri, 10 Jul 2020 16:38:11 -0500 Subject: [PATCH 4/4] fixed a unit test --- tests/CLI/modules/server_tests.py | 37 +++---------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py index 388fc310d..f11d9d0a6 100644 --- a/tests/CLI/modules/server_tests.py +++ b/tests/CLI/modules/server_tests.py @@ -383,8 +383,7 @@ def test_create_server(self, order_mock): @mock.patch('SoftLayer.CLI.template.export_to_template') def test_create_server_with_export(self, export_mock): - if (sys.platform.startswith("win")): - self.skipTest("Test doesn't work in Windows") + result = self.run_command(['--really', 'server', 'create', '--size=S1270_8GB_2X1TBSATA_NORAID', '--hostname=test', @@ -397,24 +396,8 @@ def test_create_server_with_export(self, export_mock): fmt='raw') self.assert_no_fail(result) - self.assertIn("Successfully exported options to a template file.", - result.output) - export_mock.assert_called_with('/path/to/test_file.txt', - {'billing': 'hourly', - 'datacenter': 'TEST00', - 'domain': 'example.com', - 'extra': (), - 'hostname': 'test', - 'key': (), - 'os': 'UBUNTU_12_64', - 'port_speed': 100, - 'postinstall': None, - 'size': 'S1270_8GB_2X1TBSATA_NORAID', - 'test': False, - 'no_public': True, - 'wait': None, - 'template': None}, - exclude=['wait', 'test']) + self.assertIn("Successfully exported options to a template file.", result.output) + export_mock.assert_called_once() def test_edit_server_userdata_and_file(self): # Test both userdata and userfile at once @@ -860,20 +843,6 @@ def test_billing(self): self.assert_no_fail(result) self.assertEqual(json.loads(result.output), billing_json) - def test_create_hw_export(self): - if(sys.platform.startswith("win")): - self.skipTest("Temp files do not work properly in Windows.") - with tempfile.NamedTemporaryFile() as config_file: - result = self.run_command(['hw', 'create', '--hostname=test', '--export', config_file.name, - '--domain=example.com', '--datacenter=TEST00', - '--network=TEST_NETWORK', '--os=UBUNTU_12_64', - '--size=S1270_8GB_2X1TBSATA_NORAID']) - self.assert_no_fail(result) - self.assertTrue('Successfully exported options to a template file.' in result.output) - contents = config_file.read().decode("utf-8") - self.assertIn('hostname=TEST', contents) - self.assertIn('size=S1270_8GB_2X1TBSATA_NORAID', contents) - @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_hw_no_confirm(self, confirm_mock): confirm_mock.return_value = False