Skip to content

Commit 258c215

Browse files
committed
Work on hardware ordering (adds extra options, makes it work)
1 parent c7d8325 commit 258c215

4 files changed

Lines changed: 164 additions & 26 deletions

File tree

SoftLayer/CLI/server/create.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
@click.option('--size', '-s', help="Hardware size")
1818
@click.option('--os', '-o', help="OS install code")
1919
@click.option('--datacenter', '-d', help="Datacenter shortname")
20+
@click.option('--billing',
21+
type=click.Choice(['hourly', 'monthly']),
22+
default='hourly',
23+
help="Billing rate")
24+
@click.option('--port-speed', type=click.INT, help="Port speeds")
2025
@click.option('--test',
2126
is_flag=True,
2227
help="Do not actually create the virtual server")
@@ -34,6 +39,7 @@
3439
help="The ID of the private VLAN on which you want the virtual "
3540
"server placed",
3641
type=click.INT)
42+
@click.option('--extras', default='', help="Extra options; comma-separated")
3743
@click.option('--wait',
3844
type=click.INT,
3945
help="Wait until the server is finished provisioning for up to "
@@ -63,6 +69,10 @@ def cli(env, **args):
6369
'ssh_keys': ssh_keys,
6470
'post_uri': args.get('postinstall'),
6571
'os': args['os'],
72+
'hourly': args.get('billing') == 'hourly',
73+
'port_speed': args.get('port_speed'),
74+
'no_public': args.get('no_public') or False,
75+
'extras': args.get('extras').split(','),
6676
}
6777

6878
# Do not create hardware server with --test or --export

SoftLayer/CLI/server/create_options.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# :license: MIT, see LICENSE for more details.
33
from SoftLayer.CLI import environment
44
from SoftLayer.CLI import formatting
5+
from SoftLayer.managers import hardware
56

67
import click
78

@@ -14,7 +15,15 @@ def cli(env):
1415
mask = """
1516
id,
1617
activePresets,
17-
items[keyName,description,itemCategory[id,categoryCode]],
18+
items[
19+
keyName,
20+
capacity,
21+
description,
22+
attributes[id,attributeTypeKeyName],
23+
softwareDescription[id,referenceCode,longDescription],
24+
itemCategory[id,categoryCode],
25+
prices
26+
],
1827
regions[location[location]]
1928
"""
2029
package = env.client['Product_Package'].getObject(id=200, mask=mask)
@@ -40,7 +49,24 @@ def cli(env):
4049
os_table = formatting.Table(['operating_system', 'value'])
4150
for item in package['items']:
4251
if item['itemCategory']['categoryCode'] == 'os':
43-
os_table.add_row([item['description'], item['keyName']])
52+
os_table.add_row([item['softwareDescription']['longDescription'],
53+
item['softwareDescription']['referenceCode']])
4454
tables.append(os_table)
4555

56+
# Port speed
57+
port_speed_table = formatting.Table(['port_speed', 'value'])
58+
for item in package['items']:
59+
if all([item['itemCategory']['categoryCode'] == 'port_speed',
60+
not hardware.is_private_port_speed_item(item),
61+
]):
62+
port_speed_table.add_row([item['description'], item['capacity']])
63+
tables.append(port_speed_table)
64+
65+
port_speed_table = formatting.Table(['extras', 'value'])
66+
for item in package['items']:
67+
if item['itemCategory']['categoryCode'] in ['pri_ipv6_addresses',
68+
'static_ipv6_addresses',
69+
'sec_ip_addresses']:
70+
port_speed_table.add_row([item['description'], item['keyName']])
71+
tables.append(port_speed_table)
4672
return tables

SoftLayer/CLI/virt/create.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
@click.option('--billing',
2626
type=click.Choice(['hourly', 'monthly']),
2727
default='hourly',
28-
help="""Billing rate""")
28+
help="Billing rate")
2929
@click.option('--datacenter', '-d', help="Datacenter shortname")
3030
@click.option('--dedicated/--public',
3131
is_flag=True,
@@ -114,7 +114,7 @@ def cli(env, **args):
114114
total = total_monthly
115115

116116
billing_rate = 'monthly'
117-
if args.get('hourly'):
117+
if args.get('billing') == 'hourly':
118118
billing_rate = 'hourly'
119119
table.add_row(['Total %s cost' % billing_rate, "%.2f" % total])
120120
output.append(table)

SoftLayer/managers/hardware.py

Lines changed: 124 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@
1313
# Invalid names are ignored due to long method names and short argument names
1414
# pylint: disable=invalid-name, no-self-use
1515

16-
REQUIRED_CATEGORIES = [
17-
'bandwidth',
18-
'port_speed',
19-
'pri_ip_addresses',
20-
'vpn_management',
21-
'remote_management',
22-
]
23-
2416

2517
class HardwareManager(utils.IdentifierMixin, object):
2618
"""Manage hardware devices.
@@ -309,13 +301,17 @@ def _generate_create_dict(self,
309301
domain=None,
310302
datacenter=None,
311303
os=None,
304+
port_speed=None,
312305
ssh_keys=None,
313306
public_vlan=None,
314307
private_vlan=None,
315-
post_uri=None):
316-
"""Translates arguments into a dictionary for creating a server.
308+
post_uri=None,
309+
hourly=True,
310+
no_public=False,
311+
extras=None):
312+
"""Translates arguments into a dictionary for creating a server."""
317313

