diff --git a/SoftLayer/CLI/modules/cci.py b/SoftLayer/CLI/modules/cci.py index f58c0d869..fa3791c79 100755 --- a/SoftLayer/CLI/modules/cci.py +++ b/SoftLayer/CLI/modules/cci.py @@ -21,6 +21,7 @@ reboot Reboots a running CCI reload Reload the OS on a CCI based on its current configuration resume Resumes a paused CCI + upgrade Upgrades parameters of a CCI For several commands, will be asked for. This can be the id, hostname or the ip address for a CCI. @@ -1016,3 +1017,96 @@ def execute(self, args): t.add_row(['transaction_id', capture['id']]) t.add_row(['all_disks', additional_disks]) return t + + +class UpgradeCCI(CLIRunnable): + """ +usage: sl cci upgrade [options] + +Upgrade parameters of an CCI + +Options: + --cpu=CPU Number of CPU cores + --memory=MEMORY Memory in megabytes + --network=MBPS Network port speed in Mbps + --disk=SIZE... Disks. Can be specified multiple times + --san Use SAN storage instead of local disk. Applies to + all disks specified with --disk. + --reboot Soft Reboot of CCI +""" + + action = 'upgrade' + options = ['confirm'] + + def execute(self, args): + cci = CCIManager(self.client) + data = {} + data['cpus'] = args.get('--cpu') + data['memory'] = args.get('--memory') + data['nic_speed'] = args.get('--network') + instance_id = args.get('') + if not instance_id: + raise ArgumentError('CCI ID must be provided') + reboot = False + if args['--reboot']: + reboot = True + data['local_disk'] = True + data['disk'] = "" + # Disks will be a comma-separated list. Let's make it a real list. + if isinstance(args.get('--disk'), str): + args['--disk'] = args.get('--disk').split(',') + data['disk'] = args['--disk'] + if args['--san']: + data['local_disk'] = False + if data['local_disk']: + if len(data['disk']) > 2: + raise ArgumentError( \ + 'Maximum 2 Disks are Allowed for Local') + elif len(data['disk']) > 5: + raise ArgumentError('Maximum 5 Disks are Allowed for SAN') + if data['memory']: + data['memory'] = int(data['memory']) / 1024 + if data['disk']: + raise NotImplementedError('Disk Upgrade yet to implement') + if args['--really'] or confirm( + "This action will incur charges on your account. " + "Continue?"): + item_id = [] + try: + package_items = cci.get_package_items_for_virtual_guest() + if data['cpus']: + item_id.append({'id': self.get_item_id_for_upgrade( + package_items, 'cpus', data['cpus'])}) + if data['memory']: + item_id.append({'id': self.get_item_id_for_upgrade( + package_items, 'memory', data['memory'])}) + if data['nic_speed']: + item_id.append({'id': self.get_item_id_for_upgrade( + package_items, 'nic_speed', + data['nic_speed'])}) + cci.upgrade(instance_id, item_id) + except: + raise CLIAbort('CCI Upgrade Failed') + + def get_item_id_for_upgrade(self, package_items, option, value): + """ + Find the item ids for the parameters you want to upgrade to. + :param list package_items: Contains all the items related to an CCI + :param string option: Describes type of paramter to be upgraded + :param int value: The value of the parameter to be upgraded + """ + id = {'memory': 3, 'cpus': 80, 'nic_speed': 26, + 'First Disk': 81, 'Second Disk': 82, 'Third Disk': 92, + 'Fourth Disk': 93, 'Fifth Disk': 116} + f = 0 + for item in package_items: + if len(filter(lambda x: x.get('id') == id[option], \ + item['categories'])) \ + and item.get('capacity') == str(value): + if option == 'cpus' or option =='nic_speed': + if f == 1: + return item['prices'][0]['id'] + else: + f = f + 1 + else: + return item['prices'][0]['id'] diff --git a/SoftLayer/managers/cci.py b/SoftLayer/managers/cci.py index 2190ae1d4..0f16aad82 100644 --- a/SoftLayer/managers/cci.py +++ b/SoftLayer/managers/cci.py @@ -8,7 +8,7 @@ import socket from time import sleep from itertools import repeat - +import datetime from SoftLayer.utils import NestedDict, query_filter, IdentifierMixin, lookup @@ -484,3 +484,35 @@ def capture(self, instance_id, name, additional_disks=False, notes=None): return self.guest.createArchiveTransaction( name, disks, notes, id=instance_id) + + def upgrade(self, instance_id, item_id, reboot = False): + """ + Upgrades a CCI instane + :param int instance_id: Instane id of te CCI to be upgraded + :param int cpus: The number of virtual CPUs to upgrade to + of a CCI instance. + :param int memory: RAM of the CCI to be upgraded to. + :param bool local_disk: Flag to indicate if this should be a local disk + (default) or a SAN disk. + :param list disks: A list of disk capacities for this server + :param bool reboot: Flag to indicate weather to reboot or + not upon upgrade + :param int nic_speed: The port speed to set + """ + orderClient = self.client['Product_Order'] + orderContainer = {} + orderContainer['complexType'] = \ + 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade' + orderContainer['virtualGuests'] = [{'id': int(instance_id)}] + orderContainer['prices'] = item_id + orderContainer['properties'] = [{'name': 'MAINTENANCE_WINDOW', + 'value': str(datetime.datetime.now())}] + orderClient.verifyOrder(orderContainer) + orderClient.placeOrder(orderContainer) + if reboot: + self.guest.rebootSoft(id=int(instance_id)) + + def get_package_items_for_virtual_guest(self): + mask = "mask[capacity,prices.id,categories[name,id]]" + package = self.client['Product_Package'] + return package.getItems(id=46, mask=mask) diff --git a/SoftLayer/tests/fixtures/Virtual_Guest.py b/SoftLayer/tests/fixtures/Virtual_Guest.py index 8c47f75ea..db9f77fdf 100644 --- a/SoftLayer/tests/fixtures/Virtual_Guest.py +++ b/SoftLayer/tests/fixtures/Virtual_Guest.py @@ -214,5 +214,6 @@ generateOrderTemplate = {} setUserMetadata = ['meta'] reloadOperatingSystem = 'OK' +rebootSoft = True createArchiveTransaction = {} diff --git a/SoftLayer/tests/managers/cci_tests.py b/SoftLayer/tests/managers/cci_tests.py index 2adb666ee..fdd8f5d85 100644 --- a/SoftLayer/tests/managers/cci_tests.py +++ b/SoftLayer/tests/managers/cci_tests.py @@ -532,6 +532,42 @@ def test_captures(self): archive.called_once_with('a', [{"device": 0, "uuid": 1}, {"device": 2, "uuid": 2}], "", id=1) + def test_upgrade(self): + # Testing Upgrade + orderClient = self.client['Product_Order'] + + # test single upgrade + item_ids = [{'id': 1024}] + self.cci.upgrade(1, item_ids) + + # test single upgrade with rebot + item_ids = [{'id': 1024}] + self.cci.upgrade(1, item_ids, reboot = True) + + # Now test a blank upgrade + self.assertTrue(self.cci.upgrade, 1) + + # Finally, test a full Upgrade + item_ids = [{'id': 1}, {'id': 2}, {'id': 3}] + self.cci.upgrade(1, item_ids) + order = self.test_ordercontainer_options() + orderClient.verifyOrder.called_once_with(order) + orderClient.placeOrder.called_once_with(order) + + def test_package_ids_for_virtual_guest(self): + result = self.cci.get_package_items_for_virtual_guest()[0] + self.assertEqual(4444, result['prices'][0]['id']) + + def test_ordercontainer_options(self): + orderContainer = {} + orderContainer['complexType'] = \ + 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade' + orderContainer['virtualGuests'] = [{'id': 1}] + orderContainer['prices'] = [{'id': 1645}] + orderContainer['properties'] = [{'name': 'MAINTENANCE_WINDOW', + 'value': '2014-03-21 03:12:47.942307'}] + return orderContainer + class CCIWaitReadyGoTests(unittest.TestCase):