Skip to content

Commit af96135

Browse files
Merge pull request #848 from kwienken/dedicated-host
Add support for dedicated host instances to virtual server upgrades
2 parents 6ed8afb + 230986d commit af96135

File tree

4 files changed

+212
-20
lines changed

4 files changed

+212
-20
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ build/*
1313
dist/*
1414
*.egg-info
1515
.cache
16-
16+
.idea

SoftLayer/fixtures/SoftLayer_Virtual_Guest.py

+82
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,85 @@
252252
setTags = True
253253
createArchiveTransaction = {}
254254
executeRescueLayer = True
255+
256+
getUpgradeItemPrices = [
257+
{
258+
'id': 1007,
259+
'categories': [{'id': 80,
260+
'name': 'Computing Instance',
261+
'categoryCode': 'guest_core'}],
262+
'item': {
263+
'capacity': '4',
264+
'units': 'PRIVATE_CORE',
265+
'description': 'Computing Instance (Dedicated)',
266+
}
267+
},
268+
{
269+
'id': 1144,
270+
'locationGroupId': None,
271+
'categories': [{'id': 80,
272+
'name': 'Computing Instance',
273+
'categoryCode': 'guest_core'}],
274+
'item': {
275+
'capacity': '4',
276+
'units': 'CORE',
277+
'description': 'Computing Instance',
278+
}
279+
},
280+
{
281+
'id': 332211,
282+
'locationGroupId': 1,
283+
'categories': [{'id': 80,
284+
'name': 'Computing Instance',
285+
'categoryCode': 'guest_core'}],
286+
'item': {
287+
'capacity': '4',
288+
'units': 'CORE',
289+
'description': 'Computing Instance',
290+
}
291+
},
292+
{
293+
'id': 1122,
294+
'categories': [{'id': 26,
295+
'name': 'Uplink Port Speeds',
296+
'categoryCode': 'port_speed'}],
297+
'item': {
298+
'capacity': '1000',
299+
'description': 'Public & Private Networks',
300+
}
301+
},
302+
{
303+
'id': 1144,
304+
'categories': [{'id': 26,
305+
'name': 'Uplink Port Speeds',
306+
'categoryCode': 'port_speed'}],
307+
'item': {
308+
'capacity': '1000',
309+
'description': 'Private Networks',
310+
}
311+
},
312+
{
313+
'id': 1133,
314+
'categories': [{'id': 3,
315+
'name': 'RAM',
316+
'categoryCode': 'ram'}],
317+
'item': {
318+
'capacity': '2',
319+
'description': 'RAM',
320+
}
321+
},
322+
]
323+
324+
DEDICATED_GET_UPGRADE_ITEM_PRICES = [
325+
{
326+
'id': 115566,
327+
'categories': [{'id': 80,
328+
'name': 'Computing Instance',
329+
'categoryCode': 'guest_core'}],
330+
'item': {
331+
'capacity': '4',
332+
'units': 'DEDICATED_CORE',
333+
'description': 'Computing Instance (Dedicated Host)',
334+
}
335+
},
336+
]

SoftLayer/managers/vs.py

+74-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import itertools
1010
import socket
1111
import time
12+
import warnings
1213

1314
from SoftLayer import exceptions
1415
from SoftLayer.managers import ordering
@@ -773,24 +774,24 @@ def upgrade(self, instance_id, cpus=None, memory=None,
773774
:param int instance_id: Instance id of the VS to be upgraded
774775
:param int cpus: The number of virtual CPUs to upgrade to
775776
of a VS instance.
776-
:param bool public: CPU will be in Private/Public Node.
777777
:param int memory: RAM of the VS to be upgraded to.
778778
:param int nic_speed: The port speed to set
779+
:param bool public: CPU will be in Private/Public Node.
779780
780781
:returns: bool
781782
"""
782-
package_items = self._get_package_items()
783+
upgrade_prices = self._get_upgrade_prices(instance_id)
783784
prices = []
784785

785786
for option, value in {'cpus': cpus,
786787
'memory': memory,
787788
'nic_speed': nic_speed}.items():
788789
if not value:
789790
continue
790-
price_id = self._get_price_id_for_upgrade(package_items,
791-
option,
792-
value,
793-
public)
791+
price_id = self._get_price_id_for_upgrade_option(upgrade_prices,
792+
option,
793+
value,
794+
public)
794795
if not price_id:
795796
# Every option provided is expected to have a price
796797
raise exceptions.SoftLayerError(
@@ -815,7 +816,12 @@ def upgrade(self, instance_id, cpus=None, memory=None,
815816
return False
816817

817818
def _get_package_items(self):
818-
"""Following Method gets all the item ids related to VS."""
819+
"""Following Method gets all the item ids related to VS.
820+
821+
Deprecated in favor of _get_upgrade_prices()
822+
"""
823+
warnings.warn("use _get_upgrade_prices() instead",
824+
DeprecationWarning)
819825
mask = [
820826
'description',
821827
'capacity',
@@ -833,15 +839,76 @@ def _get_package_items(self):
833839
package_service = self.client['Product_Package']
834840
return package_service.getItems(id=package['id'], mask=mask)
835841

842+
def _get_upgrade_prices(self, instance_id, include_downgrade_options=True):
843+
"""Following Method gets all the price ids related to upgrading a VS.
844+
845+
:param int instance_id: Instance id of the VS to be upgraded
846+
847+
:returns: list
848+
"""
849+
mask = [
850+
'id',
851+
'locationGroupId',
852+
'categories[name,id,categoryCode]',
853+
'item[description,capacity,units]'
854+
]
855+
mask = "mask[%s]" % ','.join(mask)
856+
return self.guest.getUpgradeItemPrices(include_downgrade_options, id=instance_id, mask=mask)
857+
858+
def _get_price_id_for_upgrade_option(self, upgrade_prices, option, value,
859+
public=True):
860+
"""Find the price id for the option and value to upgrade. This
861+
862+
:param list upgrade_prices: Contains all the prices related to a VS upgrade
863+
:param string option: Describes type of parameter to be upgraded
864+
:param int value: The value of the parameter to be upgraded
865+
:param bool public: CPU will be in Private/Public Node.
866+
"""
867+
option_category = {
868+
'memory': 'ram',
869+
'cpus': 'guest_core',
870+
'nic_speed': 'port_speed'
871+
}
872+
category_code = option_category.get(option)
873+
for price in upgrade_prices:
874+
if price.get('categories') is None or price.get('item') is None:
875+
continue
876+
877+
product = price.get('item')
878+
is_private = (product.get('units') == 'PRIVATE_CORE'
879+
or product.get('units') == 'DEDICATED_CORE')
880+
881+
for category in price.get('categories'):
882+
if not (category.get('categoryCode') == category_code
883+
and str(product.get('capacity')) == str(value)):
884+
continue
885+
886+
if option == 'cpus':
887+
# Public upgrade and public guest_core price
888+
if public and not is_private:
889+
return price.get('id')
890+
# Private upgrade and private guest_core price
891+
elif not public and is_private:
892+
return price.get('id')
893+
elif option == 'nic_speed':
894+
if 'Public' in product.get('description'):
895+
return price.get('id')
896+
else:
897+
return price.get('id')
898+
836899
def _get_price_id_for_upgrade(self, package_items, option, value,
837900
public=True):
838901
"""Find the price id for the option and value to upgrade.
839902
903+
Deprecated in favor of _get_price_id_for_upgrade_option()
904+
840905
:param list package_items: Contains all the items related to an VS
841906
:param string option: Describes type of parameter to be upgraded
842907
:param int value: The value of the parameter to be upgraded
843908
:param bool public: CPU will be in Private/Public Node.
844909
"""
910+
warnings.warn("use _get_price_id_for_upgrade_option() instead",
911+
DeprecationWarning)
845912
option_category = {
846913
'memory': 'ram',
847914
'cpus': 'guest_core',

tests/managers/vs_tests.py

+55-12
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import mock
88

99
import SoftLayer
10-
from SoftLayer import exceptions
1110
from SoftLayer import fixtures
1211
from SoftLayer import testing
1312

@@ -636,13 +635,6 @@ def test_capture_additional_disks(self):
636635
identifier=1)
637636

638637
def test_upgrade(self):
639-
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
640-
mock.return_value = [
641-
{'id': 46, 'name': 'Virtual Servers',
642-
'description': 'Virtual Server Instances',
643-
'type': {'keyName': 'VIRTUAL_SERVER_INSTANCE'}, 'isActive': 1},
644-
]
645-
646638
# test single upgrade
647639
result = self.vs.upgrade(1, cpus=4, public=False)
648640

@@ -678,10 +670,19 @@ def test_upgrade_full(self):
678670
self.assertIn({'id': 1122}, order_container['prices'])
679671
self.assertEqual(order_container['virtualGuests'], [{'id': 1}])
680672

681-
def test_upgrade_skips_location_based_prices(self):
682-
# Test that no prices that have locationGroupId set are used
683-
self.assertRaises(exceptions.SoftLayerError,
684-
self.vs.upgrade, 1, cpus=55, memory=2, public=True)
673+
def test_upgrade_dedicated_host_instance(self):
674+
mock = self.set_mock('SoftLayer_Virtual_Guest', 'getUpgradeItemPrices')
675+
mock.return_value = fixtures.SoftLayer_Virtual_Guest.DEDICATED_GET_UPGRADE_ITEM_PRICES
676+
677+
# test single upgrade
678+
result = self.vs.upgrade(1, cpus=4, public=False)
679+
680+
self.assertEqual(result, True)
681+
self.assert_called_with('SoftLayer_Product_Order', 'placeOrder')
682+
call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0]
683+
order_container = call.args[0]
684+
self.assertEqual(order_container['prices'], [{'id': 115566}])
685+
self.assertEqual(order_container['virtualGuests'], [{'id': 1}])
685686

686687
def test_get_item_id_for_upgrade(self):
687688
item_id = 0
@@ -693,6 +694,48 @@ def test_get_item_id_for_upgrade(self):
693694
break
694695
self.assertEqual(1133, item_id)
695696

697+
def test_get_package_items(self):
698+
self.vs._get_package_items()
699+
self.assert_called_with('SoftLayer_Product_Package', 'getItems')
700+
701+
def test_get_package_items_errors(self):
702+
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
703+
mock.return_value = []
704+
705+
self.assertRaises(ValueError, self.vs._get_package_items)
706+
707+
def test_get_price_id_for_upgrade(self):
708+
package_items = self.vs._get_package_items()
709+
710+
price_id = self.vs._get_price_id_for_upgrade(package_items=package_items,
711+
option='cpus',
712+
value='4')
713+
self.assertEqual(1144, price_id)
714+
715+
def test_get_price_id_for_upgrade_skips_location_price(self):
716+
package_items = self.vs._get_package_items()
717+
718+
price_id = self.vs._get_price_id_for_upgrade(package_items=package_items,
719+
option='cpus',
720+
value='55')
721+
self.assertEqual(None, price_id)
722+
723+
def test_get_price_id_for_upgrade_finds_nic_price(self):
724+
package_items = self.vs._get_package_items()
725+
726+
price_id = self.vs._get_price_id_for_upgrade(package_items=package_items,
727+
option='memory',
728+
value='2')
729+
self.assertEqual(1133, price_id)
730+
731+
def test_get_price_id_for_upgrade_finds_memory_price(self):
732+
package_items = self.vs._get_package_items()
733+
734+
price_id = self.vs._get_price_id_for_upgrade(package_items=package_items,
735+
option='nic_speed',
736+
value='1000')
737+
self.assertEqual(1122, price_id)
738+
696739

697740
class VSWaitReadyGoTests(testing.TestCase):
698741

0 commit comments

Comments
 (0)