318-
"""
314+
extras = extras or []
319315

320316
hardware = {
321317
'hostname': hostname,
@@ -332,26 +328,44 @@ def _generate_create_dict(self,
332328
package_mask = '''
333329
items[
334330
keyName,
331+
capacity,
335332
description,
333+
attributes[id,attributeTypeKeyName],
336334
itemCategory[id,categoryCode],
335+
softwareDescription[id,referenceCode],
337336
prices
338-
]
337+
],
338+
regions[location[location]]
339339
'''
340340
package = self.client['Product_Package'].getObject(id=200,
341341
mask=package_mask)
342342

343343
prices = []
344-
for category in REQUIRED_CATEGORIES:
345-
prices.append(_get_default_price_id(package['items'], category))
344+
for category in ['pri_ip_addresses',
345+
'vpn_management',
346+
'remote_management',
347+
]:
348+
prices.append(_get_default_price_id(package['items'],
349+
category,
350+
hourly))
346351

347352
prices.append(_get_os_price_id(package['items'], os))
353+
prices.append(_get_bandwidth_price_id(package['items'],
354+
hourly=hourly,
355+
no_public=no_public))
356+
prices.append(_get_port_speed_price_id(package['items'],
357+
port_speed,
358+
no_public))
359+
for extra in extras:
360+
prices.append(_get_extra_price_id(package['items'], extra, hourly))
348361

349362
order = {
350363
'hardware': [hardware],
351364
'location': datacenter, # TODO: lookup OS price id based on keyname instead of a name like "HONGKONG02"
352-
'prices': prices,
365+
'prices': [{'id': price} for price in prices],
353366
'packageId': 200, # TODO: Look this up based on package type
354367
'presetId': 70, # TODO: Verify that preset id is a thing
368+
'useHourlyPricing': hourly,
355369
}
356370

357371
if post_uri:
@@ -440,35 +454,123 @@ def update_firmware(self,
440454
id=hardware_id)
441455

442456

443-
def _get_default_price_id(items, option):
444-
"""Returns a 'free' price id given an option."""
457+
def _get_extra_price_id(items, key_name, hourly):
458+
"""Returns a price id attached to item with the given key_name."""
459+
445460
for item in items:
461+
if not utils.lookup(item, 'keyName') == key_name:
462+
continue
446463

464+
for price in item['prices']:
465+
if matches_billing(price, hourly):
466+
return price['id']
467+
468+
raise SoftLayer.SoftLayerError("Could not find valid price for %s" %
469+
key_name)
470+
471+
472+
def _get_default_price_id(items, option, hourly):
473+
"""Returns a 'free' price id given an option."""
474+
475+
for item in items:
447476
if not utils.lookup(item, 'itemCategory', 'categoryCode') == option:
448477
continue
449478

450479
for price in item['prices']:
451-
if all([float(price.get('hourlyRecurringFee', 0)) == 0.0,
452-
float(price.get('recurringFee', 0)) == 0.0,
480+
if any([float(price.get('hourlyRecurringFee', 0)) != 0.0,
481+
float(price.get('recurringFee', 0)) != 0.0,
482+
not matches_billing(price, hourly)
453483
]):
454-
return price['id']
484+
continue
485+
return price['id']
455486

456-
raise SoftLayer.SoftLayerError('Could not find valid price for %s option' %
487+
raise SoftLayer.SoftLayerError("Could not find valid price for %s option" %
457488
option)
458489

459490

491+
def _get_bandwidth_price_id(items, hourly=True, no_public=False):
492+
493+
# Prefer pay-for-use data transfer with hourly
494+
for item in items:
495+
496+
if not utils.lookup(item,
497+
'itemCategory',
498+
'categoryCode') == 'bandwidth':
499+
continue
500+
501+
capacity = float(item.get('capacity', 0))
502+
if any([
503+
# Hourly and private only do pay-as-you-go bandwidth
504+
(hourly or no_public) and capacity != 0.0,
505+
not (hourly or no_public) and capacity == 0.0,
506+
]):
507+
continue
508+
509+
for price in item['prices']:
510+
if not matches_billing(price, hourly):
511+
continue
512+
513+
return price['id']
514+
515+
raise SoftLayer.SoftLayerError(
516+
"Could not find valid price for bandwidth option")
517+
518+
460519
def _get_os_price_id(items, os):
461520
"""Returns the price id matching."""
462521
for item in items:
463522

464523
if not utils.lookup(item, 'itemCategory', 'categoryCode') == 'os':
465524
continue
466525

467-
if not utils.lookup(item, 'keyName') == os:
526+
if not utils.lookup(item,
527+
'softwareDescription',
528+
'referenceCode') == os:
468529
continue
469530

470531
for price in item['prices']:
471532
return price['id']
472533

473534
raise SoftLayer.SoftLayerError('Could not find valid price for os: "%s"' %
474535
os)
536+
537+
538+
def _get_port_speed_price_id(items, port_speed, no_public):
539+
540+
for item in items:
541+
542+
if not utils.lookup(item,
543+
'itemCategory',
544+
'categoryCode') == 'port_speed':
545+
continue
546+
547+
for item in items:
548+
549+
if not utils.lookup(item,
550+
'itemCategory',
551+
'categoryCode') == 'port_speed':
552+
continue
553+
554+
if any([int(utils.lookup(item, 'capacity')) != port_speed,
555+
is_private_port_speed_item(item) != no_public,
556+
]):
557+
continue
558+
559+
for price in item['prices']:
560+
return price['id']
561+
562+
raise SoftLayer.SoftLayerError(
563+
"Could not find valid price for port speed: '%s'" % port_speed)
564+
565+
566+
def matches_billing(price, hourly):
567+
return any([hourly and price.get('hourlyRecurringFee') is not None,
568+
not hourly and price.get('recurringFee') is not None])
569+
570+
571+
def is_private_port_speed_item(item):
572+
for attribute in item['attributes']:
573+
if attribute['attributeTypeKeyName'] == 'IS_PRIVATE_NETWORK_ONLY':
574+
return True
575+
576+
return False

0 commit comments

Comments
 (0)