From 9661c63bcf57961da8ae9fcfadd844529275b38d Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Fri, 3 Nov 2017 14:58:02 -0500 Subject: [PATCH 1/6] VIRT-4404 : adding list functionality to dedicated host --- SoftLayer/CLI/dedicatedhost/__init__.py | 3 + SoftLayer/CLI/dedicatedhost/list.py | 65 +++++++++++++++ SoftLayer/CLI/routes.py | 4 + SoftLayer/managers/__init__.py | 2 + SoftLayer/managers/dh.py | 105 ++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 SoftLayer/CLI/dedicatedhost/__init__.py create mode 100644 SoftLayer/CLI/dedicatedhost/list.py create mode 100644 SoftLayer/managers/dh.py diff --git a/SoftLayer/CLI/dedicatedhost/__init__.py b/SoftLayer/CLI/dedicatedhost/__init__.py new file mode 100644 index 000000000..6082f2b3d --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/__init__.py @@ -0,0 +1,3 @@ +"""Dedicated Host.""" +# :license: MIT, see LICENSE for more details. + diff --git a/SoftLayer/CLI/dedicatedhost/list.py b/SoftLayer/CLI/dedicatedhost/list.py new file mode 100644 index 000000000..5c6406361 --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/list.py @@ -0,0 +1,65 @@ +import click + +import SoftLayer +from SoftLayer.CLI import columns as column_helper +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting +from SoftLayer.CLI import helpers + +COLUMNS = [ + column_helper.Column('datacenter', ('datacenter', 'name')), + column_helper.Column( + 'created_by', + ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), + column_helper.Column( + 'tags', + lambda server: formatting.tags(server.get('tagReferences')), + mask="tagReferences.tag.name"), +] + +DEFAULT_COLUMNS = [ + 'id', + 'name', + 'cpuCount', + 'diskCapacity', + 'memoryCapacity', + 'datacenter', + 'guestCount', +] + +@click.command() +@click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) +@helpers.multi_option('--tag', help='Filter by tags') +@click.option('--sortby', help='Column to sort by', + default='name', + show_default=True) +@click.option('--columns', + callback=column_helper.get_formatter(COLUMNS), + help='Columns to display. [options: %s]' + % ', '.join(column.name for column in COLUMNS), + default=','.join(DEFAULT_COLUMNS), + show_default=True) +@click.option('--datacenter', '-d', help='Datacenter shortname') +@click.option('--name', '-H', help='Host portion of the FQDN') +@click.option('--memory', '-m', help='Memory capacity in mebibytes' + , type=click.INT) +@click.option('--disk', '-d', help='Disk capacity') +@environment.pass_env +def cli(env, sortby, cpu, columns, datacenter, name, memory, disk, tag): + dh = SoftLayer.DHManager(env.client) + hosts = dh.list_instances(cpus=cpu, + datacenter=datacenter, + name=name, + memory=memory, + disk=disk, + tags=tag, + mask=columns.mask()) + + table = formatting.Table(columns.columns) + table.sortby =sortby + #print hosts + for host in hosts: + table.add_row([value or formatting.blank() + for value in columns.row(host)]) + + env.fout(table) \ No newline at end of file diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py index 2ff3379ac..c9d82b0fe 100644 --- a/SoftLayer/CLI/routes.py +++ b/SoftLayer/CLI/routes.py @@ -31,6 +31,9 @@ ('virtual:upgrade', 'SoftLayer.CLI.virt.upgrade:cli'), ('virtual:credentials', 'SoftLayer.CLI.virt.credentials:cli'), + ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'), + ('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'), + ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), ('cdn:list', 'SoftLayer.CLI.cdn.list:cli'), @@ -280,4 +283,5 @@ 'server': 'hardware', 'vm': 'virtual', 'vs': 'virtual', + 'dh': 'dedicatedhopst', } diff --git a/SoftLayer/managers/__init__.py b/SoftLayer/managers/__init__.py index f404d7b9b..edbad8f9b 100644 --- a/SoftLayer/managers/__init__.py +++ b/SoftLayer/managers/__init__.py @@ -9,6 +9,7 @@ """ from SoftLayer.managers.block import BlockStorageManager from SoftLayer.managers.cdn import CDNManager +from SoftLayer.managers.dh import DHManager from SoftLayer.managers.dns import DNSManager from SoftLayer.managers.file import FileStorageManager from SoftLayer.managers.firewall import FirewallManager @@ -29,6 +30,7 @@ __all__ = [ 'BlockStorageManager', 'CDNManager', + 'DHManager', 'DNSManager', 'FileStorageManager', 'FirewallManager', diff --git a/SoftLayer/managers/dh.py b/SoftLayer/managers/dh.py new file mode 100644 index 000000000..270660427 --- /dev/null +++ b/SoftLayer/managers/dh.py @@ -0,0 +1,105 @@ +""" + SoftLayer.vs + ~~~~~~~~~~~~ + DH Manager/helpers + + :license: MIT, see License for more details. +""" + +import logging + +from SoftLayer import utils + +LOGGER = logging.getLogger(__name__) + +class DHManager(utils.IdentifierMixin, object): + """Manages SoftLayer Dedicated Hosts. + + See product information here https://www.ibm.com/cloud/dedicated + + Example:: + # Initialize the DHManager. + # env variables. These can also be specified in ~/.softlayer, + # or passed directly to SoftLayer.Client() + # SL_USERNAME = YOUR_USERNAME + # SL_API_KEY = YOUR_API_KEY + import SoftLayer + client = SoftLayer.Client() + mgr = SoftLayer.VSManager(client) + + :param SoftLayer.API.BaseClient client: the client instance + :param SoftLayer.managers.OrderingManager ordering_manager: an optional + manager to handle ordering. + If none is provided, one will be + auto initialized. + """ + + #initializer + def __init__(self, client): + self.client = client + self.account = client['Account'] + self.host = client['Virtual_DedicatedHost'] + + def list_instances(self,tags=None, cpus=None, memory=None, name=None, + disk=None, datacenter=None, **kwargs): + """Retrieve a list of all dedicated hosts on the account + + Example:: + + :param list tags: filter based on list of tags + :param integer cpus: filter based on number of CPUS + :param integer memory: filter based on amount of memory + :param string hostname: filter based on hostname + :param string disk: filter based on disk + :param string datacenter: filter based on datacenter + :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) + :returns: Returns a list of dictionaries representing the matching + dedicated host. + + + + """ + if 'mask' not in kwargs: + items = [ + 'id', + 'name', + 'cpuCount', + 'diskCapacity', + 'memoryCapacity', + 'datacenter', + 'guestCount', + ] + kwargs['mask'] = "mask[%s]" % ','.join(items) + + call = 'getDedicatedHosts' + + _filter = utils.NestedDict(kwargs.get('filter') or {}) + if tags: + _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { + 'operation': 'in', + 'options': [{'name': 'data', 'value': tags}], + } + + if name: + _filter['dedicatedHosts']['name'] = ( + utils.query_filter(name) + ) + + if cpus: + _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) + + if disk: + _filter['dedicatedHosts']['diskCapacity'] = ( + utils.query_filter(disk)) + + if memory: + _filter['dedicatedHosts']['memoryCapacity'] = ( + utils.query_filter(memory)) + + if datacenter: + _filter['dedicatedHosts']['datacenter']['name'] = ( + utils.query_filter(datacenter)) + + kwargs['filter'] = _filter.to_dict() + func = getattr(self.account, call) + return func(**kwargs) \ No newline at end of file From 1a48944deaa942c18d909431ed809f612c459c62 Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Tue, 21 Nov 2017 15:23:05 -0600 Subject: [PATCH 2/6] VIRT-4404 : Adding dedicated host functionality --- SoftLayer/CLI/dedicatedhost/__init__.py | 1 - SoftLayer/CLI/dedicatedhost/create-options.py | 34 ++ SoftLayer/CLI/dedicatedhost/create.py | 99 ++++ SoftLayer/CLI/dedicatedhost/list.py | 32 +- SoftLayer/CLI/routes.py | 4 +- SoftLayer/fixtures/SoftLayer_Account.py | 14 +- .../fixtures/SoftLayer_Product_Package.py | 298 +++++++++- .../SoftLayer_Virtual_DedicatedHost.py | 8 + SoftLayer/managers/__init__.py | 4 +- SoftLayer/managers/dedicated_host.py | 294 ++++++++++ SoftLayer/managers/dh.py | 105 ---- tests/CLI/modules/dedicatedhost_tests.py | 163 ++++++ tests/managers/dedicated_host_tests.py | 524 ++++++++++++++++++ 13 files changed, 1450 insertions(+), 130 deletions(-) create mode 100644 SoftLayer/CLI/dedicatedhost/create-options.py create mode 100644 SoftLayer/CLI/dedicatedhost/create.py create mode 100644 SoftLayer/managers/dedicated_host.py delete mode 100644 SoftLayer/managers/dh.py create mode 100644 tests/CLI/modules/dedicatedhost_tests.py create mode 100644 tests/managers/dedicated_host_tests.py diff --git a/SoftLayer/CLI/dedicatedhost/__init__.py b/SoftLayer/CLI/dedicatedhost/__init__.py index 6082f2b3d..55d5d799a 100644 --- a/SoftLayer/CLI/dedicatedhost/__init__.py +++ b/SoftLayer/CLI/dedicatedhost/__init__.py @@ -1,3 +1,2 @@ """Dedicated Host.""" # :license: MIT, see LICENSE for more details. - diff --git a/SoftLayer/CLI/dedicatedhost/create-options.py b/SoftLayer/CLI/dedicatedhost/create-options.py new file mode 100644 index 000000000..8f07c5b3e --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/create-options.py @@ -0,0 +1,34 @@ +"""Options for ordering a dedicated host""" +# :license: MIT, see LICENSE for more details. + +import click +import SoftLayer + +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting + + +@click.command() +@environment.pass_env +def cli(env): + """host order options for a given dedicated host.""" + + mgr = SoftLayer.DedicatedHostManager(env.client) + options = mgr.get_create_options() + + tables = [] + + # Datacenters + dc_table = formatting.Table(['datacenter', 'value']) + dc_table.sortby = 'value' + for location in options['locations']: + dc_table.add_row([location['name'], location['key']]) + tables.append(dc_table) + + dh_table = formatting.Table(['dedicated Virtual Host', 'value']) + dh_table.sortby = 'value' + for item in options['dedicated_host']: + dh_table.add_row([item['name'], item['key']]) + tables.append(dh_table) + + env.fout(formatting.listing(tables, separator='\n')) diff --git a/SoftLayer/CLI/dedicatedhost/create.py b/SoftLayer/CLI/dedicatedhost/create.py new file mode 100644 index 000000000..19890cdd6 --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/create.py @@ -0,0 +1,99 @@ +"""Order/create a dedicated Host.""" +# :license: MIT, see LICENSE for more details. + +import click + +import SoftLayer +from SoftLayer.CLI import environment +from SoftLayer.CLI import exceptions +from SoftLayer.CLI import formatting +from SoftLayer.CLI import helpers + + +@click.command( + epilog="See 'slcli dedicatedhost create-options' for valid options.") +@click.option('--hostname', '-H', + help="Host portion of the FQDN", + required=True, + prompt=True) +@click.option('--router', '-r', + help="Router id", + show_default=True) +@click.option('--domain', '-D', + help="Domain portion of the FQDN", + required=True, + prompt=True) +@click.option('--datacenter', '-d', help="Datacenter shortname", + required=True, + prompt=True) +@click.option('--billing', + type=click.Choice(['hourly', 'monthly']), + default='hourly', + show_default=True, + help="Billing rate") +@click.option('--test', + is_flag=True, + help="Do not actually create the server") +@helpers.multi_option('--extra', '-e', help="Extra options") +@environment.pass_env +def cli(env, **args): + """Order/create a dedicated host.""" + mgr = SoftLayer.DedicatedHostManager(env.client) + + order = { + 'hostname': args['hostname'], + 'domain': args['domain'], + 'router': args['router'], + 'location': args.get('datacenter'), + 'hourly': args.get('billing') == 'hourly', + } + + do_create = not (args['test']) + + output = None + + if args.get('test'): + result = mgr.verify_order(**order) + + table = formatting.Table(['Item', 'cost']) + table.align['Item'] = 'r' + table.align['cost'] = 'r' + + for price in result['prices']: + if order['hourly']: + total = float(price.get('hourlyRecurringFee', 0.0)) + rate = "%.2f" % float(price['hourlyRecurringFee']) + else: + total = float(price.get('recurringFee', 0.0)) + rate = "%.2f" % float(price['recurringFee']) + + table.add_row([price['item']['description'], rate]) + + if order['hourly']: + table.add_row(['Total hourly cost', "%.2f" % total]) + else: + table.add_row(['Total monthly cost', "%.2f" % total]) + + output = [] + output.append(table) + output.append(formatting.FormattedItem( + '', + ' -- ! Prices reflected here are retail and do not ' + 'take account level discounts and are not guaranteed.')) + + if do_create: + if not (env.skip_confirmations or formatting.confirm( + "This action will incur charges on your account. " + "Continue?")): + raise exceptions.CLIAbort('Aborting dedicated host order.') + + result = mgr.place_order(**order) + + table = formatting.KeyValueTable(['name', 'value']) + table.align['name'] = 'r' + table.align['value'] = 'l' + table.add_row(['id', result['orderId']]) + table.add_row(['created', result['orderDate']]) + output = table + + env.fout(output) diff --git a/SoftLayer/CLI/dedicatedhost/list.py b/SoftLayer/CLI/dedicatedhost/list.py index 5c6406361..5b5bb8906 100644 --- a/SoftLayer/CLI/dedicatedhost/list.py +++ b/SoftLayer/CLI/dedicatedhost/list.py @@ -1,3 +1,6 @@ +"""List dedicated servers.""" +# :license: MIT, see LICENSE for more details. + import click import SoftLayer @@ -27,6 +30,7 @@ 'guestCount', ] + @click.command() @click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) @helpers.multi_option('--tag', help='Filter by tags') @@ -41,25 +45,25 @@ show_default=True) @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--name', '-H', help='Host portion of the FQDN') -@click.option('--memory', '-m', help='Memory capacity in mebibytes' - , type=click.INT) +@click.option('--memory', '-m', help='Memory capacity in mebibytes', + type=click.INT) @click.option('--disk', '-d', help='Disk capacity') @environment.pass_env def cli(env, sortby, cpu, columns, datacenter, name, memory, disk, tag): - dh = SoftLayer.DHManager(env.client) - hosts = dh.list_instances(cpus=cpu, - datacenter=datacenter, - name=name, - memory=memory, - disk=disk, - tags=tag, - mask=columns.mask()) - + """List dedicated host.""" + mgr = SoftLayer.DedicatedHostManager(env.client) + hosts = mgr.list_instances(cpus=cpu, + datacenter=datacenter, + hostname=name, + memory=memory, + disk=disk, + tags=tag, + mask=columns.mask()) table = formatting.Table(columns.columns) - table.sortby =sortby - #print hosts + table.sortby = sortby + for host in hosts: table.add_row([value or formatting.blank() for value in columns.row(host)]) - env.fout(table) \ No newline at end of file + env.fout(table) diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py index c9d82b0fe..4095dab13 100644 --- a/SoftLayer/CLI/routes.py +++ b/SoftLayer/CLI/routes.py @@ -33,6 +33,8 @@ ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'), ('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'), + ('dedicatedhost:create', 'SoftLayer.CLI.dedicatedhost.create:cli'), + ('dedicatedhost:create-options', 'SoftLayer.CLI.dedicatedhost.create-options:cli'), ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), @@ -283,5 +285,5 @@ 'server': 'hardware', 'vm': 'virtual', 'vs': 'virtual', - 'dh': 'dedicatedhopst', + 'dh': 'dedicatedhost', } diff --git a/SoftLayer/fixtures/SoftLayer_Account.py b/SoftLayer/fixtures/SoftLayer_Account.py index b8005e0ee..0f3a0a6a9 100644 --- a/SoftLayer/fixtures/SoftLayer_Account.py +++ b/SoftLayer/fixtures/SoftLayer_Account.py @@ -84,7 +84,6 @@ getHourlyVirtualGuests = [vs for vs in getVirtualGuests if vs['hourlyBillingFlag']] - getHardware = [{ 'id': 1000, 'metricTrackingObject': {'id': 3}, @@ -488,7 +487,6 @@ 'quoteKey': '1234test4321', }] - getOrders = [{ 'id': 1234, 'resourceType': '1 x 2.0 GHz Core', @@ -548,3 +546,15 @@ 'name': 'my first pool', 'metricTrackingObjectId': 10, }] + +getDedicatedHosts = [{ + 'datacenter': { + 'name': 'dal05' + }, + 'memoryCapacity': 242, + 'name': 'khnguyendh', + 'diskCapacity': 1200, + 'guestCount': 1, + 'cpuCount': 56, + 'id': 44701 +}] diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package.py b/SoftLayer/fixtures/SoftLayer_Product_Package.py index e458e0f76..044ba5ee1 100644 --- a/SoftLayer/fixtures/SoftLayer_Product_Package.py +++ b/SoftLayer/fixtures/SoftLayer_Product_Package.py @@ -155,7 +155,6 @@ 'setupFee': '0', 'sort': 99}]}] - ENTERPRISE_PACKAGE = { 'categories': [ {'categoryCode': 'storage_service_enterprise'} @@ -322,7 +321,6 @@ ] } - PERFORMANCE_PACKAGE = { 'categories': [ {'categoryCode': 'performance_storage_iscsi'}, @@ -419,7 +417,6 @@ ] } - SAAS_PACKAGE = { 'categories': [ {'categoryCode': 'storage_as_a_service'} @@ -669,7 +666,6 @@ ] } - getAllObjects = [{ 'activePresets': [{ 'description': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID', @@ -825,7 +821,6 @@ 'prices': [{'id': 611}], }] - getItemPrices = [ { 'currentPriceFlag': '', @@ -892,3 +887,296 @@ 'setupFee': '0', 'sort': 0 }] + +getAllObjectsDH = [{ + 'subDescription': 'Dedicated Host', + 'name': 'Dedicated Host', + 'items': [{ + 'prices': [{ + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '2099', + 'tierMinimumThreshold': '', + 'hourlyRecurringFee': '3.164', + 'oneTimeFee': '0', + 'currentPriceFlag': '', + 'id': 200269, + 'sort': 0, + 'onSaleFlag': '', + 'laborFee': '0', + 'locationGroupId': '', + 'quantity': '' + }, + { + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '2161.97', + 'tierMinimumThreshold': '', + 'hourlyRecurringFee': '3.258', + 'oneTimeFee': '0', + 'currentPriceFlag': '', + 'id': 200271, + 'sort': 0, + 'onSaleFlag': '', + 'laborFee': '0', + 'locationGroupId': 503, + 'quantity': '' + } + ], + 'itemCategory': { + 'categoryCode': 'dedicated_virtual_hosts' + }, + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }], + 'keyName': 'DEDICATED_HOST', + 'unitSize': '', + 'regions': [{ + 'location': { + 'locationPackageDetails': [{ + 'isAvailable': 1, + 'locationId': 265592, + 'packageId': 813 + }], + 'location': { + 'statusId': 2, + 'priceGroups': [{ + 'locationGroupTypeId': 82, + 'description': 'Location Group 2', + 'locationGroupType': { + 'name': 'PRICING' + }, + 'securityLevelId': '', + 'id': 503, + 'name': 'Location Group 2' + }], + 'id': 265592, + 'name': 'ams01', + 'longName': 'Amsterdam 1' + } + }, + 'keyname': 'AMSTERDAM', + 'description': 'AMS01 - Amsterdam', + 'sortOrder': 0 + }, + { + 'location': { + 'locationPackageDetails': [{ + 'isAvailable': 1, + 'locationId': 814994, + 'packageId': 813 + }], + 'location': { + 'statusId': 2, + 'priceGroups': [{ + 'locationGroupTypeId': 82, + 'description': 'Location Group 2', + 'locationGroupType': { + 'name': 'PRICING' + }, + 'securityLevelId': '', + 'id': 503, + 'name': 'Location Group 2' + }, + { + 'locationGroupTypeId': 82, + 'description': 'COS Cross Region - EU', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1303, + 'name': 'eu'}], + 'id': 814994, + 'name': 'ams03', + 'longName': 'Amsterdam 3'}}, + 'keyname': 'AMSTERDAM03', + 'description': 'AMS03 - Amsterdam', + 'sortOrder': 2}, + {'location': { + 'locationPackageDetails': [ + { + 'isAvailable': 1, + 'locationId': 138124, + 'packageId': 813}], + 'location': { + 'statusId': 2, + 'priceGroups': [ + { + 'locationGroupTypeId': 82, + 'description': 'CDN - North America - Akamai', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1463, + 'name': 'NORTH-AMERICA-AKAMAI'}], + 'id': 138124, + 'name': 'dal05', + 'longName': 'Dallas 5'}}, + 'keyname': 'DALLAS05', + 'description': 'DAL05 - Dallas', + 'sortOrder': 12}, + {'location': { + 'locationPackageDetails': [ + { + 'isAvailable': 1, + 'locationId': 2017603, + 'packageId': 813}], + 'location': { + 'statusId': 2, + 'priceGroups': [ + { + 'locationGroupTypeId': 82, + 'description': 'COS Regional - US East', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1305, + 'name': 'us-east'}], + 'id': 2017603, + 'name': 'wdc07', + 'longName': 'Washington 7'}}, + 'keyname': 'WASHINGTON07', + 'description': 'WDC07 - Washington, DC', + 'sortOrder': 76}], + 'firstOrderStepId': '', 'id': 813, 'isActive': 1, + 'description': 'Dedicated Host'}] + +verifyOrderDH = { + 'preTaxSetup': '0', + 'storageGroups': [], + 'postTaxRecurring': '3.164', + 'billingOrderItemId': '', + 'presetId': '', + 'hardware': [ + { + 'domain': 't.com', + 'hostname': 't', + 'bareMetalInstanceFlag': '', + 'hardwareStatusId': '', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + }, + 'networkVlanId': '' + }, + 'accountId': '' + } + ], + 'prices': [ + { + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '3.164', + 'oneTimeFee': '0', + 'id': 200269, + 'item': { + 'thirdPartyPolicyAssignments': [], + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'bundle': [ + { + 'category': { + 'categoryCode': 'dedicated_host_ram', + 'id': 850, + 'name': 'Dedicated Host RAM' + }, + 'itemPriceId': 200301, + 'itemPrice': { + 'itemId': 10199, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '0', + 'oneTimeFee': '0', + 'id': 200301, + 'laborFee': '0' + }, + 'bundleItemId': 10195, + 'bundleItem': { + 'units': 'CORE', + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }, + 'id': 41763 + }, + { + 'category': { + 'categoryCode': 'dedicated_host_disk', + 'id': 851, + 'name': 'Dedicated Host Disk' + }, + 'itemPriceId': 200299, + 'itemPrice': { + 'itemId': 10197, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '0', + 'oneTimeFee': '0', + 'id': 200299, + 'laborFee': '0' + }, + 'bundleItemId': 10195, + 'bundleItem': { + 'units': 'CORE', + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }, + 'id': 41761 + } + ], + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'units': 'CORE', + 'id': 10195 + }, + 'laborFee': '0', + 'categories': [ + { + 'categoryCode': 'dedicated_virtual_hosts', + 'id': 848, + 'name': 'Dedicated Host' + } + ] + } + ], + 'sendQuoteEmailFlag': '', + 'packageId': 813, + 'useHourlyPricing': True, + 'preTaxRecurringMonthly': '0', + 'message': '', + 'preTaxRecurring': '3.164', + 'primaryDiskPartitionId': '', + 'locationObject': { + 'id': 138124, + 'name': 'dal05', + 'longName': 'Dallas 5' + }, + 'taxCompletedFlag': False, + 'isManagedOrder': '', + 'imageTemplateId': '', + 'postTaxRecurringMonthly': '0', + 'resourceGroupTemplateId': '', + 'postTaxSetup': '0', + 'sshKeys': [], + 'location': '138124', + 'stepId': '', + 'proratedInitialCharge': '0', + 'totalRecurringTax': '0', + 'paymentType': '', + 'resourceGroupId': '', + 'sourceVirtualGuestId': '', + 'bigDataOrderFlag': False, + 'extendedHardwareTesting': '', + 'preTaxRecurringHourly': '3.164', + 'postTaxRecurringHourly': '3.164', + 'currencyShortName': 'USD', + 'containerSplHash': '000000003699c54000007f38ef8b0102', + 'proratedOrderTotal': '0', + 'serverCoreCount': '', + 'privateCloudOrderFlag': False, + 'totalSetupTax': '0', + 'quantity': 1 +} diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py index 926d84ed9..073992f15 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py @@ -8,3 +8,11 @@ 'cpuCount': 56, 'accountId': 1199911 } + + +getAvailableRouters = [ + {'hostname': 'bcr01a.dal05', 'id': 51218}, + {'hostname': 'bcr02a.dal05', 'id': 83361}, + {'hostname': 'bcr03a.dal05', 'id': 122762}, + {'hostname': 'bcr04a.dal05', 'id': 147566} +] diff --git a/SoftLayer/managers/__init__.py b/SoftLayer/managers/__init__.py index edbad8f9b..044da5a50 100644 --- a/SoftLayer/managers/__init__.py +++ b/SoftLayer/managers/__init__.py @@ -9,7 +9,7 @@ """ from SoftLayer.managers.block import BlockStorageManager from SoftLayer.managers.cdn import CDNManager -from SoftLayer.managers.dh import DHManager +from SoftLayer.managers.dedicated_host import DedicatedHostManager from SoftLayer.managers.dns import DNSManager from SoftLayer.managers.file import FileStorageManager from SoftLayer.managers.firewall import FirewallManager @@ -30,7 +30,7 @@ __all__ = [ 'BlockStorageManager', 'CDNManager', - 'DHManager', + 'DedicatedHostManager', 'DNSManager', 'FileStorageManager', 'FirewallManager', diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py new file mode 100644 index 000000000..5429f17d4 --- /dev/null +++ b/SoftLayer/managers/dedicated_host.py @@ -0,0 +1,294 @@ +""" + SoftLayer.dedicatedhost + ~~~~~~~~~~~~ + DH Manager/helpers + + :license: MIT, see License for more details. +""" + +import logging +import SoftLayer + +from SoftLayer.managers import ordering +from SoftLayer import utils + +LOGGER = logging.getLogger(__name__) + + +class DedicatedHostManager(utils.IdentifierMixin, object): + """Manages SoftLayer Dedicated Hosts. + + See product information here https://www.ibm.com/cloud/dedicated + + Example:: + # Initialize the DedicatedHostManager. + # env variables. These can also be specified in ~/.softlayer, + # or passed directly to SoftLayer.Client() + # SL_USERNAME = YOUR_USERNAME + # SL_API_KEY = YOUR_API_KEY + import SoftLayer + client = SoftLayer.Client() + mgr = SoftLayer.DedicatedHostManager(client) + + :param SoftLayer.API.BaseClient client: the client instance + :param SoftLayer.managers.OrderingManager ordering_manager: an optional + manager to handle ordering. + If none is provided, one will be + auto initialized. + """ + + def __init__(self, client, ordering_manager=None): + self.client = client + self.account = client['Account'] + self.host = client['Virtual_DedicatedHost'] + + if ordering_manager is None: + self.ordering_manager = ordering.OrderingManager(client) + + def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, + disk=None, datacenter=None, **kwargs): + """Retrieve a list of all dedicated hosts on the account + + Example:: + + :param list tags: filter based on list of tags + :param integer cpus: filter based on number of CPUS + :param integer memory: filter based on amount of memory + :param string hostname: filter based on hostname + :param string disk: filter based on disk + :param string datacenter: filter based on datacenter + :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) + :returns: Returns a list of dictionaries representing the matching + dedicated host. + + + + """ + if 'mask' not in kwargs: + items = [ + 'id', + 'name', + 'cpuCount', + 'diskCapacity', + 'memoryCapacity', + 'datacenter', + 'guestCount', + ] + kwargs['mask'] = "mask[%s]" % ','.join(items) + + call = 'getDedicatedHosts' + + _filter = utils.NestedDict(kwargs.get('filter') or {}) + if tags: + _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { + 'operation': 'in', + 'options': [{'name': 'data', 'value': tags}], + } + + if hostname: + _filter['dedicatedHosts']['name'] = ( + utils.query_filter(hostname) + ) + + if cpus: + _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) + + if disk: + _filter['dedicatedHosts']['diskCapacity'] = ( + utils.query_filter(disk)) + + if memory: + _filter['dedicatedHosts']['memoryCapacity'] = ( + utils.query_filter(memory)) + + if datacenter: + _filter['dedicatedHosts']['datacenter']['name'] = ( + utils.query_filter(datacenter)) + + kwargs['filter'] = _filter.to_dict() + func = getattr(self.account, call) + return func(**kwargs) + + def place_order(self, hostname, domain, location, hourly, router=None): + """Places an order for a dedicated host. + + See get_create_options() for valid arguments. + + :param string hostname: server hostname + :param string domain: server domain name + :param string location: location (datacenter) name + :param boolean hourly: True if using hourly pricing (default). + False for monthly. + :param int router: an optional value for selecting a backend router + """ + create_options = self._generate_create_dict(hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + return self.client['Product_Order'].placeOrder(create_options) + + def verify_order(self, hostname, domain, location, hourly, router=None): + """Verifies an order for a dedicated host. + + See :func:`place_order` for a list of available options. + """ + + create_options = self._generate_create_dict(hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + return self.client['Product_Order'].verifyOrder(create_options) + + def _generate_create_dict(self, + hostname=None, + router=None, + domain=None, + datacenter=None, + hourly=True): + """Translates args into a dictionary for creating a dedicated host.""" + package = self._get_package() + item = self._get_item(package) + location = self._get_location(package['regions'], datacenter) + price = self._get_price(item) + + if router is None: + routers = self._get_backend_router( + location['location']['locationPackageDetails']) + router = self._get_default_router(routers) + + hardware = { + 'hostname': hostname, + 'domain': domain, + 'primaryBackendNetworkComponent': { + 'router': { + 'id': router + } + } + } + + complex_type = "SoftLayer_Container_Product_Order_Virtual_DedicatedHost" + + order = { + "complexType": complex_type, + "quantity": 1, + 'location': location['keyname'], + 'packageId': package['id'], + 'prices': [{'id': price}], + 'hardware': [hardware], + 'useHourlyPricing': hourly, + } + return order + + def _get_package(self): + """Get the package related to simple dedicated host ordering.""" + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + + package_keyname = 'DEDICATED_HOST' + + package = self.ordering_manager.get_package_by_key(package_keyname, + mask=mask) + + if package is None: + raise SoftLayer.SoftLayerError("Ordering package not found") + + return package + + def _get_location(self, regions, datacenter): + """Get the longer key with a short location(datacenter) name.""" + for region in regions: + # list of locations + if region['location']['location']['name'] == datacenter: + return region + + raise SoftLayer.SoftLayerError("Could not find valid location for: '%s'" + % datacenter) + + def get_create_options(self): + """Returns valid options for ordering a dedicated host.""" + + package = self._get_package() + # Locations + locations = [] + for region in package['regions']: + locations.append({ + 'name': region['location']['location']['longName'], + 'key': region['location']['location']['name'], + }) + dedicated_host = [] + for item in package['items']: + if item['itemCategory']['categoryCode'] == \ + 'dedicated_virtual_hosts': + dedicated_host.append({ + 'name': item['description'], + 'key': item['id'], + }) + + return { + 'locations': locations, + 'dedicated_host': dedicated_host, + } + + def _get_price(self, package): + """Returns valid price for ordering a dedicated host.""" + + for price in package['prices']: + if price.get('locationGroupId') is '': + return price['id'] + + raise SoftLayer.SoftLayerError( + "Could not find valid price") + + def _get_item(self, package): + """Returns the item for ordering a dedicated host.""" + description = '56 Cores X 242 RAM X 1.2 TB' + + for item in package['items']: + if item['description'] == description: + return item + + raise SoftLayer.SoftLayerError("Could not find valid item for: '%s'" + % description) + + def _get_backend_router(self, locations): + """Returns valid router options for ordering a dedicated host.""" + mask = ''' + id, + hostname + ''' + + if locations is not None: + for location in locations: + if location['locationId'] is not None: + loc_id = location['locationId'] + host = { + 'cpuCount': 56, + 'memoryCapacity': 242, + 'diskCapacity': 1200, + 'datacenter': { + 'id': loc_id + } + } + routers = self.host.getAvailableRouters(host, mask=mask) + return routers + + raise SoftLayer.SoftLayerError("Could not find available routers") + + def _get_default_router(self, routers): + """Returns the default router for ordering a dedicated host.""" + for router in routers: + if router['id'] is not None: + return router['id'] + + raise SoftLayer.SoftLayerError("Could not find valid default router") diff --git a/SoftLayer/managers/dh.py b/SoftLayer/managers/dh.py deleted file mode 100644 index 270660427..000000000 --- a/SoftLayer/managers/dh.py +++ /dev/null @@ -1,105 +0,0 @@ -""" - SoftLayer.vs - ~~~~~~~~~~~~ - DH Manager/helpers - - :license: MIT, see License for more details. -""" - -import logging - -from SoftLayer import utils - -LOGGER = logging.getLogger(__name__) - -class DHManager(utils.IdentifierMixin, object): - """Manages SoftLayer Dedicated Hosts. - - See product information here https://www.ibm.com/cloud/dedicated - - Example:: - # Initialize the DHManager. - # env variables. These can also be specified in ~/.softlayer, - # or passed directly to SoftLayer.Client() - # SL_USERNAME = YOUR_USERNAME - # SL_API_KEY = YOUR_API_KEY - import SoftLayer - client = SoftLayer.Client() - mgr = SoftLayer.VSManager(client) - - :param SoftLayer.API.BaseClient client: the client instance - :param SoftLayer.managers.OrderingManager ordering_manager: an optional - manager to handle ordering. - If none is provided, one will be - auto initialized. - """ - - #initializer - def __init__(self, client): - self.client = client - self.account = client['Account'] - self.host = client['Virtual_DedicatedHost'] - - def list_instances(self,tags=None, cpus=None, memory=None, name=None, - disk=None, datacenter=None, **kwargs): - """Retrieve a list of all dedicated hosts on the account - - Example:: - - :param list tags: filter based on list of tags - :param integer cpus: filter based on number of CPUS - :param integer memory: filter based on amount of memory - :param string hostname: filter based on hostname - :param string disk: filter based on disk - :param string datacenter: filter based on datacenter - :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) - :returns: Returns a list of dictionaries representing the matching - dedicated host. - - - - """ - if 'mask' not in kwargs: - items = [ - 'id', - 'name', - 'cpuCount', - 'diskCapacity', - 'memoryCapacity', - 'datacenter', - 'guestCount', - ] - kwargs['mask'] = "mask[%s]" % ','.join(items) - - call = 'getDedicatedHosts' - - _filter = utils.NestedDict(kwargs.get('filter') or {}) - if tags: - _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { - 'operation': 'in', - 'options': [{'name': 'data', 'value': tags}], - } - - if name: - _filter['dedicatedHosts']['name'] = ( - utils.query_filter(name) - ) - - if cpus: - _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) - - if disk: - _filter['dedicatedHosts']['diskCapacity'] = ( - utils.query_filter(disk)) - - if memory: - _filter['dedicatedHosts']['memoryCapacity'] = ( - utils.query_filter(memory)) - - if datacenter: - _filter['dedicatedHosts']['datacenter']['name'] = ( - utils.query_filter(datacenter)) - - kwargs['filter'] = _filter.to_dict() - func = getattr(self.account, call) - return func(**kwargs) \ No newline at end of file diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py new file mode 100644 index 000000000..8f0ae0e5b --- /dev/null +++ b/tests/CLI/modules/dedicatedhost_tests.py @@ -0,0 +1,163 @@ +""" + SoftLayer.tests.CLI.modules.dedicatedhosts_tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :license: MIT, see LICENSE for more details. +""" +import json +import mock +import SoftLayer + +from SoftLayer.fixtures import SoftLayer_Product_Package +from SoftLayer.fixtures import SoftLayer_Virtual_DedicatedHost + +from SoftLayer import testing + + +class DedicatedHostsTests(testing.TestCase): + def set_up(self): + self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) + + def test_list_dedicated_hosts(self): + result = self.run_command(['dedicatedhost', 'list']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + [{ + 'cpuCount': 56, + 'datacenter': 'dal05', + 'diskCapacity': 1200, + 'guestCount': 1, + 'id': 44701, + 'memoryCapacity': 242, + 'name': 'khnguyendh' + }] + ) + + def test_create_options(self): + mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock.return_value = SoftLayer_Product_Package.getAllObjectsDH + + result = self.run_command(['dedicatedhost', 'create-options']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), [ + [{"value": "ams01", "datacenter": "Amsterdam 1"}, + {"value": "ams03", "datacenter": "Amsterdam 3"}, + {"value": "dal05", "datacenter": "Dallas 5"}, + {"value": "wdc07", "datacenter": "Washington 7"}], [ + {"dedicated Virtual Host": "56 Cores X 242 RAM X 1.2 TB", + "value": 10195}]]) + + def test_create(self): + SoftLayer.CLI.formatting.confirm = mock.Mock() + SoftLayer.CLI.formatting.confirm.return_value = True + mock_package_obj = \ + self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = \ + SoftLayer_Product_Package.getAllObjectsDH + mock_package_routers = \ + self.set_mock('SoftLayer_Virtual_DedicatedHost', + 'getAvailableRouters') + mock_package_routers.return_value = \ + SoftLayer_Virtual_DedicatedHost.getAvailableRouters + + result = self.run_command(['dedicatedhost', 'create', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=hourly']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), + {'created': '2013-08-01 15:23:45', 'id': 1234}) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': u'host', + 'domain': u'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', + args=args) + + def test_create_verify(self): + SoftLayer.CLI.formatting.confirm = mock.Mock() + SoftLayer.CLI.formatting.confirm.return_value = True + mock_package_obj = \ + self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = \ + SoftLayer_Product_Package.getAllObjectsDH + mock_package_routers = \ + self.set_mock('SoftLayer_Virtual_DedicatedHost', + 'getAvailableRouters') + mock_package_routers.return_value = \ + SoftLayer_Virtual_DedicatedHost.getAvailableRouters + mock_package = \ + self.set_mock('SoftLayer_Product_Order', 'verifyOrder') + mock_package.return_value = \ + SoftLayer_Product_Package.verifyOrderDH + + result = self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=hourly']) + self.assert_no_fail(result) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': u'host', + 'domain': u'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', + args=args) + + result = self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=monthly']) + self.assert_no_fail(result) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': 'host', + 'domain': 'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', + args=args) diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py new file mode 100644 index 000000000..ddcfd1ba7 --- /dev/null +++ b/tests/managers/dedicated_host_tests.py @@ -0,0 +1,524 @@ +""" + SoftLayer.tests.managers.dedicated_host_tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :license: MIT, see LICENSE for more details. +""" +import mock +import SoftLayer + +from SoftLayer import exceptions +from SoftLayer import fixtures +from SoftLayer import testing + + +class DedicatedHostTests(testing.TestCase): + def set_up(self): + self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) + + def test_list_instances(self): + results = self.dedicated_host.list_instances() + + self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) + self.assert_called_with('SoftLayer_Account', 'getDedicatedHosts') + + def test_list_instances_with_filters(self): + results = self.dedicated_host.list_instances( + tags=['tag1', 'tag2'], + cpus=2, + memory=1, + hostname='hostname', + datacenter='dal05', + disk=1 + ) + self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) + + def test_place_order(self): + create_dict = self.dedicated_host._generate_create_dict = mock.Mock() + + values = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + create_dict.return_value = values + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + self.dedicated_host.place_order(hostname=hostname, + domain=domain, + location=location, + hourly=hourly) + + create_dict.assert_called_once_with(hostname=hostname, + router=None, + domain=domain, + datacenter=location, + hourly=True) + + self.assert_called_with('SoftLayer_Product_Order', + 'placeOrder', + args=(values,)) + + def test_verify_order(self): + create_dict = self.dedicated_host._generate_create_dict = mock.Mock() + + values = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + create_dict.return_value = values + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + self.dedicated_host.verify_order(hostname=hostname, + domain=domain, + location=location, + hourly=hourly) + + create_dict.assert_called_once_with(hostname=hostname, + router=None, + domain=domain, + datacenter=location, + hourly=True) + + self.assert_called_with('SoftLayer_Product_Order', + 'verifyOrder', + args=(values,)) + + def test_generate_create_dict_without_router(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + self.dedicated_host._get_backend_router = mock.Mock() + self.dedicated_host._get_backend_router.return_value = self \ + ._get_routers_sample() + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + results = self.dedicated_host._generate_create_dict(hostname=hostname, + domain=domain, + datacenter=location, + hourly=hourly) + + testResults = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + + self.assertEqual(results, testResults) + + def test_generate_create_dict_with_router(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + + location = 'ams01' + router = 55901 + hostname = 'test' + domain = 'test.com' + hourly = True + + results = self.dedicated_host._generate_create_dict( + hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + testResults = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 55901 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': + 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + + self.assertEqual(results, testResults) + + def test_get_package(self): + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + self.dedicated_host.ordering_manager = mock.Mock() + + self.dedicated_host.ordering_manager.get_package_by_key.return_value = \ + "test" + + package = self.dedicated_host._get_package() + + package_keyname = 'DEDICATED_HOST' + + self.assertEqual('test', package) + self.dedicated_host.ordering_manager.get_package_by_key. \ + assert_called_once_with(package_keyname, mask=mask) + + def test_get_package_no_package_found(self): + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + self.dedicated_host.ordering_manager = mock.Mock() + + self.dedicated_host.ordering_manager.get_package_by_key.return_value = \ + None + + package_keyname = 'DEDICATED_HOST' + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_package) + + self.dedicated_host.ordering_manager.get_package_by_key. \ + assert_called_once_with(package_keyname, mask=mask) + + def test_get_location(self): + regions = [{ + "location": { + "location": { + "name": "dal05", + } + } + }] + + region = { + 'location': + { + 'location': { + 'name': 'dal05', + } + } + } + + testing = self.dedicated_host._get_location(regions, 'dal05') + + self.assertEqual(testing, region) + + def test_get_location_no_location_found(self): + regions = [{ + "location": { + "location": { + "name": "dal05", + } + } + }] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_location, regions, 'dal10') + + def test_get_create_options(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + + results = { + 'dedicated_host': [ + { + 'key': 10195, + 'name': '56 Cores X 242 RAM X 1.2 TB' + } + ], + 'locations': [ + { + 'key': 'ams01', + 'name': 'Amsterdam 1' + } + ] + } + + self.assertEqual(self.dedicated_host.get_create_options(), results) + + def test_get_price(self): + package = self._get_package() + item = package['items'][0] + price_id = 200269 + + self.assertEqual(self.dedicated_host._get_price(item), price_id) + + def test_get_price_no_price_found(self): + package = self._get_package() + package['items'][0]['prices'][0]['locationGroupId'] = 33 + item = package['items'][0] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_price, item) + + def test_get_item(self): + """Returns the item for ordering a dedicated host.""" + package = self._get_package() + + item = { + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195, + 'itemCategory': { + 'categoryCode': 'dedicated_virtual_hosts' + }, + 'prices': [ + { + 'currentPriceFlag': '', + 'hourlyRecurringFee': '3.164', + 'id': 200269, + 'itemId': 10195, + 'laborFee': '0', + 'locationGroupId': '', + 'onSaleFlag': '', + 'oneTimeFee': '0', + 'quantity': '', + 'recurringFee': '2099', + 'setupFee': '0', + 'sort': 0, + 'tierMinimumThreshold': '' + } + ] + } + + self.assertEqual(self.dedicated_host._get_item(package), item) + + def test_get_item_no_item_found(self): + package = self._get_package() + + package['items'][0]['description'] = 'not found' + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_item, package) + + def test_get_backend_router(self): + location = [ + { + 'isAvailable': 1, + 'locationId': 138124, + 'packageId': 813 + } + ] + + locId = location[0]['locationId'] + + mask = ''' + id, + hostname + ''' + + host = { + 'cpuCount': 56, + 'memoryCapacity': 242, + 'diskCapacity': 1200, + 'datacenter': { + 'id': locId + } + } + + self.dedicated_host.host = mock.Mock() + + routers = self.dedicated_host.host.getAvailableRouters.return_value = \ + self._get_routers_sample() + + routers_test = self.dedicated_host._get_backend_router(location) + + self.assertEqual(routers, routers_test) + self.dedicated_host.host.getAvailableRouters. \ + assert_called_once_with(host, mask=mask) + + def test_get_backend_router_no_routers_found(self): + location = [] + + self.dedicated_host.host = mock.Mock() + + routers_test = self.dedicated_host._get_backend_router + + self.assertRaises(exceptions.SoftLayerError, routers_test, location) + + def test_get_default_router(self): + routers = self._get_routers_sample() + + router = 51218 + + router_test = self.dedicated_host._get_default_router(routers) + + self.assertEqual(router_test, router) + + def test_get_default_router_no_router_found(self): + routers = [] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_default_router, routers) + + def _get_routers_sample(self): + routers = [ + { + 'hostname': 'bcr01a.dal05', + 'id': 51218 + }, + { + 'hostname': 'bcr02a.dal05', + 'id': 83361 + }, + { + 'hostname': 'bcr03a.dal05', + 'id': 122762 + }, + { + 'hostname': 'bcr04a.dal05', + 'id': 147566 + } + ] + + return routers + + def _get_package(self): + package = { + "items": [ + { + "prices": [ + { + "itemId": 10195, + "setupFee": "0", + "recurringFee": "2099", + "tierMinimumThreshold": "", + "hourlyRecurringFee": "3.164", + "oneTimeFee": "0", + "currentPriceFlag": "", + "id": 200269, + "sort": 0, + "onSaleFlag": "", + "laborFee": "0", + "locationGroupId": "", + "quantity": "" + } + ], + "itemCategory": { + "categoryCode": "dedicated_virtual_hosts" + }, + "description": "56 Cores X 242 RAM X 1.2 TB", + "id": 10195 + } + ], + "regions": [ + { + "location": { + "locationPackageDetails": [ + { + "isAvailable": 1, + "locationId": 265592, + "packageId": 813 + } + ], + "location": { + "statusId": 2, + "priceGroups": [ + { + "locationGroupTypeId": 82, + "description": "Location Group 2", + "locationGroupType": { + "name": "PRICING" + }, + "securityLevelId": "", + "id": 503, + "name": "Location Group 2" + } + ], + "id": 265592, + "name": "ams01", + "longName": "Amsterdam 1" + } + }, + "keyname": "AMSTERDAM", + "description": "AMS01 - Amsterdam", + "sortOrder": 0 + } + ], + "firstOrderStepId": "", + "id": 813, + "isActive": 1, + "description": "Dedicated Host" + } + + return package From aa944c7e6b271f0d4580635624022777d300f911 Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Tue, 21 Nov 2017 15:23:05 -0600 Subject: [PATCH 3/6] VIRT-4404 : Adding dedicated host functionality --- SoftLayer/CLI/dedicatedhost/__init__.py | 1 - SoftLayer/CLI/dedicatedhost/create.py | 99 ++++ SoftLayer/CLI/dedicatedhost/create_options.py | 34 ++ SoftLayer/CLI/dedicatedhost/list.py | 33 +- SoftLayer/CLI/routes.py | 4 +- SoftLayer/fixtures/SoftLayer_Account.py | 14 +- .../fixtures/SoftLayer_Product_Package.py | 298 +++++++++- .../SoftLayer_Virtual_DedicatedHost.py | 8 + SoftLayer/managers/__init__.py | 4 +- SoftLayer/managers/dedicated_host.py | 294 ++++++++++ SoftLayer/managers/dh.py | 105 ---- tests/CLI/modules/dedicatedhost_tests.py | 176 ++++++ tests/managers/dedicated_host_tests.py | 524 ++++++++++++++++++ 13 files changed, 1464 insertions(+), 130 deletions(-) create mode 100644 SoftLayer/CLI/dedicatedhost/create.py create mode 100644 SoftLayer/CLI/dedicatedhost/create_options.py create mode 100644 SoftLayer/managers/dedicated_host.py delete mode 100644 SoftLayer/managers/dh.py create mode 100644 tests/CLI/modules/dedicatedhost_tests.py create mode 100644 tests/managers/dedicated_host_tests.py diff --git a/SoftLayer/CLI/dedicatedhost/__init__.py b/SoftLayer/CLI/dedicatedhost/__init__.py index 6082f2b3d..55d5d799a 100644 --- a/SoftLayer/CLI/dedicatedhost/__init__.py +++ b/SoftLayer/CLI/dedicatedhost/__init__.py @@ -1,3 +1,2 @@ """Dedicated Host.""" # :license: MIT, see LICENSE for more details. - diff --git a/SoftLayer/CLI/dedicatedhost/create.py b/SoftLayer/CLI/dedicatedhost/create.py new file mode 100644 index 000000000..19890cdd6 --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/create.py @@ -0,0 +1,99 @@ +"""Order/create a dedicated Host.""" +# :license: MIT, see LICENSE for more details. + +import click + +import SoftLayer +from SoftLayer.CLI import environment +from SoftLayer.CLI import exceptions +from SoftLayer.CLI import formatting +from SoftLayer.CLI import helpers + + +@click.command( + epilog="See 'slcli dedicatedhost create-options' for valid options.") +@click.option('--hostname', '-H', + help="Host portion of the FQDN", + required=True, + prompt=True) +@click.option('--router', '-r', + help="Router id", + show_default=True) +@click.option('--domain', '-D', + help="Domain portion of the FQDN", + required=True, + prompt=True) +@click.option('--datacenter', '-d', help="Datacenter shortname", + required=True, + prompt=True) +@click.option('--billing', + type=click.Choice(['hourly', 'monthly']), + default='hourly', + show_default=True, + help="Billing rate") +@click.option('--test', + is_flag=True, + help="Do not actually create the server") +@helpers.multi_option('--extra', '-e', help="Extra options") +@environment.pass_env +def cli(env, **args): + """Order/create a dedicated host.""" + mgr = SoftLayer.DedicatedHostManager(env.client) + + order = { + 'hostname': args['hostname'], + 'domain': args['domain'], + 'router': args['router'], + 'location': args.get('datacenter'), + 'hourly': args.get('billing') == 'hourly', + } + + do_create = not (args['test']) + + output = None + + if args.get('test'): + result = mgr.verify_order(**order) + + table = formatting.Table(['Item', 'cost']) + table.align['Item'] = 'r' + table.align['cost'] = 'r' + + for price in result['prices']: + if order['hourly']: + total = float(price.get('hourlyRecurringFee', 0.0)) + rate = "%.2f" % float(price['hourlyRecurringFee']) + else: + total = float(price.get('recurringFee', 0.0)) + rate = "%.2f" % float(price['recurringFee']) + + table.add_row([price['item']['description'], rate]) + + if order['hourly']: + table.add_row(['Total hourly cost', "%.2f" % total]) + else: + table.add_row(['Total monthly cost', "%.2f" % total]) + + output = [] + output.append(table) + output.append(formatting.FormattedItem( + '', + ' -- ! Prices reflected here are retail and do not ' + 'take account level discounts and are not guaranteed.')) + + if do_create: + if not (env.skip_confirmations or formatting.confirm( + "This action will incur charges on your account. " + "Continue?")): + raise exceptions.CLIAbort('Aborting dedicated host order.') + + result = mgr.place_order(**order) + + table = formatting.KeyValueTable(['name', 'value']) + table.align['name'] = 'r' + table.align['value'] = 'l' + table.add_row(['id', result['orderId']]) + table.add_row(['created', result['orderDate']]) + output = table + + env.fout(output) diff --git a/SoftLayer/CLI/dedicatedhost/create_options.py b/SoftLayer/CLI/dedicatedhost/create_options.py new file mode 100644 index 000000000..8f07c5b3e --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/create_options.py @@ -0,0 +1,34 @@ +"""Options for ordering a dedicated host""" +# :license: MIT, see LICENSE for more details. + +import click +import SoftLayer + +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting + + +@click.command() +@environment.pass_env +def cli(env): + """host order options for a given dedicated host.""" + + mgr = SoftLayer.DedicatedHostManager(env.client) + options = mgr.get_create_options() + + tables = [] + + # Datacenters + dc_table = formatting.Table(['datacenter', 'value']) + dc_table.sortby = 'value' + for location in options['locations']: + dc_table.add_row([location['name'], location['key']]) + tables.append(dc_table) + + dh_table = formatting.Table(['dedicated Virtual Host', 'value']) + dh_table.sortby = 'value' + for item in options['dedicated_host']: + dh_table.add_row([item['name'], item['key']]) + tables.append(dh_table) + + env.fout(formatting.listing(tables, separator='\n')) diff --git a/SoftLayer/CLI/dedicatedhost/list.py b/SoftLayer/CLI/dedicatedhost/list.py index 5c6406361..56feefd9a 100644 --- a/SoftLayer/CLI/dedicatedhost/list.py +++ b/SoftLayer/CLI/dedicatedhost/list.py @@ -1,3 +1,6 @@ +"""List dedicated servers.""" +# :license: MIT, see LICENSE for more details. + import click import SoftLayer @@ -27,6 +30,7 @@ 'guestCount', ] + @click.command() @click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) @helpers.multi_option('--tag', help='Filter by tags') @@ -41,25 +45,26 @@ show_default=True) @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--name', '-H', help='Host portion of the FQDN') -@click.option('--memory', '-m', help='Memory capacity in mebibytes' - , type=click.INT) -@click.option('--disk', '-d', help='Disk capacity') +@click.option('--memory', '-m', help='Memory capacity in mebibytes', + type=click.INT) +@click.option('--disk', '-D', help='Disk capacity') @environment.pass_env def cli(env, sortby, cpu, columns, datacenter, name, memory, disk, tag): - dh = SoftLayer.DHManager(env.client) - hosts = dh.list_instances(cpus=cpu, - datacenter=datacenter, - name=name, - memory=memory, - disk=disk, - tags=tag, - mask=columns.mask()) + """List dedicated host.""" + mgr = SoftLayer.DedicatedHostManager(env.client) + hosts = mgr.list_instances(cpus=cpu, + datacenter=datacenter, + hostname=name, + memory=memory, + disk=disk, + tags=tag, + mask=columns.mask()) table = formatting.Table(columns.columns) - table.sortby =sortby - #print hosts + table.sortby = sortby + for host in hosts: table.add_row([value or formatting.blank() for value in columns.row(host)]) - env.fout(table) \ No newline at end of file + env.fout(table) diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py index c9d82b0fe..29cb902f9 100644 --- a/SoftLayer/CLI/routes.py +++ b/SoftLayer/CLI/routes.py @@ -33,6 +33,8 @@ ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'), ('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'), + ('dedicatedhost:create', 'SoftLayer.CLI.dedicatedhost.create:cli'), + ('dedicatedhost:create-options', 'SoftLayer.CLI.dedicatedhost.create_options:cli'), ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), @@ -283,5 +285,5 @@ 'server': 'hardware', 'vm': 'virtual', 'vs': 'virtual', - 'dh': 'dedicatedhopst', + 'dh': 'dedicatedhost', } diff --git a/SoftLayer/fixtures/SoftLayer_Account.py b/SoftLayer/fixtures/SoftLayer_Account.py index b8005e0ee..0f3a0a6a9 100644 --- a/SoftLayer/fixtures/SoftLayer_Account.py +++ b/SoftLayer/fixtures/SoftLayer_Account.py @@ -84,7 +84,6 @@ getHourlyVirtualGuests = [vs for vs in getVirtualGuests if vs['hourlyBillingFlag']] - getHardware = [{ 'id': 1000, 'metricTrackingObject': {'id': 3}, @@ -488,7 +487,6 @@ 'quoteKey': '1234test4321', }] - getOrders = [{ 'id': 1234, 'resourceType': '1 x 2.0 GHz Core', @@ -548,3 +546,15 @@ 'name': 'my first pool', 'metricTrackingObjectId': 10, }] + +getDedicatedHosts = [{ + 'datacenter': { + 'name': 'dal05' + }, + 'memoryCapacity': 242, + 'name': 'khnguyendh', + 'diskCapacity': 1200, + 'guestCount': 1, + 'cpuCount': 56, + 'id': 44701 +}] diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package.py b/SoftLayer/fixtures/SoftLayer_Product_Package.py index e458e0f76..044ba5ee1 100644 --- a/SoftLayer/fixtures/SoftLayer_Product_Package.py +++ b/SoftLayer/fixtures/SoftLayer_Product_Package.py @@ -155,7 +155,6 @@ 'setupFee': '0', 'sort': 99}]}] - ENTERPRISE_PACKAGE = { 'categories': [ {'categoryCode': 'storage_service_enterprise'} @@ -322,7 +321,6 @@ ] } - PERFORMANCE_PACKAGE = { 'categories': [ {'categoryCode': 'performance_storage_iscsi'}, @@ -419,7 +417,6 @@ ] } - SAAS_PACKAGE = { 'categories': [ {'categoryCode': 'storage_as_a_service'} @@ -669,7 +666,6 @@ ] } - getAllObjects = [{ 'activePresets': [{ 'description': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID', @@ -825,7 +821,6 @@ 'prices': [{'id': 611}], }] - getItemPrices = [ { 'currentPriceFlag': '', @@ -892,3 +887,296 @@ 'setupFee': '0', 'sort': 0 }] + +getAllObjectsDH = [{ + 'subDescription': 'Dedicated Host', + 'name': 'Dedicated Host', + 'items': [{ + 'prices': [{ + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '2099', + 'tierMinimumThreshold': '', + 'hourlyRecurringFee': '3.164', + 'oneTimeFee': '0', + 'currentPriceFlag': '', + 'id': 200269, + 'sort': 0, + 'onSaleFlag': '', + 'laborFee': '0', + 'locationGroupId': '', + 'quantity': '' + }, + { + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '2161.97', + 'tierMinimumThreshold': '', + 'hourlyRecurringFee': '3.258', + 'oneTimeFee': '0', + 'currentPriceFlag': '', + 'id': 200271, + 'sort': 0, + 'onSaleFlag': '', + 'laborFee': '0', + 'locationGroupId': 503, + 'quantity': '' + } + ], + 'itemCategory': { + 'categoryCode': 'dedicated_virtual_hosts' + }, + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }], + 'keyName': 'DEDICATED_HOST', + 'unitSize': '', + 'regions': [{ + 'location': { + 'locationPackageDetails': [{ + 'isAvailable': 1, + 'locationId': 265592, + 'packageId': 813 + }], + 'location': { + 'statusId': 2, + 'priceGroups': [{ + 'locationGroupTypeId': 82, + 'description': 'Location Group 2', + 'locationGroupType': { + 'name': 'PRICING' + }, + 'securityLevelId': '', + 'id': 503, + 'name': 'Location Group 2' + }], + 'id': 265592, + 'name': 'ams01', + 'longName': 'Amsterdam 1' + } + }, + 'keyname': 'AMSTERDAM', + 'description': 'AMS01 - Amsterdam', + 'sortOrder': 0 + }, + { + 'location': { + 'locationPackageDetails': [{ + 'isAvailable': 1, + 'locationId': 814994, + 'packageId': 813 + }], + 'location': { + 'statusId': 2, + 'priceGroups': [{ + 'locationGroupTypeId': 82, + 'description': 'Location Group 2', + 'locationGroupType': { + 'name': 'PRICING' + }, + 'securityLevelId': '', + 'id': 503, + 'name': 'Location Group 2' + }, + { + 'locationGroupTypeId': 82, + 'description': 'COS Cross Region - EU', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1303, + 'name': 'eu'}], + 'id': 814994, + 'name': 'ams03', + 'longName': 'Amsterdam 3'}}, + 'keyname': 'AMSTERDAM03', + 'description': 'AMS03 - Amsterdam', + 'sortOrder': 2}, + {'location': { + 'locationPackageDetails': [ + { + 'isAvailable': 1, + 'locationId': 138124, + 'packageId': 813}], + 'location': { + 'statusId': 2, + 'priceGroups': [ + { + 'locationGroupTypeId': 82, + 'description': 'CDN - North America - Akamai', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1463, + 'name': 'NORTH-AMERICA-AKAMAI'}], + 'id': 138124, + 'name': 'dal05', + 'longName': 'Dallas 5'}}, + 'keyname': 'DALLAS05', + 'description': 'DAL05 - Dallas', + 'sortOrder': 12}, + {'location': { + 'locationPackageDetails': [ + { + 'isAvailable': 1, + 'locationId': 2017603, + 'packageId': 813}], + 'location': { + 'statusId': 2, + 'priceGroups': [ + { + 'locationGroupTypeId': 82, + 'description': 'COS Regional - US East', + 'locationGroupType': { + 'name': 'PRICING'}, + 'securityLevelId': '', + 'id': 1305, + 'name': 'us-east'}], + 'id': 2017603, + 'name': 'wdc07', + 'longName': 'Washington 7'}}, + 'keyname': 'WASHINGTON07', + 'description': 'WDC07 - Washington, DC', + 'sortOrder': 76}], + 'firstOrderStepId': '', 'id': 813, 'isActive': 1, + 'description': 'Dedicated Host'}] + +verifyOrderDH = { + 'preTaxSetup': '0', + 'storageGroups': [], + 'postTaxRecurring': '3.164', + 'billingOrderItemId': '', + 'presetId': '', + 'hardware': [ + { + 'domain': 't.com', + 'hostname': 't', + 'bareMetalInstanceFlag': '', + 'hardwareStatusId': '', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + }, + 'networkVlanId': '' + }, + 'accountId': '' + } + ], + 'prices': [ + { + 'itemId': 10195, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '3.164', + 'oneTimeFee': '0', + 'id': 200269, + 'item': { + 'thirdPartyPolicyAssignments': [], + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'bundle': [ + { + 'category': { + 'categoryCode': 'dedicated_host_ram', + 'id': 850, + 'name': 'Dedicated Host RAM' + }, + 'itemPriceId': 200301, + 'itemPrice': { + 'itemId': 10199, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '0', + 'oneTimeFee': '0', + 'id': 200301, + 'laborFee': '0' + }, + 'bundleItemId': 10195, + 'bundleItem': { + 'units': 'CORE', + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }, + 'id': 41763 + }, + { + 'category': { + 'categoryCode': 'dedicated_host_disk', + 'id': 851, + 'name': 'Dedicated Host Disk' + }, + 'itemPriceId': 200299, + 'itemPrice': { + 'itemId': 10197, + 'setupFee': '0', + 'recurringFee': '0', + 'hourlyRecurringFee': '0', + 'oneTimeFee': '0', + 'id': 200299, + 'laborFee': '0' + }, + 'bundleItemId': 10195, + 'bundleItem': { + 'units': 'CORE', + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'capacity': '56', + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195 + }, + 'id': 41761 + } + ], + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'units': 'CORE', + 'id': 10195 + }, + 'laborFee': '0', + 'categories': [ + { + 'categoryCode': 'dedicated_virtual_hosts', + 'id': 848, + 'name': 'Dedicated Host' + } + ] + } + ], + 'sendQuoteEmailFlag': '', + 'packageId': 813, + 'useHourlyPricing': True, + 'preTaxRecurringMonthly': '0', + 'message': '', + 'preTaxRecurring': '3.164', + 'primaryDiskPartitionId': '', + 'locationObject': { + 'id': 138124, + 'name': 'dal05', + 'longName': 'Dallas 5' + }, + 'taxCompletedFlag': False, + 'isManagedOrder': '', + 'imageTemplateId': '', + 'postTaxRecurringMonthly': '0', + 'resourceGroupTemplateId': '', + 'postTaxSetup': '0', + 'sshKeys': [], + 'location': '138124', + 'stepId': '', + 'proratedInitialCharge': '0', + 'totalRecurringTax': '0', + 'paymentType': '', + 'resourceGroupId': '', + 'sourceVirtualGuestId': '', + 'bigDataOrderFlag': False, + 'extendedHardwareTesting': '', + 'preTaxRecurringHourly': '3.164', + 'postTaxRecurringHourly': '3.164', + 'currencyShortName': 'USD', + 'containerSplHash': '000000003699c54000007f38ef8b0102', + 'proratedOrderTotal': '0', + 'serverCoreCount': '', + 'privateCloudOrderFlag': False, + 'totalSetupTax': '0', + 'quantity': 1 +} diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py index 926d84ed9..073992f15 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py @@ -8,3 +8,11 @@ 'cpuCount': 56, 'accountId': 1199911 } + + +getAvailableRouters = [ + {'hostname': 'bcr01a.dal05', 'id': 51218}, + {'hostname': 'bcr02a.dal05', 'id': 83361}, + {'hostname': 'bcr03a.dal05', 'id': 122762}, + {'hostname': 'bcr04a.dal05', 'id': 147566} +] diff --git a/SoftLayer/managers/__init__.py b/SoftLayer/managers/__init__.py index edbad8f9b..044da5a50 100644 --- a/SoftLayer/managers/__init__.py +++ b/SoftLayer/managers/__init__.py @@ -9,7 +9,7 @@ """ from SoftLayer.managers.block import BlockStorageManager from SoftLayer.managers.cdn import CDNManager -from SoftLayer.managers.dh import DHManager +from SoftLayer.managers.dedicated_host import DedicatedHostManager from SoftLayer.managers.dns import DNSManager from SoftLayer.managers.file import FileStorageManager from SoftLayer.managers.firewall import FirewallManager @@ -30,7 +30,7 @@ __all__ = [ 'BlockStorageManager', 'CDNManager', - 'DHManager', + 'DedicatedHostManager', 'DNSManager', 'FileStorageManager', 'FirewallManager', diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py new file mode 100644 index 000000000..5429f17d4 --- /dev/null +++ b/SoftLayer/managers/dedicated_host.py @@ -0,0 +1,294 @@ +""" + SoftLayer.dedicatedhost + ~~~~~~~~~~~~ + DH Manager/helpers + + :license: MIT, see License for more details. +""" + +import logging +import SoftLayer + +from SoftLayer.managers import ordering +from SoftLayer import utils + +LOGGER = logging.getLogger(__name__) + + +class DedicatedHostManager(utils.IdentifierMixin, object): + """Manages SoftLayer Dedicated Hosts. + + See product information here https://www.ibm.com/cloud/dedicated + + Example:: + # Initialize the DedicatedHostManager. + # env variables. These can also be specified in ~/.softlayer, + # or passed directly to SoftLayer.Client() + # SL_USERNAME = YOUR_USERNAME + # SL_API_KEY = YOUR_API_KEY + import SoftLayer + client = SoftLayer.Client() + mgr = SoftLayer.DedicatedHostManager(client) + + :param SoftLayer.API.BaseClient client: the client instance + :param SoftLayer.managers.OrderingManager ordering_manager: an optional + manager to handle ordering. + If none is provided, one will be + auto initialized. + """ + + def __init__(self, client, ordering_manager=None): + self.client = client + self.account = client['Account'] + self.host = client['Virtual_DedicatedHost'] + + if ordering_manager is None: + self.ordering_manager = ordering.OrderingManager(client) + + def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, + disk=None, datacenter=None, **kwargs): + """Retrieve a list of all dedicated hosts on the account + + Example:: + + :param list tags: filter based on list of tags + :param integer cpus: filter based on number of CPUS + :param integer memory: filter based on amount of memory + :param string hostname: filter based on hostname + :param string disk: filter based on disk + :param string datacenter: filter based on datacenter + :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) + :returns: Returns a list of dictionaries representing the matching + dedicated host. + + + + """ + if 'mask' not in kwargs: + items = [ + 'id', + 'name', + 'cpuCount', + 'diskCapacity', + 'memoryCapacity', + 'datacenter', + 'guestCount', + ] + kwargs['mask'] = "mask[%s]" % ','.join(items) + + call = 'getDedicatedHosts' + + _filter = utils.NestedDict(kwargs.get('filter') or {}) + if tags: + _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { + 'operation': 'in', + 'options': [{'name': 'data', 'value': tags}], + } + + if hostname: + _filter['dedicatedHosts']['name'] = ( + utils.query_filter(hostname) + ) + + if cpus: + _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) + + if disk: + _filter['dedicatedHosts']['diskCapacity'] = ( + utils.query_filter(disk)) + + if memory: + _filter['dedicatedHosts']['memoryCapacity'] = ( + utils.query_filter(memory)) + + if datacenter: + _filter['dedicatedHosts']['datacenter']['name'] = ( + utils.query_filter(datacenter)) + + kwargs['filter'] = _filter.to_dict() + func = getattr(self.account, call) + return func(**kwargs) + + def place_order(self, hostname, domain, location, hourly, router=None): + """Places an order for a dedicated host. + + See get_create_options() for valid arguments. + + :param string hostname: server hostname + :param string domain: server domain name + :param string location: location (datacenter) name + :param boolean hourly: True if using hourly pricing (default). + False for monthly. + :param int router: an optional value for selecting a backend router + """ + create_options = self._generate_create_dict(hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + return self.client['Product_Order'].placeOrder(create_options) + + def verify_order(self, hostname, domain, location, hourly, router=None): + """Verifies an order for a dedicated host. + + See :func:`place_order` for a list of available options. + """ + + create_options = self._generate_create_dict(hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + return self.client['Product_Order'].verifyOrder(create_options) + + def _generate_create_dict(self, + hostname=None, + router=None, + domain=None, + datacenter=None, + hourly=True): + """Translates args into a dictionary for creating a dedicated host.""" + package = self._get_package() + item = self._get_item(package) + location = self._get_location(package['regions'], datacenter) + price = self._get_price(item) + + if router is None: + routers = self._get_backend_router( + location['location']['locationPackageDetails']) + router = self._get_default_router(routers) + + hardware = { + 'hostname': hostname, + 'domain': domain, + 'primaryBackendNetworkComponent': { + 'router': { + 'id': router + } + } + } + + complex_type = "SoftLayer_Container_Product_Order_Virtual_DedicatedHost" + + order = { + "complexType": complex_type, + "quantity": 1, + 'location': location['keyname'], + 'packageId': package['id'], + 'prices': [{'id': price}], + 'hardware': [hardware], + 'useHourlyPricing': hourly, + } + return order + + def _get_package(self): + """Get the package related to simple dedicated host ordering.""" + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + + package_keyname = 'DEDICATED_HOST' + + package = self.ordering_manager.get_package_by_key(package_keyname, + mask=mask) + + if package is None: + raise SoftLayer.SoftLayerError("Ordering package not found") + + return package + + def _get_location(self, regions, datacenter): + """Get the longer key with a short location(datacenter) name.""" + for region in regions: + # list of locations + if region['location']['location']['name'] == datacenter: + return region + + raise SoftLayer.SoftLayerError("Could not find valid location for: '%s'" + % datacenter) + + def get_create_options(self): + """Returns valid options for ordering a dedicated host.""" + + package = self._get_package() + # Locations + locations = [] + for region in package['regions']: + locations.append({ + 'name': region['location']['location']['longName'], + 'key': region['location']['location']['name'], + }) + dedicated_host = [] + for item in package['items']: + if item['itemCategory']['categoryCode'] == \ + 'dedicated_virtual_hosts': + dedicated_host.append({ + 'name': item['description'], + 'key': item['id'], + }) + + return { + 'locations': locations, + 'dedicated_host': dedicated_host, + } + + def _get_price(self, package): + """Returns valid price for ordering a dedicated host.""" + + for price in package['prices']: + if price.get('locationGroupId') is '': + return price['id'] + + raise SoftLayer.SoftLayerError( + "Could not find valid price") + + def _get_item(self, package): + """Returns the item for ordering a dedicated host.""" + description = '56 Cores X 242 RAM X 1.2 TB' + + for item in package['items']: + if item['description'] == description: + return item + + raise SoftLayer.SoftLayerError("Could not find valid item for: '%s'" + % description) + + def _get_backend_router(self, locations): + """Returns valid router options for ordering a dedicated host.""" + mask = ''' + id, + hostname + ''' + + if locations is not None: + for location in locations: + if location['locationId'] is not None: + loc_id = location['locationId'] + host = { + 'cpuCount': 56, + 'memoryCapacity': 242, + 'diskCapacity': 1200, + 'datacenter': { + 'id': loc_id + } + } + routers = self.host.getAvailableRouters(host, mask=mask) + return routers + + raise SoftLayer.SoftLayerError("Could not find available routers") + + def _get_default_router(self, routers): + """Returns the default router for ordering a dedicated host.""" + for router in routers: + if router['id'] is not None: + return router['id'] + + raise SoftLayer.SoftLayerError("Could not find valid default router") diff --git a/SoftLayer/managers/dh.py b/SoftLayer/managers/dh.py deleted file mode 100644 index 270660427..000000000 --- a/SoftLayer/managers/dh.py +++ /dev/null @@ -1,105 +0,0 @@ -""" - SoftLayer.vs - ~~~~~~~~~~~~ - DH Manager/helpers - - :license: MIT, see License for more details. -""" - -import logging - -from SoftLayer import utils - -LOGGER = logging.getLogger(__name__) - -class DHManager(utils.IdentifierMixin, object): - """Manages SoftLayer Dedicated Hosts. - - See product information here https://www.ibm.com/cloud/dedicated - - Example:: - # Initialize the DHManager. - # env variables. These can also be specified in ~/.softlayer, - # or passed directly to SoftLayer.Client() - # SL_USERNAME = YOUR_USERNAME - # SL_API_KEY = YOUR_API_KEY - import SoftLayer - client = SoftLayer.Client() - mgr = SoftLayer.VSManager(client) - - :param SoftLayer.API.BaseClient client: the client instance - :param SoftLayer.managers.OrderingManager ordering_manager: an optional - manager to handle ordering. - If none is provided, one will be - auto initialized. - """ - - #initializer - def __init__(self, client): - self.client = client - self.account = client['Account'] - self.host = client['Virtual_DedicatedHost'] - - def list_instances(self,tags=None, cpus=None, memory=None, name=None, - disk=None, datacenter=None, **kwargs): - """Retrieve a list of all dedicated hosts on the account - - Example:: - - :param list tags: filter based on list of tags - :param integer cpus: filter based on number of CPUS - :param integer memory: filter based on amount of memory - :param string hostname: filter based on hostname - :param string disk: filter based on disk - :param string datacenter: filter based on datacenter - :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) - :returns: Returns a list of dictionaries representing the matching - dedicated host. - - - - """ - if 'mask' not in kwargs: - items = [ - 'id', - 'name', - 'cpuCount', - 'diskCapacity', - 'memoryCapacity', - 'datacenter', - 'guestCount', - ] - kwargs['mask'] = "mask[%s]" % ','.join(items) - - call = 'getDedicatedHosts' - - _filter = utils.NestedDict(kwargs.get('filter') or {}) - if tags: - _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { - 'operation': 'in', - 'options': [{'name': 'data', 'value': tags}], - } - - if name: - _filter['dedicatedHosts']['name'] = ( - utils.query_filter(name) - ) - - if cpus: - _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) - - if disk: - _filter['dedicatedHosts']['diskCapacity'] = ( - utils.query_filter(disk)) - - if memory: - _filter['dedicatedHosts']['memoryCapacity'] = ( - utils.query_filter(memory)) - - if datacenter: - _filter['dedicatedHosts']['datacenter']['name'] = ( - utils.query_filter(datacenter)) - - kwargs['filter'] = _filter.to_dict() - func = getattr(self.account, call) - return func(**kwargs) \ No newline at end of file diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py new file mode 100644 index 000000000..133587f1c --- /dev/null +++ b/tests/CLI/modules/dedicatedhost_tests.py @@ -0,0 +1,176 @@ +""" + SoftLayer.tests.CLI.modules.dedicatedhosts_tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :license: MIT, see LICENSE for more details. +""" +import json +import mock +import SoftLayer + +from SoftLayer.CLI import exceptions +from SoftLayer.fixtures import SoftLayer_Product_Package +from SoftLayer.fixtures import SoftLayer_Virtual_DedicatedHost +from SoftLayer import testing + + +class DedicatedHostsTests(testing.TestCase): + def set_up(self): + self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) + + def test_list_dedicated_hosts(self): + result = self.run_command(['dedicatedhost', 'list']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + [{ + 'cpuCount': 56, + 'datacenter': 'dal05', + 'diskCapacity': 1200, + 'guestCount': 1, + 'id': 44701, + 'memoryCapacity': 242, + 'name': 'khnguyendh' + }] + ) + + def test_create_options(self): + mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock.return_value = SoftLayer_Product_Package.getAllObjectsDH + + result = self.run_command(['dedicatedhost', 'create-options']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), [ + [{"value": "ams01", "datacenter": "Amsterdam 1"}, + {"value": "ams03", "datacenter": "Amsterdam 3"}, + {"value": "dal05", "datacenter": "Dallas 5"}, + {"value": "wdc07", "datacenter": "Washington 7"}], [ + {"dedicated Virtual Host": "56 Cores X 242 RAM X 1.2 TB", + "value": 10195}]]) + + def test_create(self): + SoftLayer.CLI.formatting.confirm = mock.Mock() + SoftLayer.CLI.formatting.confirm.return_value = True + mock_package_obj = \ + self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = \ + SoftLayer_Product_Package.getAllObjectsDH + mock_package_routers = \ + self.set_mock('SoftLayer_Virtual_DedicatedHost', + 'getAvailableRouters') + mock_package_routers.return_value = \ + SoftLayer_Virtual_DedicatedHost.getAvailableRouters + + result = self.run_command(['dedicatedhost', 'create', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=hourly']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), + {'created': '2013-08-01 15:23:45', 'id': 1234}) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': u'host', + 'domain': u'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', + args=args) + + def test_create_verify(self): + SoftLayer.CLI.formatting.confirm = mock.Mock() + SoftLayer.CLI.formatting.confirm.return_value = True + mock_package_obj = \ + self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = \ + SoftLayer_Product_Package.getAllObjectsDH + mock_package_routers = \ + self.set_mock('SoftLayer_Virtual_DedicatedHost', + 'getAvailableRouters') + mock_package_routers.return_value = \ + SoftLayer_Virtual_DedicatedHost.getAvailableRouters + mock_package = \ + self.set_mock('SoftLayer_Product_Order', 'verifyOrder') + mock_package.return_value = \ + SoftLayer_Product_Package.verifyOrderDH + + result = self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=hourly']) + self.assert_no_fail(result) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': 'host', + 'domain': 'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', + args=args) + + result = self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=monthly']) + self.assert_no_fail(result) + + args = ({ + 'useHourlyPricing': True, + 'hardware': [{ + 'hostname': 'host', + 'domain': 'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + } + }], + 'packageId': 813, 'prices': [{'id': 200269}], + 'location': 'DALLAS05', + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', + args=args) + + def test_create_aborted(self): + SoftLayer.CLI.formatting.confirm = mock.Mock() + SoftLayer.CLI.formatting.confirm.return_value = False + + result = self.run_command(['dedicatedhost', + 'create', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--billing=monthly']) + self.assertEqual(result.exit_code, 2) + self.assertIsInstance(result.exception, exceptions.CLIAbort) diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py new file mode 100644 index 000000000..ddcfd1ba7 --- /dev/null +++ b/tests/managers/dedicated_host_tests.py @@ -0,0 +1,524 @@ +""" + SoftLayer.tests.managers.dedicated_host_tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :license: MIT, see LICENSE for more details. +""" +import mock +import SoftLayer + +from SoftLayer import exceptions +from SoftLayer import fixtures +from SoftLayer import testing + + +class DedicatedHostTests(testing.TestCase): + def set_up(self): + self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) + + def test_list_instances(self): + results = self.dedicated_host.list_instances() + + self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) + self.assert_called_with('SoftLayer_Account', 'getDedicatedHosts') + + def test_list_instances_with_filters(self): + results = self.dedicated_host.list_instances( + tags=['tag1', 'tag2'], + cpus=2, + memory=1, + hostname='hostname', + datacenter='dal05', + disk=1 + ) + self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) + + def test_place_order(self): + create_dict = self.dedicated_host._generate_create_dict = mock.Mock() + + values = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + create_dict.return_value = values + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + self.dedicated_host.place_order(hostname=hostname, + domain=domain, + location=location, + hourly=hourly) + + create_dict.assert_called_once_with(hostname=hostname, + router=None, + domain=domain, + datacenter=location, + hourly=True) + + self.assert_called_with('SoftLayer_Product_Order', + 'placeOrder', + args=(values,)) + + def test_verify_order(self): + create_dict = self.dedicated_host._generate_create_dict = mock.Mock() + + values = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + create_dict.return_value = values + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + self.dedicated_host.verify_order(hostname=hostname, + domain=domain, + location=location, + hourly=hourly) + + create_dict.assert_called_once_with(hostname=hostname, + router=None, + domain=domain, + datacenter=location, + hourly=True) + + self.assert_called_with('SoftLayer_Product_Order', + 'verifyOrder', + args=(values,)) + + def test_generate_create_dict_without_router(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + self.dedicated_host._get_backend_router = mock.Mock() + self.dedicated_host._get_backend_router.return_value = self \ + ._get_routers_sample() + + location = 'ams01' + hostname = 'test' + domain = 'test.com' + hourly = True + + results = self.dedicated_host._generate_create_dict(hostname=hostname, + domain=domain, + datacenter=location, + hourly=hourly) + + testResults = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + + self.assertEqual(results, testResults) + + def test_generate_create_dict_with_router(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + + location = 'ams01' + router = 55901 + hostname = 'test' + domain = 'test.com' + hourly = True + + results = self.dedicated_host._generate_create_dict( + hostname=hostname, + router=router, + domain=domain, + datacenter=location, + hourly=hourly) + + testResults = { + 'hardware': [ + { + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 55901 + } + }, + 'domain': u'test.com', + 'hostname': u'test' + } + ], + 'useHourlyPricing': True, + 'location': 'AMSTERDAM', + 'packageId': 813, + 'complexType': + 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'prices': [ + { + 'id': 200269 + } + ], + 'quantity': 1 + } + + self.assertEqual(results, testResults) + + def test_get_package(self): + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + self.dedicated_host.ordering_manager = mock.Mock() + + self.dedicated_host.ordering_manager.get_package_by_key.return_value = \ + "test" + + package = self.dedicated_host._get_package() + + package_keyname = 'DEDICATED_HOST' + + self.assertEqual('test', package) + self.dedicated_host.ordering_manager.get_package_by_key. \ + assert_called_once_with(package_keyname, mask=mask) + + def test_get_package_no_package_found(self): + mask = ''' + items[ + id, + description, + prices, + itemCategory[categoryCode] + ], + regions[location[location[priceGroups]]] + ''' + self.dedicated_host.ordering_manager = mock.Mock() + + self.dedicated_host.ordering_manager.get_package_by_key.return_value = \ + None + + package_keyname = 'DEDICATED_HOST' + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_package) + + self.dedicated_host.ordering_manager.get_package_by_key. \ + assert_called_once_with(package_keyname, mask=mask) + + def test_get_location(self): + regions = [{ + "location": { + "location": { + "name": "dal05", + } + } + }] + + region = { + 'location': + { + 'location': { + 'name': 'dal05', + } + } + } + + testing = self.dedicated_host._get_location(regions, 'dal05') + + self.assertEqual(testing, region) + + def test_get_location_no_location_found(self): + regions = [{ + "location": { + "location": { + "name": "dal05", + } + } + }] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_location, regions, 'dal10') + + def test_get_create_options(self): + self.dedicated_host._get_package = mock.MagicMock() + self.dedicated_host._get_package.return_value = self._get_package() + + results = { + 'dedicated_host': [ + { + 'key': 10195, + 'name': '56 Cores X 242 RAM X 1.2 TB' + } + ], + 'locations': [ + { + 'key': 'ams01', + 'name': 'Amsterdam 1' + } + ] + } + + self.assertEqual(self.dedicated_host.get_create_options(), results) + + def test_get_price(self): + package = self._get_package() + item = package['items'][0] + price_id = 200269 + + self.assertEqual(self.dedicated_host._get_price(item), price_id) + + def test_get_price_no_price_found(self): + package = self._get_package() + package['items'][0]['prices'][0]['locationGroupId'] = 33 + item = package['items'][0] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_price, item) + + def test_get_item(self): + """Returns the item for ordering a dedicated host.""" + package = self._get_package() + + item = { + 'description': '56 Cores X 242 RAM X 1.2 TB', + 'id': 10195, + 'itemCategory': { + 'categoryCode': 'dedicated_virtual_hosts' + }, + 'prices': [ + { + 'currentPriceFlag': '', + 'hourlyRecurringFee': '3.164', + 'id': 200269, + 'itemId': 10195, + 'laborFee': '0', + 'locationGroupId': '', + 'onSaleFlag': '', + 'oneTimeFee': '0', + 'quantity': '', + 'recurringFee': '2099', + 'setupFee': '0', + 'sort': 0, + 'tierMinimumThreshold': '' + } + ] + } + + self.assertEqual(self.dedicated_host._get_item(package), item) + + def test_get_item_no_item_found(self): + package = self._get_package() + + package['items'][0]['description'] = 'not found' + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_item, package) + + def test_get_backend_router(self): + location = [ + { + 'isAvailable': 1, + 'locationId': 138124, + 'packageId': 813 + } + ] + + locId = location[0]['locationId'] + + mask = ''' + id, + hostname + ''' + + host = { + 'cpuCount': 56, + 'memoryCapacity': 242, + 'diskCapacity': 1200, + 'datacenter': { + 'id': locId + } + } + + self.dedicated_host.host = mock.Mock() + + routers = self.dedicated_host.host.getAvailableRouters.return_value = \ + self._get_routers_sample() + + routers_test = self.dedicated_host._get_backend_router(location) + + self.assertEqual(routers, routers_test) + self.dedicated_host.host.getAvailableRouters. \ + assert_called_once_with(host, mask=mask) + + def test_get_backend_router_no_routers_found(self): + location = [] + + self.dedicated_host.host = mock.Mock() + + routers_test = self.dedicated_host._get_backend_router + + self.assertRaises(exceptions.SoftLayerError, routers_test, location) + + def test_get_default_router(self): + routers = self._get_routers_sample() + + router = 51218 + + router_test = self.dedicated_host._get_default_router(routers) + + self.assertEqual(router_test, router) + + def test_get_default_router_no_router_found(self): + routers = [] + + self.assertRaises(exceptions.SoftLayerError, + self.dedicated_host._get_default_router, routers) + + def _get_routers_sample(self): + routers = [ + { + 'hostname': 'bcr01a.dal05', + 'id': 51218 + }, + { + 'hostname': 'bcr02a.dal05', + 'id': 83361 + }, + { + 'hostname': 'bcr03a.dal05', + 'id': 122762 + }, + { + 'hostname': 'bcr04a.dal05', + 'id': 147566 + } + ] + + return routers + + def _get_package(self): + package = { + "items": [ + { + "prices": [ + { + "itemId": 10195, + "setupFee": "0", + "recurringFee": "2099", + "tierMinimumThreshold": "", + "hourlyRecurringFee": "3.164", + "oneTimeFee": "0", + "currentPriceFlag": "", + "id": 200269, + "sort": 0, + "onSaleFlag": "", + "laborFee": "0", + "locationGroupId": "", + "quantity": "" + } + ], + "itemCategory": { + "categoryCode": "dedicated_virtual_hosts" + }, + "description": "56 Cores X 242 RAM X 1.2 TB", + "id": 10195 + } + ], + "regions": [ + { + "location": { + "locationPackageDetails": [ + { + "isAvailable": 1, + "locationId": 265592, + "packageId": 813 + } + ], + "location": { + "statusId": 2, + "priceGroups": [ + { + "locationGroupTypeId": 82, + "description": "Location Group 2", + "locationGroupType": { + "name": "PRICING" + }, + "securityLevelId": "", + "id": 503, + "name": "Location Group 2" + } + ], + "id": 265592, + "name": "ams01", + "longName": "Amsterdam 1" + } + }, + "keyname": "AMSTERDAM", + "description": "AMS01 - Amsterdam", + "sortOrder": 0 + } + ], + "firstOrderStepId": "", + "id": 813, + "isActive": 1, + "description": "Dedicated Host" + } + + return package From a1339712734047b37bd07192d38b13aaa58807aa Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Wed, 22 Nov 2017 13:48:16 -0600 Subject: [PATCH 4/6] VIRT-4404 : Adding details functionality --- SoftLayer/CLI/dedicatedhost/detail.py | 70 +++++++++++++++++++ SoftLayer/CLI/routes.py | 2 +- .../SoftLayer_Virtual_DedicatedHost.py | 58 +++++++++++++++ SoftLayer/managers/dedicated_host.py | 39 +++++++++++ tests/CLI/modules/dedicatedhost_tests.py | 31 ++++++++ tests/managers/dedicated_host_tests.py | 25 +++++++ 6 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 SoftLayer/CLI/dedicatedhost/detail.py diff --git a/SoftLayer/CLI/dedicatedhost/detail.py b/SoftLayer/CLI/dedicatedhost/detail.py new file mode 100644 index 000000000..4767ed1ca --- /dev/null +++ b/SoftLayer/CLI/dedicatedhost/detail.py @@ -0,0 +1,70 @@ +"""Get details for a dedicated host.""" +# :license: MIT, see LICENSE for more details. + +import logging + +import click + +import SoftLayer +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting +from SoftLayer import utils + +LOGGER = logging.getLogger(__name__) + + +@click.command() +@click.argument('identifier') +@click.option('--price', is_flag=True, help='Show associated prices') +@click.option('--guests', is_flag=True, help='Show guests on dedicated host') +@environment.pass_env +def cli(env, identifier, price=False, guests=False): + """Get details for a virtual server.""" + dh = SoftLayer.DedicatedHostManager(env.client) + + table = formatting.KeyValueTable(['name', 'value']) + table.align['name'] = 'r' + table.align['value'] = 'l' + + result = dh.get_host(identifier) + result = utils.NestedDict(result) + + table.add_row(['id', result['id']]) + table.add_row(['name', result['name']]) + table.add_row(['cpu count', result['cpuCount']]) + table.add_row(['memory capacity', result['memoryCapacity']]) + table.add_row(['disk capacity', result['diskCapacity']]) + table.add_row(['create date', result['createDate']]) + table.add_row(['modify date', result['modifyDate']]) + table.add_row(['router id', result['backendRouter']['id']]) + if utils.lookup(result, 'billingItem') != []: + table.add_row(['owner', formatting.FormattedItem( + utils.lookup(result, 'billingItem', 'orderItem', + 'order', 'userRecord', + 'username') or formatting.blank(), + )]) + else: + table.add_row(['owner', formatting.blank()]) + + if price: + total_price = utils.lookup(result, + 'billingItem', + 'nextInvoiceTotalRecurringAmount') or 0 + total_price += sum(p['nextInvoiceTotalRecurringAmount'] + for p + in utils.lookup(result, + 'billingItem', + 'children') or []) + table.add_row(['price_rate', total_price]) + + table.add_row(['guest count', result['guestCount']]) + if guests: + guest_table = formatting.Table(['id', 'hostname', 'domain', 'uuid']) + for guest in result['guests']: + guest_table.add_row([ + guest['id'], guest['hostname'], guest['domain'], guest['uuid']]) + table.add_row(['guests', guest_table]) + + table.add_row(['datacenter', result['datacenter']['name']]) + + env.fout(table) diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py index 0902dc16d..4f246dfbe 100644 --- a/SoftLayer/CLI/routes.py +++ b/SoftLayer/CLI/routes.py @@ -34,8 +34,8 @@ ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'), ('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'), ('dedicatedhost:create', 'SoftLayer.CLI.dedicatedhost.create:cli'), - ('dedicatedhost:create-options', 'SoftLayer.CLI.dedicatedhost.create_options:cli'), + ('dedicatedhost:detail', 'SoftLayer.CLI.dedicatedhost.detail:cli'), ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py index 073992f15..e5caa7332 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py @@ -16,3 +16,61 @@ {'hostname': 'bcr03a.dal05', 'id': 122762}, {'hostname': 'bcr04a.dal05', 'id': 147566} ] + +getObjectById = { + 'datacenter': { + 'id': 138124, + 'name': 'dal05', + 'longName': 'Dallas 5' + }, + 'memoryCapacity': 242, + 'modifyDate': '2017-11-06T11:38:20-06:00', + 'name': 'khnguyendh', + 'diskCapacity': 1200, + 'backendRouter': { + 'domain': 'softlayer.com', + 'hostname': 'bcr01a.dal05', + 'id': 51218 + }, + 'guestCount': 1, + 'cpuCount': 56, + 'guests': [{ + 'domain': 'Softlayer.com', + 'hostname': 'khnguyenDHI', + 'id': 43546081, + 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2' + }], + 'billingItem': { + 'nextInvoiceTotalRecurringAmount': 1515.556, + 'orderItem': { + 'id': 263060473, + 'order': { + 'status': 'APPROVED', + 'privateCloudOrderFlag': False, + 'modifyDate': '2017-11-02T11:42:50-07:00', + 'orderQuoteId': '', + 'userRecordId': 6908745, + 'createDate': '2017-11-02T11:40:56-07:00', + 'impersonatingUserRecordId': '', + 'orderTypeId': 7, + 'presaleEventId': '', + 'userRecord': { + 'username': '232298_khuong' + }, + 'id': 20093269, + 'accountId': 232298 + } + }, + 'id': 235379377, + 'children': [{ + 'nextInvoiceTotalRecurringAmount': 0.0, + 'categoryCode': 'dedicated_host_ram' + }, + { + 'nextInvoiceTotalRecurringAmount': 0.0, + 'categoryCode': 'dedicated_host_disk' + } + ]}, + 'id': 44701, + 'createDate': '2017-11-02T11:40:56-07:00' +} diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py index 5429f17d4..1051932e2 100644 --- a/SoftLayer/managers/dedicated_host.py +++ b/SoftLayer/managers/dedicated_host.py @@ -109,6 +109,45 @@ def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, func = getattr(self.account, call) return func(**kwargs) + def get_host(self, host_id, **kwargs): + """Get details about a dedicated host. + + :param integer : the host ID + :returns: A dictionary containing a large amount of information about + the specified instance. + + Example:: + + # Print out host ID 12345. + dh = mgr.get_host(12345) + print dh + + # Print out only name and backendRouter for instance 12345 + object_mask = "mask[name,backendRouter[id]]" + dh = mgr.get_host(12345, mask=mask) + print dh + + """ + if 'mask' not in kwargs: + kwargs['mask'] = ( + 'id,' + 'name,' + 'cpuCount,' + 'memoryCapacity,' + 'diskCapacity,' + 'createDate,' + 'modifyDate,' + 'backendRouter[id, hostname, domain],' + 'billingItem[id, nextInvoiceTotalRecurringAmount, ' + 'children[categoryCode,nextInvoiceTotalRecurringAmount],' + 'orderItem[id, order.userRecord[username]]],' + 'datacenter[id, name, longName],' + 'guests[id, hostname, domain, uuid],' + 'guestCount' + ) + + return self.host.getObject(id=host_id, **kwargs) + def place_order(self, hostname, domain, location, hourly, router=None): """Places an order for a dedicated host. diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py index 5b195cab6..cc30a694d 100644 --- a/tests/CLI/modules/dedicatedhost_tests.py +++ b/tests/CLI/modules/dedicatedhost_tests.py @@ -34,6 +34,37 @@ def test_list_dedicated_hosts(self): }] ) + def test_details(self): + mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') + mock.return_value = SoftLayer_Virtual_DedicatedHost.getObjectById + + result = self.run_command(['dedicatedhost', 'detail', '44701', '--price', '--guests']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), { + "datacenter": "dal05", + "id": 44701, + "name": "khnguyendh", + "create date": "2017-11-02T11:40:56-07:00", + "price_rate": 1515.556, + "owner": "232298_khuong", + "modify date": "2017-11-06T11:38:20-06:00", + "memory capacity": 242, + "guests": [ + { + "uuid": "806a56ec-0383-4c2e-e6a9-7dc89c4b29a2", + "hostname": "khnguyenDHI", + "domain": "Softlayer.com", + "id": 43546081 + } + ], + "guest count": 1, + "cpu count": 56, + "router id": 51218, + "disk capacity": 1200 + } + ) + def test_create_options(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsDH diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py index ddcfd1ba7..c40d8127a 100644 --- a/tests/managers/dedicated_host_tests.py +++ b/tests/managers/dedicated_host_tests.py @@ -33,6 +33,31 @@ def test_list_instances_with_filters(self): ) self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) + def test_get_host(self): + + self.dedicated_host.host = mock.Mock() + self.dedicated_host.host.getObject.return_value = 'test' + + self.dedicated_host.get_host(12345) + + mask = ( + 'id,' + 'name,' + 'cpuCount,' + 'memoryCapacity,' + 'diskCapacity,' + 'createDate,' + 'modifyDate,' + 'backendRouter[id, hostname, domain],' + 'billingItem[id, nextInvoiceTotalRecurringAmount, ' + 'children[categoryCode,nextInvoiceTotalRecurringAmount],' + 'orderItem[id, order.userRecord[username]]],' + 'datacenter[id, name, longName],' + 'guests[id, hostname, domain, uuid],' + 'guestCount' + ) + self.dedicated_host.host.getObject.assert_called_once_with(id=12345, mask=mask) + def test_place_order(self): create_dict = self.dedicated_host._generate_create_dict = mock.Mock() From 913e721841e6a170cc1e2a400067009a6061764f Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Mon, 4 Dec 2017 13:23:38 -0600 Subject: [PATCH 5/6] Virt-4404 : Made suggested code changes and added dedicated host functionality --- SoftLayer/CLI/dedicatedhost/create.py | 83 +++--- SoftLayer/CLI/dedicatedhost/create_options.py | 59 ++-- SoftLayer/CLI/dedicatedhost/detail.py | 7 +- SoftLayer/CLI/hardware/create.py | 1 + .../fixtures/SoftLayer_Product_Package.py | 247 +++++++---------- .../SoftLayer_Virtual_DedicatedHost.py | 12 +- SoftLayer/managers/dedicated_host.py | 80 ++++-- tests/CLI/modules/dedicatedhost_tests.py | 252 +++++++++++++----- tests/managers/dedicated_host_tests.py | 195 ++++++++------ 9 files changed, 556 insertions(+), 380 deletions(-) diff --git a/SoftLayer/CLI/dedicatedhost/create.py b/SoftLayer/CLI/dedicatedhost/create.py index 19890cdd6..2810c6ea7 100644 --- a/SoftLayer/CLI/dedicatedhost/create.py +++ b/SoftLayer/CLI/dedicatedhost/create.py @@ -7,7 +7,7 @@ from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting -from SoftLayer.CLI import helpers +from SoftLayer.CLI import template @click.command( @@ -17,7 +17,7 @@ required=True, prompt=True) @click.option('--router', '-r', - help="Router id", + help="Router hostname ex. fcr02a.dal13", show_default=True) @click.option('--domain', '-D', help="Domain portion of the FQDN", @@ -26,6 +26,9 @@ @click.option('--datacenter', '-d', help="Datacenter shortname", required=True, prompt=True) +@click.option('--flavor', '-f', help="Dedicated Virtual Host flavor", + required=True, + prompt=True) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='hourly', @@ -34,7 +37,14 @@ @click.option('--test', is_flag=True, help="Do not actually create the server") -@helpers.multi_option('--extra', '-e', help="Extra options") +@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), + help="Exports options to a template file") @environment.pass_env def cli(env, **args): """Order/create a dedicated host.""" @@ -43,48 +53,51 @@ def cli(env, **args): order = { 'hostname': args['hostname'], 'domain': args['domain'], + 'flavor': args['flavor'], 'router': args['router'], 'location': args.get('datacenter'), 'hourly': args.get('billing') == 'hourly', } - do_create = not (args['test']) + do_create = not (args['export'] or args['test']) output = None - if args.get('test'): - result = mgr.verify_order(**order) - - table = formatting.Table(['Item', 'cost']) - table.align['Item'] = 'r' - table.align['cost'] = 'r' - - for price in result['prices']: - if order['hourly']: - total = float(price.get('hourlyRecurringFee', 0.0)) - rate = "%.2f" % float(price['hourlyRecurringFee']) - else: - total = float(price.get('recurringFee', 0.0)) - rate = "%.2f" % float(price['recurringFee']) - - table.add_row([price['item']['description'], rate]) - - if order['hourly']: - table.add_row(['Total hourly cost', "%.2f" % total]) - else: - table.add_row(['Total monthly cost', "%.2f" % total]) - - output = [] - output.append(table) - output.append(formatting.FormattedItem( - '', - ' -- ! Prices reflected here are retail and do not ' - 'take account level discounts and are not guaranteed.')) + result = mgr.verify_order(**order) + table = formatting.Table(['Item', 'cost']) + table.align['Item'] = 'r' + table.align['cost'] = 'r' + if len(result['prices']) != 1: + raise exceptions.ArgumentError("More than 1 price was found or no " + "prices found") + price = result['prices'] + if order['hourly']: + total = float(price[0].get('hourlyRecurringFee', 0.0)) + else: + total = float(price[0].get('recurringFee', 0.0)) + + if order['hourly']: + table.add_row(['Total hourly cost', "%.2f" % total]) + else: + table.add_row(['Total monthly cost', "%.2f" % total]) + + output = [] + output.append(table) + output.append(formatting.FormattedItem( + '', + ' -- ! Prices reflected here are retail and do not ' + 'take account level discounts and are not guaranteed.')) + + if args['export']: + export_file = args.pop('export') + template.export_to_template(export_file, args, + exclude=['wait', 'test']) + env.fout('Successfully exported options to a template file.') if do_create: - if not (env.skip_confirmations or formatting.confirm( + if not env.skip_confirmations and not formatting.confirm( "This action will incur charges on your account. " - "Continue?")): + "Continue?"): raise exceptions.CLIAbort('Aborting dedicated host order.') result = mgr.place_order(**order) @@ -94,6 +107,6 @@ def cli(env, **args): table.align['value'] = 'l' table.add_row(['id', result['orderId']]) table.add_row(['created', result['orderDate']]) - output = table + output.append(table) env.fout(output) diff --git a/SoftLayer/CLI/dedicatedhost/create_options.py b/SoftLayer/CLI/dedicatedhost/create_options.py index 8f07c5b3e..1cf2e35af 100644 --- a/SoftLayer/CLI/dedicatedhost/create_options.py +++ b/SoftLayer/CLI/dedicatedhost/create_options.py @@ -5,30 +5,57 @@ import SoftLayer from SoftLayer.CLI import environment +from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() +@click.option('--datacenter', '-d', + help="Router hostname (requires --flavor) " + "ex. ams01", + show_default=True) +@click.option('--flavor', '-f', + help="Dedicated Virtual Host flavor (requires --datacenter)" + " ex. 56_CORES_X_242_RAM_X_1_4_TB", + show_default=True) @environment.pass_env -def cli(env): - """host order options for a given dedicated host.""" +def cli(env, **args): + """host order options for a given dedicated host. - mgr = SoftLayer.DedicatedHostManager(env.client) - options = mgr.get_create_options() + To get a list of available backend routers see example: + slcli dh create-options --datacenter dal05 --flavor 56_CORES_X_242_RAM_X_1_4_TB + """ + mgr = SoftLayer.DedicatedHostManager(env.client) tables = [] - # Datacenters - dc_table = formatting.Table(['datacenter', 'value']) - dc_table.sortby = 'value' - for location in options['locations']: - dc_table.add_row([location['name'], location['key']]) - tables.append(dc_table) - - dh_table = formatting.Table(['dedicated Virtual Host', 'value']) - dh_table.sortby = 'value' - for item in options['dedicated_host']: - dh_table.add_row([item['name'], item['key']]) - tables.append(dh_table) + if not args['flavor'] and not args['datacenter']: + options = mgr.get_create_options() + + # Datacenters + dc_table = formatting.Table(['datacenter', 'value']) + dc_table.sortby = 'value' + for location in options['locations']: + dc_table.add_row([location['name'], location['key']]) + tables.append(dc_table) + + dh_table = formatting.Table(['Dedicated Virtual Host Flavor(s)', 'value']) + dh_table.sortby = 'value' + for item in options['dedicated_host']: + dh_table.add_row([item['name'], item['key']]) + tables.append(dh_table) + else: + if args['flavor'] is None or args['datacenter'] is None: + raise exceptions.ArgumentError('Both a flavor and datacenter need ' + 'to be passed as arguments\n' + 'ex. slcli dh create-options -d ' + 'ams01 -f ' + '56_CORES_X_242_RAM_X_1_4_TB') + router_opt = mgr.get_router_options(args['datacenter'], args['flavor']) + br_table = formatting.Table( + ['Available Backend Routers']) + for router in router_opt: + br_table.add_row([router['hostname']]) + tables.append(br_table) env.fout(formatting.listing(tables, separator='\n')) diff --git a/SoftLayer/CLI/dedicatedhost/detail.py b/SoftLayer/CLI/dedicatedhost/detail.py index 4767ed1ca..4fba618bd 100644 --- a/SoftLayer/CLI/dedicatedhost/detail.py +++ b/SoftLayer/CLI/dedicatedhost/detail.py @@ -20,13 +20,13 @@ @environment.pass_env def cli(env, identifier, price=False, guests=False): """Get details for a virtual server.""" - dh = SoftLayer.DedicatedHostManager(env.client) + dhost = SoftLayer.DedicatedHostManager(env.client) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' - result = dh.get_host(identifier) + result = dhost.get_host(identifier) result = utils.NestedDict(result) table.add_row(['id', result['id']]) @@ -37,7 +37,8 @@ def cli(env, identifier, price=False, guests=False): table.add_row(['create date', result['createDate']]) table.add_row(['modify date', result['modifyDate']]) table.add_row(['router id', result['backendRouter']['id']]) - if utils.lookup(result, 'billingItem') != []: + table.add_row(['router hostname', result['backendRouter']['hostname']]) + if utils.lookup(result, 'billingItem') != {}: table.add_row(['owner', formatting.FormattedItem( utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', diff --git a/SoftLayer/CLI/hardware/create.py b/SoftLayer/CLI/hardware/create.py index 472cec2e2..db945e019 100644 --- a/SoftLayer/CLI/hardware/create.py +++ b/SoftLayer/CLI/hardware/create.py @@ -122,6 +122,7 @@ def cli(env, **args): return if do_create: + if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. " "Continue?")): diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package.py b/SoftLayer/fixtures/SoftLayer_Product_Package.py index 044ba5ee1..f46176ad4 100644 --- a/SoftLayer/fixtures/SoftLayer_Product_Package.py +++ b/SoftLayer/fixtures/SoftLayer_Product_Package.py @@ -888,159 +888,6 @@ 'sort': 0 }] -getAllObjectsDH = [{ - 'subDescription': 'Dedicated Host', - 'name': 'Dedicated Host', - 'items': [{ - 'prices': [{ - 'itemId': 10195, - 'setupFee': '0', - 'recurringFee': '2099', - 'tierMinimumThreshold': '', - 'hourlyRecurringFee': '3.164', - 'oneTimeFee': '0', - 'currentPriceFlag': '', - 'id': 200269, - 'sort': 0, - 'onSaleFlag': '', - 'laborFee': '0', - 'locationGroupId': '', - 'quantity': '' - }, - { - 'itemId': 10195, - 'setupFee': '0', - 'recurringFee': '2161.97', - 'tierMinimumThreshold': '', - 'hourlyRecurringFee': '3.258', - 'oneTimeFee': '0', - 'currentPriceFlag': '', - 'id': 200271, - 'sort': 0, - 'onSaleFlag': '', - 'laborFee': '0', - 'locationGroupId': 503, - 'quantity': '' - } - ], - 'itemCategory': { - 'categoryCode': 'dedicated_virtual_hosts' - }, - 'description': '56 Cores X 242 RAM X 1.2 TB', - 'id': 10195 - }], - 'keyName': 'DEDICATED_HOST', - 'unitSize': '', - 'regions': [{ - 'location': { - 'locationPackageDetails': [{ - 'isAvailable': 1, - 'locationId': 265592, - 'packageId': 813 - }], - 'location': { - 'statusId': 2, - 'priceGroups': [{ - 'locationGroupTypeId': 82, - 'description': 'Location Group 2', - 'locationGroupType': { - 'name': 'PRICING' - }, - 'securityLevelId': '', - 'id': 503, - 'name': 'Location Group 2' - }], - 'id': 265592, - 'name': 'ams01', - 'longName': 'Amsterdam 1' - } - }, - 'keyname': 'AMSTERDAM', - 'description': 'AMS01 - Amsterdam', - 'sortOrder': 0 - }, - { - 'location': { - 'locationPackageDetails': [{ - 'isAvailable': 1, - 'locationId': 814994, - 'packageId': 813 - }], - 'location': { - 'statusId': 2, - 'priceGroups': [{ - 'locationGroupTypeId': 82, - 'description': 'Location Group 2', - 'locationGroupType': { - 'name': 'PRICING' - }, - 'securityLevelId': '', - 'id': 503, - 'name': 'Location Group 2' - }, - { - 'locationGroupTypeId': 82, - 'description': 'COS Cross Region - EU', - 'locationGroupType': { - 'name': 'PRICING'}, - 'securityLevelId': '', - 'id': 1303, - 'name': 'eu'}], - 'id': 814994, - 'name': 'ams03', - 'longName': 'Amsterdam 3'}}, - 'keyname': 'AMSTERDAM03', - 'description': 'AMS03 - Amsterdam', - 'sortOrder': 2}, - {'location': { - 'locationPackageDetails': [ - { - 'isAvailable': 1, - 'locationId': 138124, - 'packageId': 813}], - 'location': { - 'statusId': 2, - 'priceGroups': [ - { - 'locationGroupTypeId': 82, - 'description': 'CDN - North America - Akamai', - 'locationGroupType': { - 'name': 'PRICING'}, - 'securityLevelId': '', - 'id': 1463, - 'name': 'NORTH-AMERICA-AKAMAI'}], - 'id': 138124, - 'name': 'dal05', - 'longName': 'Dallas 5'}}, - 'keyname': 'DALLAS05', - 'description': 'DAL05 - Dallas', - 'sortOrder': 12}, - {'location': { - 'locationPackageDetails': [ - { - 'isAvailable': 1, - 'locationId': 2017603, - 'packageId': 813}], - 'location': { - 'statusId': 2, - 'priceGroups': [ - { - 'locationGroupTypeId': 82, - 'description': 'COS Regional - US East', - 'locationGroupType': { - 'name': 'PRICING'}, - 'securityLevelId': '', - 'id': 1305, - 'name': 'us-east'}], - 'id': 2017603, - 'name': 'wdc07', - 'longName': 'Washington 7'}}, - 'keyname': 'WASHINGTON07', - 'description': 'WDC07 - Washington, DC', - 'sortOrder': 76}], - 'firstOrderStepId': '', 'id': 813, 'isActive': 1, - 'description': 'Dedicated Host'}] - verifyOrderDH = { 'preTaxSetup': '0', 'storageGroups': [], @@ -1180,3 +1027,97 @@ 'totalSetupTax': '0', 'quantity': 1 } + +getAllObjectsDH = [{ + "subDescription": "Dedicated Host", + "name": "Dedicated Host", + "items": [{ + "capacity": "56", + "description": "56 Cores X 242 RAM X 1.2 TB", + "bundleItems": [ + { + "capacity": "1200", + "categories": [{ + "categoryCode": "dedicated_host_disk" + }] + }, + { + "capacity": "242", + "categories": [{ + "categoryCode": "dedicated_host_ram" + }] + } + ], + "prices": [ + { + "itemId": 10195, + "setupFee": "0", + "recurringFee": "2099", + "tierMinimumThreshold": "", + "hourlyRecurringFee": "3.164", + "oneTimeFee": "0", + "currentPriceFlag": "", + "id": 200269, + "sort": 0, + "onSaleFlag": "", + "laborFee": "0", + "locationGroupId": "", + "quantity": "" + }, + { + "itemId": 10195, + "setupFee": "0", + "recurringFee": "2161.97", + "tierMinimumThreshold": "", + "hourlyRecurringFee": "3.258", + "oneTimeFee": "0", + "currentPriceFlag": "", + "id": 200271, + "sort": 0, + "onSaleFlag": "", + "laborFee": "0", + "locationGroupId": 503, + "quantity": "" + } + ], + "keyName": "56_CORES_X_242_RAM_X_1_4_TB", + "id": 10195, + "itemCategory": { + "categoryCode": "dedicated_virtual_hosts" + } + }], + "keyName": "DEDICATED_HOST", + "unitSize": "", + "regions": [{ + "location": { + "locationPackageDetails": [{ + "isAvailable": 1, + "locationId": 138124, + "packageId": 813 + }], + "location": { + "statusId": 2, + "priceGroups": [{ + "locationGroupTypeId": 82, + "description": "CDN - North America - Akamai", + "locationGroupType": { + "name": "PRICING" + }, + "securityLevelId": "", + "id": 1463, + "name": "NORTH-AMERICA-AKAMAI" + }], + "id": 138124, + "name": "dal05", + "longName": "Dallas 5" + } + }, + "keyname": "DALLAS05", + "description": "DAL05 - Dallas", + "sortOrder": 12 + }], + "firstOrderStepId": "", + "id": 813, + "isActive": 1, + "description": "Dedicated Host" +}] diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py index e5caa7332..ea1775eb9 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py @@ -62,15 +62,17 @@ } }, 'id': 235379377, - 'children': [{ - 'nextInvoiceTotalRecurringAmount': 0.0, - 'categoryCode': 'dedicated_host_ram' - }, + 'children': [ + { + 'nextInvoiceTotalRecurringAmount': 0.0, + 'categoryCode': 'dedicated_host_ram' + }, { 'nextInvoiceTotalRecurringAmount': 0.0, 'categoryCode': 'dedicated_host_disk' } - ]}, + ] + }, 'id': 44701, 'createDate': '2017-11-02T11:40:56-07:00' } diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py index 1051932e2..6d85fae88 100644 --- a/SoftLayer/managers/dedicated_host.py +++ b/SoftLayer/managers/dedicated_host.py @@ -12,6 +12,9 @@ from SoftLayer.managers import ordering from SoftLayer import utils +# Invalid names are ignored due to long method names and short argument names +# pylint: disable=invalid-name, no-self-use + LOGGER = logging.getLogger(__name__) @@ -148,7 +151,7 @@ def get_host(self, host_id, **kwargs): return self.host.getObject(id=host_id, **kwargs) - def place_order(self, hostname, domain, location, hourly, router=None): + def place_order(self, hostname, domain, location, flavor, hourly, router=None): """Places an order for a dedicated host. See get_create_options() for valid arguments. @@ -163,12 +166,13 @@ def place_order(self, hostname, domain, location, hourly, router=None): create_options = self._generate_create_dict(hostname=hostname, router=router, domain=domain, + flavor=flavor, datacenter=location, hourly=hourly) return self.client['Product_Order'].placeOrder(create_options) - def verify_order(self, hostname, domain, location, hourly, router=None): + def verify_order(self, hostname, domain, location, hourly, flavor, router=None): """Verifies an order for a dedicated host. See :func:`place_order` for a list of available options. @@ -177,6 +181,7 @@ def verify_order(self, hostname, domain, location, hourly, router=None): create_options = self._generate_create_dict(hostname=hostname, router=router, domain=domain, + flavor=flavor, datacenter=location, hourly=hourly) @@ -184,20 +189,22 @@ def verify_order(self, hostname, domain, location, hourly, router=None): def _generate_create_dict(self, hostname=None, - router=None, domain=None, + flavor=None, + router=None, datacenter=None, hourly=True): """Translates args into a dictionary for creating a dedicated host.""" package = self._get_package() - item = self._get_item(package) + item = self._get_item(package, flavor) + location = self._get_location(package['regions'], datacenter) price = self._get_price(item) - if router is None: - routers = self._get_backend_router( - location['location']['locationPackageDetails']) - router = self._get_default_router(routers) + routers = self._get_backend_router( + location['location']['locationPackageDetails'], item) + + router = self._get_default_router(routers, router) hardware = { 'hostname': hostname, @@ -229,7 +236,10 @@ def _get_package(self): id, description, prices, - itemCategory[categoryCode] + capacity, + keyName, + itemCategory[categoryCode], + bundleItems[capacity, categories[categoryCode]] ], regions[location[location[priceGroups]]] ''' @@ -241,7 +251,6 @@ def _get_package(self): if package is None: raise SoftLayer.SoftLayerError("Ordering package not found") - return package def _get_location(self, regions, datacenter): @@ -256,7 +265,6 @@ def _get_location(self, regions, datacenter): def get_create_options(self): """Returns valid options for ordering a dedicated host.""" - package = self._get_package() # Locations locations = [] @@ -265,13 +273,14 @@ def get_create_options(self): 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) + # flavors dedicated_host = [] for item in package['items']: if item['itemCategory']['categoryCode'] == \ 'dedicated_virtual_hosts': dedicated_host.append({ 'name': item['description'], - 'key': item['id'], + 'key': item['keyName'], }) return { @@ -283,38 +292,45 @@ def _get_price(self, package): """Returns valid price for ordering a dedicated host.""" for price in package['prices']: - if price.get('locationGroupId') is '': + if not price.get('locationGroupId'): return price['id'] raise SoftLayer.SoftLayerError( "Could not find valid price") - def _get_item(self, package): + def _get_item(self, package, flavor): """Returns the item for ordering a dedicated host.""" - description = '56 Cores X 242 RAM X 1.2 TB' for item in package['items']: - if item['description'] == description: + if item['keyName'] == flavor: return item raise SoftLayer.SoftLayerError("Could not find valid item for: '%s'" - % description) + % flavor) - def _get_backend_router(self, locations): + def _get_backend_router(self, locations, item): """Returns valid router options for ordering a dedicated host.""" mask = ''' id, hostname ''' + cpu_count = item['capacity'] + + for capacity in item['bundleItems']: + for category in capacity['categories']: + if category['categoryCode'] == 'dedicated_host_ram': + mem_capacity = capacity['capacity'] + if category['categoryCode'] == 'dedicated_host_disk': + disk_capacity = capacity['capacity'] if locations is not None: for location in locations: if location['locationId'] is not None: loc_id = location['locationId'] host = { - 'cpuCount': 56, - 'memoryCapacity': 242, - 'diskCapacity': 1200, + 'cpuCount': cpu_count, + 'memoryCapacity': mem_capacity, + 'diskCapacity': disk_capacity, 'datacenter': { 'id': loc_id } @@ -324,10 +340,24 @@ def _get_backend_router(self, locations): raise SoftLayer.SoftLayerError("Could not find available routers") - def _get_default_router(self, routers): + def _get_default_router(self, routers, router_name): """Returns the default router for ordering a dedicated host.""" - for router in routers: - if router['id'] is not None: - return router['id'] + if router_name is None: + for router in routers: + if router['id'] is not None: + return router['id'] + else: + for router in routers: + if router['hostname'] == router_name: + return router['id'] raise SoftLayer.SoftLayerError("Could not find valid default router") + + def get_router_options(self, datacenter=None, flavor=None): + """Returns available backend routers for the dedicated host.""" + package = self._get_package() + + location = self._get_location(package['regions'], datacenter) + item = self._get_item(package, flavor) + + return self._get_backend_router(location['location']['locationPackageDetails'], item) diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py index cc30a694d..2d07e3a13 100644 --- a/tests/CLI/modules/dedicatedhost_tests.py +++ b/tests/CLI/modules/dedicatedhost_tests.py @@ -6,6 +6,7 @@ """ import json import mock +import os import SoftLayer from SoftLayer.CLI import exceptions @@ -34,6 +35,10 @@ def test_list_dedicated_hosts(self): }] ) + def tear_down(self): + if os.path.exists("test.txt"): + os.remove("test.txt") + def test_details(self): mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') mock.return_value = SoftLayer_Virtual_DedicatedHost.getObjectById @@ -41,83 +46,145 @@ def test_details(self): result = self.run_command(['dedicatedhost', 'detail', '44701', '--price', '--guests']) self.assert_no_fail(result) - self.assertEqual(json.loads(result.output), { - "datacenter": "dal05", - "id": 44701, - "name": "khnguyendh", - "create date": "2017-11-02T11:40:56-07:00", - "price_rate": 1515.556, - "owner": "232298_khuong", - "modify date": "2017-11-06T11:38:20-06:00", - "memory capacity": 242, - "guests": [ - { - "uuid": "806a56ec-0383-4c2e-e6a9-7dc89c4b29a2", - "hostname": "khnguyenDHI", - "domain": "Softlayer.com", - "id": 43546081 - } - ], - "guest count": 1, - "cpu count": 56, - "router id": 51218, - "disk capacity": 1200 - } - ) + self.assertEqual(json.loads(result.output), + { + 'cpu count': 56, + 'create date': '2017-11-02T11:40:56-07:00', + 'datacenter': 'dal05', + 'disk capacity': 1200, + 'guest count': 1, + 'guests': [{ + 'domain': 'Softlayer.com', + 'hostname': 'khnguyenDHI', + 'id': 43546081, + 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2' + }], + 'id': 44701, + 'memory capacity': 242, + 'modify date': '2017-11-06T11:38:20-06:00', + 'name': 'khnguyendh', + 'owner': '232298_khuong', + 'price_rate': 1515.556, + 'router hostname': 'bcr01a.dal05', + 'router id': 51218} + ) + + def test_details_no_owner(self): + mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') + retVal = SoftLayer_Virtual_DedicatedHost.getObjectById + retVal['billingItem'] = {} + mock.return_value = retVal + + result = self.run_command( + ['dedicatedhost', 'detail', '44701', '--price', '--guests']) + self.assert_no_fail(result) + + self.assertEqual(json.loads(result.output), {'cpu count': 56, + 'create date': '2017-11-02T11:40:56-07:00', + 'datacenter': 'dal05', + 'disk capacity': 1200, + 'guest count': 1, + 'guests': [{ + 'domain': 'Softlayer.com', + 'hostname': 'khnguyenDHI', + 'id': 43546081, + 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2'}], + 'id': 44701, + 'memory capacity': 242, + 'modify date': '2017-11-06T11:38:20-06:00', + 'name': 'khnguyendh', + 'owner': None, + 'price_rate': 0, + 'router hostname': 'bcr01a.dal05', + 'router id': 51218} + ) def test_create_options(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsDH - result = self.run_command(['dedicatedhost', 'create-options']) + result = self.run_command(['dh', 'create-options']) self.assert_no_fail(result) - self.assertEqual(json.loads(result.output), [ - [{"value": "ams01", "datacenter": "Amsterdam 1"}, - {"value": "ams03", "datacenter": "Amsterdam 3"}, - {"value": "dal05", "datacenter": "Dallas 5"}, - {"value": "wdc07", "datacenter": "Washington 7"}], [ - {"dedicated Virtual Host": "56 Cores X 242 RAM X 1.2 TB", - "value": 10195}]]) + self.assertEqual(json.loads(result.output), [[ + { + 'datacenter': 'Dallas 5', + 'value': 'dal05' + }], + [{ + 'Dedicated Virtual Host Flavor(s)': + '56 Cores X 242 RAM X 1.2 TB', + 'value': '56_CORES_X_242_RAM_X_1_4_TB' + } + ]] + ) + + def test_create_options_with_only_datacenter(self): + mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock.return_value = SoftLayer_Product_Package.getAllObjectsDH + + result = self.run_command(['dh', 'create-options', '-d=dal05']) + self.assertIsInstance(result.exception, exceptions.ArgumentError) + + def test_create_options_get_routers(self): + mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock.return_value = SoftLayer_Product_Package.getAllObjectsDH + + result = self.run_command(['dh', + 'create-options', + '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB']) + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), [[ + { + "Available Backend Routers": "bcr01a.dal05" + }, + { + "Available Backend Routers": "bcr02a.dal05" + }, + { + "Available Backend Routers": "bcr03a.dal05" + }, + { + "Available Backend Routers": "bcr04a.dal05" + } + ]] + ) def test_create(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = True - mock_package_obj = \ - self.set_mock('SoftLayer_Product_Package', 'getAllObjects') - mock_package_obj.return_value = \ - SoftLayer_Product_Package.getAllObjectsDH - mock_package_routers = \ - self.set_mock('SoftLayer_Virtual_DedicatedHost', - 'getAvailableRouters') - mock_package_routers.return_value = \ - SoftLayer_Virtual_DedicatedHost.getAvailableRouters + mock_package_obj = self.set_mock('SoftLayer_Product_Package', + 'getAllObjects') + mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dedicatedhost', 'create', '--hostname=host', '--domain=example.com', '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assert_no_fail(result) - - self.assertEqual(json.loads(result.output), - {'created': '2013-08-01 15:23:45', 'id': 1234}) - args = ({ - 'useHourlyPricing': True, 'hardware': [{ - 'hostname': u'host', - 'domain': u'example.com', + 'domain': 'example.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } - } + }, + 'hostname': 'host' + }], + 'prices': [{ + 'id': 200269 }], - 'packageId': 813, 'prices': [{'id': 200269}], 'location': 'DALLAS05', - 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', - 'quantity': 1},) + 'packageId': 813, + 'complexType': + 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'useHourlyPricing': True, + 'quantity': 1}, + ) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) @@ -125,25 +192,17 @@ def test_create(self): def test_create_verify(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = True - mock_package_obj = \ - self.set_mock('SoftLayer_Product_Package', 'getAllObjects') - mock_package_obj.return_value = \ - SoftLayer_Product_Package.getAllObjectsDH - mock_package_routers = \ - self.set_mock('SoftLayer_Virtual_DedicatedHost', - 'getAvailableRouters') - mock_package_routers.return_value = \ - SoftLayer_Virtual_DedicatedHost.getAvailableRouters - mock_package = \ - self.set_mock('SoftLayer_Product_Order', 'verifyOrder') - mock_package.return_value = \ - SoftLayer_Product_Package.verifyOrderDH + mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH + mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') + mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH result = self.run_command(['dedicatedhost', 'create', '--test', '--hostname=host', '--domain=example.com', '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assert_no_fail(result) @@ -168,11 +227,12 @@ def test_create_verify(self): self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args) - result = self.run_command(['dedicatedhost', 'create', + result = self.run_command(['dh', 'create', '--test', '--hostname=host', '--domain=example.com', '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=monthly']) self.assert_no_fail(result) @@ -198,12 +258,70 @@ def test_create_verify(self): def test_create_aborted(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = False + mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH - result = self.run_command(['dedicatedhost', - 'create', + result = self.run_command(['dh', 'create', '--hostname=host', '--domain=example.com', '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=monthly']) + self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) + + def test_create_export(self): + mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH + mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') + mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH + + self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', + '--billing=hourly', + '--export=test.txt']) + + self.assertEqual(os.path.exists("test.txt"), True) + + def test_create_verify_no_price_or_more_than_one(self): + mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') + mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH + mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') + ret_val = SoftLayer_Product_Package.verifyOrderDH + ret_val['prices'] = [] + mock_package.return_value = ret_val + + result = self.run_command(['dedicatedhost', 'create', + '--test', + '--hostname=host', + '--domain=example.com', + '--datacenter=dal05', + '--flavor=56_CORES_X_242_RAM_X_1_4_TB', + '--billing=hourly']) + + self.assertIsInstance(result.exception, exceptions.ArgumentError) + args = ({ + 'hardware': [{ + 'domain': 'example.com', + 'primaryBackendNetworkComponent': { + 'router': { + 'id': 51218 + } + }, + 'hostname': 'host' + }], + 'prices': [{ + 'id': 200269 + }], + 'location': 'DALLAS05', + 'packageId': 813, + 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', + 'useHourlyPricing': True, + 'quantity': 1},) + + self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args) diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py index c40d8127a..9ebf6822b 100644 --- a/tests/managers/dedicated_host_tests.py +++ b/tests/managers/dedicated_host_tests.py @@ -86,20 +86,23 @@ def test_place_order(self): } create_dict.return_value = values - location = 'ams01' + location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True + flavor = '56_CORES_X_242_RAM_X_1_4_TB' self.dedicated_host.place_order(hostname=hostname, domain=domain, location=location, + flavor=flavor, hourly=hourly) create_dict.assert_called_once_with(hostname=hostname, router=None, domain=domain, datacenter=location, + flavor=flavor, hourly=True) self.assert_called_with('SoftLayer_Product_Order', @@ -117,8 +120,8 @@ def test_verify_order(self): 'id': 51218 } }, - 'domain': u'test.com', - 'hostname': u'test' + 'domain': 'test.com', + 'hostname': 'test' } ], 'useHourlyPricing': True, @@ -134,20 +137,23 @@ def test_verify_order(self): } create_dict.return_value = values - location = 'ams01' + location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True + flavor = '56_CORES_X_242_RAM_X_1_4_TB' self.dedicated_host.verify_order(hostname=hostname, domain=domain, location=location, + flavor=flavor, hourly=hourly) create_dict.assert_called_once_with(hostname=hostname, router=None, domain=domain, datacenter=location, + flavor=flavor, hourly=True) self.assert_called_with('SoftLayer_Product_Order', @@ -161,14 +167,16 @@ def test_generate_create_dict_without_router(self): self.dedicated_host._get_backend_router.return_value = self \ ._get_routers_sample() - location = 'ams01' + location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True + flavor = '56_CORES_X_242_RAM_X_1_4_TB' results = self.dedicated_host._generate_create_dict(hostname=hostname, domain=domain, datacenter=location, + flavor=flavor, hourly=hourly) testResults = { @@ -179,12 +187,12 @@ def test_generate_create_dict_without_router(self): 'id': 51218 } }, - 'domain': u'test.com', - 'hostname': u'test' + 'domain': 'test.com', + 'hostname': 'test' } ], 'useHourlyPricing': True, - 'location': 'AMSTERDAM', + 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ @@ -200,18 +208,22 @@ def test_generate_create_dict_without_router(self): def test_generate_create_dict_with_router(self): self.dedicated_host._get_package = mock.MagicMock() self.dedicated_host._get_package.return_value = self._get_package() + self.dedicated_host._get_default_router = mock.Mock() + self.dedicated_host._get_default_router.return_value = 51218 - location = 'ams01' - router = 55901 + location = 'dal05' + router = 51218 hostname = 'test' domain = 'test.com' hourly = True + flavor = '56_CORES_X_242_RAM_X_1_4_TB' results = self.dedicated_host._generate_create_dict( hostname=hostname, router=router, domain=domain, datacenter=location, + flavor=flavor, hourly=hourly) testResults = { @@ -219,15 +231,15 @@ def test_generate_create_dict_with_router(self): { 'primaryBackendNetworkComponent': { 'router': { - 'id': 55901 + 'id': 51218 } }, - 'domain': u'test.com', - 'hostname': u'test' + 'domain': 'test.com', + 'hostname': 'test' } ], 'useHourlyPricing': True, - 'location': 'AMSTERDAM', + 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', @@ -247,7 +259,10 @@ def test_get_package(self): id, description, prices, - itemCategory[categoryCode] + capacity, + keyName, + itemCategory[categoryCode], + bundleItems[capacity, categories[categoryCode]] ], regions[location[location[priceGroups]]] ''' @@ -270,7 +285,10 @@ def test_get_package_no_package_found(self): id, description, prices, - itemCategory[categoryCode] + capacity, + keyName, + itemCategory[categoryCode], + bundleItems[capacity, categories[categoryCode]] ], regions[location[location[priceGroups]]] ''' @@ -326,16 +344,18 @@ def test_get_create_options(self): self.dedicated_host._get_package.return_value = self._get_package() results = { - 'dedicated_host': [ - { - 'key': 10195, - 'name': '56 Cores X 242 RAM X 1.2 TB' - } - ], + 'dedicated_host': [{ + 'key': '56_CORES_X_242_RAM_X_1_4_TB', + 'name': '56 Cores X 242 RAM X 1.2 TB' + }], 'locations': [ { 'key': 'ams01', 'name': 'Amsterdam 1' + }, + { + 'key': 'dal05', + 'name': 'Dallas 5' } ] } @@ -360,41 +380,46 @@ def test_get_price_no_price_found(self): def test_get_item(self): """Returns the item for ordering a dedicated host.""" package = self._get_package() + flavor = '56_CORES_X_242_RAM_X_1_4_TB' item = { + 'bundleItems': [{ + 'capacity': '1200', + 'categories': [{ + 'categoryCode': 'dedicated_host_disk' + }] + }, + { + 'capacity': '242', + 'categories': [{ + 'categoryCode': 'dedicated_host_ram' + }] + }], + 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'id': 10195, 'itemCategory': { 'categoryCode': 'dedicated_virtual_hosts' }, - 'prices': [ - { - 'currentPriceFlag': '', - 'hourlyRecurringFee': '3.164', - 'id': 200269, - 'itemId': 10195, - 'laborFee': '0', - 'locationGroupId': '', - 'onSaleFlag': '', - 'oneTimeFee': '0', - 'quantity': '', - 'recurringFee': '2099', - 'setupFee': '0', - 'sort': 0, - 'tierMinimumThreshold': '' - } - ] + 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', + 'prices': [{ + 'hourlyRecurringFee': '3.164', + 'id': 200269, + 'itemId': 10195, + 'recurringFee': '2099', + }] } - self.assertEqual(self.dedicated_host._get_item(package), item) + self.assertEqual(self.dedicated_host._get_item(package, flavor), item) def test_get_item_no_item_found(self): package = self._get_package() - package['items'][0]['description'] = 'not found' + flavor = '56_CORES_X_242_RAM_X_1_4_TB' + package['items'][0]['keyName'] = 'not found' self.assertRaises(exceptions.SoftLayerError, - self.dedicated_host._get_item, package) + self.dedicated_host._get_item, package, flavor) def test_get_backend_router(self): location = [ @@ -413,9 +438,9 @@ def test_get_backend_router(self): ''' host = { - 'cpuCount': 56, - 'memoryCapacity': 242, - 'diskCapacity': 1200, + 'cpuCount': '56', + 'memoryCapacity': '242', + 'diskCapacity': '1200', 'datacenter': { 'id': locId } @@ -426,7 +451,9 @@ def test_get_backend_router(self): routers = self.dedicated_host.host.getAvailableRouters.return_value = \ self._get_routers_sample() - routers_test = self.dedicated_host._get_backend_router(location) + item = self._get_package()['items'][0] + + routers_test = self.dedicated_host._get_backend_router(location, item) self.assertEqual(routers, routers_test) self.dedicated_host.host.getAvailableRouters. \ @@ -439,14 +466,16 @@ def test_get_backend_router_no_routers_found(self): routers_test = self.dedicated_host._get_backend_router - self.assertRaises(exceptions.SoftLayerError, routers_test, location) + item = self._get_package()['items'][0] + + self.assertRaises(exceptions.SoftLayerError, routers_test, location, item) def test_get_default_router(self): routers = self._get_routers_sample() router = 51218 - router_test = self.dedicated_host._get_default_router(routers) + router_test = self.dedicated_host._get_default_router(routers, 'bcr01a.dal05') self.assertEqual(router_test, router) @@ -454,7 +483,7 @@ def test_get_default_router_no_router_found(self): routers = [] self.assertRaises(exceptions.SoftLayerError, - self.dedicated_host._get_default_router, routers) + self.dedicated_host._get_default_router, routers, 'notFound') def _get_routers_sample(self): routers = [ @@ -482,28 +511,39 @@ def _get_package(self): package = { "items": [ { + "capacity": "56", + "description": "56 Cores X 242 RAM X 1.2 TB", + "bundleItems": [ + { + "capacity": "1200", + "categories": [ + { + "categoryCode": "dedicated_host_disk" + } + ] + }, + { + "capacity": "242", + "categories": [ + { + "categoryCode": "dedicated_host_ram" + } + ] + } + ], "prices": [ { "itemId": 10195, - "setupFee": "0", "recurringFee": "2099", - "tierMinimumThreshold": "", "hourlyRecurringFee": "3.164", - "oneTimeFee": "0", - "currentPriceFlag": "", "id": 200269, - "sort": 0, - "onSaleFlag": "", - "laborFee": "0", - "locationGroupId": "", - "quantity": "" } ], + "keyName": "56_CORES_X_242_RAM_X_1_4_TB", + "id": 10195, "itemCategory": { "categoryCode": "dedicated_virtual_hosts" }, - "description": "56 Cores X 242 RAM X 1.2 TB", - "id": 10195 } ], "regions": [ @@ -511,25 +551,11 @@ def _get_package(self): "location": { "locationPackageDetails": [ { - "isAvailable": 1, "locationId": 265592, "packageId": 813 } ], "location": { - "statusId": 2, - "priceGroups": [ - { - "locationGroupTypeId": 82, - "description": "Location Group 2", - "locationGroupType": { - "name": "PRICING" - }, - "securityLevelId": "", - "id": 503, - "name": "Location Group 2" - } - ], "id": 265592, "name": "ams01", "longName": "Amsterdam 1" @@ -538,11 +564,28 @@ def _get_package(self): "keyname": "AMSTERDAM", "description": "AMS01 - Amsterdam", "sortOrder": 0 + }, + { + "location": { + "locationPackageDetails": [ + { + "isAvailable": 1, + "locationId": 138124, + "packageId": 813 + } + ], + "location": { + "id": 138124, + "name": "dal05", + "longName": "Dallas 5" + } + }, + "keyname": "DALLAS05", + "description": "DAL05 - Dallas", } + ], - "firstOrderStepId": "", "id": 813, - "isActive": 1, "description": "Dedicated Host" } From 2abc785992cc3e61ae66e3ccc72ebebc453fcbe6 Mon Sep 17 00:00:00 2001 From: Khuong-Nguyen Date: Mon, 11 Dec 2017 09:18:12 -0600 Subject: [PATCH 6/6] Added Dedicated host functionality --- SoftLayer/CLI/dedicatedhost/create.py | 30 ++++----- SoftLayer/CLI/dedicatedhost/create_options.py | 10 +-- SoftLayer/CLI/dedicatedhost/detail.py | 10 +-- SoftLayer/CLI/hardware/create.py | 1 - SoftLayer/managers/dedicated_host.py | 63 ++++++++++++------- tests/CLI/modules/dedicatedhost_tests.py | 8 +-- tests/managers/dedicated_host_tests.py | 56 ++++++++++++----- 7 files changed, 109 insertions(+), 69 deletions(-) diff --git a/SoftLayer/CLI/dedicatedhost/create.py b/SoftLayer/CLI/dedicatedhost/create.py index 2810c6ea7..491da2110 100644 --- a/SoftLayer/CLI/dedicatedhost/create.py +++ b/SoftLayer/CLI/dedicatedhost/create.py @@ -34,9 +34,9 @@ default='hourly', show_default=True, help="Billing rate") -@click.option('--test', +@click.option('--verify', is_flag=True, - help="Do not actually create the server") + help="Verify dedicatedhost without creating it.") @click.option('--template', '-t', is_eager=True, callback=template.TemplateCallback(list_args=['key']), @@ -46,20 +46,22 @@ type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") @environment.pass_env -def cli(env, **args): +def cli(env, **kwargs): """Order/create a dedicated host.""" mgr = SoftLayer.DedicatedHostManager(env.client) order = { - 'hostname': args['hostname'], - 'domain': args['domain'], - 'flavor': args['flavor'], - 'router': args['router'], - 'location': args.get('datacenter'), - 'hourly': args.get('billing') == 'hourly', + 'hostname': kwargs['hostname'], + 'domain': kwargs['domain'], + 'flavor': kwargs['flavor'], + 'location': kwargs['datacenter'], + 'hourly': kwargs.get('billing') == 'hourly', } - do_create = not (args['export'] or args['test']) + if kwargs['router']: + order['router'] = kwargs['router'] + + do_create = not (kwargs['export'] or kwargs['verify']) output = None @@ -88,10 +90,10 @@ def cli(env, **args): ' -- ! Prices reflected here are retail and do not ' 'take account level discounts and are not guaranteed.')) - if args['export']: - export_file = args.pop('export') - template.export_to_template(export_file, args, - exclude=['wait', 'test']) + if kwargs['export']: + export_file = kwargs.pop('export') + template.export_to_template(export_file, kwargs, + exclude=['wait', 'verify']) env.fout('Successfully exported options to a template file.') if do_create: diff --git a/SoftLayer/CLI/dedicatedhost/create_options.py b/SoftLayer/CLI/dedicatedhost/create_options.py index 1cf2e35af..94727ce68 100644 --- a/SoftLayer/CLI/dedicatedhost/create_options.py +++ b/SoftLayer/CLI/dedicatedhost/create_options.py @@ -19,7 +19,7 @@ " ex. 56_CORES_X_242_RAM_X_1_4_TB", show_default=True) @environment.pass_env -def cli(env, **args): +def cli(env, **kwargs): """host order options for a given dedicated host. To get a list of available backend routers see example: @@ -29,7 +29,7 @@ def cli(env, **args): mgr = SoftLayer.DedicatedHostManager(env.client) tables = [] - if not args['flavor'] and not args['datacenter']: + if not kwargs['flavor'] and not kwargs['datacenter']: options = mgr.get_create_options() # Datacenters @@ -45,13 +45,13 @@ def cli(env, **args): dh_table.add_row([item['name'], item['key']]) tables.append(dh_table) else: - if args['flavor'] is None or args['datacenter'] is None: + if kwargs['flavor'] is None or kwargs['datacenter'] is None: raise exceptions.ArgumentError('Both a flavor and datacenter need ' - 'to be passed as arguments\n' + 'to be passed as arguments ' 'ex. slcli dh create-options -d ' 'ams01 -f ' '56_CORES_X_242_RAM_X_1_4_TB') - router_opt = mgr.get_router_options(args['datacenter'], args['flavor']) + router_opt = mgr.get_router_options(kwargs['datacenter'], kwargs['flavor']) br_table = formatting.Table( ['Available Backend Routers']) for router in router_opt: diff --git a/SoftLayer/CLI/dedicatedhost/detail.py b/SoftLayer/CLI/dedicatedhost/detail.py index 4fba618bd..e1c46b962 100644 --- a/SoftLayer/CLI/dedicatedhost/detail.py +++ b/SoftLayer/CLI/dedicatedhost/detail.py @@ -38,14 +38,8 @@ def cli(env, identifier, price=False, guests=False): table.add_row(['modify date', result['modifyDate']]) table.add_row(['router id', result['backendRouter']['id']]) table.add_row(['router hostname', result['backendRouter']['hostname']]) - if utils.lookup(result, 'billingItem') != {}: - table.add_row(['owner', formatting.FormattedItem( - utils.lookup(result, 'billingItem', 'orderItem', - 'order', 'userRecord', - 'username') or formatting.blank(), - )]) - else: - table.add_row(['owner', formatting.blank()]) + table.add_row(['owner', formatting.FormattedItem( + utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', 'username') or formatting.blank(),)]) if price: total_price = utils.lookup(result, diff --git a/SoftLayer/CLI/hardware/create.py b/SoftLayer/CLI/hardware/create.py index db945e019..472cec2e2 100644 --- a/SoftLayer/CLI/hardware/create.py +++ b/SoftLayer/CLI/hardware/create.py @@ -122,7 +122,6 @@ def cli(env, **args): return if do_create: - if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. " "Continue?")): diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py index 6d85fae88..80e0f3759 100644 --- a/SoftLayer/managers/dedicated_host.py +++ b/SoftLayer/managers/dedicated_host.py @@ -79,8 +79,6 @@ def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, ] kwargs['mask'] = "mask[%s]" % ','.join(items) - call = 'getDedicatedHosts' - _filter = utils.NestedDict(kwargs.get('filter') or {}) if tags: _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { @@ -109,8 +107,7 @@ def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, utils.query_filter(datacenter)) kwargs['filter'] = _filter.to_dict() - func = getattr(self.account, call) - return func(**kwargs) + return self.account.getDedicatedHosts(**kwargs) def get_host(self, host_id, **kwargs): """Get details about a dedicated host. @@ -132,22 +129,46 @@ def get_host(self, host_id, **kwargs): """ if 'mask' not in kwargs: - kwargs['mask'] = ( - 'id,' - 'name,' - 'cpuCount,' - 'memoryCapacity,' - 'diskCapacity,' - 'createDate,' - 'modifyDate,' - 'backendRouter[id, hostname, domain],' - 'billingItem[id, nextInvoiceTotalRecurringAmount, ' - 'children[categoryCode,nextInvoiceTotalRecurringAmount],' - 'orderItem[id, order.userRecord[username]]],' - 'datacenter[id, name, longName],' - 'guests[id, hostname, domain, uuid],' - 'guestCount' - ) + kwargs['mask'] = (''' + id, + name, + cpuCount, + memoryCapacity, + diskCapacity, + createDate, + modifyDate, + backendRouter[ + id, + hostname, + domain + ], + billingItem[ + id, + nextInvoiceTotalRecurringAmount, + children[ + categoryCode, + nextInvoiceTotalRecurringAmount + ], + orderItem[ + id, + order.userRecord[ + username + ] + ] + ], + datacenter[ + id, + name, + longName + ], + guests[ + id, + hostname, + domain, + uuid + ], + guestCount + ''') return self.host.getObject(id=host_id, **kwargs) @@ -340,7 +361,7 @@ def _get_backend_router(self, locations, item): raise SoftLayer.SoftLayerError("Could not find available routers") - def _get_default_router(self, routers, router_name): + def _get_default_router(self, routers, router_name=None): """Returns the default router for ordering a dedicated host.""" if router_name is None: for router in routers: diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py index 2d07e3a13..ab62063c8 100644 --- a/tests/CLI/modules/dedicatedhost_tests.py +++ b/tests/CLI/modules/dedicatedhost_tests.py @@ -198,7 +198,7 @@ def test_create_verify(self): mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH result = self.run_command(['dedicatedhost', 'create', - '--test', + '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', @@ -228,7 +228,7 @@ def test_create_verify(self): args=args) result = self.run_command(['dh', 'create', - '--test', + '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', @@ -278,7 +278,7 @@ def test_create_export(self): mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH self.run_command(['dedicatedhost', 'create', - '--test', + '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', @@ -297,7 +297,7 @@ def test_create_verify_no_price_or_more_than_one(self): mock_package.return_value = ret_val result = self.run_command(['dedicatedhost', 'create', - '--test', + '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py index 9ebf6822b..a20fc6349 100644 --- a/tests/managers/dedicated_host_tests.py +++ b/tests/managers/dedicated_host_tests.py @@ -40,22 +40,46 @@ def test_get_host(self): self.dedicated_host.get_host(12345) - mask = ( - 'id,' - 'name,' - 'cpuCount,' - 'memoryCapacity,' - 'diskCapacity,' - 'createDate,' - 'modifyDate,' - 'backendRouter[id, hostname, domain],' - 'billingItem[id, nextInvoiceTotalRecurringAmount, ' - 'children[categoryCode,nextInvoiceTotalRecurringAmount],' - 'orderItem[id, order.userRecord[username]]],' - 'datacenter[id, name, longName],' - 'guests[id, hostname, domain, uuid],' - 'guestCount' - ) + mask = (''' + id, + name, + cpuCount, + memoryCapacity, + diskCapacity, + createDate, + modifyDate, + backendRouter[ + id, + hostname, + domain + ], + billingItem[ + id, + nextInvoiceTotalRecurringAmount, + children[ + categoryCode, + nextInvoiceTotalRecurringAmount + ], + orderItem[ + id, + order.userRecord[ + username + ] + ] + ], + datacenter[ + id, + name, + longName + ], + guests[ + id, + hostname, + domain, + uuid + ], + guestCount + ''') self.dedicated_host.host.getObject.assert_called_once_with(id=12345, mask=mask) def test_place_order(self):