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
2517class 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 = '''
333329items[
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+
460519def _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